diff options
Diffstat (limited to 'erts')
-rw-r--r-- | erts/emulator/beam/bif.tab | 1 | ||||
-rw-r--r-- | erts/emulator/beam/erl_map.c | 66 | ||||
-rw-r--r-- | erts/emulator/beam/io.c | 10 | ||||
-rw-r--r-- | erts/emulator/test/Makefile | 1 | ||||
-rw-r--r-- | erts/emulator/test/map_SUITE.erl | 66 | ||||
-rw-r--r-- | erts/emulator/test/prim_eval_SUITE.erl | 78 | ||||
-rw-r--r-- | erts/preloaded/ebin/erts_internal.beam | bin | 10876 -> 11072 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/prim_eval.beam | bin | 1276 -> 1428 bytes | |||
-rw-r--r-- | erts/preloaded/src/erts_internal.erl | 12 | ||||
-rw-r--r-- | erts/preloaded/src/prim_eval.S | 41 |
10 files changed, 243 insertions, 32 deletions
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index a6510ace81..ce2cffa498 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -671,3 +671,4 @@ bif math:floor/1 bif math:ceil/1 bif math:fmod/2 bif os:set_signal/2 +bif erts_internal:maps_to_list/2 diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 9062e44b10..a80f5d6a16 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -91,7 +91,7 @@ static BIF_RETTYPE hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB, int swap_ static Export hashmap_merge_trap_export; static BIF_RETTYPE maps_merge_trap_1(BIF_ALIST_1); static Uint hashmap_subtree_size(Eterm node); -static Eterm hashmap_to_list(Process *p, Eterm map); +static Eterm hashmap_to_list(Process *p, Eterm map, Sint n); static Eterm hashmap_keys(Process *p, Eterm map); static Eterm hashmap_values(Process *p, Eterm map); static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node, Eterm *value); @@ -161,13 +161,58 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { BIF_RET(res); } else if (is_hashmap(BIF_ARG_1)) { - return hashmap_to_list(BIF_P, BIF_ARG_1); + return hashmap_to_list(BIF_P, BIF_ARG_1, -1); } BIF_P->fvalue = BIF_ARG_1; BIF_ERROR(BIF_P, BADMAP); } +/* erts_internal:maps_to_list/2 + * + * This function should be removed once iterators are in place. + * Never document it. + * Never encourage its usage. + * + * A negative value in ARG 2 means the entire map. + */ + +BIF_RETTYPE erts_internal_maps_to_list_2(BIF_ALIST_2) { + Sint m; + if (term_to_Sint(BIF_ARG_2, &m)) { + if (is_flatmap(BIF_ARG_1)) { + Uint n; + Eterm* hp; + Eterm *ks,*vs, res, tup; + flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1); + + ks = flatmap_get_keys(mp); + vs = flatmap_get_values(mp); + n = flatmap_get_size(mp); + + if (m >= 0) { + n = m < n ? m : n; + } + + hp = HAlloc(BIF_P, (2 + 3) * n); + res = NIL; + + while(n--) { + tup = TUPLE2(hp, ks[n], vs[n]); hp += 3; + res = CONS(hp, tup, res); hp += 2; + } + + BIF_RET(res); + } else if (is_hashmap(BIF_ARG_1)) { + return hashmap_to_list(BIF_P, BIF_ARG_1, m); + } + BIF_P->fvalue = BIF_ARG_1; + BIF_ERROR(BIF_P, BADMAP); + } + BIF_ERROR(BIF_P, BADARG); +} + + /* maps:find/2 * return value if key *matches* a key in the map */ @@ -1917,15 +1962,22 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADMAP); } -static Eterm hashmap_to_list(Process *p, Eterm node) { +static Eterm hashmap_to_list(Process *p, Eterm node, Sint m) { DECLARE_WSTACK(stack); Eterm *hp, *kv; - Eterm res = NIL; + Eterm tup, res = NIL; + Uint n = hashmap_size(node); - hp = HAlloc(p, hashmap_size(node) * (2 + 3)); + if (m >= 0) { + n = m < n ? m : n; + } + + hp = HAlloc(p, n * (2 + 3)); hashmap_iterator_init(&stack, node, 0); - while ((kv=hashmap_iterator_next(&stack)) != NULL) { - Eterm tup = TUPLE2(hp, CAR(kv), CDR(kv)); + while (n--) { + kv = hashmap_iterator_next(&stack); + ASSERT(kv != NULL); + tup = TUPLE2(hp, CAR(kv), CDR(kv)); hp += 3; res = CONS(hp, tup, res); hp += 2; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index ebff564421..ddff862607 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4887,10 +4887,18 @@ erts_port_control(Process* c_p, copy = 1; else { ProcBin *pb = (ProcBin *) ebinp; + int offset = bufp - pb->val->orig_bytes; - if (pb->flags) + ASSERT(pb->val->orig_bytes <= bufp + && bufp + size <= pb->val->orig_bytes + pb->val->orig_size); + + if (pb->flags) { erts_emasculate_writable_binary(pb); + /* The procbin may have been reallocated, so update bufp */ + bufp = pb->val->orig_bytes + offset; + } + binp = pb->val; ASSERT(bufp <= bufp + size); ASSERT(binp->orig_bytes <= bufp diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 6f9827002a..5478932b13 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -89,6 +89,7 @@ MODULES= \ os_signal_SUITE \ port_SUITE \ port_bif_SUITE \ + prim_eval_SUITE \ process_SUITE \ pseudoknot_SUITE \ receive_SUITE \ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index dfa1629112..02f3c89318 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -52,6 +52,7 @@ t_bif_map_values/1, t_bif_map_to_list/1, t_bif_map_from_list/1, + t_bif_erts_internal_maps_to_list/1, %% erlang t_erlang_hash/1, @@ -118,6 +119,7 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large, t_bif_map_update, t_bif_map_values, t_bif_map_to_list, t_bif_map_from_list, + t_bif_erts_internal_maps_to_list, %% erlang t_erlang_hash, t_map_encode_decode, @@ -2362,23 +2364,55 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. -t_bif_build_and_check(Config) when is_list(Config) -> - ok = check_build_and_remove(750,[ - fun(K) -> [K,K] end, - fun(K) -> [float(K),K] end, - fun(K) -> K end, - fun(K) -> {1,K} end, - fun(K) -> {K} end, - fun(K) -> [K|K] end, - fun(K) -> [K,1,2,3,4] end, - fun(K) -> {K,atom} end, - fun(K) -> float(K) end, - fun(K) -> integer_to_list(K) end, - fun(K) -> list_to_atom(integer_to_list(K)) end, - fun(K) -> [K,{K,[K,{K,[K]}]}] end, - fun(K) -> <<K:32>> end - ]), +t_bif_erts_internal_maps_to_list(Config) when is_list(Config) -> + %% small maps + [] = erts_internal:maps_to_list(#{},-1), + [] = erts_internal:maps_to_list(#{},-2), + [] = erts_internal:maps_to_list(#{},10), + [{a,1},{b,2}] = lists:sort(erts_internal:maps_to_list(#{a=>1,b=>2}, 2)), + [{a,1},{b,2}] = lists:sort(erts_internal:maps_to_list(#{a=>1,b=>2}, -1)), + [{_,_}] = erts_internal:maps_to_list(#{a=>1,b=>2}, 1), + [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},-2)), + [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},3)), + [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},5)), + [{_,_},{_,_}] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},2), + [{_,_}] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},1), + [] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},0), + + %% big maps + M = maps:from_list([{I,ok}||I <- lists:seq(1,500)]), + [] = erts_internal:maps_to_list(M,0), + [{_,_}] = erts_internal:maps_to_list(M,1), + [{_,_},{_,_}] = erts_internal:maps_to_list(M,2), + Ls1 = erts_internal:maps_to_list(M,10), + 10 = length(Ls1), + Ls2 = erts_internal:maps_to_list(M,20), + 20 = length(Ls2), + Ls3 = erts_internal:maps_to_list(M,120), + 120 = length(Ls3), + Ls4 = erts_internal:maps_to_list(M,-1), + 500 = length(Ls4), + %% error cases + {'EXIT', {{badmap,[{a,b},b]},_}} = (catch erts_internal:maps_to_list(id([{a,b},b]),id(1))), + {'EXIT', {badarg,_}} = (catch erts_internal:maps_to_list(id(#{}),id(a))), + {'EXIT', {badarg,_}} = (catch erts_internal:maps_to_list(id(#{1=>2}),id(<<>>))), + ok. + +t_bif_build_and_check(Config) when is_list(Config) -> + ok = check_build_and_remove(750,[fun(K) -> [K,K] end, + fun(K) -> [float(K),K] end, + fun(K) -> K end, + fun(K) -> {1,K} end, + fun(K) -> {K} end, + fun(K) -> [K|K] end, + fun(K) -> [K,1,2,3,4] end, + fun(K) -> {K,atom} end, + fun(K) -> float(K) end, + fun(K) -> integer_to_list(K) end, + fun(K) -> list_to_atom(integer_to_list(K)) end, + fun(K) -> [K,{K,[K,{K,[K]}]}] end, + fun(K) -> <<K:32>> end]), ok. check_build_and_remove(_,[]) -> ok; diff --git a/erts/emulator/test/prim_eval_SUITE.erl b/erts/emulator/test/prim_eval_SUITE.erl new file mode 100644 index 0000000000..3f4965f96d --- /dev/null +++ b/erts/emulator/test/prim_eval_SUITE.erl @@ -0,0 +1,78 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(prim_eval_SUITE). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2, + init_per_group/2, end_per_group/2]). + +-export(['ERL-365'/1]). + +init_per_testcase(_Case, Config) -> + Config. + +end_per_testcase(_Case, _Config) -> + ok. + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +all() -> + ['ERL-365']. + +'ERL-365'(Config) when is_list(Config) -> + %% def_arg_reg[0] is used for storage of timeout instruction + %% when a 'receive after' is executed. When a process was + %% scheduled out inside prim_eval:'receive'/0 due to a function + %% call, def_arg_reg[0] was overwritten due to storage of live + %% registers. + P = spawn_link(fun () -> + prim_eval:'receive'(fun (_M) -> + erlang:bump_reductions((1 bsl 27)-1), + id(true), + nomatch + end, + 200) + end), + receive after 100 -> ok end, + P ! {wont, match}, + receive after 200 -> ok end, + ok. + + + +id(X) -> + X. diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex fe99cc769b..c0ff53f503 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex 66cc919bf1..a011890c1a 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index a27cb2ba38..bcc779e6f6 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -31,7 +31,8 @@ -export([await_port_send_result/3]). -export([cmp_term/2]). --export([map_to_tuple_keys/1, term_type/1, map_hashmap_children/1]). +-export([map_to_tuple_keys/1, term_type/1, map_hashmap_children/1, + maps_to_list/2]). -export([open_port/2, port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). @@ -369,6 +370,15 @@ map_hashmap_children(_M) -> Multi :: boolean(), Res :: term(). +%% return a list of key value pairs, at most of length N +-spec maps_to_list(M,N) -> Pairs when + M :: map(), + N :: integer(), + Pairs :: list(). + +maps_to_list(_M, _N) -> + erlang:nif_error(undefined). + %% erlang:demonitor(Ref, [flush]) traps to %% erts_internal:flush_monitor_messages(Ref, Res) when %% it needs to flush monitor messages. diff --git a/erts/preloaded/src/prim_eval.S b/erts/preloaded/src/prim_eval.S index e7f09a870c..c6623f8e03 100644 --- a/erts/preloaded/src/prim_eval.S +++ b/erts/preloaded/src/prim_eval.S @@ -26,7 +26,7 @@ {attributes, []}. -{labels, 10}. +{labels, 14}. {function, 'receive', 2, 2}. @@ -36,6 +36,9 @@ {allocate,2,2}. {move,{x,1},{y,0}}. {move,{x,0},{y,1}}. + %% Call arg_reg_alloc() in order to ensure + %% that def_arg_reg[0] isn't clobbered + {call,0,{f,7}}. {label,3}. {loop_rec,{f,5},{x,0}}. {move,{y,1},{x,1}}. @@ -53,19 +56,43 @@ {deallocate,2}. return. - -{function, module_info, 0, 8}. +{function, arg_reg_alloc, 0, 7}. {label,6}. - {func_info,{atom,prim_eval},{atom,module_info},0}. + {func_info,{atom,prim_eval},{atom,arg_reg_alloc},0}. {label,7}. + {allocate,0,0}. + {move,{integer,134217727},{x,0}}. + {call_ext,1,{extfunc,erlang,bump_reductions,1}}. + {move,{atom,true},{x,3}}. + {move,{atom,true},{x,4}}. + {move,{atom,true},{x,2}}. + {move,{atom,true},{x,5}}. + {move,{atom,true},{x,1}}. + {move,{atom,true},{x,6}}. + {move,{atom,true},{x,0}}. + {call_last,7,{f,9},0}. + + +{function, arg_reg_alloc, 7, 9}. + {label,8}. + {func_info,{atom,prim_eval},{atom,arg_reg_alloc},7}. + {label,9}. + {move,{atom,ok},{x,0}}. + return. + + +{function, module_info, 0, 11}. + {label,10}. + {func_info,{atom,prim_eval},{atom,module_info},0}. + {label,11}. {move,{atom,prim_eval},{x,0}}. {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. -{function, module_info, 1, 10}. - {label,8}. +{function, module_info, 1, 13}. + {label,12}. {func_info,{atom,prim_eval},{atom,module_info},1}. - {label,9}. + {label,13}. {move,{x,0},{x,1}}. {move,{atom,prim_eval},{x,0}}. {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. |