aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/emulator/beam/bif.tab1
-rw-r--r--erts/emulator/beam/erl_map.c66
-rw-r--r--erts/emulator/beam/io.c10
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/map_SUITE.erl66
-rw-r--r--erts/emulator/test/prim_eval_SUITE.erl78
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin10876 -> 11072 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1276 -> 1428 bytes
-rw-r--r--erts/preloaded/src/erts_internal.erl12
-rw-r--r--erts/preloaded/src/prim_eval.S41
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
index fe99cc769b..c0ff53f503 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 66cc919bf1..a011890c1a 100644
--- a/erts/preloaded/ebin/prim_eval.beam
+++ b/erts/preloaded/ebin/prim_eval.beam
Binary files differ
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}}.