Skip to content

Commit 395f800

Browse files
committed
Support for function comparison
Signed-off-by: Mateusz Front <mateusz.front@swmansion.com>
1 parent 08cfa39 commit 395f800

4 files changed

Lines changed: 150 additions & 1 deletion

File tree

src/libAtomVM/term.c

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,93 @@ TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalC
10241024
break;
10251025
}
10261026
}
1027-
default:
1027+
case TERM_TYPE_INDEX_FUNCTION:
1028+
if (term_is_external_fun(t) && term_is_external_fun(other)) {
1029+
const term *boxed_value = term_to_const_term_ptr(t);
1030+
term module_atom = boxed_value[1];
1031+
term function_atom = boxed_value[2];
1032+
term arity = boxed_value[3];
1033+
1034+
const term *other_boxed_value = term_to_const_term_ptr(other);
1035+
term other_module_atom = other_boxed_value[1];
1036+
term other_function_atom = other_boxed_value[2];
1037+
term other_arity = other_boxed_value[3];
1038+
1039+
if (temp_stack_push(&temp_stack, arity) != TempStackOk
1040+
|| temp_stack_push(&temp_stack, other_arity) != TempStackOk
1041+
|| temp_stack_push(&temp_stack, function_atom) != TempStackOk
1042+
|| temp_stack_push(&temp_stack, other_function_atom) != TempStackOk) {
1043+
return TermCompareMemoryAllocFail;
1044+
}
1045+
1046+
t = module_atom;
1047+
other = other_module_atom;
1048+
} else if (!term_is_external_fun(t) && !term_is_external_fun(other)) {
1049+
const term *boxed_value = term_to_const_term_ptr(t);
1050+
Module *fun_module = (Module *) boxed_value[1];
1051+
term module_name_atom = module_get_name(fun_module);
1052+
atom_index_t module_atom_index = term_to_atom_index(module_name_atom);
1053+
1054+
const term *other_boxed_value = term_to_const_term_ptr(other);
1055+
Module *other_fun_module = (Module *) other_boxed_value[1];
1056+
term other_module_name_atom = module_get_name(other_fun_module);
1057+
atom_index_t other_module_atom_index = term_to_atom_index(other_module_name_atom);
1058+
1059+
int module_cmp_result = atom_table_cmp_using_atom_index(
1060+
global->atom_table, module_atom_index, other_module_atom_index);
1061+
1062+
if (module_cmp_result != 0) {
1063+
result = (module_cmp_result > 0) ? TermGreaterThan : TermLessThan;
1064+
goto unequal;
1065+
}
1066+
1067+
uint32_t fun_index = term_to_int32(boxed_value[2]);
1068+
uint32_t other_fun_index = term_to_int32(other_boxed_value[2]);
1069+
1070+
if (fun_index != other_fun_index) {
1071+
result = (fun_index > other_fun_index) ? TermGreaterThan : TermLessThan;
1072+
goto unequal;
1073+
}
1074+
1075+
uint32_t arity, old_index, old_uniq;
1076+
module_get_fun_arity_old_index_uniq(fun_module, fun_index, &arity, &old_index, &old_uniq);
1077+
uint32_t other_arity, other_old_index, other_old_uniq;
1078+
module_get_fun_arity_old_index_uniq(other_fun_module, other_fun_index, &other_arity, &other_old_index, &other_old_uniq);
1079+
1080+
if (old_uniq != other_old_uniq) {
1081+
result = (old_uniq > other_old_uniq) ? TermGreaterThan : TermLessThan;
1082+
goto unequal;
1083+
}
1084+
1085+
uint32_t num_freeze = module_get_fun_freeze(fun_module, fun_index);
1086+
uint32_t other_num_freeze = module_get_fun_freeze(other_fun_module, other_fun_index);
1087+
1088+
if (num_freeze != other_num_freeze) {
1089+
result = (num_freeze > other_num_freeze) ? TermGreaterThan : TermLessThan;
1090+
goto unequal;
1091+
} else if (num_freeze == 0) {
1092+
CMP_POP_AND_CONTINUE();
1093+
} else {
1094+
uint32_t freeze_base = 3;
1095+
for (uint32_t i = num_freeze; i >= 1; i--) {
1096+
if (temp_stack_push(&temp_stack, boxed_value[i + freeze_base]) != TempStackOk
1097+
|| temp_stack_push(&temp_stack, other_boxed_value[i + freeze_base]) != TempStackOk) {
1098+
return TermCompareMemoryAllocFail;
1099+
}
1100+
}
1101+
1102+
t = boxed_value[freeze_base];
1103+
other = other_boxed_value[freeze_base];
1104+
}
1105+
1106+
} else {
1107+
result = term_is_external_fun(t) ? TermGreaterThan : TermLessThan;
1108+
goto unequal;
1109+
}
1110+
1111+
break;
1112+
case TERM_TYPE_INDEX_NIL: // This would mean we compare two equal terms, we check for that earlier
1113+
case TERM_TYPE_INDEX_INVALID:
10281114
UNREACHABLE();
10291115
}
10301116
} else if ((((type_t == TERM_TYPE_INDEX_FLOAT && type_other == TERM_TYPE_INDEX_INTEGER)

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: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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, sort/1, insert/2, check/2]).
24+
25+
start() ->
26+
F1 = fun(X, Y) -> X + Y end,
27+
F2 = fun(X, Y) -> X - Y end,
28+
Sorted = sort([fun erlang:exit/1, F2, fun application:ensure_started/1, F1, fun erlang:apply/3]),
29+
check(Sorted, [F1, F2, fun application:ensure_started/1, fun erlang:apply/3, fun erlang:exit/1]) +
30+
bool_to_n(Sorted < [fun erlang:whereis/1]) * 2 +
31+
bool_to_n(Sorted > {fun erlang:whereis/1}) * 4.
32+
33+
sort(L) ->
34+
sort(L, []).
35+
36+
sort([], Sorted) ->
37+
Sorted;
38+
sort([H | Unsorted], Sorted) ->
39+
NextSorted = insert(Sorted, H),
40+
sort(Unsorted, NextSorted).
41+
42+
insert(L, I) ->
43+
insert(L, [], I).
44+
45+
insert([], HL, I) ->
46+
HL ++ [I];
47+
insert([H | T], HL, I) when I < H ->
48+
HL ++ [I, H | T];
49+
insert([H | T], HL, I) ->
50+
insert(T, HL ++ [H], I).
51+
52+
check(T, Expected) when T == Expected ->
53+
1;
54+
check(T, Expected) when T /= Expected ->
55+
0.
56+
57+
bool_to_n(true) ->
58+
1;
59+
bool_to_n(false) ->
60+
0.

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_EXPECTED(test_function_ordering, 7),
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)