Skip to content

Commit d75b9f1

Browse files
committed
Merge pull request atomvm#2085 from mat-hek/mf/upstream-compare-funs
Support for comparison of functions These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 08cfa39 + c4978bf commit d75b9f1

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

src/libAtomVM/term.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,92 @@ TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalC
819819
}
820820
}
821821
}
822+
case TERM_TYPE_INDEX_FUNCTION: {
823+
if (term_is_external_fun(t) && term_is_external_fun(other)) {
824+
const term *boxed_value = term_to_const_term_ptr(t);
825+
term module_atom = boxed_value[1];
826+
term function_atom = boxed_value[2];
827+
term arity = boxed_value[3];
828+
829+
const term *other_boxed_value = term_to_const_term_ptr(other);
830+
term other_module_atom = other_boxed_value[1];
831+
term other_function_atom = other_boxed_value[2];
832+
term other_arity = other_boxed_value[3];
833+
834+
if (temp_stack_push(&temp_stack, arity) != TempStackOk
835+
|| temp_stack_push(&temp_stack, other_arity) != TempStackOk
836+
|| temp_stack_push(&temp_stack, function_atom) != TempStackOk
837+
|| temp_stack_push(&temp_stack, other_function_atom) != TempStackOk) {
838+
return TermCompareMemoryAllocFail;
839+
}
840+
841+
t = module_atom;
842+
other = other_module_atom;
843+
} else if (!term_is_external_fun(t) && !term_is_external_fun(other)) {
844+
const term *boxed_value = term_to_const_term_ptr(t);
845+
Module *fun_module = (Module *) boxed_value[1];
846+
term module_name_atom = module_get_name(fun_module);
847+
atom_index_t module_atom_index = term_to_atom_index(module_name_atom);
848+
849+
const term *other_boxed_value = term_to_const_term_ptr(other);
850+
Module *other_fun_module = (Module *) other_boxed_value[1];
851+
term other_module_name_atom = module_get_name(other_fun_module);
852+
atom_index_t other_module_atom_index = term_to_atom_index(other_module_name_atom);
853+
854+
int module_cmp_result = atom_table_cmp_using_atom_index(
855+
global->atom_table, module_atom_index, other_module_atom_index);
856+
857+
if (module_cmp_result != 0) {
858+
result = (module_cmp_result > 0) ? TermGreaterThan : TermLessThan;
859+
goto unequal;
860+
}
861+
862+
uint32_t fun_index = term_to_int32(boxed_value[2]);
863+
uint32_t other_fun_index = term_to_int32(other_boxed_value[2]);
864+
865+
if (fun_index != other_fun_index) {
866+
result = (fun_index > other_fun_index) ? TermGreaterThan : TermLessThan;
867+
goto unequal;
868+
}
869+
870+
uint32_t arity, old_index, old_uniq;
871+
module_get_fun_arity_old_index_uniq(fun_module, fun_index, &arity, &old_index, &old_uniq);
872+
uint32_t other_arity, other_old_index, other_old_uniq;
873+
module_get_fun_arity_old_index_uniq(other_fun_module, other_fun_index, &other_arity, &other_old_index, &other_old_uniq);
874+
875+
if (old_uniq != other_old_uniq) {
876+
result = (old_uniq > other_old_uniq) ? TermGreaterThan : TermLessThan;
877+
goto unequal;
878+
}
879+
880+
uint32_t num_freeze = module_get_fun_freeze(fun_module, fun_index);
881+
uint32_t other_num_freeze = module_get_fun_freeze(other_fun_module, other_fun_index);
882+
883+
if (num_freeze != other_num_freeze) {
884+
result = (num_freeze > other_num_freeze) ? TermGreaterThan : TermLessThan;
885+
goto unequal;
886+
} else if (num_freeze == 0) {
887+
CMP_POP_AND_CONTINUE();
888+
} else {
889+
uint32_t freeze_base = 3;
890+
for (uint32_t i = num_freeze - 1; i >= 1; i--) {
891+
if (temp_stack_push(&temp_stack, boxed_value[i + freeze_base]) != TempStackOk
892+
|| temp_stack_push(&temp_stack, other_boxed_value[i + freeze_base]) != TempStackOk) {
893+
return TermCompareMemoryAllocFail;
894+
}
895+
}
896+
897+
t = boxed_value[freeze_base];
898+
other = other_boxed_value[freeze_base];
899+
}
900+
901+
} else {
902+
result = term_is_external_fun(t) ? TermGreaterThan : TermLessThan;
903+
goto unequal;
904+
}
905+
906+
break;
907+
}
822908
case TERM_TYPE_INDEX_NON_EMPTY_LIST: {
823909
term t_tail = term_get_list_tail(t);
824910
term other_tail = term_get_list_tail(other);

tests/erlang_tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ compile_erlang(test_types_ordering)
421421
compile_erlang(test_bigintegers_ordering)
422422
compile_erlang(test_refs_ordering)
423423
compile_erlang(test_atom_ordering)
424+
compile_erlang(test_function_ordering)
424425
compile_erlang(test_pids_ordering)
425426
compile_erlang(test_list_match)
426427
compile_erlang(test_match)
@@ -949,6 +950,7 @@ set(erlang_test_beams
949950
test_bigintegers_ordering.beam
950951
test_refs_ordering.beam
951952
test_atom_ordering.beam
953+
test_function_ordering.beam
952954
test_pids_ordering.beam
953955
test_list_match.beam
954956
test_match.beam
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
%
2+
% This file is part of AtomVM.
3+
%
4+
% Copyright 2026 AtomVM Contributors
5+
%
6+
% Licensed under the Apache License, Version 2.0 (the "License");
7+
% you may not use this file except in compliance with the License.
8+
% You may obtain a copy of the License at
9+
%
10+
% http://www.apache.org/licenses/LICENSE-2.0
11+
%
12+
% Unless required by applicable law or agreed to in writing, software
13+
% distributed under the License is distributed on an "AS IS" BASIS,
14+
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
% See the License for the specific language governing permissions and
16+
% limitations under the License.
17+
%
18+
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
%
20+
21+
-module(test_function_ordering).
22+
23+
-export([start/0, id/1]).
24+
25+
start() ->
26+
Self = self(),
27+
Self2 = ?MODULE:id(self()),
28+
Five = ?MODULE:id(5),
29+
Six = ?MODULE:id(6),
30+
F1 = fun(X, Y) -> X + Y end,
31+
F2 = fun(X, Y) -> X + Y end,
32+
F3 = fun(X) -> X + Five + Six end,
33+
% Increasing the last byte of the binary representation
34+
% effectively increases the last value of the closure,
35+
% which is the value of variable Six. Thus, F4 only differs
36+
% form F3 by the last element of the closure.
37+
F4 = erlang:binary_to_term(increase_last_byte(erlang:term_to_binary(F3))),
38+
F5 = fun() -> Self ! hello end,
39+
F6 = fun() -> Self ! Self2 end,
40+
F7 = fun() -> Self ! Five end,
41+
F8 = fun(X) -> Self ! {X, Self} end,
42+
F9 = fun(X, Y) -> Self ! {X, Y, Self} end,
43+
44+
true = F3 == erlang:binary_to_term(erlang:term_to_binary(F3)),
45+
46+
Funs = [
47+
F4,
48+
F2,
49+
fun erlang:exit/1,
50+
F9,
51+
F3,
52+
F6,
53+
fun application:ensure_started/1,
54+
F5,
55+
F8,
56+
F1,
57+
fun erlang:apply/3,
58+
F7
59+
],
60+
false = has_duplicates(Funs),
61+
true = ?MODULE:id(Funs) == ?MODULE:id(Funs),
62+
true = ?MODULE:id(Funs) =:= ?MODULE:id(Funs),
63+
Sorted = sort(Funs),
64+
Sorted = [
65+
F1,
66+
F2,
67+
F3,
68+
F4,
69+
F5,
70+
F6,
71+
F7,
72+
F8,
73+
F9,
74+
fun application:ensure_started/1,
75+
fun erlang:apply/3,
76+
fun erlang:exit/1
77+
],
78+
true = Sorted < [fun erlang:whereis/1],
79+
true = Sorted > {fun erlang:whereis/1},
80+
0.
81+
82+
sort(L) ->
83+
sort(L, []).
84+
85+
sort([], Sorted) ->
86+
Sorted;
87+
sort([H | Unsorted], Sorted) ->
88+
NextSorted = insert(Sorted, H),
89+
sort(Unsorted, NextSorted).
90+
91+
insert(L, I) ->
92+
insert(L, [], I).
93+
94+
insert([], HL, I) ->
95+
HL ++ [I];
96+
insert([H | T], HL, I) when I < H ->
97+
HL ++ [I, H | T];
98+
insert([H | T], HL, I) ->
99+
insert(T, HL ++ [H], I).
100+
101+
has_duplicates([]) ->
102+
false;
103+
has_duplicates([H | T]) ->
104+
case lists:member(H, T) of
105+
true -> true;
106+
false -> has_duplicates(T)
107+
end.
108+
109+
increase_last_byte(Binary) ->
110+
Size = byte_size(Binary) - 1,
111+
<<Prefix:Size/binary, V:8>> = Binary,
112+
<<Prefix/binary, (V + 1):8>>.
113+
114+
id(X) -> X.

tests/test.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ struct Test tests[] = {
397397
TEST_CASE_EXPECTED(test_bigintegers_ordering, 7),
398398
TEST_CASE_EXPECTED(test_refs_ordering, 7),
399399
TEST_CASE_EXPECTED(test_atom_ordering, 7),
400+
TEST_CASE(test_function_ordering),
400401
TEST_CASE_EXPECTED(test_pids_ordering, 7),
401402
TEST_CASE_EXPECTED(test_list_match, 31),
402403
TEST_CASE(test_match),

0 commit comments

Comments
 (0)