From 331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Wed, 23 May 2012 16:08:59 +0200
Subject: BEAM loader: Support preservation of extra operand in transforms
It was not possible to preserve extra arguments in transformations.
The following (hypothetical) example will now work:
some_op Lit=c SizeArg Rest=* => move Lit x | some_op x SizeArg Rest
---
erts/emulator/beam/beam_load.c | 18 ++++++++++++++++--
erts/emulator/utils/beam_makeops | 23 ++++++++++++++++-------
2 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 938fd8f2c9..58207ec75b 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -4376,6 +4376,7 @@ transform_engine(LoaderState* st)
Uint* restart; /* Where to restart if current match fails. */
GenOpArg def_vars[TE_MAX_VARS]; /* Default buffer for variables. */
GenOpArg* var = def_vars;
+ int num_vars = 0;
int i; /* General index. */
Uint mask;
GenOp* instr;
@@ -4578,9 +4579,9 @@ transform_engine(LoaderState* st)
{
int n = *pc++;
int formal_arity = gen_opc[instr->op].arity;
- int num_vars = n + (instr->arity - formal_arity);
int j = formal_arity;
+ num_vars = n + (instr->arity - formal_arity);
var = erts_alloc(ERTS_ALC_T_LOADER_TMP,
num_vars * sizeof(GenOpArg));
for (i = 0; i < n; i++) {
@@ -4592,7 +4593,6 @@ transform_engine(LoaderState* st)
}
break;
#endif
-
case TOP_next_arg:
ap++;
break;
@@ -4680,6 +4680,20 @@ transform_engine(LoaderState* st)
instr->a[ap].val = var[i].val;
ap++;
break;
+#if defined(TOP_store_rest_args)
+ case TOP_store_rest_args:
+ {
+ int n = *pc++;
+ int num_extra = num_vars - n;
+
+ ASSERT(n <= num_vars);
+ GENOP_ARITY(instr, instr->arity+num_extra);
+ memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
+ memcpy(instr->a+ap, var+n, num_extra*sizeof(GenOpArg));
+ ap += num_extra;
+ }
+ break;
+#endif
case TOP_try_me_else:
restart = pc + 1;
restart += *pc++;
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 16a949c2a6..0b7c16f606 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -1202,6 +1202,7 @@ sub parse_transformation {
my($from, $to) = split(/\s*=>\s*/);
my(@op);
+ my $rest_var;
# The source instructions.
@@ -1212,7 +1213,7 @@ sub parse_transformation {
$_ = (&compile_transform_function($name, split(/\s*,\s*/, $arglist)));
} else {
(@op) = split;
- $_ = &compile_transform(1, @op);
+ ($rest_var,$_) = compile_transform(1, $rest_var, @op);
}
}
@@ -1230,7 +1231,7 @@ sub parse_transformation {
@to = split(/\s*\|\s*/, $to);
foreach (@to) {
(@op) = split;
- $_ = &compile_transform(0, @op);
+ (undef,$_) = compile_transform(0, $rest_var, @op);
}
}
push(@transformations, [$., $orig, [@from], [reverse @to]]);
@@ -1243,12 +1244,18 @@ sub compile_transform_function {
}
sub compile_transform {
- my($src, $name, @ops) = @_;
+ my($src, $rest_var, $name, @ops) = @_;
my $arity = 0;
-
+
foreach (@ops) {
my(@list) = &tr_parse_op($src, $_);
- $arity++ unless $list[1] eq '*';
+ if ($list[1] eq '*') {
+ $rest_var = $list[0];
+ } elsif (defined $rest_var and $list[0] eq $rest_var) {
+ $list[1] = '*';
+ } else {
+ $arity++;
+ }
$_ = [ @list ];
}
@@ -1260,7 +1267,7 @@ sub compile_transform {
$is_transformed{$name,$arity} = 1;
}
- [$name,$arity,@ops];
+ ($rest_var,[$name,$arity,@ops]);
}
sub tr_parse_op {
@@ -1681,7 +1688,9 @@ sub tr_gen_to {
foreach $op (@ops) {
my($var, $type, $type_val) = @$op;
- if ($var ne '') {
+ if ($type eq '*') {
+ push(@code, make_op($var, 'store_rest_args', $var{$var}));
+ } elsif ($var ne '') {
&error($where, "variable '$var' unbound")
unless defined $var{$var};
push(@code, &make_op($var, 'store_var_next_arg', $var{$var}));
--
cgit v1.2.3
From 723fa9efadf7f841e7b835393ce801aa0a03e1cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 15 Apr 2013 18:28:15 +0200
Subject: Teach erl_scan to recognize '=>'
---
lib/stdlib/src/erl_scan.erl | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 4ba6dd01fa..52376a8f3a 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -569,7 +569,7 @@ scan1("++"++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "++", '++', 2);
scan1("+"=Cs, _St, Line, Col, Toks) ->
{more,{Cs,Col,Toks,Line,[],fun scan/6}};
-%% =:= =/= =< ==
+%% =:= =/= =< == =>
scan1("=:="++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "=:=", '=:=', 3);
scan1("=:"=Cs, _St, Line, Col, Toks) ->
@@ -580,6 +580,8 @@ scan1("=/"=Cs, _St, Line, Col, Toks) ->
{more,{Cs,Col,Toks,Line,[],fun scan/6}};
scan1("=<"++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "=<", '=<', 2);
+scan1("=>"++Cs, St, Line, Col, Toks) ->
+ tok2(Cs, St, Line, Col, Toks, "=>", '=>', 2);
scan1("=="++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "==", '==', 2);
scan1("="=Cs, _St, Line, Col, Toks) ->
--
cgit v1.2.3
From 703a9aa47168f7f8c11bf835502281bd59ba77f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 15 Apr 2013 18:09:50 +0200
Subject: Extend erl_parse with the new map syntax
Example how to match or construct:
#{ K1 => V1, K2 => V2 }
How to update:
M#{ K => V }
---
lib/stdlib/src/erl_parse.yrl | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 59a05a48ee..5a846266c5 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -34,6 +34,7 @@ binary_comprehension
tuple
%struct
record_expr record_tuple record_field record_fields
+map_expr map_tuple map_field map_fields map_key
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
fun_expr fun_clause fun_clauses atom_or_var integer_or_var
try_expr try_catch try_clause try_clauses
@@ -59,7 +60,7 @@ char integer float atom string var
'*' '/' 'div' 'rem' 'band' 'and'
'+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor'
'++' '--'
-'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<='
+'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '=>'
'<<' '>>'
'!' '=' '::' '..' '...'
'spec' 'callback' % helper
@@ -247,6 +248,7 @@ expr_500 -> expr_600 : '$1'.
expr_600 -> prefix_op expr_700 :
?mkop1('$1', '$2').
+expr_600 -> map_expr : '$1'.
expr_600 -> expr_700 : '$1'.
expr_700 -> function_call : '$1'.
@@ -327,6 +329,26 @@ tuple -> '{' exprs '}' : {tuple,?line('$1'),'$2'}.
%%struct -> atom tuple :
%% {struct,?line('$1'),element(3, '$1'),element(3, '$2')}.
+map_expr -> '#' map_tuple :
+ {map, ?line('$1'),'$2'}.
+map_expr -> expr_max '#' map_tuple :
+ {map, ?line('$2'),'$1','$3'}.
+map_expr -> map_expr '#' map_tuple :
+ {map, ?line('$2'),'$1','$3'}.
+map_expr -> map_expr '.' atom :
+ {map_field, ?line('$2'),'$1','$3'}.
+
+map_tuple -> '{' '}' : [].
+map_tuple -> '{' map_fields '}' : '$2'.
+
+map_fields -> map_field : ['$1'].
+map_fields -> map_field ',' map_fields : ['$1' | '$3'].
+
+map_field -> map_key '=>' expr :
+ {map_field,?line('$1'),'$1','$3'}.
+
+map_key -> expr : '$1'.
+
%% N.B. This is called from expr_700.
%% N.B. Field names are returned as the complete object, even if they are
@@ -1060,3 +1082,5 @@ get_attribute(L, Name) ->
get_attributes(L) ->
erl_scan:attributes_info(L).
+
+%% vim: ft=erlang
--
cgit v1.2.3
From 860171895f5858314abd85f8461568f34e76c427 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Wed, 16 May 2012 10:47:58 +0200
Subject: Update erl_lint, erl_expand_records, sys_pre_expand for maps
---
lib/compiler/src/sys_pre_expand.erl | 16 ++++++++++++++++
lib/stdlib/src/erl_expand_records.erl | 14 ++++++++++++++
lib/stdlib/src/erl_lint.erl | 17 +++++++++++++++++
3 files changed, 47 insertions(+)
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 48d9c16718..969e95e8dc 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -228,6 +228,12 @@ pattern({cons,Line,H,T}, St0) ->
pattern({tuple,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{tuple,Line,TPs},St1};
+pattern({map,Line,Ps}, St0) ->
+ {TPs,St1} = pattern_list(Ps, St0),
+ {{map,Line,TPs},St1};
+pattern({map_field,Line,Key,V0}, St0) ->
+ {V,St1} = pattern(V0, St0),
+ {{map_field,Line,Key,V},St1};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1};
@@ -321,6 +327,16 @@ expr({tuple,Line,Es0}, St0) ->
%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
%% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1};
+expr({map,Line,Es0}, St0) ->
+ {Es1,St1} = expr_list(Es0, St0),
+ {{map,Line,Es1},St1};
+expr({map,Line,Var,Es0}, St0) ->
+ {Es1,St1} = expr_list(Es0, St0),
+ {{map,Line,Var,Es1},St1};
+expr({map_field,Line,K0,V0}, St0) ->
+ {K,St1} = expr(K0, St0),
+ {V,St2} = expr(V0, St1),
+ {{map_field,Line,K,V},St2};
expr({bin,Line,Es0}, St0) ->
{Es1,St1} = expr_bin(Es0, St0),
{{bin,Line,Es1},St1};
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 776b433613..7fc4cc7887 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -132,6 +132,12 @@ pattern({cons,Line,H,T}, St0) ->
pattern({tuple,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{tuple,Line,TPs},St1};
+pattern({map,Line,Ps}, St0) ->
+ {TPs,St1} = pattern_list(Ps, St0),
+ {{map,Line,TPs},St1};
+pattern({map_field,Line,Key,V0}, St0) ->
+ {V,St1} = pattern(V0, St0),
+ {{map_field,Line,Key,V},St1};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{struct,Line,Tag,TPs},TPsvs,St1};
@@ -301,6 +307,14 @@ expr({bc,Line,E0,Qs0}, St0) ->
expr({tuple,Line,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{tuple,Line,Es1},St1};
+expr({map,Line,Es0}, St0) ->
+ {Es1,St1} = expr_list(Es0, St0),
+ {{map,Line,Es1},St1};
+expr({map,Line,Var,Es0}, St0) ->
+ {Es1,St1} = expr_list(Es0, St0),
+ {{map,Line,Var,Es1},St1};
+expr({map_field,_Line,_,_}=M, St0) ->
+ {M,St0};
%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
%% {{struct,Line,Tag,Es1},Esvs,Esus,St1};
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index f0d50df4c7..b74a3ed924 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -1366,6 +1366,11 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) ->
{vtmerge_pat(Hvt, Tvt),vtmerge_pat(Bvt1,Bvt2),St2};
pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
+pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
+ pattern_list(Ps, Vt, Old, Bvt, St);
+pattern({map_field,_Line,_,P}, Vt, Old, Bvt0, St0) ->
+ {Pvt,Bvt,St1} = pattern(P, Vt, Old, Bvt0, St0),
+ {Pvt,Bvt,St1};
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
@@ -1753,6 +1758,12 @@ gexpr({cons,_Line,H,T}, Vt, St) ->
gexpr_list([H,T], Vt, St);
gexpr({tuple,_Line,Es}, Vt, St) ->
gexpr_list(Es, Vt, St);
+gexpr({map,_Line,Es}, Vt, St) ->
+ gexpr_list(Es, Vt, St);
+gexpr({map,_Line,Src,Es}, Vt, St) ->
+ gexpr_list([Src|Es], Vt, St);
+gexpr({map_field,_Line,K,V}, Vt, St) ->
+ gexpr_list([K,V], Vt, St);
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end );
@@ -1970,6 +1981,12 @@ expr({bc,_Line,E,Qs}, Vt, St) ->
handle_comprehension(E, Qs, Vt, St);
expr({tuple,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
+expr({map,_Line,Es}, Vt, St) ->
+ expr_list(Es, Vt, St);
+expr({map,_Line,Src,Es}, Vt, St) ->
+ expr_list([Src|Es], Vt, St);
+expr({map_field,_Line,K,V}, Vt, St) ->
+ expr_list([K,V], Vt, St);
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
--
cgit v1.2.3
From d3ece60d52880fb273da3c4aa07655a0eeddeafb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Wed, 16 May 2012 10:49:36 +0200
Subject: Introduce a representation for maps in Core Erlang
---
lib/compiler/src/core_parse.hrl | 6 ++++++
lib/compiler/src/core_parse.yrl | 30 +++++++++++++++++++++++++++++-
lib/compiler/src/core_pp.erl | 16 ++++++++++++++++
3 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index 0b8f4d8895..c134747991 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -96,3 +96,9 @@
-record(c_values, {anno=[], es}). % es :: [Tree]
-record(c_var, {anno=[], name :: cerl:var_name()}).
+
+-record(c_map_pair, {anno=[], key, val}).
+
+-record(c_map, {anno=[],
+ var=#c_literal{val=[]} :: #c_var{} | #c_literal{},
+ es :: [#c_map_pair{}]}).
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index 4e98a8c2da..b072950eeb 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -21,6 +21,8 @@
%% Have explicit productions for annotated phrases named anno_XXX.
%% This just does an XXX and adds the annotation.
+Expect 1.
+
Nonterminals
module_definition module_export module_attribute module_defs
@@ -44,6 +46,9 @@ receive_expr timeout try_expr
sequence catch_expr
variable clause clause_pattern
+map_expr map_pairs map_pair
+map_pattern map_pair_patterns map_pair_pattern
+
annotation anno_fun anno_expression anno_expressions
anno_variable anno_variables anno_pattern anno_patterns
anno_function_name
@@ -53,7 +58,7 @@ Terminals
%% Separators
-'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#'
+'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' '~'
%% Keywords (atoms are assumed to always be single-quoted).
@@ -166,6 +171,7 @@ anno_patterns -> anno_pattern : ['$1'].
other_pattern -> atomic_pattern : '$1'.
other_pattern -> tuple_pattern : '$1'.
+other_pattern -> map_pattern : '$1'.
other_pattern -> cons_pattern : '$1'.
other_pattern -> binary_pattern : '$1'.
other_pattern -> anno_variable '=' anno_pattern :
@@ -176,6 +182,16 @@ atomic_pattern -> atomic_literal : '$1'.
tuple_pattern -> '{' '}' : c_tuple([]).
tuple_pattern -> '{' anno_patterns '}' : c_tuple('$2').
+map_pattern -> '~' '{' '}' '~' : #c_map{es=[]}.
+map_pattern -> '~' '{' map_pair_patterns '}' '~' :
+ #c_map{es=lists:sort('$3')}.
+
+map_pair_patterns -> map_pair_pattern : ['$1'].
+map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
+
+map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' :
+ #c_map_pair{key='$3',val='$5'}.
+
cons_pattern -> '[' anno_pattern tail_pattern :
#c_cons{hd='$2',tl='$3'}.
@@ -240,6 +256,7 @@ single_expression -> primop_expr : '$1'.
single_expression -> try_expr : '$1'.
single_expression -> sequence : '$1'.
single_expression -> catch_expr : '$1'.
+single_expression -> map_expr : '$1'.
literal -> atomic_literal : '$1'.
literal -> tuple_literal : '$1'.
@@ -267,6 +284,17 @@ tail_literal -> ',' literal tail_literal : #c_cons{hd='$2',tl='$3'}.
tuple -> '{' '}' : c_tuple([]).
tuple -> '{' anno_expressions '}' : c_tuple('$2').
+map_expr -> '~' '{' '}' '~' : #c_map{es=[]}.
+map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}.
+map_expr -> variable '~' '{' '}' '~' : #c_map{var='$',es=[]}.
+map_expr -> variable '~' '{' map_pairs '}' '~' : #c_map{var='$1',es='$4'}.
+
+map_pairs -> map_pair : ['$1'].
+map_pairs -> map_pair ',' map_pairs : ['$1' | '$3'].
+
+map_pair -> '~' '<' anno_expression ',' anno_expression'>' :
+ #c_map_pair{key='$3',val='$5'}.
+
cons -> '[' anno_expression tail : c_cons('$2', '$3').
tail -> ']' : #c_literal{val=[]}.
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index 1f91a52be3..c8c59b9246 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -161,6 +161,22 @@ format_1(#c_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
$}
];
+format_1(#c_map{var=#c_var{}=Var,es=Es}, Ctxt) ->
+ [format_1(Var, Ctxt),
+ "~{",
+ format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
+ "}~"
+ ];
+format_1(#c_map{es=Es}, Ctxt) ->
+ ["~{",
+ format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
+ "}~"
+ ];
+format_1(#c_map_pair{key=K,val=V}, Ctxt) ->
+ ["~<",
+ format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2),
+ ">"
+ ];
format_1(#c_cons{hd=H,tl=T}, Ctxt) ->
Txt = ["["|format(H, add_indent(Ctxt, 1))],
[Txt|format_list_tail(T, add_indent(Ctxt, width(Txt, Ctxt)))];
--
cgit v1.2.3
From 64ee859fc4c17259ab95192abf7493fed8f2b0ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Wed, 16 May 2012 10:50:30 +0200
Subject: Implement support for maps in the compiler
To make it possible to build the entire OTP system, also define
dummys for the instructions in ops.tab.
---
erts/emulator/beam/ops.tab | 9 +++++
lib/compiler/src/beam_block.erl | 5 +++
lib/compiler/src/beam_clean.erl | 29 ++++++++++++++++
lib/compiler/src/beam_flatten.erl | 4 +++
lib/compiler/src/beam_jump.erl | 4 +++
lib/compiler/src/beam_split.erl | 6 ++++
lib/compiler/src/beam_validator.erl | 15 +++++++++
lib/compiler/src/core_lib.erl | 4 +++
lib/compiler/src/genop.tab | 7 ++++
lib/compiler/src/sys_core_dsetel.erl | 12 +++++++
lib/compiler/src/sys_core_fold.erl | 32 ++++++++++++++++++
lib/compiler/src/v3_codegen.erl | 64 +++++++++++++++++++++++++++++++++++-
lib/compiler/src/v3_core.erl | 32 ++++++++++++++++++
lib/compiler/src/v3_kernel.erl | 60 ++++++++++++++++++++++++++++++---
lib/compiler/src/v3_kernel.hrl | 2 ++
lib/compiler/src/v3_kernel_pp.erl | 13 ++++++++
lib/compiler/src/v3_life.erl | 14 ++++++--
17 files changed, 305 insertions(+), 7 deletions(-)
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index c29f3f9b1b..b52ecfaefc 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1465,6 +1465,15 @@ fclearerror
apply I
apply_last I P
+#
+# Map instructions in R16.
+#
+
+put_map Fail Src Dst Live Size Rest=* => jump Fail
+is_map Fail Src => jump Fail
+has_map_field Fail Src Key => jump Fail
+get_map_element Fail Src Key Dst => jump Fail
+
#
# Optimize addition and subtraction of small literals using
# the i_increment/4 instruction (in bodies, not in guards).
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 402fbe2e2e..f9e90b81d2 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -152,6 +152,10 @@ collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}};
collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}};
collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
collect(remove_message) -> {set,[],[],remove_message};
+collect({put_map,F,S,D,R,{list,Puts}}) ->
+ {set,[D],[S|Puts],{alloc,R,{put_map,F}}};
+collect({get_map_element,F,S,K,D}) ->
+ {set,[D],[S],{get_map_element,K,F}};
collect({'catch',R,L}) -> {set,[R],[],{'catch',L}};
collect(fclearerror) -> {set,[],[],fclearerror};
collect({fcheckerror,{f,0}}) -> {set,[],[],fcheckerror};
@@ -383,6 +387,7 @@ gen_init(Fs, Regs, Y, Acc) ->
init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg;
init_yreg([{set,_,_,{alloc,_,{gc_bif,_,_}}}|_], Reg) -> Reg;
+init_yreg([{set,_,_,{alloc,_,{put_map,_}}}|_], Reg) -> Reg;
init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg));
init_yreg(_Is, Reg) -> Reg.
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index 9d89e21a4e..6f802d0436 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -234,6 +234,35 @@ replace([{bs_init,{f,Lbl},Info,Live,Ss,Dst}|Is], Acc, D) when Lbl =/= 0 ->
replace(Is, [{bs_init,{f,label(Lbl, D)},Info,Live,Ss,Dst}|Acc], D);
replace([{bs_put,{f,Lbl},Info,Ss}|Is], Acc, D) when Lbl =/= 0 ->
replace(Is, [{bs_put,{f,label(Lbl, D)},Info,Ss}|Acc], D);
+replace([{bs_init2,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_init2,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D);
+replace([{bs_init_bits,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_init_bits,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D);
+replace([{bs_put_integer,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_integer,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_put_utf8=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D);
+replace([{bs_put_utf16=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D);
+replace([{bs_put_utf32=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D);
+replace([{bs_put_binary,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_binary,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_put_float,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_float,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_add,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_add,{f,label(Lbl, D)},Src,Dst}|Acc], D);
+replace([{bs_append,{f,Lbl},_,_,_,_,_,_,_}=I0|Is], Acc, D) when Lbl =/= 0 ->
+ I = setelement(2, I0, {f,label(Lbl, D)}),
+ replace(Is, [I|Acc], D);
+replace([{bs_utf8_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D);
+replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D);
+replace([{put_map=I,{f,Lbl},Src,Dst,Live,List}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Src,Dst,Live,List}|Acc], D);
+replace([{get_map_element=I,{f,Lbl},Src,Key,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Src,Key,Dst}|Acc], D);
replace([I|Is], Acc, D) ->
replace(Is, [I|Acc], D);
replace([], Acc, _) -> Acc.
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 5603a677e8..ad9591ff05 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -61,6 +61,10 @@ norm({set,[],[S],put}) -> {put,S};
norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
+norm({set,[D],[S|Puts],{alloc,R,{put_map,F}}}) ->
+ {put_map,F,S,D,R,{list,Puts}};
+norm({set,[D],[S],{get_map_element,K,F}}) ->
+ {get_map_element,F,S,K,D};
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],fclearerror}) -> fclearerror;
norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}.
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index d57fb80ac2..bbef75c219 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -527,6 +527,10 @@ ulbl({bs_init,Lbl,_,_,_,_}, Used) ->
mark_used(Lbl, Used);
ulbl({bs_put,Lbl,_,_}, Used) ->
mark_used(Lbl, Used);
+ulbl({put_map,Lbl,_Src,_Dst,_Live,_List}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({get_map_element,Lbl,_Src,_Key,_Dst}, Used) ->
+ mark_used(Lbl, Used);
ulbl(_, Used) -> Used.
mark_used({f,0}, Used) -> Used;
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index cacaaebffe..bbd4695289 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -49,6 +49,12 @@ split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 ->
split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc)
when Lbl =/= 0 ->
split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]);
+split_block([{set,[D],[S|Puts],{alloc,R,{put_map,{f,Lbl}=Fail}}}|Is], Bl, Acc)
+ when Lbl =/= 0 ->
+ split_block(Is, [], [{put_map,Fail,S,D,R,{list,Puts}}|make_block(Bl, Acc)]);
+split_block([{set,[D],[S],{get_map_element,K,{f,Lbl}=Fail}}|Is], Bl, Acc)
+ when Lbl =/= 0 ->
+ split_block(Is, [], [{get_map_element,Fail,S,K,D}|make_block(Bl, Acc)]);
split_block([{set,[R],[],{'catch',L}}|Is], Bl, Acc) ->
split_block(Is, [], [{'catch',R,L}|make_block(Bl, Acc)]);
split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 48f5135aca..37b08f43d3 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -865,6 +865,21 @@ valfun_4({bs_final,{f,Fail},Dst}, Vst0) ->
valfun_4({bs_final2,Src,Dst}, Vst0) ->
assert_term(Src, Vst0),
set_type_reg(binary, Dst, Vst0);
+%% Map instructions.
+valfun_4({put_map,{f,Fail},Src,Dst,Live,{list,List}}, Vst0) ->
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
+ [assert_term(Term, Vst0) || Term <- List],
+ assert_term(Src, Vst0),
+ Vst1 = heap_alloc(0, Vst0),
+ Vst2 = branch_state(Fail, Vst1),
+ Vst = prune_x_regs(Live, Vst2),
+ set_type_reg(term, Dst, Vst);
+valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) ->
+ assert_term(Src, Vst0),
+ assert_term(Key, Vst0),
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg(term, Dst, Vst);
valfun_4(_, _) ->
error(unknown_instruction).
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index 824be9ff7f..f506901099 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -105,6 +105,10 @@ vu_expr(V, #c_cons{hd=H,tl=T}) ->
vu_expr(V, H) orelse vu_expr(V, T);
vu_expr(V, #c_tuple{es=Es}) ->
vu_expr_list(V, Es);
+vu_expr(V, #c_map{es=Es}) ->
+ vu_expr_list(V, Es);
+vu_expr(V, #c_map_pair{key=Key,val=Val}) ->
+ vu_expr_list(V, [Key,Val]);
vu_expr(V, #c_binary{segments=Ss}) ->
vu_seg_list(V, Ss);
vu_expr(V, #c_fun{vars=Vs,body=B}) ->
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index ebc9b1c85b..861acd3932 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -528,3 +528,10 @@ BEAM_FORMAT_NUMBER=0
# R15A
153: line/1
+
+# R16
+
+154: put_map/5
+155: is_map/2
+156: has_map_field/3
+157: get_map_element/4
diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl
index f6696992b9..c7c8343c35 100644
--- a/lib/compiler/src/sys_core_dsetel.erl
+++ b/lib/compiler/src/sys_core_dsetel.erl
@@ -102,6 +102,13 @@ visit(Env, #c_literal{}=R) ->
visit(Env0, #c_tuple{es=Es0}=R) ->
{Es1,Env1} = visit_list(Env0, Es0),
{R#c_tuple{es=Es1}, Env1};
+visit(Env0, #c_map{es=Es0}=R) ->
+ {Es1,Env1} = visit_list(Env0, Es0),
+ {R#c_map{es=Es1}, Env1};
+visit(Env0, #c_map_pair{key=K0,val=V0}=R) ->
+ {K,Env1} = visit(Env0, K0),
+ {V,Env2} = visit(Env1, V0),
+ {R#c_map_pair{key=K,val=V}, Env2};
visit(Env0, #c_cons{hd=H0,tl=T0}=R) ->
{H1,Env1} = visit(Env0, H0),
{T1,Env2} = visit(Env1, T0),
@@ -212,6 +219,11 @@ visit_pat(Env0, #c_var{name=V}, Vs) ->
{[V|Vs], dict:store(V, 0, Env0)};
visit_pat(Env0, #c_tuple{es=Es}, Vs) ->
visit_pats(Es, Env0, Vs);
+visit_pat(Env0, #c_map{es=Es}, Vs) ->
+ visit_pats(Es, Env0, Vs);
+visit_pat(Env0, #c_map_pair{key=V,val=K}, Vs0) ->
+ {Vs1, Env1} = visit_pat(Env0, V, Vs0),
+ visit_pat(Env1, K, Vs1);
visit_pat(Env0, #c_cons{hd=H,tl=T}, Vs0) ->
{Vs1, Env1} = visit_pat(Env0, H, Vs0),
visit_pat(Env1, T, Vs1);
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index a388960312..672a8b94c0 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -246,6 +246,16 @@ expr(#c_tuple{anno=Anno,es=Es0}=Tuple, Ctxt, Sub) ->
value ->
ann_c_tuple(Anno, Es)
end;
+expr(#c_map{var=V0,es=Es0}=Map, Ctxt, Sub) ->
+ Es = pair_list(Es0, Ctxt, Sub),
+ case Ctxt of
+ effect ->
+ add_warning(Map, useless_building),
+ expr(make_effect_seq(Es, Sub), Ctxt, Sub);
+ value ->
+ V = expr(V0, Ctxt, Sub),
+ Map#c_map{var=V,es=Es}
+ end;
expr(#c_binary{segments=Ss}=Bin0, Ctxt, Sub) ->
%% Warn for useless building, but always build the binary
%% anyway to preserve a possible exception.
@@ -408,6 +418,16 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0)
expr_list(Es, Ctxt, Sub) ->
[expr(E, Ctxt, Sub) || E <- Es].
+pair_list(Es, Ctxt, Sub) ->
+ [pair(E, Ctxt, Sub) || E <- Es].
+
+pair(#c_map_pair{key=K,val=V}, effect, Sub) ->
+ make_effect_seq([K,V], Sub);
+pair(#c_map_pair{key=K0,val=V0}=Pair, value=Ctxt, Sub) ->
+ K = expr(K0, Ctxt, Sub),
+ V = expr(V0, Ctxt, Sub),
+ Pair#c_map_pair{key=K,val=V}.
+
bitstr_list(Es, Sub) ->
[bitstr(E, Sub) || E <- Es].
@@ -1500,6 +1520,9 @@ pattern(#c_cons{anno=Anno,hd=H0,tl=T0}, Isub, Osub0) ->
pattern(#c_tuple{anno=Anno,es=Es0}, Isub, Osub0) ->
{Es1,Osub1} = pattern_list(Es0, Isub, Osub0),
{ann_c_tuple(Anno, Es1),Osub1};
+pattern(#c_map{anno=Anno,es=Es0}=Map, Isub, Osub0) ->
+ {Es1,Osub1} = map_pair_pattern_list(Es0, Isub, Osub0),
+ {Map#c_map{anno=Anno,es=Es1},Osub1};
pattern(#c_binary{segments=V0}=Pat, Isub, Osub0) ->
{V1,Osub1} = bin_pattern_list(V0, Isub, Osub0),
{Pat#c_binary{segments=V1},Osub1};
@@ -1509,6 +1532,15 @@ pattern(#c_alias{var=V0,pat=P0}=Pat, Isub, Osub0) ->
Osub = update_types(V1, [P1], Osub2),
{Pat#c_alias{var=V1,pat=P1},Osub}.
+map_pair_pattern_list(Ps0, Isub, Osub0) ->
+ {Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0),
+ {Ps,Osub}.
+
+map_pair_pattern(#c_map_pair{key=K0,val=V0}=Pair, {Isub,Osub0}) ->
+ {K,Osub1} = pattern(K0, Isub, Osub0),
+ {V,Osub} = pattern(V0, Isub, Osub1),
+ {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.
+
bin_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0),
{Ps,Osub}.
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index f534500671..eff43b584a 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -210,6 +210,8 @@ need_heap_0([], H, Acc) ->
need_heap_1(#l{ke={set,_,{binary,_}},i=I}, H) ->
{need_heap_need(I, H),0};
+need_heap_1(#l{ke={set,_,{map,_,_}},i=I}, H) ->
+ {need_heap_need(I, H),0};
need_heap_1(#l{ke={set,_,Val}}, H) ->
%% Just pass through adding to needed heap.
{[],H + case Val of
@@ -623,6 +625,8 @@ select_cg(#l{ke={type_clause,bin_int,S}}, {var,V}, Tf, _Vf, Bef, St) ->
select_bin_segs(S, V, Tf, Bef, St);
select_cg(#l{ke={type_clause,bin_end,[S]}}, {var,V}, Tf, _Vf, Bef, St) ->
select_bin_end(S, V, Tf, Bef, St);
+select_cg(#l{ke={type_clause,map,S}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_map(S, V, Tf, Vf, Bef, St);
select_cg(#l{ke={type_clause,Type,Scs}}, {var,V}, Tf, Vf, Bef, St0) ->
{Vis,{Aft,St1}} =
mapfoldl(fun (S, {Int,Sta}) ->
@@ -637,6 +641,10 @@ select_val_cg(tuple, R, [Arity,{f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
[{test,is_tuple,{f,Tf},[R]},{test,test_arity,{f,Vf},[R,Arity]}|Sis];
select_val_cg(tuple, R, Vls, Tf, Vf, Sis) ->
[{test,is_tuple,{f,Tf},[R]},{select_tuple_arity,R,{f,Vf},{list,Vls}}|Sis];
+select_val_cg(map, R, [_Val,{f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) ->
+ [{test,is_map,{f,Fail},[R]}|Sis];
+select_val_cg(map, R, [_Val,{f,Lbl}|_], Tf, _Vf, [{label,Lbl}|Sis]) ->
+ [{test,is_map,{f,Tf},[R]}|Sis];
select_val_cg(Type, R, [Val, {f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) ->
[{test,is_eq_exact,{f,Fail},[R,{Type,Val}]}|Sis];
select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
@@ -915,6 +923,36 @@ select_extract_tuple(Src, Vs, I, Vdb, Bef, St) ->
{Es,{Aft,_}} = flatmapfoldl(F, {Bef,0}, Vs),
{Es,Aft,St}.
+select_map(Scs, V, Tf, Vf, Bef, St0) ->
+ Reg = fetch_var(V, Bef),
+ {Is,Aft,St1} =
+ match_fmf(fun(#l{ke={val_clause,{map,Es},B},i=I,vdb=Vdb}, Fail, St1) ->
+ select_map_val(V, Es, B, Fail, I, Vdb, Bef, St1)
+ end, Vf, St0, Scs),
+ {[{test,is_map,{f,Tf},[Reg]}|Is],Aft,St1}.
+
+select_map_val(V, Es, B, Fail, I, Vdb, Bef, St0) ->
+ {Eis,Int,St1} = select_extract_map(V, Es, Fail, I, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
+ {Eis++Bis,Aft,St2}.
+
+select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) ->
+ F = fun ({map_pair,Key,{var,V}}, Int0) ->
+ Rsrc = fetch_var(Src, Int0),
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L =< I ->
+ {[{test,has_map_field,{f,Fail},[Rsrc,Key]}],Int0};
+ _Other ->
+ Reg1 = put_reg(V, Int0#sr.reg),
+ Int1 = Int0#sr{reg=Reg1},
+ {[{get_map_element,{f,Fail},
+ Rsrc,Key,fetch_reg(V, Reg1)}],
+ Int1}
+ end
+ end,
+ {Es,Aft} = flatmapfoldl(F, Bef, Vs),
+ {Es,Aft,St}.
+
select_extract_cons(Src, [{var,Hd}, {var,Tl}], I, Vdb, Bef, St) ->
{Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of
{{_,_,Lhd}, {_,_,Ltl}} when Lhd =< I, Ltl =< I ->
@@ -1408,7 +1446,7 @@ catch_cg(C, {var,R}, Le, Vdb, Bef, St0) ->
%% annotation must reflect this and make sure that the return
%% variable is allocated first.
%%
-%% put_list for constructing a cons is an atomic instruction
+%% put_list and put_map are atomic instructions, both of
%% which can safely resuse one of the source registers as target.
set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) ->
@@ -1448,6 +1486,30 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
%% Now generate the complete code for constructing the binary.
Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),
{Sis++Code,Aft,St};
+set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef,
+ #cg{in_catch=InCatch,bfail=Bfail}=St) ->
+ Fail = {f,Bfail},
+ {Sis,Int0} =
+ case InCatch of
+ true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
+ false -> {[],Bef}
+ end,
+ Line = line(Le#l.a),
+ SrcReg = case SrcMap of
+ {var,SrcVar} -> fetch_var(SrcVar, Int0);
+ _ -> SrcMap
+ end,
+ List = flatmap(fun({map_pair,K,{var,V}}) ->
+ [K,fetch_var(V, Int0)];
+ ({map_pair,K,E}) ->
+ [K,E]
+ end, sort(Es)),
+ Live = max_reg(Bef#sr.reg),
+ Int1 = clear_dead(Int0, Le#l.i, Vdb),
+ Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)},
+ Target = fetch_reg(R, Aft#sr.reg),
+ Code = [Line,{put_map,Fail,SrcReg,Target,Live,{list,List}}],
+ {Sis++Code,Aft,St};
set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
%% Find a place for the return register first.
Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index a5f31f3844..1ef710a260 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -487,6 +487,15 @@ expr({tuple,L,Es0}, St0) ->
{Es1,Eps,St1} = safe_list(Es0, St0),
A = lineno_anno(L, St1),
{ann_c_tuple(A, Es1),Eps,St1};
+expr({map,L,Es0}, St0) ->
+ {Es1,Eps,St1} = map_pair_list(Es0, St0),
+ A = lineno_anno(L, St1),
+ {#c_map{anno=A,es=Es1},Eps,St1};
+expr({map,L,{var,Vl,Map},Es0}, St0) ->
+ {Es1,Eps,St1} = map_pair_list(Es0, St0),
+ A = lineno_anno(L, St1),
+ Av = lineno_anno(Vl, St1),
+ {#c_map{anno=A,var=#c_var{anno=Av,name=Map},es=Es1},Eps,St1};
expr({bin,L,Es0}, St0) ->
try expr_bin(Es0, lineno_anno(L, St0), St0) of
{_,_,_}=Res -> Res
@@ -695,6 +704,14 @@ make_bool_switch_guard(L, E, V, T, F) ->
{clause,NegL,[V],[],[V]}
]}.
+map_pair_list(Es, St) ->
+ foldr(fun ({map_field,L,K0,V0}, {Ces,Esp,St0}) ->
+ {K,Ep0,St1} = safe(K0, St0),
+ {V,Ep1,St2} = safe(V0, St1),
+ A = lineno_anno(L, St2),
+ Pair = #c_map_pair{anno=A,key=K,val=V},
+ {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
+ end, {[],[],St}, Es).
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
@@ -1478,6 +1495,12 @@ pattern({cons,L,H,T}, St) ->
ann_c_cons(lineno_anno(L, St), pattern(H, St), pattern(T, St));
pattern({tuple,L,Ps}, St) ->
ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St));
+pattern({map,L,Ps}, St) ->
+ #c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))};
+pattern({map_field,L,K,V}, St) ->
+ #c_map_pair{anno=lineno_anno(L, St),
+ key=pattern(K, St),
+ val=pattern(V, St)};
pattern({bin,L,Ps}, St) ->
%% We don't create a #ibinary record here, since there is
%% no need to hold any used/new annotations in a pattern.
@@ -1823,6 +1846,12 @@ upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) ->
upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
{Tuple#c_tuple{es=Es1},Esg,Esv,Eus,St1};
+upattern(#c_map{es=Es0}=Map, Ks, St0) ->
+ {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
+ {Map#c_map{es=Es1},Esg,Esv,Eus,St1};
+upattern(#c_map_pair{val=V0}=MapPair, Ks, St0) ->
+ {V,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
+ {MapPair#c_map_pair{val=V},Vg,Vv,Vu,St1};
upattern(#c_binary{segments=Es0}=Bin, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0),
{Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1};
@@ -2152,6 +2181,9 @@ is_simple(#c_literal{}) -> true;
is_simple(#c_cons{hd=H,tl=T}) ->
is_simple(H) andalso is_simple(T);
is_simple(#c_tuple{es=Es}) -> is_simple_list(Es);
+is_simple(#c_map{es=Es}) -> is_simple_list(Es);
+is_simple(#c_map_pair{key=K,val=V}) ->
+ is_simple(K) andalso is_simple(V);
is_simple(_) -> false.
-spec is_simple_list([cerl:cerl()]) -> boolean().
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 65f1251099..84b3a4e37f 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -272,6 +272,10 @@ expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->
{Kes,Ep,St1} = atomic_list(Ces, Sub, St0),
{#k_tuple{anno=A,es=Kes},Ep,St1};
+expr(#c_map{anno=A,var=Var0,es=Ces}, Sub, St0) ->
+ {Var,[],St1} = expr(Var0, Sub, St0),
+ {Kes,Ep,St2} = map_pairs(Ces, Sub, St1),
+ {#k_map{anno=A,var=Var,es=Kes},Ep,St2};
expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
try atomic_bin(Cv, Sub, St0) of
{Kv,Ep,St1} ->
@@ -493,6 +497,13 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
translate_fc(Args) ->
[#c_literal{val=function_clause},make_list(Args)].
+map_pairs(Es, Sub, St) ->
+ foldr(fun(#c_map_pair{key=K0,val=V0}, {Kes,Esp,St0}) ->
+ {K,[],St1} = expr(K0, Sub, St0),
+ {V,Ep,St2} = atomic(V0, Sub, St1),
+ {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2}
+ end, {[],[],St}, Es).
+
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
%% Classify the call.
call_type(#c_literal{val=M}, #c_literal{val=F}, Ar) when is_atom(M), is_atom(F) ->
@@ -648,6 +659,13 @@ pattern(#c_cons{anno=A,hd=Ch,tl=Ct}, Isub, Osub0, St0) ->
pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
{#k_tuple{anno=A,es=Kes},Osub1,St1};
+pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
+ {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
+ {#k_map{anno=A,es=Kes},Osub1,St1};
+pattern(#c_map_pair{anno=A,key=Kk0,val=Cv},Isub, Osub0, St0) ->
+ Kk = #k_atom{val=Kk0#c_literal.val},
+ {Kv,Osub1,St1} = pattern(Cv, Isub, Osub0, St0),
+ {#k_map_pair{anno=A,key=Kk,val=Kv},Osub1,St1};
pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->
{Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),
{#k_binary{anno=A,segs=Kv},Osub1,St1};
@@ -1015,7 +1033,8 @@ match_con_1([U|_Us] = L, Cs, Def, St0) ->
%% Extract clauses for different constructors (types).
%%ok = io:format("match_con ~p~n", [Cs]),
Ttcs = select_types([k_binary], Cs) ++ select_bin_con(Cs) ++
- select_types([k_cons,k_tuple,k_atom,k_float,k_int,k_nil,k_literal], Cs),
+ select_types([k_cons,k_tuple,k_map,k_atom,k_float,k_int,
+ k_nil,k_literal], Cs),
%%ok = io:format("ttcs = ~p~n", [Ttcs]),
{Scs,St1} =
mapfoldl(fun ({T,Tcs}, St) ->
@@ -1255,6 +1274,8 @@ group_value(k_bin_seg, Cs) ->
group_bin_seg(Cs);
group_value(k_bin_int, Cs) ->
[Cs];
+group_value(k_map, Cs) ->
+ group_map(Cs);
group_value(_, Cs) ->
%% group_value(Cs).
Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end,
@@ -1267,6 +1288,12 @@ group_bin_seg([C1|Cs]) ->
[[C1|More]|group_bin_seg(Rest)];
group_bin_seg([]) -> [].
+group_map([C1|Cs]) ->
+ V1 = clause_val(C1),
+ {More,Rest} = splitwith(fun (C) -> clause_val(C) =:= V1 end, Cs),
+ [[C1|More]|group_map(Rest)];
+group_map([]) -> [].
+
%% Profiling shows that this quadratic implementation account for a big amount
%% of the execution time if there are many values.
% group_value([C|Cs]) ->
@@ -1315,6 +1342,12 @@ get_match(#k_bin_int{}=BinInt, St0) ->
get_match(#k_tuple{es=Es}, St0) ->
{Mes,St1} = new_vars(length(Es), St0),
{#k_tuple{es=Mes},Mes,St1};
+get_match(#k_map{es=Es0}, St0) ->
+ {Mes,St1} = new_vars(length(Es0), St0),
+ {Es,_} = mapfoldl(fun(#k_map_pair{}=Pair, [V|Vs]) ->
+ {Pair#k_map_pair{val=V},Vs}
+ end, Mes, Es0),
+ {#k_map{es=Es},Mes,St1};
get_match(M, St) ->
{M,[],St}.
@@ -1331,7 +1364,11 @@ new_clauses(Cs0, U, St) ->
[S,N|As];
#k_bin_int{next=N} ->
[N|As];
- _Other -> As
+ #k_map{es=Es} ->
+ Vals = [V || #k_map_pair{val=V} <- Es],
+ Vals ++ As;
+ _Other ->
+ As
end,
Vs = arg_alias(Arg),
Osub1 = foldl(fun (#k_var{name=V}, Acc) ->
@@ -1406,6 +1443,7 @@ arg_con(Arg) ->
#k_nil{} -> k_nil;
#k_cons{} -> k_cons;
#k_tuple{} -> k_tuple;
+ #k_map{} -> k_map;
#k_binary{} -> k_binary;
#k_bin_end{} -> k_bin_end;
#k_bin_seg{} -> k_bin_seg;
@@ -1426,7 +1464,13 @@ arg_val(Arg, C) ->
{#k_var{name=get_vsub(V, Isub)},U,T,Fs};
_ ->
{set_kanno(S, []),U,T,Fs}
- end
+ end;
+ #k_map{es=Es} ->
+ Keys = [begin
+ #k_map_pair{key=#k_atom{val=Key}} = Pair,
+ Key
+ end || Pair <- Es],
+ ordsets:from_list(Keys)
end.
%% ubody_used_vars(Expr, State) -> [UsedVar]
@@ -1795,6 +1839,10 @@ lit_vars(#k_atom{}) -> [];
lit_vars(#k_nil{}) -> [];
lit_vars(#k_cons{hd=H,tl=T}) ->
union(lit_vars(H), lit_vars(T));
+lit_vars(#k_map{var=Var,es=Es}) ->
+ lit_list_vars([Var|Es]);
+lit_vars(#k_map_pair{key=K,val=V}) ->
+ union(lit_vars(K), lit_vars(V));
lit_vars(#k_binary{segs=V}) -> lit_vars(V);
lit_vars(#k_bin_end{}) -> [];
lit_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
@@ -1830,7 +1878,11 @@ pat_vars(#k_bin_int{size=Size}) ->
{U,[]};
pat_vars(#k_bin_end{}) -> {[],[]};
pat_vars(#k_tuple{es=Es}) ->
- pat_list_vars(Es).
+ pat_list_vars(Es);
+pat_vars(#k_map{es=Es}) ->
+ pat_list_vars(Es);
+pat_vars(#k_map_pair{val=V}) ->
+ pat_vars(V).
pat_list_vars(Ps) ->
foldl(fun (P, {Used0,New0}) ->
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index fb8baf398b..6671109afb 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -38,6 +38,8 @@
-record(k_nil, {anno=[]}).
-record(k_tuple, {anno=[],es}).
+-record(k_map, {anno=[],var,es}).
+-record(k_map_pair, {anno=[],key,val}).
-record(k_cons, {anno=[],hd,tl}).
-record(k_binary, {anno=[],segs}).
-record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index e363a5387a..1af90792d3 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -104,6 +104,19 @@ format_1(#k_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
$}
];
+format_1(#k_map{var=#k_var{}=Var,es=Es}, Ctxt) ->
+ [$~,${,
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ " | ",format_1(Var, Ctxt),
+ $},$~
+ ];
+format_1(#k_map{es=Es}, Ctxt) ->
+ [$~,${,
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ $},$~
+ ];
+format_1(#k_map_pair{key=K,val=V}, Ctxt) ->
+ ["~<",format(K, Ctxt),",",format(V, Ctxt),">"];
format_1(#k_binary{segs=S}, Ctxt) ->
["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
format_1(#k_bin_seg{next=Next}=S, Ctxt) ->
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index 2cc3493570..507f5d0df1 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -323,7 +323,9 @@ type(k_tuple) -> tuple;
type(k_binary) -> binary;
type(k_bin_seg) -> bin_seg;
type(k_bin_int) -> bin_int;
-type(k_bin_end) -> bin_end.
+type(k_bin_end) -> bin_end;
+type(k_map) -> map;
+type(k_map_pair) -> map_pair.
%% variable(Klit) -> Lit.
%% var_list([Klit]) -> [Lit].
@@ -365,6 +367,10 @@ literal(#k_bin_end{}, Ctxt) ->
{bin_end,Ctxt};
literal(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list(Es, Ctxt)};
+literal(#k_map{var=Var,es=Es}, Ctxt) ->
+ {map,literal(Var, Ctxt),literal_list(Es, Ctxt)};
+literal(#k_map_pair{key=K,val=V}, Ctxt) ->
+ {map_pair,literal(K, Ctxt),literal(V, Ctxt)};
literal(#k_literal{val=V}, _Ctxt) ->
{literal,V}.
@@ -393,7 +399,11 @@ literal2(#k_bin_int{size=S,unit=U,flags=Fs,val=Int,next=N}, Ctxt) ->
literal2(#k_bin_end{}, Ctxt) ->
{bin_end,Ctxt};
literal2(#k_tuple{es=Es}, Ctxt) ->
- {tuple,literal_list2(Es, Ctxt)}.
+ {tuple,literal_list2(Es, Ctxt)};
+literal2(#k_map{es=Es}, Ctxt) ->
+ {map,literal_list2(Es, Ctxt)};
+literal2(#k_map_pair{key=K,val=V}, Ctxt) ->
+ {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}.
literal_list2(Ks, Ctxt) ->
[literal2(K, Ctxt) || K <- Ks].
--
cgit v1.2.3
From fa49c8ecac4996b47b17f9025240e202eb273389 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 17 Sep 2013 16:48:13 +0200
Subject: compiler: Fix stack register reordering
Can now handle {list [reg()]} elements in instructions.
---
lib/compiler/src/v3_codegen.erl | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index eff43b584a..bd5ced39e5 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -596,14 +596,13 @@ top_level_block(Keis, Bef, MaxRegs, _St) ->
%% number to the outer catch, which is wrong.
turn_yregs(0, Tp, _) -> Tp;
-turn_yregs(El, Tp, MaxY) when element(1, element(El, Tp)) =:= yy ->
- turn_yregs(El-1, setelement(El, Tp, {y,MaxY-element(2, element(El, Tp))}), MaxY);
-turn_yregs(El, Tp, MaxY) when is_list(element(El, Tp)) ->
- New = map(fun ({yy,YY}) -> {y,MaxY-YY};
- (Other) -> Other end, element(El, Tp)),
- turn_yregs(El-1, setelement(El, Tp, New), MaxY);
turn_yregs(El, Tp, MaxY) ->
- turn_yregs(El-1, Tp, MaxY).
+ turn_yregs(El-1,setelement(El,Tp,turn_yreg(element(El,Tp),MaxY)),MaxY).
+
+turn_yreg({yy,YY},MaxY) -> {y,MaxY-YY};
+turn_yreg({list,Ls},MaxY) -> {list, turn_yreg(Ls,MaxY)};
+turn_yreg(Ts,MaxY) when is_list(Ts) -> [turn_yreg(T,MaxY)||T<-Ts];
+turn_yreg(Other,_MaxY) -> Other.
%% select_cg(Sclause, V, TypeFail, ValueFail, StackReg, State) ->
%% {Is,StackReg,State}.
--
cgit v1.2.3
From 2a943f14a44903eb8e67549978de5dbf7275ad57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 20 Sep 2013 16:28:30 +0200
Subject: compiler: Fix no basic blocks for maps
---
lib/compiler/src/v3_codegen.erl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index bd5ced39e5..667f10a7f5 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -455,8 +455,11 @@ basic_block([Le|Les], Acc) ->
end;
no_block -> {reverse(Acc, [Le]),Les}
end.
+
+%% sets that may garbage collect are not allowed in basic blocks.
collect_block({set,_,{binary,_}}) -> no_block;
+collect_block({set,_,{map,_,_}}) -> no_block;
collect_block({set,_,_}) -> include;
collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]};
collect_block({call,Func,As,_Rs}) -> {block_end,As++func_vars(Func)};
--
cgit v1.2.3
From 08a9a9a7113d310de2e6511d898a06cb8a559ce8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 17 Sep 2013 16:50:05 +0200
Subject: compiler: Handle literals, not just atoms, as keys in maps
---
lib/compiler/src/sys_pre_expand.erl | 7 ++++---
lib/compiler/src/v3_core.erl | 15 ++++++++++++++-
lib/compiler/src/v3_kernel.erl | 19 ++++++++-----------
3 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 969e95e8dc..04084d22ff 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -231,9 +231,10 @@ pattern({tuple,Line,Ps}, St0) ->
pattern({map,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{map,Line,TPs},St1};
-pattern({map_field,Line,Key,V0}, St0) ->
- {V,St1} = pattern(V0, St0),
- {{map_field,Line,Key,V},St1};
+pattern({map_field,Line,K0,V0}, St0) ->
+ {K,St1} = pattern(K0, St0),
+ {V,St2} = pattern(V0, St1),
+ {{map_field,Line,K,V},St2};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1};
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 1ef710a260..32b65333a4 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -1498,8 +1498,21 @@ pattern({tuple,L,Ps}, St) ->
pattern({map,L,Ps}, St) ->
#c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))};
pattern({map_field,L,K,V}, St) ->
+ %% FIXME: Better way to construct literals? or missing case
+ %% {Key,_,_} = expr(K, St),
+ Key = case K of
+ {bin,L,Es0} ->
+ case constant_bin(Es0) of
+ error ->
+ throw(badmatch);
+ Bin ->
+ #c_literal{anno=lineno_anno(L,St),val=Bin}
+ end;
+ _ ->
+ pattern(K,St)
+ end,
#c_map_pair{anno=lineno_anno(L, St),
- key=pattern(K, St),
+ key=Key,
val=pattern(V, St)};
pattern({bin,L,Ps}, St) ->
%% We don't create a #ibinary record here, since there is
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 84b3a4e37f..3d2dbf2088 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -662,10 +662,10 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
{#k_map{anno=A,es=Kes},Osub1,St1};
-pattern(#c_map_pair{anno=A,key=Kk0,val=Cv},Isub, Osub0, St0) ->
- Kk = #k_atom{val=Kk0#c_literal.val},
- {Kv,Osub1,St1} = pattern(Cv, Isub, Osub0, St0),
- {#k_map_pair{anno=A,key=Kk,val=Kv},Osub1,St1};
+pattern(#c_map_pair{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
+ {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
+ {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
+ {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2};
pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->
{Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),
{#k_binary{anno=A,segs=Kv},Osub1,St1};
@@ -1270,12 +1270,9 @@ group_value(k_cons, Cs) -> [Cs]; %These are single valued
group_value(k_nil, Cs) -> [Cs];
group_value(k_binary, Cs) -> [Cs];
group_value(k_bin_end, Cs) -> [Cs];
-group_value(k_bin_seg, Cs) ->
- group_bin_seg(Cs);
-group_value(k_bin_int, Cs) ->
- [Cs];
-group_value(k_map, Cs) ->
- group_map(Cs);
+group_value(k_bin_seg, Cs) -> group_bin_seg(Cs);
+group_value(k_bin_int, Cs) -> [Cs];
+group_value(k_map, Cs) -> group_map(Cs);
group_value(_, Cs) ->
%% group_value(Cs).
Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end,
@@ -1467,7 +1464,7 @@ arg_val(Arg, C) ->
end;
#k_map{es=Es} ->
Keys = [begin
- #k_map_pair{key=#k_atom{val=Key}} = Pair,
+ #k_map_pair{key=#k_literal{val=Key}} = Pair,
Key
end || Pair <- Es],
ordsets:from_list(Keys)
--
cgit v1.2.3
From be782d05bcd259b229efcac47c0566e4026cf1f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 24 Sep 2013 15:22:41 +0200
Subject: compiler: Check for duplicate key literals in Map
If a literal key already is present in a Map update the latter should be used.
Warn for previous duplicates in the Map.
---
lib/compiler/src/sys_core_fold.erl | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 672a8b94c0..cdbe2d0753 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -418,8 +418,31 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0)
expr_list(Es, Ctxt, Sub) ->
[expr(E, Ctxt, Sub) || E <- Es].
+%% traverse pairs in reverse
+%% - remove later literals since they will be overwritten.
+
pair_list(Es, Ctxt, Sub) ->
- [pair(E, Ctxt, Sub) || E <- Es].
+ pair_list_reversed(lists:reverse(Es), Ctxt, Sub, [], gb_sets:empty()).
+
+pair_list_reversed([],_,_,Es,_) -> Es;
+pair_list_reversed([E|Es],Ctxt,Sub,Out,Keys) ->
+ Pair = pair(E,Ctxt,Sub),
+ case map_has_key(Pair,Keys) of
+ {false,Keys1} ->
+ pair_list_reversed(Es,Ctxt,Sub,[Pair|Out],Keys1);
+ {true,K} ->
+ add_warning(E, {map_pair_key_overloaded,K}),
+ pair_list_reversed(Es,Ctxt,Sub,Out,Keys)
+ end.
+
+%% check if key already is present in map, i.e. #{ a=>1, a=>2 }
+%% where 'a' is duplicate. Update maps set with the key if not present.
+
+map_has_key(#c_map_pair{key=#c_literal{val=K}},Ks) ->
+ case gb_sets:is_element(K,Ks) of
+ false -> {false, gb_sets:add(K,Ks)};
+ true -> {true, K}
+ end.
pair(#c_map_pair{key=K,val=V}, effect, Sub) ->
make_effect_seq([K,V], Sub);
@@ -697,7 +720,7 @@ useless_call(effect, #c_call{anno=Anno,
useless_call(_, _) -> no.
%% make_effect_seq([Expr], Sub) -> #c_seq{}|void()
-%% Convert a list of epressions evaluated in effect context to a chain of
+%% Convert a list of expressions evaluated in effect context to a chain of
%% #c_seq{}. The body in the innermost #c_seq{} will be void().
%% Anything that will not have any effect will be thrown away.
@@ -3019,6 +3042,9 @@ format_error(result_ignored) ->
"(suppress the warning by assigning the expression to the _ variable)";
format_error(useless_building) ->
"a term is constructed, but never used";
+format_error({map_pair_key_overloaded,K}) ->
+ M = io_lib:format("the key ~p is used multiple times in map value association",[K]),
+ flatten(M);
format_error(bin_opt_alias) ->
"INFO: the '=' operator will prevent delayed sub binary optimization";
format_error(bin_partition) ->
--
cgit v1.2.3
From 1b701e059c36154d88caa24c4aaa68a2d19971cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 25 Sep 2013 18:17:58 +0200
Subject: compiler: Fix multiple same keys
---
lib/compiler/src/v3_kernel.erl | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 3d2dbf2088..f4ba60e529 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -1467,7 +1467,9 @@ arg_val(Arg, C) ->
#k_map_pair{key=#k_literal{val=Key}} = Pair,
Key
end || Pair <- Es],
- ordsets:from_list(Keys)
+ %% multiple keys may have the same name
+ %% do not use ordsets
+ lists:sort(Keys)
end.
%% ubody_used_vars(Expr, State) -> [UsedVar]
--
cgit v1.2.3
From e491dc2ff804107daecf36244587566210682850 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 1 Oct 2013 17:52:33 +0200
Subject: compiler: Fix sorted keys in put_map instruction
All pairs in a Map needs to be in strict ascending key order.
---
lib/compiler/src/v3_codegen.erl | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index 667f10a7f5..e8eec08323 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1501,11 +1501,18 @@ set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef,
{var,SrcVar} -> fetch_var(SrcVar, Int0);
_ -> SrcMap
end,
- List = flatmap(fun({map_pair,K,{var,V}}) ->
- [K,fetch_var(V, Int0)];
- ({map_pair,K,E}) ->
- [K,E]
- end, sort(Es)),
+
+ % MapPairs in put_map must be sorted in ascending Key Order
+ % Key literals must be unique when arriving here in v3_codegen
+
+ SortedEs = lists:sort(fun
+ ({map_pair,{_T1,K1},_},{map_pair,{_T2,K2},_}) when K1 < K2 -> true;
+ ({map_pair,{_,_},_},{map_pair,{_,_},_}) -> false
+ end, Es),
+ List = flatmap(fun
+ ({map_pair,K,{var,V}}) -> [K,fetch_var(V, Int0)];
+ ({map_pair,K,E}) -> [K,E]
+ end, SortedEs),
Live = max_reg(Bef#sr.reg),
Int1 = clear_dead(Int0, Le#l.i, Vdb),
Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)},
--
cgit v1.2.3
From 345bf9d4634f81ad0343e8c60442bdd293e41835 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Tue, 21 May 2013 17:00:11 +0200
Subject: erts: Maps beam-instruction definitions
---
erts/emulator/beam/ops.tab | 67 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 61 insertions(+), 6 deletions(-)
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index b52ecfaefc..b89bdb2e3a 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1466,14 +1466,69 @@ apply I
apply_last I P
#
-# Map instructions in R16.
-#
-
-put_map Fail Src Dst Live Size Rest=* => jump Fail
-is_map Fail Src => jump Fail
-has_map_field Fail Src Key => jump Fail
+# Map instructions in R17.
+#
+
+# put_map Fail Src Dst Live Size Rest=* => jump Fail
+# is_map Fail Src => jump Fail
+# has_map_field Fail Src Key => jump Fail
+# get_map_element Fail Src Key Dst => jump Fail
+
+put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest
+put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest
+
+new_map j d I I
+update_map j d d I I
+
+is_map Fail cq => jump Fail
+
+%macro: is_map IsMap -fail_action
+is_map f r
+is_map f x
+is_map f y
+
+has_map_field Fail Src=rxy Key=arxy => i_has_map_field Fail Src Key
+has_map_field Fail Src Key => move Key x | i_has_map_field Fail Src x
+
+%macro: i_has_map_field HasMapField -fail_action
+i_has_map_field f r a
+i_has_map_field f x a
+i_has_map_field f y a
+i_has_map_field f r r
+i_has_map_field f x r
+i_has_map_field f y r
+i_has_map_field f r x
+i_has_map_field f x x
+i_has_map_field f y x
+i_has_map_field f r y
+i_has_map_field f x y
+i_has_map_field f y y
+
+get_map_element Fail Src=rxy Key=ax Dst => i_get_map_element Fail Src Key Dst
+get_map_element Fail Src=rxy Key=rycq Dst => \
+ move Key x | i_get_map_element Fail Src x Dst
get_map_element Fail Src Key Dst => jump Fail
+%macro: i_get_map_element GetMapElement -fail_action
+i_get_map_element f r a r
+i_get_map_element f x a r
+i_get_map_element f y a r
+i_get_map_element f r a x
+i_get_map_element f x a x
+i_get_map_element f y a x
+i_get_map_element f r a y
+i_get_map_element f x a y
+i_get_map_element f y a y
+i_get_map_element f r x r
+i_get_map_element f x x r
+i_get_map_element f y x r
+i_get_map_element f r x x
+i_get_map_element f x x x
+i_get_map_element f y x x
+i_get_map_element f r x y
+i_get_map_element f x x y
+i_get_map_element f y x y
+
#
# Optimize addition and subtraction of small literals using
# the i_increment/4 instruction (in bodies, not in guards).
--
cgit v1.2.3
From 03d5bb0d1bd5d7c4e09ee793a680f2d538fe0122 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 21 May 2013 18:09:55 +0200
Subject: erts: Initial Map instructions, type and structure
---
erts/emulator/Makefile.in | 2 +-
erts/emulator/beam/beam_emu.c | 341 ++++++++++++++++++++++++++++++++++-
erts/emulator/beam/bif.tab | 6 +
erts/emulator/beam/copy.c | 32 ++++
erts/emulator/beam/erl_bif_op.c | 12 +-
erts/emulator/beam/erl_db_util.c | 1 +
erts/emulator/beam/erl_debug.c | 1 +
erts/emulator/beam/erl_gc.c | 1 +
erts/emulator/beam/erl_gc.h | 37 ++--
erts/emulator/beam/erl_map.c | 225 +++++++++++++++++++++++
erts/emulator/beam/erl_map.h | 64 +++++++
erts/emulator/beam/erl_printf_term.c | 52 +++++-
erts/emulator/beam/erl_term.c | 6 +-
erts/emulator/beam/erl_term.h | 44 +++--
erts/emulator/beam/utils.c | 41 ++++-
erts/emulator/hipe/hipe_bif2.c | 1 +
erts/emulator/hipe/hipe_debug.c | 1 +
17 files changed, 811 insertions(+), 56 deletions(-)
create mode 100644 erts/emulator/beam/erl_map.c
create mode 100644 erts/emulator/beam/erl_map.h
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index b270099566..54365f279c 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -780,7 +780,7 @@ RUN_OBJS = \
$(OBJDIR)/erl_zlib.o $(OBJDIR)/erl_nif.o \
$(OBJDIR)/erl_bif_binary.o $(OBJDIR)/erl_ao_firstfit_alloc.o \
$(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o \
- $(OBJDIR)/erl_ptab.o
+ $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o
ifeq ($(TARGET),win32)
DRV_OBJS = \
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 7fecdd5c5f..5c955b9566 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -31,6 +31,7 @@
#include "big.h"
#include "beam_load.h"
#include "erl_binary.h"
+#include "erl_map.h"
#include "erl_bits.h"
#include "dist.h"
#include "beam_bp.h"
@@ -701,6 +702,19 @@ extern int count_instructions;
Fail; \
}
+#define IsMap(Src, Fail) if (is_not_map(Src)) { Fail; }
+
+#define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; }
+
+#define GetMapElement(Src, Key, Dst, Fail) \
+ do { \
+ Eterm _res = get_map_element(Src, Key); \
+ if (is_non_value(_res)) { \
+ Fail; \
+ } \
+ Dst = _res; \
+ } while (0)
+
#define IsFunction(X, Action) \
do { \
if ( !(is_any_fun(X)) ) { \
@@ -944,7 +958,11 @@ static BeamInstr* apply_fun(Process* p, Eterm fun,
Eterm args, Eterm* reg) NOINLINE;
static Eterm new_fun(Process* p, Eterm* reg,
ErlFunEntry* fe, int num_free) NOINLINE;
-
+static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE;
+static Eterm update_map(Process* p, Eterm* reg,
+ Eterm map, BeamInstr* I) NOINLINE;
+static int has_not_map_field(Eterm map, Eterm key);
+static Eterm get_map_element(Eterm map, Eterm key);
/*
* Functions not directly called by process_main(). OK to inline.
@@ -2323,6 +2341,37 @@ void process_main(void)
Goto(*I);
}
+ OpCase(new_map_jdII): {
+ Eterm res;
+
+ x(0) = r(0);
+ SWAPOUT;
+ res = new_map(c_p, reg, I);
+ SWAPIN;
+ r(0) = x(0);
+ StoreResult(res, Arg(1));
+ Next(4+Arg(3));
+ }
+
+ OpCase(update_map_jddII): {
+ Eterm res;
+ Eterm map;
+
+ GetArg1(1, map);
+ x(0) = r(0);
+ SWAPOUT;
+ res = update_map(c_p, reg, map, I);
+ SWAPIN;
+ if (res) {
+ r(0) = x(0);
+ StoreResult(res, Arg(2));
+ Next(5+Arg(4));
+ } else {
+ goto lb_Cl_error;
+ }
+ }
+
+
/*
* All guards with zero arguments have special instructions:
* self/0
@@ -6227,6 +6276,296 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
return make_fun(funp);
}
+static int has_not_map_field(Eterm map, Eterm key)
+{
+ map_t* mp;
+ Eterm* keys;
+ Uint i;
+ Uint n;
+
+ mp = (map_t *)map_val(map);
+ keys = map_get_keys(mp);
+ n = map_get_size(mp);
+ if (is_immed(key)) {
+ for (i = 0; i < n; i++) {
+ if (keys[i] == key) {
+ return 0;
+ }
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ if (eq(keys[i], key)) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static Eterm get_map_element(Eterm map, Eterm key)
+{
+ map_t *mp;
+ Eterm* ks, *vs;
+ Uint i;
+ Uint n;
+
+ mp = (map_t *)map_val(map);
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+ n = map_get_size(mp);
+ if (is_immed(key)) {
+ for (i = 0; i < n; i++) {
+ if (ks[i] == key) {
+ return vs[i];
+ }
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ if (eq(ks[i], key)) {
+ return vs[i];
+ }
+ }
+ }
+ return THE_NON_VALUE;
+}
+
+#define GET_TERM(term, dest) \
+do { \
+ Eterm src = term; \
+ switch (src & _TAG_IMMED1_MASK) { \
+ case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
+ dest = x(0); \
+ break; \
+ case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
+ dest = x(src >> _TAG_IMMED1_SIZE); \
+ break; \
+ case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
+ dest = y(src >> _TAG_IMMED1_SIZE); \
+ break; \
+ default: \
+ dest = src; \
+ break; \
+ } \
+} while(0)
+
+
+static Eterm
+new_map(Process* p, Eterm* reg, BeamInstr* I)
+{
+ Uint n = Arg(3);
+ Uint i;
+ Uint need = n + 1 /* hdr */ + 1 /*size*/ + 1 /* ptr */ + 1 /* arity */;
+ Eterm keys;
+ Eterm *mhp,*thp;
+ Eterm *E;
+ Eterm *tp;
+ map_t *mp;
+
+ if (HeapWordsLeft(p) < need) {
+ erts_garbage_collect(p, need, reg, Arg(2));
+ }
+
+ thp = p->htop;
+ mhp = thp + 1 + n/2;
+ E = p->stop;
+ tp = &Arg(4);
+ keys = make_tuple(thp);
+ *thp++ = make_arityval(n/2);
+
+ mp = (map_t *)mhp; mhp += 3;
+ mp->thing_word = MAP_HEADER;
+ mp->size = n/2;
+ mp->keys = keys;
+
+ for (i = 0; i < n/2; i++) {
+ GET_TERM(*tp++, *thp++);
+ GET_TERM(*tp++, *mhp++);
+ }
+ p->htop = mhp;
+ return make_map(mp);
+}
+
+
+/* This entire instruction will be split into two.
+ * 1) update_map_exact (literals) <- this can be much more optimized
+ * 2) update_map_assoc (literals)
+ * Also update_map is pretty bad code as it stands now.
+ */
+
+static Eterm
+update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
+{
+ Uint n;
+ Uint i;
+ Uint num_old;
+ Uint num_updates;
+ Uint need;
+ map_t *old_mp, *mp;
+ Eterm res;
+ Eterm* hp;
+ Eterm* E;
+ Eterm* old_keys;
+ Eterm* old_vals;
+ Eterm* new_p;
+ Eterm new_key;
+ Eterm* kp;
+
+ if (is_not_map(map)) {
+ return 0;
+ }
+
+ old_mp = (map_t *) map_val(map);
+ num_old = map_get_size(old_mp);
+
+ /*
+ * If the old map is empty, create a new map.
+ */
+
+ if (num_old == 0) {
+ return new_map(p, reg, I+1);
+ }
+
+ /*
+ * Allocate heap space for the worst case (i.e. all keys are new).
+ */
+
+ num_updates = Arg(4) / 2;
+ need = 2*(num_old+num_updates) + 4;
+ if (HeapWordsLeft(p) < need) {
+ Uint live = Arg(3);
+ reg[live] = map;
+ erts_garbage_collect(p, need, reg, live+1);
+ map = reg[live];
+ old_mp = (map_t *)map_val(map);
+ }
+
+ /*
+ * Update map, optimistically assuming that there are no
+ * new keys, allowing us to keep the old key tuple.
+ */
+
+ hp = p->htop;
+ E = p->stop;
+
+ old_vals = map_get_values(old_mp);
+ old_keys = map_get_keys(old_mp);
+
+ res = make_map(hp);
+ mp = (map_t *)hp; hp += 3;
+ mp->thing_word = MAP_HEADER;
+ mp->size = num_old;
+ mp->keys = old_mp->keys;
+
+ ASSERT(num_updates > 0);
+
+ /* Get array of key/value pairs to be updated */
+ new_p = &Arg(5);
+ GET_TERM(*new_p, new_key);
+
+ n = num_updates;
+
+ for (i = 0; i < num_old; i++) {
+ if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) {
+ /* not same keys */
+ *hp++ = *old_vals;
+ } else {
+ GET_TERM(new_p[1], *hp);
+ hp++;
+ n--;
+ if (n == 0) {
+ new_key = THE_NON_VALUE;
+ } else {
+ new_p += 2;
+ GET_TERM(*new_p, new_key);
+ }
+ }
+ old_vals++, old_keys++;
+ }
+
+ /*
+ * If we have exhausted the update list we are done.
+ */
+
+ if (n == 0) {
+ p->htop = hp;
+ return res;
+ }
+
+ /*
+ * There were some new keys. We'll have to start over and rebuild
+ * the key tuple too.
+ */
+
+ kp = p->htop;
+ *kp++ = make_arityval(0);
+
+ res = make_map(hp);
+ mp = (map_t *)hp; hp += 3;
+ mp->keys = make_tuple(kp-1);
+
+ old_vals = map_get_values(old_mp);
+ old_keys = map_get_keys(old_mp);
+
+ new_p = &Arg(5);
+ GET_TERM(*new_p, new_key);
+ n = num_updates;
+
+ for (;;) {
+ Eterm key;
+ Sint c;
+
+ ASSERT(kp < (Eterm *)mp);
+ key = *old_keys;
+ if ((c = cmp(key, new_key)) < 0) {
+ *kp++ = key;
+ *hp++ = *old_vals;
+ old_keys++, old_vals++, num_old--;
+ } else { /* Replace or insert new */
+ *kp++ = new_key;
+ GET_TERM(new_p[1], *hp++);
+ if (c == 0) { /* If replacement */
+ old_keys++, old_vals++, num_old--;
+ }
+ n--;
+ if (n == 0) {
+ break;
+ } else {
+ new_p += 2;
+ GET_TERM(*new_p, new_key);
+ }
+ }
+ if (num_old == 0) {
+ break;
+ }
+ }
+
+ while (n-- > 0) {
+ GET_TERM(new_p[0], *kp++);
+ GET_TERM(new_p[1], *hp++);
+ new_p += 2;
+ }
+
+ /*
+ * All updates done. Now copy the remaining part of the frame's
+ * keys and values.
+ */
+
+ while (num_old-- > 0) {
+ ASSERT(kp < (Eterm *)mp);
+ *kp++ = *old_keys++;
+ *hp++ = *old_vals++;
+ }
+ if ((n = (Eterm *)mp - kp) > 0) {
+ *kp = make_pos_bignum_header(n-1);
+ }
+ n = kp - p->htop - 1; /* Actual number of keys/values */
+ *p->htop = make_arityval(n);
+ mp->thing_word = MAP_HEADER;
+ mp->size = n;
+ p->htop = hp;
+ return res;
+}
+#undef GET_TERM
int catchlevel(Process *p)
{
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 3ec534f0bc..42515d0494 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -578,6 +578,12 @@ bif os:unsetenv/1
bif re:inspect/2
+ubif erlang:is_map/1
+bif map:to_list/1
+bif map:new/0
+bif map:get/2
+bif map:put/3
+
#
# Obsolete
#
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 23c0fca6aa..3a987e213b 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -27,6 +27,7 @@
#include "erl_process.h"
#include "erl_gc.h"
#include "big.h"
+#include "erl_map.h"
#include "erl_binary.h"
#include "erl_bits.h"
#include "dtrace-wrapper.h"
@@ -150,6 +151,24 @@ Uint size_object(Eterm obj)
goto pop_next;
}
break;
+ case MAP_SUBTAG:
+ {
+ Uint n;
+ map_t *mp;
+ mp = (map_t*)map_val_rel(obj,base);
+ ptr = (Eterm *)mp;
+ n = map_get_size(mp) + 1;
+ sum += n + 2;
+ ptr += 2; /* hdr + size words */
+ while (n--) {
+ obj = *ptr++;
+ if (!IS_CONST(obj)) {
+ ESTACK_PUSH(s, obj);
+ }
+ }
+ goto pop_next;
+ }
+ break;
case BIN_MATCHSTATE_SUBTAG:
erl_exit(ERTS_ABORT_EXIT,
"size_object: matchstate term not allowed");
@@ -318,6 +337,15 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
}
}
break;
+ case MAP_SUBTAG:
+ {
+ i = map_get_size(objp) + 3;
+ *argp = make_map_rel(htop, dst_base);
+ while (i--) {
+ *htop++ = *objp++;
+ }
+ }
+ break;
case REFC_BINARY_SUBTAG:
{
ProcBin* pb;
@@ -537,6 +565,10 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
}
goto off_heap_common;
+ case MAP_SUBTAG:
+ *hp++ = *tp++;
+ sz--;
+ break;
case EXTERNAL_PID_SUBTAG:
case EXTERNAL_PORT_SUBTAG:
case EXTERNAL_REF_SUBTAG:
diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c
index adac0052d6..37dd6457db 100644
--- a/erts/emulator/beam/erl_bif_op.c
+++ b/erts/emulator/beam/erl_bif_op.c
@@ -36,6 +36,7 @@
#include "dist.h"
#include "erl_version.h"
#include "erl_binary.h"
+#include "erl_map.h"
BIF_RETTYPE and_2(BIF_ALIST_2)
{
@@ -321,7 +322,10 @@ BIF_RETTYPE is_record_3(BIF_ALIST_3)
BIF_RET(am_false);
}
-
-
-
-
+BIF_RETTYPE is_map_1(BIF_ALIST_1)
+{
+ if (is_map(BIF_ARG_1)) {
+ BIF_RET(am_true);
+ }
+ BIF_RET(am_false);
+}
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index a358ecf326..d903053aa4 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -35,6 +35,7 @@
#include "bif.h"
#include "big.h"
#include "erl_binary.h"
+#include "erl_map.h"
#include "erl_thr_progress.h"
#include "erl_db_util.h"
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index 873a9860da..50bdc79506 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -29,6 +29,7 @@
#include "bif.h"
#include "beam_catches.h"
#include "erl_debug.h"
+#include "erl_map.h"
#define WITHIN(ptr, x, y) ((x) <= (ptr) && (ptr) < (y))
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index ab8448e8a1..2022f70cbb 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -28,6 +28,7 @@
#include "beam_catches.h"
#include "erl_binary.h"
#include "erl_bits.h"
+#include "erl_map.h"
#include "error.h"
#include "big.h"
#include "erl_gc.h"
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index 1801df359a..5203dda263 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -20,6 +20,8 @@
#ifndef __ERL_GC_H__
#define __ERL_GC_H__
+#include "erl_map.h"
+
/* GC declarations shared by beam/erl_gc.c and hipe/hipe_gc.c */
#if defined(DEBUG) && !ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -42,23 +44,24 @@ do { \
HTOP += 2; /* update tospace htop */ \
} while(0)
-#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \
-do { \
- Eterm gval; \
- Sint nelts; \
- \
- ASSERT(is_header(HDR)); \
- gval = make_boxed(HTOP); \
- *ORIG = gval; \
- *HTOP++ = HDR; \
- *PTR++ = gval; \
- nelts = header_arity(HDR); \
- switch ((HDR) & _HEADER_SUBTAG_MASK) { \
- case SUB_BINARY_SUBTAG: nelts++; break; \
- case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR-1))->num_free+1; break; \
- } \
- while (nelts--) \
- *HTOP++ = *PTR++; \
+#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \
+do { \
+ Eterm gval; \
+ Sint nelts; \
+ \
+ ASSERT(is_header(HDR)); \
+ nelts = header_arity(HDR); \
+ switch ((HDR) & _HEADER_SUBTAG_MASK) { \
+ case SUB_BINARY_SUBTAG: nelts++; break; \
+ case MAP_SUBTAG: nelts+=map_get_size(PTR) + 1; break; \
+ case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR))->num_free+1; break; \
+ } \
+ gval = make_boxed(HTOP); \
+ *ORIG = gval; \
+ *HTOP++ = HDR; \
+ *PTR++ = gval; \
+ while (nelts--) *HTOP++ = *PTR++; \
+ \
} while(0)
#define in_area(ptr,start,nbytes) \
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
new file mode 100644
index 0000000000..1fd7943c30
--- /dev/null
+++ b/erts/emulator/beam/erl_map.c
@@ -0,0 +1,225 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2013. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ * Author: Björn-Egil Dahlberg
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "error.h"
+#include "bif.h"
+
+
+#include "erl_map.h"
+
+
+BIF_RETTYPE map_to_list_1(BIF_ALIST_1) {
+ if (is_map(BIF_ARG_1)) {
+ Uint n;
+ Eterm* hp;
+ Eterm *ks,*vs, res, tup;
+ map_t *mp = (map_t*)map_val(BIF_ARG_1);
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+ n = map_get_size(mp);
+ 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);
+ }
+
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+BIF_RETTYPE map_new_0(BIF_ALIST_0) {
+ Eterm* hp;
+ Eterm tup;
+ map_t *mp;
+
+ hp = HAlloc(BIF_P, (3 + 1));
+ tup = make_tuple(hp);
+ *hp++ = make_arityval(0);
+
+ mp = (map_t*)hp;
+ mp->thing_word = MAP_HEADER;
+ mp->size = 0;
+ mp->keys = tup;
+
+ BIF_RET(make_map(mp));
+}
+
+BIF_RETTYPE map_get_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm *hp, *ks,*vs, key, error;
+ map_t *mp;
+ Uint n,i;
+ char *s_error;
+
+ mp = (map_t*)map_val(BIF_ARG_2);
+ key = BIF_ARG_1;
+ n = map_get_size(mp);
+
+ if (n == 0)
+ goto error;
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+
+ if (is_immed(key)) {
+ for( i = 0; i < n; i++) {
+ if (ks[i] == key) {
+ BIF_RET(vs[i]);
+ }
+ }
+ }
+
+ for( i = 0; i < n; i++) {
+ if (eq(ks[i], key)) {
+ BIF_RET(vs[i]);
+ }
+ }
+error:
+
+ s_error = "bad_key";
+ error = am_atom_put(s_error, sys_strlen(s_error));
+
+ hp = HAlloc(BIF_P, 3);
+ BIF_P->fvalue = TUPLE2(hp, error, key);
+ BIF_ERROR(BIF_P, EXC_ERROR_2);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+BIF_RETTYPE map_put_3(BIF_ALIST_3) {
+ if (is_map(BIF_ARG_3)) {
+ Sint n,i;
+ Sint c = 0;
+ Eterm* hp, *shp;
+ Eterm *ks,*vs, res, key, tup;
+ map_t *mp = (map_t*)map_val(BIF_ARG_3);
+
+ key = BIF_ARG_1;
+ n = map_get_size(mp);
+
+ if (n == 0) {
+ hp = HAlloc(BIF_P, 4 + 2);
+ tup = make_tuple(hp);
+ *hp++ = make_arityval(1);
+ *hp++ = key;
+ res = make_map(hp);
+ *hp++ = MAP_HEADER;
+ *hp++ = 1;
+ *hp++ = tup;
+ *hp++ = BIF_ARG_2;
+
+ BIF_RET(res);
+ }
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+ /* only allocate for values,
+ * assume key-tuple will be intact
+ */
+
+ hp = HAlloc(BIF_P, 3 + n);
+ shp = hp; /* save hp, used if optimistic update fails */
+ res = make_map(hp);
+ *hp++ = MAP_HEADER;
+ *hp++ = n;
+ *hp++ = mp->keys;
+
+ if (is_immed(key)) {
+ for( i = 0; i < n; i ++) {
+ if (ks[i] == key) {
+ *hp++ = BIF_ARG_2;
+ vs++;
+ c = 1;
+ } else {
+ *hp++ = *vs++;
+ }
+ }
+ } else {
+ for( i = 0; i < n; i ++) {
+ if (eq(ks[i], key)) {
+ *hp++ = BIF_ARG_2;
+ vs++;
+ c = 1;
+ } else {
+ *hp++ = *vs++;
+ }
+ }
+ }
+
+ if (c)
+ BIF_RET(res);
+
+ /* need to make a new tuple,
+ * use old hp since it needs to be recreated anyway.
+ */
+ tup = make_tuple(shp);
+ *shp++ = make_arityval(n+1);
+
+ hp = HAlloc(BIF_P, 3 + n + 1);
+ res = make_map(hp);
+ *hp++ = MAP_HEADER;
+ *hp++ = n + 1;
+ *hp++ = tup;
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+
+ ASSERT(n >= 0);
+
+ /* copy map in order */
+ while (n && ((c = CMP(*ks, key)) < 0)) {
+ *shp++ = *ks++;
+ *hp++ = *vs++;
+ n--;
+ }
+
+ *shp++ = key;
+ *hp++ = BIF_ARG_2;
+
+ ASSERT(n >= 0);
+
+ while(n--) {
+ *shp++ = *ks++;
+ *hp++ = *vs++;
+ }
+ /* we have one word remaining
+ * this will work out fine once we get the size word
+ * in the header.
+ */
+ *shp = make_pos_bignum_header(0);
+ BIF_RET(res);
+ }
+
+ BIF_ERROR(BIF_P, BADARG);
+}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
new file mode 100644
index 0000000000..ca8ce0f974
--- /dev/null
+++ b/erts/emulator/beam/erl_map.h
@@ -0,0 +1,64 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2013. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+
+#ifndef __ERL_MAP_H__
+#define __ERL_MAP_H__
+
+#include "sys.h"
+/* MAP */
+
+typedef struct map_s {
+ Eterm thing_word;
+ Uint size;
+ Eterm keys; /* tuple */
+} map_t;
+/* map node
+ *
+ * -----------
+ * Eterm THING
+ * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity
+ * ----
+ * Eterm V1
+ * ...
+ * Eterm Vn, where n = arity
+ * -----------
+ */
+
+
+
+/* erl_term.h stuff */
+#define make_map(x) make_boxed((Eterm*)(x))
+#define make_map_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE))
+#define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val((x))))
+#define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE))
+#define is_not_map(x) (!is_map((x)))
+#define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP)
+#define header_is_map(x) ((((x) & (_HEADER_SUBTAG_MASK)) == MAP_SUBTAG))
+#define map_val(x) (_unchecked_boxed_val((x)))
+#define map_val_rel(RTERM, BASE) map_val(rterm2wterm(RTERM, BASE))
+
+#define map_get_values(x) (((Eterm *)(x)) + 3)
+#define map_get_keys(x) (((Eterm *)tuple_val(((map_t *)(x))->keys)) + 1)
+#define map_get_size(x) (((map_t*)(x))->size)
+
+#define MAP_HEADER _make_header(1,_TAG_HEADER_MAP)
+
+#endif
+
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index 436147749e..d18760dc43 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -24,6 +24,7 @@
#include "erl_printf_term.h"
#include "sys.h"
#include "big.h"
+#include "erl_map.h"
#define PRINT_CHAR(CNT, FN, ARG, C) \
do { \
@@ -216,14 +217,15 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount)
}
-#define PRT_BAR ((Eterm) 0)
-#define PRT_COMMA ((Eterm) 1)
-#define PRT_CLOSE_LIST ((Eterm) 2)
-#define PRT_CLOSE_TUPLE ((Eterm) 3)
-#define PRT_TERM ((Eterm) 4)
-#define PRT_ONE_CONS ((Eterm) 5)
-#define PRT_PATCH_FUN_SIZE ((Eterm) 6)
-#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 7) /* Note! Must be last... */
+#define PRT_BAR ((Eterm) 0)
+#define PRT_COMMA ((Eterm) 1)
+#define PRT_CLOSE_LIST ((Eterm) 2)
+#define PRT_CLOSE_TUPLE ((Eterm) 3)
+#define PRT_ASSOC ((Eterm) 4)
+#define PRT_TERM ((Eterm) 5)
+#define PRT_ONE_CONS ((Eterm) 6)
+#define PRT_PATCH_FUN_SIZE ((Eterm) 7)
+#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 8) /* Note! Must be last... */
static int
print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
@@ -260,6 +262,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
case PRT_CLOSE_TUPLE:
PRINT_CHAR(res, fn, arg, '}');
goto L_outer_loop;
+ case PRT_ASSOC:
+ PRINT_STRING(res, fn, arg, "=>");
+ goto L_outer_loop;
default:
popped.word = WSTACK_POP(s);
@@ -483,6 +488,37 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
PRINT_CHAR(res, fn, arg, '>');
}
break;
+ case MAP_DEF:
+ {
+ Uint n;
+ Eterm *ks, *vs;
+ map_t *mp = (map_t *)map_val(wobj);
+ n = map_get_size(mp);
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+
+ PRINT_CHAR(res, fn, arg, '#');
+ PRINT_CHAR(res, fn, arg, '{');
+ WSTACK_PUSH(s, PRT_CLOSE_TUPLE);
+ if (n > 0) {
+ n--;
+ WSTACK_PUSH(s, vs[n]);
+ WSTACK_PUSH(s, PRT_TERM);
+ WSTACK_PUSH(s, PRT_ASSOC);
+ WSTACK_PUSH(s, ks[n]);
+ WSTACK_PUSH(s, PRT_TERM);
+
+ while (n--) {
+ WSTACK_PUSH(s, PRT_COMMA);
+ WSTACK_PUSH(s, vs[n]);
+ WSTACK_PUSH(s, PRT_TERM);
+ WSTACK_PUSH(s, PRT_ASSOC);
+ WSTACK_PUSH(s, ks[n]);
+ WSTACK_PUSH(s, PRT_TERM);
+ }
+ }
+ }
+ break;
default:
PRINT_STRING(res, fn, arg, "
#include
@@ -85,7 +86,10 @@ unsigned tag_val_def(Wterm x)
case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF;
case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF;
case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF;
- default: return BINARY_DEF;
+ case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
+ case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
+ case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF;
+ case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF;
}
break;
}
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index 50d3e63c58..f10a3a9d38 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -135,11 +135,12 @@ struct erl_node_; /* Declared in erl_node_tables.h */
#define REF_SUBTAG (0x4 << _TAG_PRIMARY_SIZE) /* REF */
#define FUN_SUBTAG (0x5 << _TAG_PRIMARY_SIZE) /* FUN */
#define FLOAT_SUBTAG (0x6 << _TAG_PRIMARY_SIZE) /* FLOAT */
-#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */
+#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */
#define _BINARY_XXX_MASK (0x3 << _TAG_PRIMARY_SIZE)
#define REFC_BINARY_SUBTAG (0x8 << _TAG_PRIMARY_SIZE) /* BINARY */
#define HEAP_BINARY_SUBTAG (0x9 << _TAG_PRIMARY_SIZE) /* BINARY */
#define SUB_BINARY_SUBTAG (0xA << _TAG_PRIMARY_SIZE) /* BINARY */
+#define MAP_SUBTAG (0xB << _TAG_PRIMARY_SIZE) /* MAP */
#define EXTERNAL_PID_SUBTAG (0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */
#define EXTERNAL_PORT_SUBTAG (0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */
#define EXTERNAL_REF_SUBTAG (0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */
@@ -155,6 +156,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */
#define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG)
#define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG)
#define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG)
+#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG)
#define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG)
#define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG)
#define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG)
@@ -354,7 +356,10 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm)
#define is_value(x) ((x) != THE_NON_VALUE)
/* binary object access methods */
-#define is_binary_header(x) (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_REFC_BIN)
+#define is_binary_header(x) \
+ ((((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_REFC_BIN) || \
+ (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HEAP_BIN) || \
+ (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_SUB_BIN))
#define make_binary(x) make_boxed((Eterm*)(x))
#define is_binary(x) (is_boxed((x)) && is_binary_header(*boxed_val((x))))
#define is_not_binary(x) (!is_binary((x)))
@@ -1064,8 +1069,8 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint)
/*
* Backwards compatibility definitions:
- * - #define virtal *_DEF constants with values that fit term order:
- * number < atom < ref < fun < port < pid < tuple < nil < cons < binary
+ * - #define virtual *_DEF constants with values that fit term order:
+ * number < atom < ref < fun < port < pid < tuple < map < nil < cons < binary
* - tag_val_def() function generates virtual _DEF tag
* - not_eq_tags() and NUMBER_CODE() defined in terms
* of the tag_val_def() function
@@ -1074,19 +1079,20 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint)
#define BINARY_DEF 0x0
#define LIST_DEF 0x1
#define NIL_DEF 0x2
-#define TUPLE_DEF 0x3
-#define PID_DEF 0x4
-#define EXTERNAL_PID_DEF 0x5
-#define PORT_DEF 0x6
-#define EXTERNAL_PORT_DEF 0x7
-#define EXPORT_DEF 0x8
-#define FUN_DEF 0x9
-#define REF_DEF 0xa
-#define EXTERNAL_REF_DEF 0xb
-#define ATOM_DEF 0xc
-#define FLOAT_DEF 0xd
-#define BIG_DEF 0xe
-#define SMALL_DEF 0xf
+#define MAP_DEF 0x3
+#define TUPLE_DEF 0x4
+#define PID_DEF 0x5
+#define EXTERNAL_PID_DEF 0x6
+#define PORT_DEF 0x7
+#define EXTERNAL_PORT_DEF 0x8
+#define EXPORT_DEF 0x9
+#define FUN_DEF 0xa
+#define REF_DEF 0xb
+#define EXTERNAL_REF_DEF 0xc
+#define ATOM_DEF 0xd
+#define FLOAT_DEF 0xe
+#define BIG_DEF 0xf
+#define SMALL_DEF 0x10
#if ET_DEBUG
extern unsigned tag_val_def_debug(Wterm, const char*, unsigned);
@@ -1096,8 +1102,8 @@ extern unsigned tag_val_def(Wterm);
#endif
#define not_eq_tags(X,Y) (tag_val_def((X)) ^ tag_val_def((Y)))
-#define NUMBER_CODE(x,y) ((tag_val_def(x) << 4) | tag_val_def(y))
-#define _NUMBER_CODE(TX,TY) ((TX << 4) | TY)
+#define NUMBER_CODE(x,y) ((tag_val_def(x) << 5) | tag_val_def(y))
+#define _NUMBER_CODE(TX,TY) ((TX << 5) | TY)
#define SMALL_SMALL _NUMBER_CODE(SMALL_DEF,SMALL_DEF)
#define SMALL_BIG _NUMBER_CODE(SMALL_DEF,BIG_DEF)
#define SMALL_FLOAT _NUMBER_CODE(SMALL_DEF,FLOAT_DEF)
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index e0776cf67d..6cdfd1c989 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -31,6 +31,7 @@
#include "bif.h"
#include "erl_binary.h"
#include "erl_bits.h"
+#include "erl_map.h"
#include "packet_parser.h"
#include "erl_gc.h"
#define ERTS_WANT_DB_INTERNAL__
@@ -785,10 +786,10 @@ Uint32 make_hash(Eterm term_arg)
unsigned op;
/* Must not collide with the real tag_val_def's: */
-#define MAKE_HASH_TUPLE_OP 0x10
-#define MAKE_HASH_FUN_OP 0x11
-#define MAKE_HASH_CDR_PRE_OP 0x12
-#define MAKE_HASH_CDR_POST_OP 0x13
+#define MAKE_HASH_TUPLE_OP 0x11
+#define MAKE_HASH_FUN_OP 0x12
+#define MAKE_HASH_CDR_PRE_OP 0x13
+#define MAKE_HASH_CDR_POST_OP 0x14
/*
** Convenience macro for calculating a bytewise hash on an unsigned 32 bit
@@ -2007,6 +2008,18 @@ tailrecur_ne:
++bb;
goto term_array;
}
+ case MAP_SUBTAG:
+ {
+ aa = map_val_rel(a, a_base);
+ if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa)
+ goto not_equal;
+ bb = map_val_rel(b,b_base);
+ if ((sz = map_get_size((map_t*)aa)) == 0) goto pop_next;
+ aa += 2;
+ bb += 2;
+ sz += 1; /* increment for tuple-keys */
+ goto term_array;
+ }
case REFC_BINARY_SUBTAG:
case HEAP_BINARY_SUBTAG:
case SUB_BINARY_SUBTAG:
@@ -2281,7 +2294,7 @@ static int cmpbytes(byte *s1, int l1, byte *s2, int l2)
*
* According to the Erlang Standard, types are orderered as follows:
* numbers < (characters) < atoms < refs < funs < ports < pids <
- * tuples < [] < conses < binaries.
+ * tuples < maps < [] < conses < binaries.
*
* Note that characters are currently not implemented.
*
@@ -2464,7 +2477,25 @@ tailrecur_ne:
++aa;
++bb;
goto term_array;
+ case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE) :
+ if (!is_map_rel(b,b_base)) {
+ a_tag = MAP_DEF;
+ goto mixed_types;
+ }
+ aa = (Eterm *)map_val_rel(a,a_base);
+ bb = (Eterm *)map_val_rel(b,b_base);
+ i = map_get_size((map_t*)aa);
+ if (i != map_get_size((map_t*)bb)) {
+ RETURN_NEQ((int)(i - map_get_size((map_t*)bb)));
+ }
+ if (i == 0) {
+ goto pop_next;
+ }
+ aa += 2;
+ bb += 2;
+ i += 1; /* increment for tuple-keys */
+ goto term_array;
case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
if (!is_float_rel(b,b_base)) {
a_tag = FLOAT_DEF;
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index e09988e2c5..c3687681cf 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -31,6 +31,7 @@
#include "erl_process.h"
#include "bif.h"
#include "big.h"
+#include "erl_map.h"
#include "hipe_debug.h"
#include "hipe_mode_switch.h"
#include "hipe_arch.h"
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index bf25ba82af..32694a8f97 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -36,6 +36,7 @@
#include "beam_load.h"
#include "hipe_mode_switch.h"
#include "hipe_debug.h"
+#include "erl_map.h"
static const char dashes[2*sizeof(long)+5] = {
[0 ... 2*sizeof(long)+3] = '-'
--
cgit v1.2.3
From 92303a2e1abdf74aa3bc3af095131a59a601c45e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 1 Oct 2013 19:27:54 +0200
Subject: erts: Add phash2 Map functionality
---
erts/emulator/beam/utils.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 6cdfd1c989..bc7e91295d 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1088,6 +1088,7 @@ make_hash2(Eterm term)
#define HCONST_13 0x08d12e65UL
#define HCONST_14 0xa708a81eUL
#define HCONST_15 0x454021d7UL
+#define HCONST_16 0xe3779b90UL
#define UINT32_HASH_2(Expr1, Expr2, AConst) \
do { \
@@ -1190,6 +1191,28 @@ make_hash2(Eterm term)
term = elem[1];
}
break;
+ case MAP_SUBTAG:
+ {
+ map_t *mp = (map_t *)map_val(term);
+ int i;
+ int size = map_get_size(mp);
+ Eterm *ks = map_get_keys(mp);
+ Eterm *vs = map_get_values(mp);
+ UINT32_HASH(size, HCONST_16);
+ if (size == 0) /* Empty Map */
+ goto hash2_common;
+
+ for (i = size - 1; i >= 0; i--) {
+ tmp = vs[i];
+ ESTACK_PUSH(s, tmp);
+ }
+ for (i = size - 1; i >= 1; i--) {
+ tmp = ks[i];
+ ESTACK_PUSH(s, tmp);
+ }
+ term = ks[0];
+ }
+ break;
case EXPORT_SUBTAG:
{
Export* ep = *((Export **) (export_val(term) + 1));
--
cgit v1.2.3
From 63ef0bbfdfb70673fe7f3ce2fc6fa4f0f801747d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 19 Sep 2013 17:48:28 +0200
Subject: erts: Add the size-testing guard BIF map_size/1
---
erts/emulator/beam/beam_emu.c | 2 ++
erts/emulator/beam/beam_load.c | 2 ++
erts/emulator/beam/bif.tab | 1 +
erts/emulator/beam/erl_bif_guard.c | 23 +++++++++++++++++++++++
erts/emulator/beam/erl_map.c | 23 +++++++++++++++++++++--
erts/emulator/beam/global.h | 1 +
lib/stdlib/src/erl_internal.erl | 2 ++
7 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 5c955b9566..1cad31be2c 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -5080,6 +5080,8 @@ translate_gc_bif(void* gcf)
return bit_size_1;
} else if (gcf == erts_gc_byte_size_1) {
return byte_size_1;
+ } else if (gcf == erts_gc_map_size_1) {
+ return map_size_1;
} else if (gcf == erts_gc_abs_1) {
return abs_1;
} else if (gcf == erts_gc_float_1) {
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 58207ec75b..b589d1c930 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -3783,6 +3783,8 @@ gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif,
op->a[1].val = (BeamInstr) (void *) erts_gc_bit_size_1;
} else if (bf == byte_size_1) {
op->a[1].val = (BeamInstr) (void *) erts_gc_byte_size_1;
+ } else if (bf == map_size_1) {
+ op->a[1].val = (BeamInstr) (void *) erts_gc_map_size_1;
} else if (bf == abs_1) {
op->a[1].val = (BeamInstr) (void *) erts_gc_abs_1;
} else if (bf == float_1) {
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 42515d0494..a8623fcf61 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -579,6 +579,7 @@ bif os:unsetenv/1
bif re:inspect/2
ubif erlang:is_map/1
+ubif erlang:map_size/1
bif map:to_list/1
bif map:new/0
bif map:get/2
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index a715756c15..bbd8aa31d9 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -33,6 +33,7 @@
#include "bif.h"
#include "big.h"
#include "erl_binary.h"
+#include "erl_map.h"
static Eterm gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live);
@@ -455,6 +456,28 @@ Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live)
}
}
+Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live)
+{
+ Eterm arg = reg[live];
+ if (is_map(arg)) {
+ map_t *mp = (map_t*)map_val(arg);
+ Uint size = map_get_size(mp);
+ if (IS_USMALL(0, size)) {
+ return make_small(size);
+ } else {
+ Eterm* hp;
+ if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) {
+ erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live);
+ }
+ hp = p->htop;
+ p->htop += BIG_UINT_HEAP_SIZE;
+ return uint_to_big(size, hp);
+ }
+ } else {
+ BIF_ERROR(p, BADARG);
+ }
+}
+
Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live)
{
Eterm arg;
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 1fd7943c30..6a59fa29b8 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -30,10 +30,8 @@
#include "error.h"
#include "bif.h"
-
#include "erl_map.h"
-
BIF_RETTYPE map_to_list_1(BIF_ALIST_1) {
if (is_map(BIF_ARG_1)) {
Uint n;
@@ -58,6 +56,27 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) {
BIF_ERROR(BIF_P, BADARG);
}
+
+/* erlang:map_size/1
+ * the corresponding instruction is implemented in:
+ * beam/erl_bif_guard.c
+ */
+
+BIF_RETTYPE map_size_1(BIF_ALIST_1) {
+ if (is_map(BIF_ARG_1)) {
+ Eterm *hp;
+ Uint hsz = 0;
+ map_t *mp = (map_t*)map_val(BIF_ARG_1);
+ Uint n = map_get_size(mp);
+
+ erts_bld_uint(NULL, &hsz, n);
+ hp = HAlloc(BIF_P, hsz);
+ BIF_RET(erts_bld_uint(&hp, NULL, n));
+ }
+
+ BIF_ERROR(BIF_P, BADARG);
+}
+
BIF_RETTYPE map_new_0(BIF_ALIST_0) {
Eterm* hp;
Eterm tup;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 83a8911a36..8fcb95d0e2 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -998,6 +998,7 @@ Eterm erts_gc_length_1(Process* p, Eterm* reg, Uint live);
Eterm erts_gc_size_1(Process* p, Eterm* reg, Uint live);
Eterm erts_gc_bit_size_1(Process* p, Eterm* reg, Uint live);
Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live);
+Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live);
Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live);
Eterm erts_gc_float_1(Process* p, Eterm* reg, Uint live);
Eterm erts_gc_round_1(Process* p, Eterm* reg, Uint live);
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 28de7205ea..480675554a 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -70,6 +70,7 @@ guard_bif(bit_size, 1) -> true;
guard_bif(byte_size, 1) -> true;
guard_bif(element, 2) -> true;
guard_bif(self, 0) -> true;
+guard_bif(map_size, 1) -> true;
guard_bif(node, 0) -> true;
guard_bif(node, 1) -> true;
guard_bif(tuple_size, 1) -> true;
@@ -335,6 +336,7 @@ bif(list_to_pid, 1) -> true;
bif(list_to_tuple, 1) -> true;
bif(load_module, 2) -> true;
bif(make_ref, 0) -> true;
+bif(map_size,1) -> true;
bif(max,2) -> true;
bif(min,2) -> true;
bif(module_loaded, 1) -> true;
--
cgit v1.2.3
From 43ed0cc716039c3b2f65a5395f00424169639309 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 21 May 2013 18:11:39 +0200
Subject: erts: Add the type-testing guard BIF is_map/1
To add a type-testing guard BIF, the following steps are needed:
* The BIF itself is added to bif.tab (note that it should be declared
using "ubif", not "bif"), and its implementation to erl_bif_op.c.
* erl_internal must be modified in 3 places: The type test must be
recognized as guard BIF, as a type test, and it must be auto-imported.
* There must be an instruction that implements the same type test as
the BIF (it will be used in guards). beam_utils:bif_to_test/3 must
be updated to recognize the new guard BIF.
---
erts/emulator/beam/bif.tab | 1 +
lib/compiler/src/beam_utils.erl | 1 +
lib/stdlib/src/erl_internal.erl | 3 +++
3 files changed, 5 insertions(+)
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index a8623fcf61..8f85f931c9 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -572,6 +572,7 @@ bif erlang:binary_to_float/1
bif io:printable_range/0
bif os:unsetenv/1
+
#
# New in R17A
#
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 36f3200d11..a3f16cfa8f 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -152,6 +152,7 @@ bif_to_test(is_function, [_]=Ops, Fail) -> {test,is_function,Fail,Ops};
bif_to_test(is_function, [_,_]=Ops, Fail) -> {test,is_function2,Fail,Ops};
bif_to_test(is_integer, [_]=Ops, Fail) -> {test,is_integer,Fail,Ops};
bif_to_test(is_list, [_]=Ops, Fail) -> {test,is_list,Fail,Ops};
+bif_to_test(is_map, [_]=Ops, Fail) -> {test,is_map,Fail,Ops};
bif_to_test(is_number, [_]=Ops, Fail) -> {test,is_number,Fail,Ops};
bif_to_test(is_pid, [_]=Ops, Fail) -> {test,is_pid,Fail,Ops};
bif_to_test(is_port, [_]=Ops, Fail) -> {test,is_port,Fail,Ops};
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 480675554a..edfb097de0 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -83,6 +83,7 @@ guard_bif(is_function, 1) -> true;
guard_bif(is_function, 2) -> true;
guard_bif(is_integer, 1) -> true;
guard_bif(is_list, 1) -> true;
+guard_bif(is_map, 1) -> true;
guard_bif(is_number, 1) -> true;
guard_bif(is_pid, 1) -> true;
guard_bif(is_port, 1) -> true;
@@ -114,6 +115,7 @@ new_type_test(is_function, 1) -> true;
new_type_test(is_function, 2) -> true;
new_type_test(is_integer, 1) -> true;
new_type_test(is_list, 1) -> true;
+new_type_test(is_map, 1) -> true;
new_type_test(is_number, 1) -> true;
new_type_test(is_pid, 1) -> true;
new_type_test(is_port, 1) -> true;
@@ -316,6 +318,7 @@ bif(is_function, 1) -> true;
bif(is_function, 2) -> true;
bif(is_integer, 1) -> true;
bif(is_list, 1) -> true;
+bif(is_map, 1) -> true;
bif(is_number, 1) -> true;
bif(is_pid, 1) -> true;
bif(is_port, 1) -> true;
--
cgit v1.2.3
From bc0960c095e9482ba0f452c9df3623f5c9c54e1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 30 Sep 2013 20:13:07 +0200
Subject: erts: Introduce more Maps BIFs
* map:remove/2
* map:keys/1
* map:values/1
* map:is_key/2
* map:update/3
- Equivalent to ':=' operator in #{ K := V } maps.
* map:from_list/1
- map:from_list/1 takes any unsorted key/value list, [{K,V}], and
produces a map. Duplicate keys are removed. The latest key is kept.
* map:find/2
- Searches for a pair that *equals* input key.
* map:merge/2
- Merge two maps to one map.
---
erts/emulator/beam/bif.tab | 11 +-
erts/emulator/beam/erl_map.c | 565 ++++++++++++++++++++++++++++++++++++++++---
erts/emulator/beam/erl_map.h | 6 +-
3 files changed, 547 insertions(+), 35 deletions(-)
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 8f85f931c9..306861a4c5 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -572,7 +572,6 @@ bif erlang:binary_to_float/1
bif io:printable_range/0
bif os:unsetenv/1
-
#
# New in R17A
#
@@ -582,9 +581,17 @@ bif re:inspect/2
ubif erlang:is_map/1
ubif erlang:map_size/1
bif map:to_list/1
-bif map:new/0
+bif map:find/2
bif map:get/2
+bif map:from_list/1
+bif map:is_key/2
+bif map:keys/1
+bif map:merge/2
+bif map:new/0
bif map:put/3
+bif map:remove/2
+bif map:update/3
+bif map:values/1
#
# Obsolete
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 6a59fa29b8..6fd60f0689 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -32,6 +32,57 @@
#include "erl_map.h"
+/* BIFs
+ *
+ * DONE:
+ * - erlang:is_map/1
+ * - erlang:map_size/1
+ *
+ * - map:find/2
+ * - map:from_list/1
+ * - map:get/2
+ * - map:is_key/2
+ * - map:keys/1
+ * - map:merge/2
+ * - map:new/0
+ * - map:put/3
+ * - map:remove/2
+ * - map:to_list/1
+ * - map:update/3
+ * - map:values/1
+ *
+ * TODO:
+ * - map:foldl/3
+ * - map:foldr/3
+ * - map:map/3
+ * - map:size/1
+ * - map:without/2
+ *
+ */
+
+/* erlang:map_size/1
+ * the corresponding instruction is implemented in:
+ * beam/erl_bif_guard.c
+ */
+
+BIF_RETTYPE map_size_1(BIF_ALIST_1) {
+ if (is_map(BIF_ARG_1)) {
+ Eterm *hp;
+ Uint hsz = 0;
+ map_t *mp = (map_t*)map_val(BIF_ARG_1);
+ Uint n = map_get_size(mp);
+
+ erts_bld_uint(NULL, &hsz, n);
+ hp = HAlloc(BIF_P, hsz);
+ BIF_RET(erts_bld_uint(&hp, NULL, n));
+ }
+
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+/* map:to_list/1
+ */
+
BIF_RETTYPE map_to_list_1(BIF_ALIST_1) {
if (is_map(BIF_ARG_1)) {
Uint n;
@@ -56,43 +107,40 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) {
BIF_ERROR(BIF_P, BADARG);
}
-
-/* erlang:map_size/1
- * the corresponding instruction is implemented in:
- * beam/erl_bif_guard.c
+/* map:find/2
+ * return value if key *equals* a key in the map
*/
-BIF_RETTYPE map_size_1(BIF_ALIST_1) {
- if (is_map(BIF_ARG_1)) {
- Eterm *hp;
- Uint hsz = 0;
- map_t *mp = (map_t*)map_val(BIF_ARG_1);
- Uint n = map_get_size(mp);
+BIF_RETTYPE map_find_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm *hp, *ks,*vs, key, res;
+ map_t *mp;
+ Uint n,i;
- erts_bld_uint(NULL, &hsz, n);
- hp = HAlloc(BIF_P, hsz);
- BIF_RET(erts_bld_uint(&hp, NULL, n));
- }
+ mp = (map_t*)map_val(BIF_ARG_2);
+ key = BIF_ARG_1;
+ n = map_get_size(mp);
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+ for( i = 0; i < n; i++) {
+ if (CMP(ks[i], key)==0) {
+ hp = HAlloc(BIF_P, 3);
+ res = make_tuple(hp);
+ *hp++ = make_arityval(2);
+ *hp++ = am_ok;
+ *hp++ = vs[i];
+ BIF_RET(res);
+ }
+ }
+ BIF_RET(am_error);
+ }
BIF_ERROR(BIF_P, BADARG);
}
-
-BIF_RETTYPE map_new_0(BIF_ALIST_0) {
- Eterm* hp;
- Eterm tup;
- map_t *mp;
-
- hp = HAlloc(BIF_P, (3 + 1));
- tup = make_tuple(hp);
- *hp++ = make_arityval(0);
-
- mp = (map_t*)hp;
- mp->thing_word = MAP_HEADER;
- mp->size = 0;
- mp->keys = tup;
-
- BIF_RET(make_map(mp));
-}
+/* map:get/2
+ * return value if key *matches* a key in the map
+ * exception bad_key if none matches
+ */
BIF_RETTYPE map_get_2(BIF_ALIST_2) {
if (is_map(BIF_ARG_2)) {
@@ -136,6 +184,297 @@ error:
BIF_ERROR(BIF_P, BADARG);
}
+/* map:from_list/1
+ * List may be unsorted [{K,V}]
+ */
+
+BIF_RETTYPE map_from_list_1(BIF_ALIST_1) {
+ Eterm *kv, item = BIF_ARG_1;
+ Eterm *hp, *thp,*vs, *ks, keys, res;
+ map_t *mp;
+ Uint size = 0, unused_size = 0;
+ Sint c = 0;
+ Sint idx = 0;
+
+ if (is_list(item) || is_nil(item)) {
+
+ /* Calculate size and check validity */
+
+ while(is_list(item)) {
+ res = CAR(list_val(item));
+ if (is_not_tuple(res))
+ goto error;
+
+ kv = tuple_val(res);
+ if (*kv != make_arityval(2))
+ goto error;
+
+ size++;
+ item = CDR(list_val(item));
+ }
+
+ if (is_not_nil(item))
+ goto error;
+
+ hp = HAlloc(BIF_P, 3 + 1 + (2 * size));
+ thp = hp;
+ keys = make_tuple(hp);
+ *hp++ = make_arityval(size);
+ ks = hp;
+ hp += size;
+ mp = (map_t*)hp;
+ res = make_map(mp);
+ hp += MAP_HEADER_SIZE;
+ vs = hp;
+
+ mp->thing_word = MAP_HEADER;
+ mp->size = size; /* set later, might shrink*/
+ mp->keys = keys;
+
+ if (size == 0)
+ BIF_RET(res);
+
+ item = BIF_ARG_1;
+
+ /* first entry */
+ kv = tuple_val(CAR(list_val(item)));
+ ks[0] = kv[1];
+ vs[0] = kv[2];
+ size = 1;
+ item = CDR(list_val(item));
+
+ /* insert sort key/value pairs */
+ while(is_list(item)) {
+
+ kv = tuple_val(CAR(list_val(item)));
+
+ /* compare ks backwards
+ * idx represent word index to be written (hole position).
+ * We cannot copy the elements when searching since we might
+ * have an equal key. So we search for just the index first =(
+ *
+ * It is perhaps faster to move the values in the first pass.
+ * Check for uniqueness during insert phase and then have a
+ * second phace compacting the map if duplicates are found
+ * during insert. .. or do someother sort .. shell-sort perhaps.
+ */
+
+ idx = size;
+
+ while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; }
+
+ if (c == 0) {
+ /* last compare was equal,
+ * i.e. we have to release memory
+ * and overwrite that key/value
+ */
+ ks[idx-1] = kv[1];
+ vs[idx-1] = kv[2];
+ unused_size++;
+ } else {
+ Uint i = size;
+ while(i > idx) {
+ ks[i] = ks[i-1];
+ vs[i] = vs[i-1];
+ i--;
+ }
+ ks[idx] = kv[1];
+ vs[idx] = kv[2];
+ size++;
+ }
+ item = CDR(list_val(item));
+ }
+
+ if (unused_size) {
+ /* the key tuple is embedded in the heap
+ * write a bignum to clear it.
+ */
+ /* release values as normal since they are on the top of the heap */
+
+ ks[size] = make_pos_bignum_header(unused_size - 1);
+ HRelease(BIF_P, vs + size + unused_size, vs + size);
+ }
+
+ *thp = make_arityval(size);
+ mp->size = size;
+ BIF_RET(res);
+ }
+
+error:
+
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+/* map:is_key/2
+ */
+
+BIF_RETTYPE map_is_key_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm *ks, key;
+ map_t *mp;
+ Uint n,i;
+
+ mp = (map_t*)map_val(BIF_ARG_2);
+ key = BIF_ARG_1;
+ n = map_get_size(mp);
+ ks = map_get_keys(mp);
+
+ if (n == 0)
+ BIF_RET(am_false);
+
+ if (is_immed(key)) {
+ for( i = 0; i < n; i++) {
+ if (ks[i] == key) {
+ BIF_RET(am_true);
+ }
+ }
+ }
+
+ for( i = 0; i < n; i++) {
+ if (eq(ks[i], key)) {
+ BIF_RET(am_true);
+ }
+ }
+ BIF_RET(am_false);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+/* map:keys/1
+ */
+
+BIF_RETTYPE map_keys_1(BIF_ALIST_1) {
+ if (is_map(BIF_ARG_1)) {
+ Eterm *hp, *ks, res = NIL;
+ map_t *mp;
+ Uint n;
+
+ mp = (map_t*)map_val(BIF_ARG_1);
+ n = map_get_size(mp);
+
+ if (n == 0)
+ BIF_RET(res);
+
+ hp = HAlloc(BIF_P, (2 * n));
+ ks = map_get_keys(mp);
+
+ while(n--) {
+ res = CONS(hp, ks[n], res); hp += 2;
+ }
+
+ BIF_RET(res);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+/* map:merge/2
+ */
+
+BIF_RETTYPE map_merge_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) {
+ Eterm *hp,*thp;
+ Eterm tup;
+ Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2;
+ map_t *mp1,*mp2,*mp_new;
+ Uint n1,n2,i1,i2,need,unused_size=0;
+ int c = 0;
+
+ mp1 = (map_t*)map_val(BIF_ARG_1);
+ mp2 = (map_t*)map_val(BIF_ARG_2);
+ n1 = map_get_size(mp1);
+ n2 = map_get_size(mp2);
+
+ need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2);
+
+ hp = HAlloc(BIF_P, need);
+ thp = hp;
+ tup = make_tuple(thp);
+ ks = hp + 1; hp += 1 + n1 + n2;
+ mp_new = (map_t*)hp; hp += MAP_HEADER_SIZE;
+ vs = hp; hp += n1 + n2;
+
+ mp_new->thing_word = MAP_HEADER;
+ mp_new->size = 0;
+ mp_new->keys = tup;
+
+ i1 = 0; i2 = 0;
+ ks1 = map_get_keys(mp1);
+ vs1 = map_get_values(mp1);
+ ks2 = map_get_keys(mp2);
+ vs2 = map_get_values(mp2);
+
+ while(i1 < n1 && i2 < n2) {
+ c = CMP(ks1[i1],ks2[i2]);
+ if ( c == 0) {
+ /* use righthand side arguments map value,
+ * but advance both maps */
+ *ks++ = ks2[i2];
+ *vs++ = vs2[i2];
+ i1++, i2++, unused_size++;
+ } else if ( c < 0) {
+ *ks++ = ks1[i1];
+ *vs++ = vs1[i1];
+ i1++;
+ } else {
+ *ks++ = ks2[i2];
+ *vs++ = vs2[i2];
+ i2++;
+ }
+ }
+
+ /* copy remaining */
+ while (i1 < n1) {
+ *ks++ = ks1[i1];
+ *vs++ = vs1[i1];
+ i1++;
+ }
+
+ while (i2 < n2) {
+ *ks++ = ks2[i2];
+ *vs++ = vs2[i2];
+ i2++;
+ }
+
+ if (unused_size) {
+ /* the key tuple is embedded in the heap, write a bignum to clear it.
+ *
+ * release values as normal since they are on the top of the heap
+ * size = n1 + n1 - unused_size
+ */
+
+ *ks = make_pos_bignum_header(unused_size - 1);
+ HRelease(BIF_P, vs + unused_size, vs);
+ }
+
+ mp_new->size = n1 + n2 - unused_size;
+ *thp = make_arityval(n1 + n2 - unused_size);
+
+ BIF_RET(make_map(mp_new));
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+/* map:new/2
+ */
+
+BIF_RETTYPE map_new_0(BIF_ALIST_0) {
+ Eterm* hp;
+ Eterm tup;
+ map_t *mp;
+
+ hp = HAlloc(BIF_P, (3 + 1));
+ tup = make_tuple(hp);
+ *hp++ = make_arityval(0);
+
+ mp = (map_t*)hp;
+ mp->thing_word = MAP_HEADER;
+ mp->size = 0;
+ mp->keys = tup;
+
+ BIF_RET(make_map(mp));
+}
+
+/* map:put/3
+ */
+
BIF_RETTYPE map_put_3(BIF_ALIST_3) {
if (is_map(BIF_ARG_3)) {
Sint n,i;
@@ -242,3 +581,167 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) {
BIF_ERROR(BIF_P, BADARG);
}
+
+/* map:remove/3
+ */
+
+BIF_RETTYPE map_remove_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Sint n;
+ Sint found = 0;
+ Uint need;
+ Eterm *thp, *mhp;
+ Eterm *ks, *vs, res, key,tup;
+ map_t *mp = (map_t*)map_val(BIF_ARG_2);
+
+ key = BIF_ARG_1;
+ n = map_get_size(mp);
+
+ if (n == 0)
+ BIF_RET(BIF_ARG_2);
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+
+ /* Assume key exists.
+ * Release allocated if it didn't.
+ * Allocate key tuple first.
+ */
+
+ need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */
+ thp = HAlloc(BIF_P, need);
+ mhp = thp + n; /* offset with tuple heap size */
+
+ tup = make_tuple(thp);
+ *thp++ = make_arityval(n - 1);
+
+ res = make_map(mhp);
+ *mhp++ = MAP_HEADER;
+ *mhp++ = n - 1;
+ *mhp++ = tup;
+
+ if (is_immed(key)) {
+ while(n--) {
+ if (*ks == key) {
+ ks++;
+ vs++;
+ found = 1;
+ } else {
+ *mhp++ = *vs++;
+ *thp++ = *ks++;
+ }
+ }
+ } else {
+ while(n--) {
+ if (eq(*ks, key)) {
+ ks++;
+ vs++;
+ found = 1;
+ } else {
+ *mhp++ = *vs++;
+ *thp++ = *ks++;
+ }
+ }
+ }
+
+ if (found)
+ BIF_RET(res);
+
+ /* Not found, remove allocated memory
+ * and return previous map.
+ */
+ HRelease(BIF_P, thp + need, thp);
+ BIF_RET(BIF_ARG_2);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+/* map:update/3
+ */
+
+BIF_RETTYPE map_update_3(BIF_ALIST_3) {
+ if (is_map(BIF_ARG_3)) {
+ Sint n,i;
+ Sint found = 0;
+ Eterm* hp,*shp;
+ Eterm *ks,*vs, res, key;
+ map_t *mp = (map_t*)map_val(BIF_ARG_3);
+
+ key = BIF_ARG_1;
+ n = map_get_size(mp);
+
+ if (n == 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+
+ /* only allocate for values,
+ * assume key-tuple will be intact
+ */
+
+ hp = HAlloc(BIF_P, 3 + n);
+ shp = hp;
+ res = make_map(hp);
+ *hp++ = MAP_HEADER;
+ *hp++ = n;
+ *hp++ = mp->keys;
+
+ if (is_immed(key)) {
+ for( i = 0; i < n; i ++) {
+ if (ks[i] == key) {
+ *hp++ = BIF_ARG_2;
+ vs++;
+ found = 1;
+ } else {
+ *hp++ = *vs++;
+ }
+ }
+ } else {
+ for( i = 0; i < n; i ++) {
+ if (eq(ks[i], key)) {
+ *hp++ = BIF_ARG_2;
+ vs++;
+ found = 1;
+ } else {
+ *hp++ = *vs++;
+ }
+ }
+ }
+
+ if (found)
+ BIF_RET(res);
+
+ HRelease(BIF_P, shp + 3 + n, shp);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+
+/* map:values/1
+ */
+
+BIF_RETTYPE map_values_1(BIF_ALIST_1) {
+ if (is_map(BIF_ARG_1)) {
+ Eterm *hp, *vs, res = NIL;
+ map_t *mp;
+ Uint n;
+
+ mp = (map_t*)map_val(BIF_ARG_1);
+ n = map_get_size(mp);
+
+ if (n == 0)
+ BIF_RET(res);
+
+ hp = HAlloc(BIF_P, (2 * n));
+ vs = map_get_values(mp);
+
+ while(n--) {
+ res = CONS(hp, vs[n], res); hp += 2;
+ }
+
+ BIF_RET(res);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index ca8ce0f974..4f0d26e100 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -33,11 +33,12 @@ typedef struct map_s {
*
* -----------
* Eterm THING
- * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity
+ * Uint size
+ * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = size
* ----
* Eterm V1
* ...
- * Eterm Vn, where n = arity
+ * Eterm Vn, where n = size
* -----------
*/
@@ -59,6 +60,7 @@ typedef struct map_s {
#define map_get_size(x) (((map_t*)(x))->size)
#define MAP_HEADER _make_header(1,_TAG_HEADER_MAP)
+#define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm))
#endif
--
cgit v1.2.3
From d6b62db709a9e8e80689416bd7a5e5444ac3d3bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 17 May 2013 11:49:48 +0200
Subject: stdlib: Update printing to handle Maps
---
lib/stdlib/src/io_lib.erl | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 375d05f359..886939761c 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -257,7 +257,9 @@ write(T, D) when is_tuple(T) ->
[write(element(1, T), D-1)|
write_tail(tl(tuple_to_list(T)), D-1, $,)],
$}]
- end.
+ end;
+%write(Term, D) when is_map(Term) -> write_map(Term, D);
+write(Term, D) -> write_map(Term, D).
%% write_tail(List, Depth, CharacterBeforeDots)
%% Test the terminating case first as this looks better with depth.
@@ -275,6 +277,18 @@ write_port(Port) ->
write_ref(Ref) ->
erlang:ref_to_list(Ref).
+write_map(Map, D) when is_integer(D) ->
+ [$#,${,write_map_body(map:to_list(Map), D),$}].
+
+write_map_body(_, 0) -> "...";
+write_map_body([],_) -> [];
+write_map_body([{K,V}],D) -> write_map_assoc(K,V,D);
+write_map_body([{K,V}|KVs], D) ->
+ [write_map_assoc(K,V,D),$, | write_map_body(KVs,D-1)].
+
+write_map_assoc(K,V,D) ->
+ [write(K,D - 1),"=>",write(V,D-1)].
+
write_binary(B, D) when is_integer(D) ->
[$<,$<,write_binary_body(B, D),$>,$>].
--
cgit v1.2.3
From 542c79e9685adbdab398856af99aaf9137966ec9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 4 Jun 2012 00:36:11 +0200
Subject: erl_eval: Add functions to interpreter for maps
---
lib/stdlib/src/erl_eval.erl | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 18d8148b15..335134d1f0 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -239,6 +239,21 @@ expr({record,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, {undef_record,Name}, stacktrace());
expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, {undef_record,Name}, stacktrace());
+
+%% map
+expr({map_field,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
+ {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none),
+ {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none),
+ ret_expr({K,V}, merge_bindings(Bs1,Bs2), RBs);
+expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) ->
+ {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs),
+ {Vs,Bs} = expr_list(Es, Bs1, Lf, Ef),
+ ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, Map0, Vs), Bs, RBs);
+expr({map,_,Es}, Bs0, Lf, Ef, RBs) ->
+ {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef),
+ ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, map:new(), Vs), Bs, RBs);
+
+
expr({block,_,Es}, Bs, Lf, Ef, RBs) ->
exprs(Es, Bs, Lf, Ef, RBs);
expr({'if',_,Cs}, Bs, Lf, Ef, RBs) ->
@@ -994,6 +1009,7 @@ type_test(port) -> is_port;
type_test(function) -> is_function;
type_test(binary) -> is_binary;
type_test(record) -> is_record;
+type_test(map) -> is_map;
type_test(Test) -> Test.
@@ -1075,6 +1091,9 @@ match1({tuple,_,Elts}, Tuple, Bs, BBs)
match_tuple(Elts, Tuple, 1, Bs, BBs);
match1({tuple,_,_}, _, _Bs, _BBs) ->
throw(nomatch);
+match1({map,_,Fs}, Map, Bs, BBs) ->
+ match_map(Fs, Map, Bs, BBs);
+
match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) ->
eval_bits:match_bits(Fs, B, Bs0, BBs,
match_fun(BBs),
@@ -1118,6 +1137,15 @@ match_tuple([E|Es], Tuple, I, Bs0, BBs) ->
match_tuple([], _, _, Bs, _BBs) ->
{match,Bs}.
+match_map([{map_field, _, K, V}|Fs], Map, Bs0, BBs) ->
+ Vm = try map:get(element(3,K),Map)
+ catch error:_ -> throw(nomatch)
+ end,
+ {match, Bs} = match1(V, Vm, Bs0, BBs),
+ match_map(Fs, Map, Bs, BBs);
+match_map([], _, Bs, _) ->
+ {match, Bs}.
+
%% match_list(PatternList, TermList, Bindings) ->
%% {match,NewBindings} | nomatch
%% Try to match a list of patterns against a list of terms with the
--
cgit v1.2.3
From 60e7d2695dcf8ac2484db87c649ca69f1d0b763c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 27 May 2013 17:35:16 +0200
Subject: erts: Add testsuite for Maps
---
erts/emulator/test/Makefile | 1 +
erts/emulator/test/map_SUITE.erl | 532 +++++++++++++++++++++++++++++++++++++++
2 files changed, 533 insertions(+)
create mode 100644 erts/emulator/test/map_SUITE.erl
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index f02ca3cb98..0b0568c31a 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -68,6 +68,7 @@ MODULES= \
hash_SUITE \
hibernate_SUITE \
list_bif_SUITE \
+ map_SUITE \
match_spec_SUITE \
module_info_SUITE \
monitor_SUITE \
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
new file mode 100644
index 0000000000..bbc5aa07b4
--- /dev/null
+++ b/erts/emulator/test/map_SUITE.erl
@@ -0,0 +1,532 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(map_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2
+ ]).
+
+-export([
+ t_build_and_match_literals/1,
+ t_update_literals/1,t_match_and_update_literals/1,
+ t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
+ t_guard_receive/1, t_guard_fun/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ t_size/1, t_map_size/1,
+
+ %% BIFs
+ t_bif_map_get/1,
+ t_bif_map_find/1,
+ t_bif_map_is_key/1,
+ t_bif_map_keys/1,
+ t_bif_map_new/1,
+ t_bif_map_put/1,
+ t_bif_map_remove/1,
+ t_bif_map_values/1
+ ]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() -> [
+ t_build_and_match_literals,
+ t_update_literals, t_match_and_update_literals,
+ t_guard_bifs, t_guard_sequence, t_guard_update,
+ t_guard_receive,t_guard_fun, t_list_comprehension,
+ t_map_sort_literals,
+
+ %% Specific Map BIFs
+ t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
+ t_bif_map_keys,t_bif_map_new,t_bif_map_put,
+ t_bif_map_remove,t_bif_map_values
+ ].
+
+groups() -> [].
+
+init_per_suite(Config) -> Config.
+end_per_suite(_Config) -> ok.
+
+init_per_group(_GroupName, Config) -> Config.
+end_per_group(_GroupName, Config) -> Config.
+
+%% tests
+
+t_build_and_match_literals(Config) when is_list(Config) ->
+ #{} = id(#{}),
+ #{1=>a} = id(#{1=>a}),
+ #{1=>a,2=>b} = id(#{1=>a,2=>b}),
+ #{1=>a,2=>b,3=>"c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1=>a,2=>b,3=>"c","4"=>"d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
+ #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
+ #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
+
+ #{<<"hi all">> => 1} = id(#{<<"hi",32,"all">> => 1}),
+
+ #{a=>X,a=>X=3,b=>4} = id(#{a=>3,b=>4}), % weird but ok =)
+
+ #{ a=>#{ b=>#{c => third, b=>second}}, b=>first} =
+ id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
+
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first} =
+ id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+
+ %% error case
+ %V = 32,
+ %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3,x=>2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x=>2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{x=>"three"}))),
+ ok.
+
+%% Tests size(Map).
+
+t_size(Config) when is_list(Config) ->
+% 0 = size(#{}),
+% 1 = size(#{a=>1}),
+% 1 = size(#{a=>#{a=>1}}),
+% 2 = size(#{a=>1, b=>2}),
+% 3 = size(#{a=>1, b=>2, b=>"3"}),
+ ok.
+
+t_map_size(Config) when is_list(Config) ->
+ 0 = map_size(id(#{})),
+ 1 = map_size(id(#{a=>1})),
+ 1 = map_size(id(#{a=>"wat"})),
+ 2 = map_size(id(#{a=>1, b=>2})),
+ 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
+
+ true = map_is_size(#{a=>1}, 1),
+ true = map_is_size(#{a=>1, a=>2}, 2),
+ M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M#{ "a" => 2}, 2),
+ false = map_is_size(M#{ "a" => 2}, 3),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch map_size([])),
+ {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
+ {'EXIT',{badarg,_}} = (catch map_size(1)),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+% test map updates without matching
+t_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>1,y=>2,z=>3,q=>4},
+ #{x=>"d",q=>"4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+loop_update_literals_x_q(Map, []) -> Map;
+loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
+ loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).
+
+% test map updates with matching
+t_match_and_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
+ #{x=>16,q=>21,y=>"untouched",z=>"also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ {1,2},{3,4},{5,6},{7,8}
+ ]),
+ M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat}),
+ M1 = id(#{}),
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 => another_number, int => 3 } = M2#{ 4 => another_number },
+ ok.
+
+loop_match_and_update_literals_x_q(Map, []) -> Map;
+loop_match_and_update_literals_x_q(#{q=>Q0,x=>X0} = Map, [{X,Q}|Vs]) ->
+ loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+
+t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_head(#{a=>1}),
+ false = map_guard_head([]),
+ true = map_guard_body(#{a=>1}),
+ false = map_guard_body({}),
+ true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
+ false = map_guard_pattern("list"),
+ ok.
+
+map_guard_head(M) when is_map(M) -> true;
+map_guard_head(_) -> false.
+
+map_guard_body(M) -> is_map(M).
+
+map_guard_pattern(#{}) -> true;
+map_guard_pattern(_) -> false.
+
+t_guard_sequence(Config) when is_list(Config) ->
+ {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),
+
+ {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})),
+ {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
+ {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+map_guard_sequence_1(#{seq=>1=Seq, val=>Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq=>2=Seq, val=>Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq=>3=Seq, val=>Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq=>4=Seq, val=>Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq=>5=Seq, val=>Val}) -> {Seq,Val}.
+
+map_guard_sequence_2(#{ a=>3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a=>4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a=>X, a=>X, b=>4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a=>X, a=>Y, b=>3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a=>X, a=>Y }=M) when X =:= Y -> {5,X,Y,M}.
+
+
+t_guard_update(Config) when is_list(Config) ->
+ error = map_guard_update(#{},#{}),
+ first = map_guard_update(#{}, #{x=>first}),
+ second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
+ ok.
+
+map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first;
+map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
+map_guard_update(_, _) -> error.
+
+t_guard_receive(Config) when is_list(Config) ->
+ M0 = #{ id => 0 },
+ Pid = spawn_link(fun() -> guard_receive_loop() end),
+ Big = 36893488147419103229,
+ B1 = <<"some text">>,
+ B2 = <<"was appended">>,
+ B3 = <>,
+
+ #{id=>1, res=>Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id=>2, res=>26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id=>3, res=>832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id=>4, res=>4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id=>5, res=>Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id=>6, res=>B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id=>7, res=>4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+
+
+ %% update old maps and check id update
+ #{id=>2, res=>B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id=>5, res=>99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+
+ %% cleanup
+ done = call(Pid, done),
+ ok.
+
+call(Pid, M) ->
+ Pid ! {self(), M}, receive {Pid, Res} -> Res end.
+
+guard_receive_loop() ->
+ receive
+ {Pid, #{ id=>Id, op=>"append", in=>{X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ Pid ! {self(), M#{ id=>Id+1, res=><>}},
+ guard_receive_loop();
+ {Pid, #{ id=>Id, op=>add, in=>{X,Y}}} ->
+ Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
+ guard_receive_loop();
+ {Pid, #{ id=>Id, op=>sub, in=>{X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
+ guard_receive_loop();
+ {Pid, #{ id=>Id, op=>idiv, in=>{X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
+ guard_receive_loop();
+ {Pid, #{ id=>Id, op=>imul, in=>{X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
+ guard_receive_loop();
+ {Pid, done} ->
+ Pid ! {self(), done};
+ {Pid, Other} ->
+ Pid ! {error, Other},
+ guard_receive_loop()
+ end.
+
+
+t_list_comprehension(Config) when is_list(Config) ->
+ [#{k=>1},#{k=>2},#{k=>3}] = [#{k=>I} || I <- [1,2,3]],
+ ok.
+
+t_guard_fun(Config) when is_list(Config) ->
+ F1 = fun
+ (#{s=>v,v=>V}) -> {v,V};
+ (#{s=>t,v=>{V,V}}) -> {t,V};
+ (#{s=>l,v=>[V,V]}) -> {l,V}
+ end,
+
+ F2 = fun
+ (#{s=>T,v=>{V,V}}) -> {T,V};
+ (#{s=>T,v=>[V,V]}) -> {T,V};
+ (#{s=>T,v=>V}) -> {T,V}
+ end,
+ V = <<"hi">>,
+
+ {v,V} = F1(#{s=>v,v=>V}),
+ {t,V} = F1(#{s=>t,v=>{V,V}}),
+ {l,V} = F1(#{s=>l,v=>[V,V]}),
+
+ {v,V} = F2(#{s=>v,v=>V}),
+ {t,V} = F2(#{s=>t,v=>{V,V}}),
+ {l,V} = F2(#{s=>l,v=>[V,V]}),
+
+ %% error case
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s=>none,v=>none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
+ ok.
+
+
+t_map_sort_literals(Config) when is_list(Config) ->
+ % test relation
+
+ %% size order
+ true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
+ true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
+ false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),
+
+ %% key order
+ true = #{ a => 1 } < id(#{ b => 1}),
+ false = #{ b => 1 } < id(#{ a => 1}),
+ true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}),
+ true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ "a" => 1 } < id(#{ <<"a">> => 1}),
+ false = #{ <<"a">> => 1 } < id(#{ "a" => 1}),
+ false = #{ 1 => 1 } < id(#{ 1.0 => 1}),
+ false = #{ 1.0 => 1 } < id(#{ 1 => 1}),
+
+ %% value order
+ true = #{ a => 1 } < id(#{ a => 2}),
+ false = #{ a => 2 } < id(#{ a => 1}),
+ false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}),
+ true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}),
+
+ true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}),
+
+ %% lists:sort
+
+ SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
+ [#{1=>ok},#{a=>ok},#{"a"=>ok},#{<<"a">>=>ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(SortVs),
+ [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(lists:reverse(SortVs)),
+
+ ok.
+
+%% BIFs
+t_bif_map_get(Config) when is_list(Config) ->
+
+ 1 = map:get(a, #{ a=> 1}),
+ 2 = map:get(b, #{ a=> 1, b => 2}),
+ "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}),
+ "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])),
+ {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)),
+ {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})),
+ {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})),
+ {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})),
+ ok.
+
+t_bif_map_find(Config) when is_list(Config) ->
+
+ {ok, 1} = map:find(a, #{ a=> 1}),
+ {ok, 2} = map:find(b, #{ a=> 1, b => 2}),
+ {ok, "int"} = map:find(1, #{ 1 => "int"}),
+ {ok, "int"} = map:find(1.0, #{ 1 => "int"}),
+ {ok, "float"} = map:find(1, #{ 1.0 => "float"}),
+ {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}),
+
+ {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}),
+ {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ error = map:find(a,#{}),
+ error = map:find(a,#{b=>1, c=>2}),
+
+ {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])),
+ {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)),
+ ok.
+
+
+t_bif_map_is_key(Config) when is_list(Config) ->
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+
+ true = map:is_key("hi", M1),
+ true = map:is_key(int, M1),
+ true = map:is_key(<<"key">>, M1),
+ true = map:is_key(4, M1),
+
+ false = map:is_key(5, M1),
+ false = map:is_key(<<"key2">>, M1),
+ false = map:is_key("h", M1),
+ false = map:is_key("hello", M1),
+ false = map:is_key(atom, M1),
+
+ false = map:is_key("hi", map:remove("hi", M1)),
+ true = map:is_key("hi", M1),
+ true = map:is_key(1, map:put(1, "number", M1)),
+ false = map:is_key(1.0, map:put(1, "number", M1)),
+ ok.
+
+t_bif_map_keys(Config) when is_list(Config) ->
+ [] = map:keys(#{}),
+
+ [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [1,2,3,4,5] = map:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ [4,int,"hi",<<"key">>] = map:keys(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)),
+ {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)),
+ {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])),
+ {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)),
+ ok.
+
+t_bif_map_new(Config) when is_list(Config) ->
+ #{} = map:new(),
+ 0 = erlang:map_size(map:new()),
+ ok.
+
+t_bif_map_put(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}),
+
+ ["hi"] = map:keys(M1),
+ ["hello"] = map:values(M1),
+
+ M2 = #{ int => 3 } = map:put(int, 3, M1),
+
+ [int,"hi"] = map:keys(M2),
+ [3,"hello"] = map:values(M2),
+
+ M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2),
+
+ [int,"hi",<<"key">>] = map:keys(M3),
+ [3,"hello",<<"value">>] = map:values(M3),
+
+ M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3),
+
+ [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4),
+ [wat,3,"hello",<<"value">>] = map:values(M4),
+
+ M0 = #{ 4 => number } = M5 = map:put(4, number, M4),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5),
+ [number,wat,3,"hello",<<"value">>] = map:values(M5),
+
+ %% error case
+ {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)),
+ {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)),
+ {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])),
+ {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)),
+ ok.
+
+t_bif_map_remove(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = map:remove("hi", M0),
+ [4,18446744073709551629,int,<<"key">>] = map:keys(M1),
+ [number,wat,3,<<"value">>] = map:values(M1),
+
+ M2 = map:remove(int, M1),
+ [4,18446744073709551629,<<"key">>] = map:keys(M2),
+ [number,wat,<<"value">>] = map:values(M2),
+
+ M3 = map:remove(<<"key">>, M2),
+ [4,18446744073709551629] = map:keys(M3),
+ [number,wat] = map:values(M3),
+
+ M4 = map:remove(18446744073709551629, M3),
+ [4] = map:keys(M4),
+ [number] = map:values(M4),
+
+ M5 = map:remove(4, M4),
+ [] = map:keys(M5),
+ [] = map:values(M5),
+
+ M0 = map:remove(5,M0),
+ M0 = map:remove("hi there",M0),
+
+ #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)),
+
+ %% error case
+ {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)),
+ {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)),
+ {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])),
+ {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)),
+ ok.
+
+
+t_bif_map_values(Config) when is_list(Config) ->
+
+ [] = map:values(#{}),
+
+ [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [a,b,c,d,e] = map:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
+ [number,3,"hello2",<<"value2">>] = map:values(M2),
+ [number,3,"hello",<<"value">>] = map:values(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)),
+ {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])),
+ {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)),
+
+ ok.
+
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
--
cgit v1.2.3
From 226e1e5898058aa3459c4d562df13132c9bc0899 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 1 Oct 2013 22:08:14 +0200
Subject: Teach erl_scan to recognize ':='
---
lib/stdlib/src/erl_scan.erl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 52376a8f3a..ae59d5f44f 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -596,6 +596,9 @@ scan1("||"++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "||", '||', 2);
scan1("|"=Cs, _St, Line, Col, Toks) ->
{more,{Cs,Col,Toks,Line,[],fun scan/6}};
+%% :=
+scan1(":="++Cs, St, Line, Col, Toks) ->
+ tok2(Cs, St, Line, Col, Toks, ":=", ':=', 2);
%% :-
scan1(":-"++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, ":-", ':-', 2);
--
cgit v1.2.3
From b15b2098627f702958771c3497949c62623a415d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 1 Oct 2013 22:29:29 +0200
Subject: Extend erl_parse with two Op Map syntax
Example how to construct:
#{ K1 => V1, K2 => V2 }
How to update:
M#{ K1 => V1, K2 := V2 }
How to match:
#{ K1 := V1, K2 := V2 } = M
---
lib/stdlib/src/erl_parse.yrl | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 5a846266c5..35c33c7796 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -34,7 +34,7 @@ binary_comprehension
tuple
%struct
record_expr record_tuple record_field record_fields
-map_expr map_tuple map_field map_fields map_key
+map_expr map_tuple map_field map_field_assoc map_field_exact map_fields map_key
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
fun_expr fun_clause fun_clauses atom_or_var integer_or_var
try_expr try_catch try_clause try_clauses
@@ -60,7 +60,7 @@ char integer float atom string var
'*' '/' 'div' 'rem' 'band' 'and'
'+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor'
'++' '--'
-'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '=>'
+'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '=>' ':='
'<<' '>>'
'!' '=' '::' '..' '...'
'spec' 'callback' % helper
@@ -344,8 +344,14 @@ map_tuple -> '{' map_fields '}' : '$2'.
map_fields -> map_field : ['$1'].
map_fields -> map_field ',' map_fields : ['$1' | '$3'].
-map_field -> map_key '=>' expr :
- {map_field,?line('$1'),'$1','$3'}.
+map_field -> map_field_assoc : '$1'.
+map_field -> map_field_exact : '$1'.
+
+map_field_assoc -> map_key '=>' expr :
+ {map_field_assoc,?line('$1'),'$1','$3'}.
+
+map_field_exact -> map_key ':=' expr :
+ {map_field_exact,?line('$1'),'$1','$3'}.
map_key -> expr : '$1'.
--
cgit v1.2.3
From a0133aa75da3756918a5b9fd427ae75a75ae1dce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Sat, 26 Oct 2013 01:21:23 +0200
Subject: stdlib: Fix erl_parse:parse_term/1
Did not handle Maps.
---
lib/stdlib/src/erl_parse.yrl | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 35c33c7796..e9a813e098 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -888,6 +888,12 @@ normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
normalise({tuple,_,Args}) ->
list_to_tuple(normalise_list(Args));
+normalise({map,_,Pairs}=M) ->
+ map:from_list(lists:map(fun
+ %% only allow '=>'
+ ({map_field_assoc,_,K,V}) -> {normalise(K),normalise(V)};
+ (_) -> erlang:error({badarg,M})
+ end, Pairs));
%% Special case for unary +/-.
normalise({op,_,'+',{char,_,I}}) -> I;
normalise({op,_,'+',{integer,_,I}}) -> I;
--
cgit v1.2.3
From 758702e24f0780130c6b3058426062a3761b7a6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 1 Oct 2013 22:42:18 +0200
Subject: Update erl_lint, erl_expand_records, sys_pre_expand for Maps
Update erlang lint and syntax expand for #{ K := V }
---
lib/compiler/src/sys_pre_expand.erl | 12 ++++++++----
lib/stdlib/src/erl_expand_records.erl | 14 ++++++++++----
lib/stdlib/src/erl_lint.erl | 12 +++++++++---
3 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 04084d22ff..9998043013 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -231,10 +231,10 @@ pattern({tuple,Line,Ps}, St0) ->
pattern({map,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{map,Line,TPs},St1};
-pattern({map_field,Line,K0,V0}, St0) ->
+pattern({map_field_exact,Line,K0,V0}, St0) ->
{K,St1} = pattern(K0, St0),
{V,St2} = pattern(V0, St1),
- {{map_field,Line,K,V},St2};
+ {{map_field_exact,Line,K,V},St2};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1};
@@ -334,10 +334,14 @@ expr({map,Line,Es0}, St0) ->
expr({map,Line,Var,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{map,Line,Var,Es1},St1};
-expr({map_field,Line,K0,V0}, St0) ->
+expr({map_field_assoc,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = expr(V0, St1),
- {{map_field,Line,K,V},St2};
+ {{map_field_assoc,Line,K,V},St2};
+expr({map_field_exact,Line,K0,V0}, St0) ->
+ {K,St1} = expr(K0, St0),
+ {V,St2} = expr(V0, St1),
+ {{map_field_exact,Line,K,V},St2};
expr({bin,Line,Es0}, St0) ->
{Es1,St1} = expr_bin(Es0, St0),
{{bin,Line,Es1},St1};
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 7fc4cc7887..4741bef6b9 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -135,9 +135,9 @@ pattern({tuple,Line,Ps}, St0) ->
pattern({map,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{map,Line,TPs},St1};
-pattern({map_field,Line,Key,V0}, St0) ->
+pattern({map_field_exact,Line,Key,V0}, St0) ->
{V,St1} = pattern(V0, St0),
- {{map_field,Line,Key,V},St1};
+ {{map_field_exact,Line,Key,V},St1};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{struct,Line,Tag,TPs},TPsvs,St1};
@@ -313,8 +313,14 @@ expr({map,Line,Es0}, St0) ->
expr({map,Line,Var,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{map,Line,Var,Es1},St1};
-expr({map_field,_Line,_,_}=M, St0) ->
- {M,St0};
+expr({map_field_assoc,Line,K0,V0}, St0) ->
+ {K,St1} = expr(K0, St0),
+ {V,St2} = expr(V0, St1),
+ {{map_field_assoc,Line,K,V},St2};
+expr({map_field_exact,Line,K0,V0}, St0) ->
+ {K,St1} = expr(K0, St0),
+ {V,St2} = expr(V0, St1),
+ {{map_field_exact,Line,K,V},St2};
%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
%% {{struct,Line,Tag,Es1},Esvs,Esus,St1};
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index b74a3ed924..86ebe82acc 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -1368,7 +1368,9 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
-pattern({map_field,_Line,_,P}, Vt, Old, Bvt0, St0) ->
+pattern({map_field_assoc,_Line,_,_}=Pat, _, _, _, St) ->
+ {[],[],add_error(element(2, Pat), illegal_pattern, St)};
+pattern({map_field_exact,_Line,_,P}, Vt, Old, Bvt0, St0) ->
{Pvt,Bvt,St1} = pattern(P, Vt, Old, Bvt0, St0),
{Pvt,Bvt,St1};
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
@@ -1762,7 +1764,9 @@ gexpr({map,_Line,Es}, Vt, St) ->
gexpr_list(Es, Vt, St);
gexpr({map,_Line,Src,Es}, Vt, St) ->
gexpr_list([Src|Es], Vt, St);
-gexpr({map_field,_Line,K,V}, Vt, St) ->
+gexpr({map_field_assoc,_Line,K,V}, Vt, St) ->
+ gexpr_list([K,V], Vt, St);
+gexpr({map_field_exact,_Line,K,V}, Vt, St) ->
gexpr_list([K,V], Vt, St);
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
@@ -1985,7 +1989,9 @@ expr({map,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
expr({map,_Line,Src,Es}, Vt, St) ->
expr_list([Src|Es], Vt, St);
-expr({map_field,_Line,K,V}, Vt, St) ->
+expr({map_field_assoc,_Line,K,V}, Vt, St) ->
+ expr_list([K,V], Vt, St);
+expr({map_field_exact,_Line,K,V}, Vt, St) ->
expr_list([K,V], Vt, St);
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
--
cgit v1.2.3
From 652ddd4c360cd2cc381ed033915d87d967310a49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 1 Oct 2013 23:15:50 +0200
Subject: Extend representation for maps in Core Erlang
---
lib/compiler/src/core_parse.hrl | 6 ++++--
lib/compiler/src/core_parse.yrl | 19 +++++++++++++------
lib/compiler/src/core_pp.erl | 7 ++++++-
3 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index c134747991..40c3ae4582 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -97,8 +97,10 @@
-record(c_var, {anno=[], name :: cerl:var_name()}).
--record(c_map_pair, {anno=[], key, val}).
+-record(c_map_pair_assoc, {anno=[], key, val}).
+
+-record(c_map_pair_exact, {anno=[], key, val}).
-record(c_map, {anno=[],
var=#c_literal{val=[]} :: #c_var{} | #c_literal{},
- es :: [#c_map_pair{}]}).
+ es :: [#c_map_pair_assoc{}|#c_map_pair_exact{}]}).
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index b072950eeb..82ba11b0fc 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -46,7 +46,7 @@ receive_expr timeout try_expr
sequence catch_expr
variable clause clause_pattern
-map_expr map_pairs map_pair
+map_expr map_pairs map_pair map_pair_assoc map_pair_exact
map_pattern map_pair_patterns map_pair_pattern
annotation anno_fun anno_expression anno_expressions
@@ -58,7 +58,7 @@ Terminals
%% Separators
-'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' '~'
+'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' '~' '::'
%% Keywords (atoms are assumed to always be single-quoted).
@@ -190,7 +190,7 @@ map_pair_patterns -> map_pair_pattern : ['$1'].
map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' :
- #c_map_pair{key='$3',val='$5'}.
+ #c_map_pair_exact{key='$3',val='$5'}.
cons_pattern -> '[' anno_pattern tail_pattern :
#c_cons{hd='$2',tl='$3'}.
@@ -286,14 +286,19 @@ tuple -> '{' anno_expressions '}' : c_tuple('$2').
map_expr -> '~' '{' '}' '~' : #c_map{es=[]}.
map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}.
-map_expr -> variable '~' '{' '}' '~' : #c_map{var='$',es=[]}.
+map_expr -> variable '~' '{' '}' '~' : #c_map{var='$1',es=[]}.
map_expr -> variable '~' '{' map_pairs '}' '~' : #c_map{var='$1',es='$4'}.
map_pairs -> map_pair : ['$1'].
map_pairs -> map_pair ',' map_pairs : ['$1' | '$3'].
-map_pair -> '~' '<' anno_expression ',' anno_expression'>' :
- #c_map_pair{key='$3',val='$5'}.
+map_pair -> map_pair_assoc : '$1'.
+map_pair -> map_pair_exact : '$1'.
+
+map_pair_assoc -> '::' '<' anno_expression ',' anno_expression'>' :
+ #c_map_pair_assoc{key='$3',val='$5'}.
+map_pair_exact -> '~' '<' anno_expression ',' anno_expression'>' :
+ #c_map_pair_exact{key='$3',val='$5'}.
cons -> '[' anno_expression tail : c_cons('$2', '$3').
@@ -409,3 +414,5 @@ Erlang code.
tok_val(T) -> element(3, T).
tok_line(T) -> element(2, T).
+
+%% vim: syntax=erlang
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index c8c59b9246..f775d87507 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -172,7 +172,12 @@ format_1(#c_map{es=Es}, Ctxt) ->
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
"}~"
];
-format_1(#c_map_pair{key=K,val=V}, Ctxt) ->
+format_1(#c_map_pair_assoc{key=K,val=V}, Ctxt) ->
+ ["::<",
+ format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2),
+ ">"
+ ];
+format_1(#c_map_pair_exact{key=K,val=V}, Ctxt) ->
["~<",
format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2),
">"
--
cgit v1.2.3
From 3c5067eab057391e101845b03c1ba7b76d2b3562 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 1 Oct 2013 23:42:17 +0200
Subject: compiler: Implement support for exact Op in Maps
The syntax is handled upto v3_kernel where it is reduced to
previous behaviour for construction and updates. Meaning,
the ':=' operator is handled exactly as '=>' operator.
---
lib/compiler/src/core_lib.erl | 4 +++-
lib/compiler/src/sys_core_dsetel.erl | 10 +++++++---
lib/compiler/src/sys_core_fold.erl | 22 ++++++++++++++++------
lib/compiler/src/v3_core.erl | 35 +++++++++++++++++++++++------------
lib/compiler/src/v3_kernel.erl | 18 ++++++++++++------
5 files changed, 61 insertions(+), 28 deletions(-)
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index f506901099..d6221a22bb 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -107,7 +107,9 @@ vu_expr(V, #c_tuple{es=Es}) ->
vu_expr_list(V, Es);
vu_expr(V, #c_map{es=Es}) ->
vu_expr_list(V, Es);
-vu_expr(V, #c_map_pair{key=Key,val=Val}) ->
+vu_expr(V, #c_map_pair_assoc{key=Key,val=Val}) ->
+ vu_expr_list(V, [Key,Val]);
+vu_expr(V, #c_map_pair_exact{key=Key,val=Val}) ->
vu_expr_list(V, [Key,Val]);
vu_expr(V, #c_binary{segments=Ss}) ->
vu_seg_list(V, Ss);
diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl
index c7c8343c35..f921429409 100644
--- a/lib/compiler/src/sys_core_dsetel.erl
+++ b/lib/compiler/src/sys_core_dsetel.erl
@@ -105,10 +105,14 @@ visit(Env0, #c_tuple{es=Es0}=R) ->
visit(Env0, #c_map{es=Es0}=R) ->
{Es1,Env1} = visit_list(Env0, Es0),
{R#c_map{es=Es1}, Env1};
-visit(Env0, #c_map_pair{key=K0,val=V0}=R) ->
+visit(Env0, #c_map_pair_assoc{key=K0,val=V0}=R) ->
{K,Env1} = visit(Env0, K0),
{V,Env2} = visit(Env1, V0),
- {R#c_map_pair{key=K,val=V}, Env2};
+ {R#c_map_pair_assoc{key=K,val=V}, Env2};
+visit(Env0, #c_map_pair_exact{key=K0,val=V0}=R) ->
+ {K,Env1} = visit(Env0, K0),
+ {V,Env2} = visit(Env1, V0),
+ {R#c_map_pair_exact{key=K,val=V}, Env2};
visit(Env0, #c_cons{hd=H0,tl=T0}=R) ->
{H1,Env1} = visit(Env0, H0),
{T1,Env2} = visit(Env1, T0),
@@ -221,7 +225,7 @@ visit_pat(Env0, #c_tuple{es=Es}, Vs) ->
visit_pats(Es, Env0, Vs);
visit_pat(Env0, #c_map{es=Es}, Vs) ->
visit_pats(Es, Env0, Vs);
-visit_pat(Env0, #c_map_pair{key=V,val=K}, Vs0) ->
+visit_pat(Env0, #c_map_pair_exact{key=V,val=K}, Vs0) ->
{Vs1, Env1} = visit_pat(Env0, V, Vs0),
visit_pat(Env1, K, Vs1);
visit_pat(Env0, #c_cons{hd=H,tl=T}, Vs0) ->
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index cdbe2d0753..2cd6ea0e53 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -438,18 +438,28 @@ pair_list_reversed([E|Es],Ctxt,Sub,Out,Keys) ->
%% check if key already is present in map, i.e. #{ a=>1, a=>2 }
%% where 'a' is duplicate. Update maps set with the key if not present.
-map_has_key(#c_map_pair{key=#c_literal{val=K}},Ks) ->
+map_has_key(MapPair,Ks) ->
+ K = map_get_literal_key_from_pair(MapPair),
case gb_sets:is_element(K,Ks) of
false -> {false, gb_sets:add(K,Ks)};
true -> {true, K}
end.
-pair(#c_map_pair{key=K,val=V}, effect, Sub) ->
+map_get_literal_key_from_pair(#c_map_pair_assoc{key=#c_literal{val=K}}) -> K;
+map_get_literal_key_from_pair(#c_map_pair_exact{key=#c_literal{val=K}}) -> K.
+
+pair(#c_map_pair_assoc{key=K,val=V}, effect, Sub) ->
+ make_effect_seq([K,V], Sub);
+pair(#c_map_pair_exact{key=K,val=V}, effect, Sub) ->
make_effect_seq([K,V], Sub);
-pair(#c_map_pair{key=K0,val=V0}=Pair, value=Ctxt, Sub) ->
+pair(#c_map_pair_assoc{key=K0,val=V0}=Pair, value=Ctxt, Sub) ->
+ K = expr(K0, Ctxt, Sub),
+ V = expr(V0, Ctxt, Sub),
+ Pair#c_map_pair_assoc{key=K,val=V};
+pair(#c_map_pair_exact{key=K0,val=V0}=Pair, value=Ctxt, Sub) ->
K = expr(K0, Ctxt, Sub),
V = expr(V0, Ctxt, Sub),
- Pair#c_map_pair{key=K,val=V}.
+ Pair#c_map_pair_exact{key=K,val=V}.
bitstr_list(Es, Sub) ->
[bitstr(E, Sub) || E <- Es].
@@ -1559,10 +1569,10 @@ map_pair_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0),
{Ps,Osub}.
-map_pair_pattern(#c_map_pair{key=K0,val=V0}=Pair, {Isub,Osub0}) ->
+map_pair_pattern(#c_map_pair_exact{key=K0,val=V0}=Pair, {Isub,Osub0}) ->
{K,Osub1} = pattern(K0, Isub, Osub0),
{V,Osub} = pattern(V0, Isub, Osub1),
- {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.
+ {Pair#c_map_pair_exact{key=K,val=V},{Isub,Osub}}.
bin_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0),
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 32b65333a4..b33799275b 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -488,6 +488,8 @@ expr({tuple,L,Es0}, St0) ->
A = lineno_anno(L, St1),
{ann_c_tuple(A, Es1),Eps,St1};
expr({map,L,Es0}, St0) ->
+ % erl_lint should make sure only #{ K => V } are allowed
+ % in map construction.
{Es1,Eps,St1} = map_pair_list(Es0, St0),
A = lineno_anno(L, St1),
{#c_map{anno=A,es=Es1},Eps,St1};
@@ -705,13 +707,20 @@ make_bool_switch_guard(L, E, V, T, F) ->
]}.
map_pair_list(Es, St) ->
- foldr(fun ({map_field,L,K0,V0}, {Ces,Esp,St0}) ->
- {K,Ep0,St1} = safe(K0, St0),
- {V,Ep1,St2} = safe(V0, St1),
- A = lineno_anno(L, St2),
- Pair = #c_map_pair{anno=A,key=K,val=V},
- {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
- end, {[],[],St}, Es).
+ foldr(fun
+ ({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) ->
+ {K,Ep0,St1} = safe(K0, St0),
+ {V,Ep1,St2} = safe(V0, St1),
+ A = lineno_anno(L, St2),
+ Pair = #c_map_pair_assoc{anno=A,key=K,val=V},
+ {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2};
+ ({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) ->
+ {K,Ep0,St1} = safe(K0, St0),
+ {V,Ep1,St2} = safe(V0, St1),
+ A = lineno_anno(L, St2),
+ Pair = #c_map_pair_exact{anno=A,key=K,val=V},
+ {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
+ end, {[],[],St}, Es).
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
@@ -1497,7 +1506,7 @@ pattern({tuple,L,Ps}, St) ->
ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St));
pattern({map,L,Ps}, St) ->
#c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))};
-pattern({map_field,L,K,V}, St) ->
+pattern({map_field_exact,L,K,V}, St) ->
%% FIXME: Better way to construct literals? or missing case
%% {Key,_,_} = expr(K, St),
Key = case K of
@@ -1511,7 +1520,7 @@ pattern({map_field,L,K,V}, St) ->
_ ->
pattern(K,St)
end,
- #c_map_pair{anno=lineno_anno(L, St),
+ #c_map_pair_exact{anno=lineno_anno(L, St),
key=Key,
val=pattern(V, St)};
pattern({bin,L,Ps}, St) ->
@@ -1862,9 +1871,9 @@ upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
upattern(#c_map{es=Es0}=Map, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
{Map#c_map{es=Es1},Esg,Esv,Eus,St1};
-upattern(#c_map_pair{val=V0}=MapPair, Ks, St0) ->
+upattern(#c_map_pair_exact{val=V0}=MapPair, Ks, St0) ->
{V,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
- {MapPair#c_map_pair{val=V},Vg,Vv,Vu,St1};
+ {MapPair#c_map_pair_exact{val=V},Vg,Vv,Vu,St1};
upattern(#c_binary{segments=Es0}=Bin, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0),
{Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1};
@@ -2195,7 +2204,9 @@ is_simple(#c_cons{hd=H,tl=T}) ->
is_simple(H) andalso is_simple(T);
is_simple(#c_tuple{es=Es}) -> is_simple_list(Es);
is_simple(#c_map{es=Es}) -> is_simple_list(Es);
-is_simple(#c_map_pair{key=K,val=V}) ->
+is_simple(#c_map_pair_assoc{key=K,val=V}) ->
+ is_simple(K) andalso is_simple(V);
+is_simple(#c_map_pair_exact{key=K,val=V}) ->
is_simple(K) andalso is_simple(V);
is_simple(_) -> false.
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index f4ba60e529..44f853ec2b 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -497,12 +497,18 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
translate_fc(Args) ->
[#c_literal{val=function_clause},make_list(Args)].
+%% FIXME: Not completed
map_pairs(Es, Sub, St) ->
- foldr(fun(#c_map_pair{key=K0,val=V0}, {Kes,Esp,St0}) ->
- {K,[],St1} = expr(K0, Sub, St0),
- {V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2}
- end, {[],[],St}, Es).
+ foldr(fun
+ (#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) ->
+ {K,[],St1} = expr(K0, Sub, St0),
+ {V,Ep,St2} = atomic(V0, Sub, St1),
+ {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2};
+ (#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) ->
+ {K,[],St1} = expr(K0, Sub, St0),
+ {V,Ep,St2} = atomic(V0, Sub, St1),
+ {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2}
+ end, {[],[],St}, Es).
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
%% Classify the call.
@@ -662,7 +668,7 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
{#k_map{anno=A,es=Kes},Osub1,St1};
-pattern(#c_map_pair{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
+pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
{Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
{Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
{#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2};
--
cgit v1.2.3
From aee6128a1bfb2356440b27a9c7db44650336e4c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 2 Oct 2013 00:09:37 +0200
Subject: erts: Conform map_SUITE to extended syntax
---
erts/emulator/test/map_SUITE.erl | 122 +++++++++++++++++++--------------------
1 file changed, 61 insertions(+), 61 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index bbc5aa07b4..6d82a2cb59 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -70,36 +70,36 @@ end_per_group(_GroupName, Config) -> Config.
t_build_and_match_literals(Config) when is_list(Config) ->
#{} = id(#{}),
- #{1=>a} = id(#{1=>a}),
- #{1=>a,2=>b} = id(#{1=>a,2=>b}),
- #{1=>a,2=>b,3=>"c"} = id(#{1=>a,2=>b,3=>"c"}),
- #{1=>a,2=>b,3=>"c","4"=>"d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
- #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>} =
+ #{1:=a} = id(#{1=>a}),
+ #{1:=a,2:=b} = id(#{1=>a,2=>b}),
+ #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
- #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"} =
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
- #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g} =
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
- #{<<"hi all">> => 1} = id(#{<<"hi",32,"all">> => 1}),
+ #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),
- #{a=>X,a=>X=3,b=>4} = id(#{a=>3,b=>4}), % weird but ok =)
+ #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)
- #{ a=>#{ b=>#{c => third, b=>second}}, b=>first} =
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
- M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first} =
+ M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
%% error case
%V = 32,
%{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))),
- {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3,x=>2} = id(#{x=>3}))),
- {'EXIT',{{badmatch,_},_}} = (catch (#{x=>2} = id(#{x=>3}))),
- {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id({a,b,c}))),
- {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{y=>3}))),
- {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{x=>"three"}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
ok.
%% Tests size(Map).
@@ -137,7 +137,7 @@ map_is_size(_,_) -> false.
% test map updates without matching
t_update_literals(Config) when is_list(Config) ->
Map = #{x=>1,y=>2,z=>3,q=>4},
- #{x=>"d",q=>"4"} = loop_update_literals_x_q(Map, [
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
{"a","1"},{"b","2"},{"c","3"},{"d","4"}
]),
ok.
@@ -149,7 +149,7 @@ loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
% test map updates with matching
t_match_and_update_literals(Config) when is_list(Config) ->
Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
- #{x=>16,q=>21,y=>"untouched",z=>"also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
{1,2},{3,4},{5,6},{7,8}
]),
M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
@@ -159,11 +159,11 @@ t_match_and_update_literals(Config) when is_list(Config) ->
4 => number, 18446744073709551629 => wat},
M0 = M2,
- #{ 4 => another_number, int => 3 } = M2#{ 4 => another_number },
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
ok.
loop_match_and_update_literals_x_q(Map, []) -> Map;
-loop_match_and_update_literals_x_q(#{q=>Q0,x=>X0} = Map, [{X,Q}|Vs]) ->
+loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
@@ -202,17 +202,17 @@ t_guard_sequence(Config) when is_list(Config) ->
{'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
ok.
-map_guard_sequence_1(#{seq=>1=Seq, val=>Val}) -> {Seq,Val};
-map_guard_sequence_1(#{seq=>2=Seq, val=>Val}) -> {Seq,Val};
-map_guard_sequence_1(#{seq=>3=Seq, val=>Val}) -> {Seq,Val};
-map_guard_sequence_1(#{seq=>4=Seq, val=>Val}) -> {Seq,Val};
-map_guard_sequence_1(#{seq=>5=Seq, val=>Val}) -> {Seq,Val}.
+map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.
-map_guard_sequence_2(#{ a=>3 }=M) -> {1, M};
-map_guard_sequence_2(#{ a=>4 }=M) -> {2, M};
-map_guard_sequence_2(#{ a=>X, a=>X, b=>4 }=M) -> {3,X,M};
-map_guard_sequence_2(#{ a=>X, a=>Y, b=>3 }=M) when X =:= Y -> {4,X,Y,M};
-map_guard_sequence_2(#{ a=>X, a=>Y }=M) when X =:= Y -> {5,X,Y,M}.
+map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.
t_guard_update(Config) when is_list(Config) ->
@@ -233,18 +233,18 @@ t_guard_receive(Config) when is_list(Config) ->
B2 = <<"was appended">>,
B3 = <>,
- #{id=>1, res=>Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
- #{id=>2, res=>26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
- #{id=>3, res=>832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
- #{id=>4, res=>4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
- #{id=>5, res=>Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
- #{id=>6, res=>B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
- #{id=>7, res=>4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+ #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
%% update old maps and check id update
- #{id=>2, res=>B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
- #{id=>5, res=>99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+ #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
%% cleanup
done = call(Pid, done),
@@ -255,19 +255,19 @@ call(Pid, M) ->
guard_receive_loop() ->
receive
- {Pid, #{ id=>Id, op=>"append", in=>{X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
Pid ! {self(), M#{ id=>Id+1, res=><>}},
guard_receive_loop();
- {Pid, #{ id=>Id, op=>add, in=>{X,Y}}} ->
+ {Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
guard_receive_loop();
- {Pid, #{ id=>Id, op=>sub, in=>{X,Y}}=M} ->
+ {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} ->
Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
guard_receive_loop();
- {Pid, #{ id=>Id, op=>idiv, in=>{X,Y}}=M} ->
+ {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
guard_receive_loop();
- {Pid, #{ id=>Id, op=>imul, in=>{X,Y}}=M} ->
+ {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
guard_receive_loop();
{Pid, done} ->
@@ -279,20 +279,20 @@ guard_receive_loop() ->
t_list_comprehension(Config) when is_list(Config) ->
- [#{k=>1},#{k=>2},#{k=>3}] = [#{k=>I} || I <- [1,2,3]],
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]],
ok.
t_guard_fun(Config) when is_list(Config) ->
F1 = fun
- (#{s=>v,v=>V}) -> {v,V};
- (#{s=>t,v=>{V,V}}) -> {t,V};
- (#{s=>l,v=>[V,V]}) -> {l,V}
+ (#{s:=v,v:=V}) -> {v,V};
+ (#{s:=t,v:={V,V}}) -> {t,V};
+ (#{s:=l,v:=[V,V]}) -> {l,V}
end,
F2 = fun
- (#{s=>T,v=>{V,V}}) -> {T,V};
- (#{s=>T,v=>[V,V]}) -> {T,V};
- (#{s=>T,v=>V}) -> {T,V}
+ (#{s:=T,v:={V,V}}) -> {T,V};
+ (#{s:=T,v:=[V,V]}) -> {T,V};
+ (#{s:=T,v:=V}) -> {T,V}
end,
V = <<"hi">>,
@@ -305,7 +305,7 @@ t_guard_fun(Config) when is_list(Config) ->
{l,V} = F2(#{s=>l,v=>[V,V]}),
%% error case
- {'EXIT', {function_clause,[{?MODULE,_,[#{s=>none,v=>none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
ok.
@@ -339,9 +339,9 @@ t_map_sort_literals(Config) when is_list(Config) ->
%% lists:sort
SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
- [#{1=>ok},#{a=>ok},#{"a"=>ok},#{<<"a">>=>ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
- [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(SortVs),
- [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(lists:reverse(SortVs)),
+ [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
ok.
@@ -435,27 +435,27 @@ t_bif_map_put(Config) when is_list(Config) ->
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
4 => number, 18446744073709551629 => wat},
- M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}),
+ M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}),
["hi"] = map:keys(M1),
["hello"] = map:values(M1),
- M2 = #{ int => 3 } = map:put(int, 3, M1),
+ M2 = #{ int := 3 } = map:put(int, 3, M1),
[int,"hi"] = map:keys(M2),
[3,"hello"] = map:values(M2),
- M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2),
+ M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2),
[int,"hi",<<"key">>] = map:keys(M3),
[3,"hello",<<"value">>] = map:values(M3),
- M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3),
+ M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3),
[18446744073709551629,int,"hi",<<"key">>] = map:keys(M4),
[wat,3,"hello",<<"value">>] = map:values(M4),
- M0 = #{ 4 => number } = M5 = map:put(4, number, M4),
+ M0 = #{ 4 := number } = M5 = map:put(4, number, M4),
[4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5),
[number,wat,3,"hello",<<"value">>] = map:values(M5),
@@ -495,7 +495,7 @@ t_bif_map_remove(Config) when is_list(Config) ->
M0 = map:remove(5,M0),
M0 = map:remove("hi there",M0),
- #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)),
+ #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)),
%% error case
{'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)),
--
cgit v1.2.3
From c0bb7f866257168e0a05110767fc7096a93d351d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 3 Oct 2013 13:22:32 +0200
Subject: sys_core_fold: Remove optimization that has become invalid
With the => and := operators for updating maps, this optimization is
no longer valid.
---
lib/compiler/src/sys_core_fold.erl | 29 +----------------------------
1 file changed, 1 insertion(+), 28 deletions(-)
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 2cd6ea0e53..6792e2c894 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -418,35 +418,8 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0)
expr_list(Es, Ctxt, Sub) ->
[expr(E, Ctxt, Sub) || E <- Es].
-%% traverse pairs in reverse
-%% - remove later literals since they will be overwritten.
-
pair_list(Es, Ctxt, Sub) ->
- pair_list_reversed(lists:reverse(Es), Ctxt, Sub, [], gb_sets:empty()).
-
-pair_list_reversed([],_,_,Es,_) -> Es;
-pair_list_reversed([E|Es],Ctxt,Sub,Out,Keys) ->
- Pair = pair(E,Ctxt,Sub),
- case map_has_key(Pair,Keys) of
- {false,Keys1} ->
- pair_list_reversed(Es,Ctxt,Sub,[Pair|Out],Keys1);
- {true,K} ->
- add_warning(E, {map_pair_key_overloaded,K}),
- pair_list_reversed(Es,Ctxt,Sub,Out,Keys)
- end.
-
-%% check if key already is present in map, i.e. #{ a=>1, a=>2 }
-%% where 'a' is duplicate. Update maps set with the key if not present.
-
-map_has_key(MapPair,Ks) ->
- K = map_get_literal_key_from_pair(MapPair),
- case gb_sets:is_element(K,Ks) of
- false -> {false, gb_sets:add(K,Ks)};
- true -> {true, K}
- end.
-
-map_get_literal_key_from_pair(#c_map_pair_assoc{key=#c_literal{val=K}}) -> K;
-map_get_literal_key_from_pair(#c_map_pair_exact{key=#c_literal{val=K}}) -> K.
+ [pair(E, Ctxt, Sub) || E <- Es].
pair(#c_map_pair_assoc{key=K,val=V}, effect, Sub) ->
make_effect_seq([K,V], Sub);
--
cgit v1.2.3
From 760ed909f8e2a655100ea773829c7d0e7dd40088 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 3 Oct 2013 12:58:54 +0200
Subject: Pass the map pair operators through to the v3_codegen pass
---
lib/compiler/src/v3_codegen.erl | 17 +++++++++--------
lib/compiler/src/v3_kernel.erl | 25 ++++++++++++++++---------
lib/compiler/src/v3_kernel.hrl | 3 ++-
lib/compiler/src/v3_kernel_pp.erl | 4 +++-
lib/compiler/src/v3_life.erl | 10 +++++++---
5 files changed, 37 insertions(+), 22 deletions(-)
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index e8eec08323..c1d555efac 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1502,16 +1502,17 @@ set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef,
_ -> SrcMap
end,
- % MapPairs in put_map must be sorted in ascending Key Order
- % Key literals must be unique when arriving here in v3_codegen
+ %% MapPairs in put_map must be sorted in ascending Key order.
+ %% Key literals must be unique when arriving here in v3_codegen.
- SortedEs = lists:sort(fun
- ({map_pair,{_T1,K1},_},{map_pair,{_T2,K2},_}) when K1 < K2 -> true;
- ({map_pair,{_,_},_},{map_pair,{_,_},_}) -> false
- end, Es),
+ SortedEs = lists:sort(fun({_,{_T1,K1},_},{_,{_T2,K2},_}) ->
+ K1 =< K2
+ end, Es),
List = flatmap(fun
- ({map_pair,K,{var,V}}) -> [K,fetch_var(V, Int0)];
- ({map_pair,K,E}) -> [K,E]
+ ({map_pair_assoc,K,{var,V}}) -> [K,fetch_var(V, Int0)];
+ ({map_pair_exact,K,{var,V}}) -> [K,fetch_var(V, Int0)];
+ ({map_pair_assoc,K,E}) -> [K,E];
+ ({map_pair_exact,K,E}) -> [K,E]
end, SortedEs),
Live = max_reg(Bef#sr.reg),
Int1 = clear_dead(Int0, Le#l.i, Vdb),
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 44f853ec2b..14e776fd3c 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -503,11 +503,11 @@ map_pairs(Es, Sub, St) ->
(#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) ->
{K,[],St1} = expr(K0, Sub, St0),
{V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2};
+ {[#k_map_pair_assoc{key=K,val=V}|Kes],Ep ++ Esp,St2};
(#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) ->
{K,[],St1} = expr(K0, Sub, St0),
{V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2}
+ {[#k_map_pair_exact{key=K,val=V}|Kes],Ep ++ Esp,St2}
end, {[],[],St}, Es).
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
@@ -668,10 +668,14 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
{#k_map{anno=A,es=Kes},Osub1,St1};
+pattern(#c_map_pair_assoc{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
+ {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
+ {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
+ {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2};
pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
{Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
{Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
- {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2};
+ {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2};
pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->
{Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),
{#k_binary{anno=A,segs=Kv},Osub1,St1};
@@ -1347,8 +1351,8 @@ get_match(#k_tuple{es=Es}, St0) ->
{#k_tuple{es=Mes},Mes,St1};
get_match(#k_map{es=Es0}, St0) ->
{Mes,St1} = new_vars(length(Es0), St0),
- {Es,_} = mapfoldl(fun(#k_map_pair{}=Pair, [V|Vs]) ->
- {Pair#k_map_pair{val=V},Vs}
+ {Es,_} = mapfoldl(fun(#k_map_pair_assoc{}=Pair, [V|Vs]) ->
+ {Pair#k_map_pair_assoc{val=V},Vs}
end, Mes, Es0),
{#k_map{es=Es},Mes,St1};
get_match(M, St) ->
@@ -1368,7 +1372,8 @@ new_clauses(Cs0, U, St) ->
#k_bin_int{next=N} ->
[N|As];
#k_map{es=Es} ->
- Vals = [V || #k_map_pair{val=V} <- Es],
+ Vals = [V ||
+ #k_map_pair_assoc{val=V} <- Es],
Vals ++ As;
_Other ->
As
@@ -1470,7 +1475,7 @@ arg_val(Arg, C) ->
end;
#k_map{es=Es} ->
Keys = [begin
- #k_map_pair{key=#k_literal{val=Key}} = Pair,
+ #k_map_pair_assoc{key=#k_literal{val=Key}} = Pair,
Key
end || Pair <- Es],
%% multiple keys may have the same name
@@ -1846,7 +1851,9 @@ lit_vars(#k_cons{hd=H,tl=T}) ->
union(lit_vars(H), lit_vars(T));
lit_vars(#k_map{var=Var,es=Es}) ->
lit_list_vars([Var|Es]);
-lit_vars(#k_map_pair{key=K,val=V}) ->
+lit_vars(#k_map_pair_assoc{key=K,val=V}) ->
+ union(lit_vars(K), lit_vars(V));
+lit_vars(#k_map_pair_exact{key=K,val=V}) ->
union(lit_vars(K), lit_vars(V));
lit_vars(#k_binary{segs=V}) -> lit_vars(V);
lit_vars(#k_bin_end{}) -> [];
@@ -1886,7 +1893,7 @@ pat_vars(#k_tuple{es=Es}) ->
pat_list_vars(Es);
pat_vars(#k_map{es=Es}) ->
pat_list_vars(Es);
-pat_vars(#k_map_pair{val=V}) ->
+pat_vars(#k_map_pair_assoc{val=V}) ->
pat_vars(V).
pat_list_vars(Ps) ->
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index 6671109afb..9da394d50d 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -39,7 +39,8 @@
-record(k_tuple, {anno=[],es}).
-record(k_map, {anno=[],var,es}).
--record(k_map_pair, {anno=[],key,val}).
+-record(k_map_pair_assoc, {anno=[],key,val}).
+-record(k_map_pair_exact, {anno=[],key,val}).
-record(k_cons, {anno=[],hd,tl}).
-record(k_binary, {anno=[],segs}).
-record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index 1af90792d3..e8d19336f6 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -115,8 +115,10 @@ format_1(#k_map{es=Es}, Ctxt) ->
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
$},$~
];
-format_1(#k_map_pair{key=K,val=V}, Ctxt) ->
+format_1(#k_map_pair_assoc{key=K,val=V}, Ctxt) ->
["~<",format(K, Ctxt),",",format(V, Ctxt),">"];
+format_1(#k_map_pair_exact{key=K,val=V}, Ctxt) ->
+ ["::<",format(K, Ctxt),",",format(V, Ctxt),">"];
format_1(#k_binary{segs=S}, Ctxt) ->
["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
format_1(#k_bin_seg{next=Next}=S, Ctxt) ->
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index 507f5d0df1..a3bd781c98 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -369,8 +369,10 @@ literal(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list(Es, Ctxt)};
literal(#k_map{var=Var,es=Es}, Ctxt) ->
{map,literal(Var, Ctxt),literal_list(Es, Ctxt)};
-literal(#k_map_pair{key=K,val=V}, Ctxt) ->
- {map_pair,literal(K, Ctxt),literal(V, Ctxt)};
+literal(#k_map_pair_assoc{key=K,val=V}, Ctxt) ->
+ {map_pair_assoc,literal(K, Ctxt),literal(V, Ctxt)};
+literal(#k_map_pair_exact{key=K,val=V}, Ctxt) ->
+ {map_pair_exact,literal(K, Ctxt),literal(V, Ctxt)};
literal(#k_literal{val=V}, _Ctxt) ->
{literal,V}.
@@ -402,7 +404,9 @@ literal2(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list2(Es, Ctxt)};
literal2(#k_map{es=Es}, Ctxt) ->
{map,literal_list2(Es, Ctxt)};
-literal2(#k_map_pair{key=K,val=V}, Ctxt) ->
+literal2(#k_map_pair_assoc{key=K,val=V}, Ctxt) ->
+ {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)};
+literal2(#k_map_pair_exact{key=K,val=V}, Ctxt) ->
{map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}.
literal_list2(Ks, Ctxt) ->
--
cgit v1.2.3
From c2702eb35db00ad67f922708eeea48616d908306 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
Date: Thu, 3 Oct 2013 14:06:57 +0200
Subject: compiler: Implement different instructions for => and :=
---
erts/emulator/beam/beam_emu.c | 301 +++++++++++++++++++++++++-----------
erts/emulator/beam/ops.tab | 11 +-
erts/emulator/test/map_SUITE.erl | 38 +++++
lib/compiler/src/beam_a.erl | 4 +
lib/compiler/src/beam_block.erl | 6 +-
lib/compiler/src/beam_clean.erl | 5 +-
lib/compiler/src/beam_flatten.erl | 4 +-
lib/compiler/src/beam_jump.erl | 2 +-
lib/compiler/src/beam_split.erl | 7 +-
lib/compiler/src/beam_validator.erl | 23 +--
lib/compiler/src/beam_z.erl | 4 +
lib/compiler/src/genop.tab | 9 +-
lib/compiler/src/v3_codegen.erl | 111 +++++++++++--
13 files changed, 392 insertions(+), 133 deletions(-)
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 1cad31be2c..b666b7c3f7 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -959,8 +959,10 @@ static BeamInstr* apply_fun(Process* p, Eterm fun,
static Eterm new_fun(Process* p, Eterm* reg,
ErlFunEntry* fe, int num_free) NOINLINE;
static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE;
-static Eterm update_map(Process* p, Eterm* reg,
- Eterm map, BeamInstr* I) NOINLINE;
+static Eterm update_map_assoc(Process* p, Eterm* reg,
+ Eterm map, BeamInstr* I) NOINLINE;
+static Eterm update_map_exact(Process* p, Eterm* reg,
+ Eterm map, BeamInstr* I) NOINLINE;
static int has_not_map_field(Eterm map, Eterm key);
static Eterm get_map_element(Eterm map, Eterm key);
@@ -2353,21 +2355,39 @@ void process_main(void)
Next(4+Arg(3));
}
- OpCase(update_map_jddII): {
+ OpCase(update_map_assoc_jddII): {
Eterm res;
Eterm map;
GetArg1(1, map);
x(0) = r(0);
SWAPOUT;
- res = update_map(c_p, reg, map, I);
+ res = update_map_assoc(c_p, reg, map, I);
SWAPIN;
- if (res) {
+ if (is_value(res)) {
r(0) = x(0);
StoreResult(res, Arg(2));
Next(5+Arg(4));
} else {
- goto lb_Cl_error;
+ goto badarg;
+ }
+ }
+
+ OpCase(update_map_exact_jddII): {
+ Eterm res;
+ Eterm map;
+
+ GetArg1(1, map);
+ x(0) = r(0);
+ SWAPOUT;
+ res = update_map_exact(c_p, reg, map, I);
+ SWAPIN;
+ if (is_value(res)) {
+ r(0) = x(0);
+ StoreResult(res, Arg(2));
+ Next(5+Arg(4));
+ } else {
+ goto badarg;
}
}
@@ -6387,18 +6407,10 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
return make_map(mp);
}
-
-/* This entire instruction will be split into two.
- * 1) update_map_exact (literals) <- this can be much more optimized
- * 2) update_map_assoc (literals)
- * Also update_map is pretty bad code as it stands now.
- */
-
static Eterm
-update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
+update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
{
Uint n;
- Uint i;
Uint num_old;
Uint num_updates;
Uint need;
@@ -6413,7 +6425,7 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
Eterm* kp;
if (is_not_map(map)) {
- return 0;
+ return THE_NON_VALUE;
}
old_mp = (map_t *) map_val(map);
@@ -6428,11 +6440,12 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
}
/*
- * Allocate heap space for the worst case (i.e. all keys are new).
+ * Allocate heap space for the worst case (i.e. all keys in the
+ * update list are new).
*/
num_updates = Arg(4) / 2;
- need = 2*(num_old+num_updates) + 4;
+ need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm);
if (HeapWordsLeft(p) < need) {
Uint live = Arg(3);
reg[live] = map;
@@ -6442,67 +6455,37 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
}
/*
- * Update map, optimistically assuming that there are no
- * new keys, allowing us to keep the old key tuple.
+ * Build the skeleton for the map, ready to be filled in.
+ *
+ * +-----------------------------------+
+ * | (Space for aritvyal for keys) | <-----------+
+ * +-----------------------------------+ |
+ * | (Space for key 1) | | <-- kp
+ * +-----------------------------------+ |
+ * . |
+ * . |
+ * . |
+ * +-----------------------------------+ |
+ * | (Space for last key) | |
+ * +-----------------------------------+ |
+ * | MAP_HEADER | |
+ * +-----------------------------------+ |
+ * | (Space for number of keys/values) | |
+ * +-----------------------------------+ |
+ * | Boxed tuple pointer >----------------+
+ * +-----------------------------------+
+ * | (Space for value 1) | <-- hp
+ * +-----------------------------------+
*/
- hp = p->htop;
- E = p->stop;
-
- old_vals = map_get_values(old_mp);
- old_keys = map_get_keys(old_mp);
+ E = p->stop;
+ kp = p->htop + 1; /* Point to first key */
+ hp = kp + num_old + num_updates;
res = make_map(hp);
- mp = (map_t *)hp; hp += 3;
+ mp = (map_t *)hp;
+ hp += sizeof(map_t) / sizeof(Eterm);
mp->thing_word = MAP_HEADER;
- mp->size = num_old;
- mp->keys = old_mp->keys;
-
- ASSERT(num_updates > 0);
-
- /* Get array of key/value pairs to be updated */
- new_p = &Arg(5);
- GET_TERM(*new_p, new_key);
-
- n = num_updates;
-
- for (i = 0; i < num_old; i++) {
- if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) {
- /* not same keys */
- *hp++ = *old_vals;
- } else {
- GET_TERM(new_p[1], *hp);
- hp++;
- n--;
- if (n == 0) {
- new_key = THE_NON_VALUE;
- } else {
- new_p += 2;
- GET_TERM(*new_p, new_key);
- }
- }
- old_vals++, old_keys++;
- }
-
- /*
- * If we have exhausted the update list we are done.
- */
-
- if (n == 0) {
- p->htop = hp;
- return res;
- }
-
- /*
- * There were some new keys. We'll have to start over and rebuild
- * the key tuple too.
- */
-
- kp = p->htop;
- *kp++ = make_arityval(0);
-
- res = make_map(hp);
- mp = (map_t *)hp; hp += 3;
mp->keys = make_tuple(kp-1);
old_vals = map_get_values(old_mp);
@@ -6512,20 +6495,28 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
GET_TERM(*new_p, new_key);
n = num_updates;
+ /*
+ * Fill in keys and values, until we run out of either updates
+ * or old values and keys.
+ */
+
for (;;) {
Eterm key;
Sint c;
ASSERT(kp < (Eterm *)mp);
key = *old_keys;
- if ((c = cmp(key, new_key)) < 0) {
+ if ((c = CMP(key, new_key)) < 0) {
+ /* Copy old key and value */
*kp++ = key;
*hp++ = *old_vals;
old_keys++, old_vals++, num_old--;
} else { /* Replace or insert new */
- *kp++ = new_key;
GET_TERM(new_p[1], *hp++);
- if (c == 0) { /* If replacement */
+ if (c > 0) { /* If new new key */
+ *kp++ = new_key;
+ } else { /* If replacement */
+ *kp++ = key;
old_keys++, old_vals++, num_old--;
}
n--;
@@ -6541,32 +6532,162 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
}
}
- while (n-- > 0) {
- GET_TERM(new_p[0], *kp++);
- GET_TERM(new_p[1], *hp++);
- new_p += 2;
- }
-
/*
- * All updates done. Now copy the remaining part of the frame's
- * keys and values.
+ * At this point, we have run out of either old keys and values,
+ * or the update list. In other words, at least of one n and
+ * num_old must be zero.
*/
- while (num_old-- > 0) {
- ASSERT(kp < (Eterm *)mp);
- *kp++ = *old_keys++;
- *hp++ = *old_vals++;
+ if (n > 0) {
+ /*
+ * All old keys and values have been copied, but there
+ * are still new keys and values in the update list that
+ * must be copied.
+ */
+ ASSERT(num_old == 0);
+ while (n-- > 0) {
+ GET_TERM(new_p[0], *kp++);
+ GET_TERM(new_p[1], *hp++);
+ new_p += 2;
+ }
+ } else {
+ /*
+ * All updates are now done. We may still have old
+ * keys and values that we must copy.
+ */
+ ASSERT(n == 0);
+ while (num_old-- > 0) {
+ ASSERT(kp < (Eterm *)mp);
+ *kp++ = *old_keys++;
+ *hp++ = *old_vals++;
+ }
}
+
+ /*
+ * Calculate how many values that are unused at the end of the
+ * key tuple and fill it out with a bignum header.
+ */
if ((n = (Eterm *)mp - kp) > 0) {
*kp = make_pos_bignum_header(n-1);
}
+
+ /*
+ * Fill in the size of the map in both the key tuple and in the map.
+ */
+
n = kp - p->htop - 1; /* Actual number of keys/values */
*p->htop = make_arityval(n);
- mp->thing_word = MAP_HEADER;
mp->size = n;
p->htop = hp;
return res;
}
+
+/*
+ * Update values for keys that already exist in the map.
+ */
+
+static Eterm
+update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
+{
+ Uint n;
+ Uint i;
+ Uint num_old;
+ Uint need;
+ map_t *old_mp, *mp;
+ Eterm res;
+ Eterm* hp;
+ Eterm* E;
+ Eterm* old_keys;
+ Eterm* old_vals;
+ Eterm* new_p;
+ Eterm new_key;
+
+ if (is_not_map(map)) {
+ return THE_NON_VALUE;
+ }
+
+ old_mp = (map_t *) map_val(map);
+ num_old = map_get_size(old_mp);
+
+ /*
+ * If the old map is empty, create a new map.
+ */
+
+ if (num_old == 0) {
+ return new_map(p, reg, I+1);
+ }
+
+ /*
+ * Allocate the exact heap space needed.
+ */
+
+ need = num_old + sizeof(map_t) / sizeof(Eterm);
+ if (HeapWordsLeft(p) < need) {
+ Uint live = Arg(3);
+ reg[live] = map;
+ erts_garbage_collect(p, need, reg, live+1);
+ map = reg[live];
+ old_mp = (map_t *)map_val(map);
+ }
+
+ /*
+ * Update map, keeping the old key tuple.
+ */
+
+ hp = p->htop;
+ E = p->stop;
+
+ old_vals = map_get_values(old_mp);
+ old_keys = map_get_keys(old_mp);
+
+ res = make_map(hp);
+ mp = (map_t *)hp;
+ hp += sizeof(map_t) / sizeof(Eterm);
+ mp->thing_word = MAP_HEADER;
+ mp->size = num_old;
+ mp->keys = old_mp->keys;
+
+ /* Get array of key/value pairs to be updated */
+ new_p = &Arg(5);
+ GET_TERM(*new_p, new_key);
+
+ /* Update all values */
+ n = Arg(4) / 2; /* Number of values to be updated */
+ ASSERT(n > 0);
+ for (i = 0; i < num_old; i++) {
+ if (!EQ(*old_keys, new_key)) {
+ /* Not same keys */
+ *hp++ = *old_vals;
+ } else {
+ GET_TERM(new_p[1], *hp);
+ hp++;
+ n--;
+ if (n == 0) {
+ /*
+ * All updates done. Copy remaining values
+ * and return the result.
+ */
+ for (i++, old_vals++; i < num_old; i++) {
+ *hp++ = *old_vals++;
+ }
+ ASSERT(hp == p->htop + need);
+ p->htop = hp;
+ return res;
+ } else {
+ new_p += 2;
+ GET_TERM(*new_p, new_key);
+ }
+ }
+ old_vals++, old_keys++;
+ }
+
+ /*
+ * Updates left. That means that at least one the keys in the
+ * update list did not previously exist.
+ */
+ ASSERT(hp == p->htop + need);
+ return THE_NON_VALUE;
+}
#undef GET_TERM
int catchlevel(Process *p)
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index b89bdb2e3a..f35997efee 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1474,11 +1474,16 @@ apply_last I P
# has_map_field Fail Src Key => jump Fail
# get_map_element Fail Src Key Dst => jump Fail
-put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest
-put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest
+put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest
+put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest
+put_map_assoc F Src Dst Live Size Rest=* => \
+ update_map_assoc F Src Dst Live Size Rest
+put_map_exact F Src Dst Live Size Rest=* => \
+ update_map_exact F Src Dst Live Size Rest
new_map j d I I
-update_map j d d I I
+update_map_assoc j d d I I
+update_map_exact j d d I I
is_map Fail cq => jump Fail
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 6d82a2cb59..81d39fc97a 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -23,6 +23,7 @@
-export([
t_build_and_match_literals/1,
t_update_literals/1,t_match_and_update_literals/1,
+ update_assoc/1,update_exact/1,
t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
t_guard_receive/1, t_guard_fun/1,
t_list_comprehension/1,
@@ -48,6 +49,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() -> [
t_build_and_match_literals,
t_update_literals, t_match_and_update_literals,
+ update_assoc,update_exact,
t_guard_bifs, t_guard_sequence, t_guard_update,
t_guard_receive,t_guard_fun, t_list_comprehension,
t_map_sort_literals,
@@ -166,6 +168,42 @@ loop_match_and_update_literals_x_q(Map, []) -> Map;
loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+update_assoc(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1.0=>wrong,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{3.0=>new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0:=wrong,3.0=>new},
+
+ %% Errors cases.
+ BadMap = id(badmap),
+ {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
+
+ ok.
+
+update_exact(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},
+
+ M2 = M0#{3.0:=new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0=>wrong,3.0:=new},
+ M2 = M0#{3=>wrong,3.0:=new},
+
+ %% Errors cases.
+ {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+
+ ok.
t_guard_bifs(Config) when is_list(Config) ->
true = map_guard_head(#{a=>1}),
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
index c590c5e35b..3dfa67a771 100644
--- a/lib/compiler/src/beam_a.erl
+++ b/lib/compiler/src/beam_a.erl
@@ -88,6 +88,10 @@ rename_instr({bs_private_append=I,F,Sz,U,Src,Flags,Dst}) ->
{bs_init,F,{I,U,Flags},none,[Sz,Src],Dst};
rename_instr(bs_init_writable=I) ->
{bs_init,{f,0},I,1,[{x,0}],{x,0}};
+rename_instr({put_map_assoc,Fail,S,D,R,L}) ->
+ {put_map,Fail,assoc,S,D,R,L};
+rename_instr({put_map_exact,Fail,S,D,R,L}) ->
+ {put_map,Fail,exact,S,D,R,L};
rename_instr({select_val=I,Reg,Fail,{list,List}}) ->
{select,I,Reg,Fail,List};
rename_instr({select_tuple_arity=I,Reg,Fail,{list,List}}) ->
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index f9e90b81d2..d5f2ffc444 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -152,8 +152,8 @@ collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}};
collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}};
collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
collect(remove_message) -> {set,[],[],remove_message};
-collect({put_map,F,S,D,R,{list,Puts}}) ->
- {set,[D],[S|Puts],{alloc,R,{put_map,F}}};
+collect({put_map,F,Op,S,D,R,{list,Puts}}) ->
+ {set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}};
collect({get_map_element,F,S,K,D}) ->
{set,[D],[S],{get_map_element,K,F}};
collect({'catch',R,L}) -> {set,[R],[],{'catch',L}};
@@ -387,7 +387,7 @@ gen_init(Fs, Regs, Y, Acc) ->
init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg;
init_yreg([{set,_,_,{alloc,_,{gc_bif,_,_}}}|_], Reg) -> Reg;
-init_yreg([{set,_,_,{alloc,_,{put_map,_}}}|_], Reg) -> Reg;
+init_yreg([{set,_,_,{alloc,_,{put_map,_,_}}}|_], Reg) -> Reg;
init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg));
init_yreg(_Is, Reg) -> Reg.
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index 6f802d0436..55f985ad0e 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -259,8 +259,9 @@ replace([{bs_utf8_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D);
replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D);
-replace([{put_map=I,{f,Lbl},Src,Dst,Live,List}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{I,{f,label(Lbl, D)},Src,Dst,Live,List}|Acc], D);
+replace([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D)
+ when Lbl =/= 0 ->
+ replace(Is, [{I,{f,label(Lbl, D)},Op,Src,Dst,Live,List}|Acc], D);
replace([{get_map_element=I,{f,Lbl},Src,Key,Dst}|Is], Acc, D) when Lbl =/= 0 ->
replace(Is, [{I,{f,label(Lbl, D)},Src,Key,Dst}|Acc], D);
replace([I|Is], Acc, D) ->
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index ad9591ff05..534bc6d954 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -61,8 +61,8 @@ norm({set,[],[S],put}) -> {put,S};
norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
-norm({set,[D],[S|Puts],{alloc,R,{put_map,F}}}) ->
- {put_map,F,S,D,R,{list,Puts}};
+norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
+ {put_map,F,Op,S,D,R,{list,Puts}};
norm({set,[D],[S],{get_map_element,K,F}}) ->
{get_map_element,F,S,K,D};
norm({set,[],[],remove_message}) -> remove_message;
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index bbef75c219..1f720b94c3 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -527,7 +527,7 @@ ulbl({bs_init,Lbl,_,_,_,_}, Used) ->
mark_used(Lbl, Used);
ulbl({bs_put,Lbl,_,_}, Used) ->
mark_used(Lbl, Used);
-ulbl({put_map,Lbl,_Src,_Dst,_Live,_List}, Used) ->
+ulbl({put_map,Lbl,_Op,_Src,_Dst,_Live,_List}, Used) ->
mark_used(Lbl, Used);
ulbl({get_map_element,Lbl,_Src,_Key,_Dst}, Used) ->
mark_used(Lbl, Used);
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index bbd4695289..638a4826ea 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -49,9 +49,10 @@ split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 ->
split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc)
when Lbl =/= 0 ->
split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]);
-split_block([{set,[D],[S|Puts],{alloc,R,{put_map,{f,Lbl}=Fail}}}|Is], Bl, Acc)
- when Lbl =/= 0 ->
- split_block(Is, [], [{put_map,Fail,S,D,R,{list,Puts}}|make_block(Bl, Acc)]);
+split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is],
+ Bl, Acc) when Lbl =/= 0 ->
+ split_block(Is, [], [{put_map,Fail,Op,S,D,R,{list,Puts}}|
+ make_block(Bl, Acc)]);
split_block([{set,[D],[S],{get_map_element,K,{f,Lbl}=Fail}}|Is], Bl, Acc)
when Lbl =/= 0 ->
split_block(Is, [], [{get_map_element,Fail,S,K,D}|make_block(Bl, Acc)]);
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 37b08f43d3..97f84da08f 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -866,15 +866,10 @@ valfun_4({bs_final2,Src,Dst}, Vst0) ->
assert_term(Src, Vst0),
set_type_reg(binary, Dst, Vst0);
%% Map instructions.
-valfun_4({put_map,{f,Fail},Src,Dst,Live,{list,List}}, Vst0) ->
- verify_live(Live, Vst0),
- verify_y_init(Vst0),
- [assert_term(Term, Vst0) || Term <- List],
- assert_term(Src, Vst0),
- Vst1 = heap_alloc(0, Vst0),
- Vst2 = branch_state(Fail, Vst1),
- Vst = prune_x_regs(Live, Vst2),
- set_type_reg(term, Dst, Vst);
+valfun_4({put_map_assoc,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+ verify_put_map(Fail, Src, Dst, Live, List, Vst);
+valfun_4({put_map_exact,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+ verify_put_map(Fail, Src, Dst, Live, List, Vst);
valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) ->
assert_term(Src, Vst0),
assert_term(Key, Vst0),
@@ -883,6 +878,16 @@ valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) ->
valfun_4(_, _) ->
error(unknown_instruction).
+verify_put_map(Fail, Src, Dst, Live, List, Vst0) ->
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
+ [assert_term(Term, Vst0) || Term <- List],
+ assert_term(Src, Vst0),
+ Vst1 = heap_alloc(0, Vst0),
+ Vst2 = branch_state(Fail, Vst1),
+ Vst = prune_x_regs(Live, Vst2),
+ set_type_reg(term, Dst, Vst).
+
%%
%% Common code for validating bs_get* instructions.
%%
diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl
index 8c6b0c916d..9953a48710 100644
--- a/lib/compiler/src/beam_z.erl
+++ b/lib/compiler/src/beam_z.erl
@@ -74,6 +74,10 @@ undo_rename({bs_init,F,{I,Extra,U,Flags},Live,[Sz,Src],Dst}) ->
{I,F,Sz,Extra,Live,U,Src,Flags,Dst};
undo_rename({bs_init,_,bs_init_writable=I,_,_,_}) ->
I;
+undo_rename({put_map,Fail,assoc,S,D,R,L}) ->
+ {put_map_assoc,Fail,S,D,R,L};
+undo_rename({put_map,Fail,exact,S,D,R,L}) ->
+ {put_map_exact,Fail,S,D,R,L};
undo_rename({select,I,Reg,Fail,List}) ->
{I,Reg,Fail,{list,List}};
undo_rename(I) -> I.
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 861acd3932..79b467f949 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -531,7 +531,8 @@ BEAM_FORMAT_NUMBER=0
# R16
-154: put_map/5
-155: is_map/2
-156: has_map_field/3
-157: get_map_element/4
+154: put_map_assoc/5
+155: put_map_exact/5
+156: is_map/2
+157: has_map_field/3
+158: get_map_element/4
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index c1d555efac..db8ea04778 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1488,7 +1488,7 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
%% Now generate the complete code for constructing the binary.
Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),
{Sis++Code,Aft,St};
-set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef,
+set_cg([{var,R}], {map,SrcMap,Es0}, Le, Vdb, Bef,
#cg{in_catch=InCatch,bfail=Bfail}=St) ->
Fail = {f,Bfail},
{Sis,Int0} =
@@ -1501,24 +1501,41 @@ set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef,
{var,SrcVar} -> fetch_var(SrcVar, Int0);
_ -> SrcMap
end,
-
- %% MapPairs in put_map must be sorted in ascending Key order.
- %% Key literals must be unique when arriving here in v3_codegen.
-
- SortedEs = lists:sort(fun({_,{_T1,K1},_},{_,{_T2,K2},_}) ->
- K1 =< K2
- end, Es),
- List = flatmap(fun
- ({map_pair_assoc,K,{var,V}}) -> [K,fetch_var(V, Int0)];
- ({map_pair_exact,K,{var,V}}) -> [K,fetch_var(V, Int0)];
- ({map_pair_assoc,K,E}) -> [K,E];
- ({map_pair_exact,K,E}) -> [K,E]
- end, SortedEs),
- Live = max_reg(Bef#sr.reg),
+ {Assoc,Exact} =
+ try
+ cg_map_pairs(Es0)
+ catch
+ throw:badarg ->
+ {[],[{{float,0.0},{atom,badarg}},
+ {{integer,0},{atom,badarg}}]}
+ end,
+ F = fun ({K,{var,V}}) -> [K,fetch_var(V, Int0)];
+ ({K,E}) -> [K,E]
+ end,
+ AssocList = flatmap(F, Assoc),
+ ExactList = flatmap(F, Exact),
+ Live0 = max_reg(Bef#sr.reg),
Int1 = clear_dead(Int0, Le#l.i, Vdb),
Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)},
Target = fetch_reg(R, Aft#sr.reg),
- Code = [Line,{put_map,Fail,SrcReg,Target,Live,{list,List}}],
+ Code = [Line] ++
+ case {AssocList,ExactList} of
+ {[_|_],[]} ->
+ [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}}];
+ {[_|_],[_|_]} ->
+ Live = case Target of
+ {x,TargetX} when TargetX =:= Live0 ->
+ Live0 + 1;
+ _ ->
+ Live0
+ end,
+ [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}},
+ {put_map_exact,Fail,Target,Target,Live,{list,ExactList}}];
+ {[],[_|_]} ->
+ [{put_map_exact,Fail,SrcReg,Target,Live0,{list,ExactList}}];
+ {[],[]} ->
+ [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,[]}}]
+ end,
{Sis++Code,Aft,St};
set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
%% Find a place for the return register first.
@@ -1532,6 +1549,68 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
end,
{Ais,clear_dead(Int, Le#l.i, Vdb),St}.
+%% cg_map_pairs(MapPairs) -> {Assoc,Exact}
+%% Assoc = Exact = [{K,V}]
+%%
+%% Remove multiple assignments to the same key, and return
+%% one list key-value list with all keys that may or may not exist
+%% (Assoc), and one with keys that must exist (Exact).
+%%
+
+cg_map_pairs(Es0) ->
+ Es = cg_map_pairs_1(Es0, 0),
+ R0 = sofs:relation(Es),
+ R1 = sofs:relation_to_family(R0),
+ R2 = sofs:to_external(R1),
+
+ %% R2 is now [{KeyValue,[{Order,Op,OriginalKey,Value}]}]
+ R3 = [begin
+ %% The value for the last pair determines the value.
+ {_,_,_,V} = lists:last(Vs),
+ {Op,{_,SortOrder}=K} = map_pair_op_and_key(Vs),
+ {Op,{SortOrder,K,V}}
+ end || {_,Vs} <- R2],
+
+ %% R3 is now [{Op,{Key,Value}}]
+ R = lists:sort(R3),
+
+ %% R4 is now sorted with all alloc first in the list, followed by
+ %% all exact.
+ {Assoc,Exact} = lists:partition(fun({Op,_}) -> Op =:= assoc end, R),
+ {[{K,V} || {_,{_,K,V}} <- Assoc],
+ [{K,V} || {_,{_,K,V}} <- Exact]}.
+
+cg_map_pairs_1([{map_pair_assoc,{_,Kv}=K,V}|T], Order) ->
+ [{Kv,{Order,assoc,K,V}}|cg_map_pairs_1(T, Order+1)];
+cg_map_pairs_1([{map_pair_exact,{_,Kv}=K,V}|T], Order) ->
+ [{Kv,{Order,exact,K,V}}|cg_map_pairs_1(T, Order+1)];
+cg_map_pairs_1([], _) -> [].
+
+%% map_pair_op_and_key({_,Op,K,_}) -> {Operator,Key}
+%% Determine the operator and key to use. Throw a 'badarg'
+%% exception if there are contradictory exact updates.
+
+map_pair_op_and_key(L) ->
+ case [K || {_,exact,K,_} <- L] of
+ [K] ->
+ %% There is a single ':=' operator. Use that key.
+ {exact,K};
+ [K|T] ->
+ %% There is more than one ':=' operator. All of them
+ %% must have the same key.
+ case lists:all(fun(E) -> E =:= K end, T) of
+ true ->
+ {exact,K};
+ false ->
+ %% Some keys are different, e.g. 1 and 1.0.
+ throw(badarg)
+ end;
+ [] ->
+ %% Only '=>' operators. Use the first key in the list.
+ [{_,assoc,K,_}|_] = L,
+ {assoc,K}
+ end.
+
%%%
%%% Code generation for constructing binaries.
%%%
--
cgit v1.2.3
From 813f61de8e7872481a0369de3297596c1b11881d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 9 Oct 2013 15:46:03 +0200
Subject: erts: Fixup map instructions for halfword
---
erts/emulator/beam/beam_emu.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index b666b7c3f7..fe2e196785 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6353,7 +6353,7 @@ static Eterm get_map_element(Eterm map, Eterm key)
#define GET_TERM(term, dest) \
do { \
- Eterm src = term; \
+ Eterm src = (Eterm)(term); \
switch (src & _TAG_IMMED1_MASK) { \
case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \
dest = x(0); \
@@ -6380,7 +6380,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
Eterm keys;
Eterm *mhp,*thp;
Eterm *E;
- Eterm *tp;
+ BeamInstr *ptr;
map_t *mp;
if (HeapWordsLeft(p) < need) {
@@ -6390,18 +6390,18 @@ new_map(Process* p, Eterm* reg, BeamInstr* I)
thp = p->htop;
mhp = thp + 1 + n/2;
E = p->stop;
- tp = &Arg(4);
+ ptr = &Arg(4);
keys = make_tuple(thp);
*thp++ = make_arityval(n/2);
- mp = (map_t *)mhp; mhp += 3;
+ mp = (map_t *)mhp; mhp += MAP_HEADER_SIZE;
mp->thing_word = MAP_HEADER;
mp->size = n/2;
mp->keys = keys;
for (i = 0; i < n/2; i++) {
- GET_TERM(*tp++, *thp++);
- GET_TERM(*tp++, *mhp++);
+ GET_TERM(*ptr++, *thp++);
+ GET_TERM(*ptr++, *mhp++);
}
p->htop = mhp;
return make_map(mp);
@@ -6420,7 +6420,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
Eterm* E;
Eterm* old_keys;
Eterm* old_vals;
- Eterm* new_p;
+ BeamInstr* new_p;
Eterm new_key;
Eterm* kp;
@@ -6445,7 +6445,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
*/
num_updates = Arg(4) / 2;
- need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm);
+ need = 2*(num_old+num_updates) + 1 + MAP_HEADER_SIZE;
if (HeapWordsLeft(p) < need) {
Uint live = Arg(3);
reg[live] = map;
@@ -6484,7 +6484,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
res = make_map(hp);
mp = (map_t *)hp;
- hp += sizeof(map_t) / sizeof(Eterm);
+ hp += MAP_HEADER_SIZE;
mp->thing_word = MAP_HEADER;
mp->keys = make_tuple(kp-1);
@@ -6599,7 +6599,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
Eterm* E;
Eterm* old_keys;
Eterm* old_vals;
- Eterm* new_p;
+ BeamInstr* new_p;
Eterm new_key;
if (is_not_map(map)) {
@@ -6621,7 +6621,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
* Allocate the exact heap space needed.
*/
- need = num_old + sizeof(map_t) / sizeof(Eterm);
+ need = num_old + MAP_HEADER_SIZE;
if (HeapWordsLeft(p) < need) {
Uint live = Arg(3);
reg[live] = map;
@@ -6642,7 +6642,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
res = make_map(hp);
mp = (map_t *)hp;
- hp += sizeof(map_t) / sizeof(Eterm);
+ hp += MAP_HEADER_SIZE;
mp->thing_word = MAP_HEADER;
mp->size = num_old;
mp->keys = old_mp->keys;
--
cgit v1.2.3
From d4379900790b37498fefa237cd03ab2d5781bb6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 6 Nov 2013 08:58:32 +0100
Subject: compiler: Teach Maps understanding to inliner
---
lib/compiler/src/cerl.erl | 107 +++++++++++++++++++++++++++++++++++++---
lib/compiler/src/cerl_trees.erl | 49 +++++++++++++++++-
lib/compiler/src/core_lint.erl | 23 ++++++++-
3 files changed, 170 insertions(+), 9 deletions(-)
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 4b74d60e9f..30bb4c5d96 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -120,7 +120,16 @@
update_c_bitstr/5, update_c_bitstr/6, ann_c_bitstr/5,
ann_c_bitstr/6, is_c_bitstr/1, bitstr_val/1, bitstr_size/1,
bitstr_bitsize/1, bitstr_unit/1, bitstr_type/1,
- bitstr_flags/1]).
+ bitstr_flags/1,
+
+ %% keep map exports here for now
+ map_es/1,
+ update_c_map_skel/2,
+ update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2,
+ ann_c_map_skel/2,
+ ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2,
+ map_pair_es/1, map_pair_assoc_es/1, map_pair_exact_es/1
+ ]).
-export_type([c_binary/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, c_literal/0,
c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]).
@@ -250,8 +259,8 @@
-type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case'
| 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec'
- | 'literal' | 'module' | 'primop' | 'receive' | 'seq' | 'try'
- | 'tuple' | 'values' | 'var'.
+ | 'literal' | 'map' | 'module' | 'primop' | 'receive' | 'seq'
+ | 'try' | 'tuple' | 'values' | 'var'.
-spec type(cerl()) -> ctype().
@@ -268,6 +277,9 @@ type(#c_fun{}) -> 'fun';
type(#c_let{}) -> 'let';
type(#c_letrec{}) -> letrec;
type(#c_literal{}) -> literal;
+type(#c_map{}) -> map;
+type(#c_map_pair_assoc{}) -> map_pair_assoc;
+type(#c_map_pair_exact{}) -> map_pair_exact;
type(#c_module{}) -> module;
type(#c_primop{}) -> primop;
type(#c_receive{}) -> 'receive';
@@ -1557,6 +1569,58 @@ ann_make_list(_, [], Node) ->
Node.
+%% ---------------------------------------------------------------------
+%% maps
+
+map_es(#c_map{es = Es}) ->
+ Es.
+
+map_pair_assoc_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V].
+map_pair_exact_es(#c_map_pair_exact{key=K,val=V}) -> [K,V].
+map_pair_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V];
+map_pair_es(#c_map_pair_exact{key=K,val=V}) -> [K,V].
+
+update_c_map_pair_assoc_skel(Old, [K,V]) ->
+ #c_map_pair_assoc{key=K, val=V, anno = get_ann(Old)}.
+
+update_c_map_pair_exact_skel(Old, [K,V]) ->
+ #c_map_pair_exact{key=K, val=V, anno = get_ann(Old)}.
+
+ann_c_map_pair_assoc_skel(As, [K,V]) ->
+ #c_map_pair_assoc{key = K, val=V, anno = As}.
+
+ann_c_map_pair_exact_skel(As, [K,V]) ->
+ #c_map_pair_exact{key = K, val=V, anno = As}.
+
+%c_map_skel(Es) ->
+% #c_map{es = Es}.
+%
+
+ann_c_map(As, Es) ->
+ #c_map{es = Es, anno = As }.
+%% TODO: when we have map literals use a variant of
+%% case is_lit_list(Es) of
+%% false ->
+%% #c_map{es = Es, anno = As};
+%% true ->
+%% #c_literal{val = maps:from_list(lit_list_vals(Es)), anno = As}
+%% end.
+
+
+ann_c_map_pair_assoc(As, [K,V]) ->
+ #c_map_pair_assoc{key = K, val=V, anno = As}.
+
+ann_c_map_pair_exact(As, [K,V]) ->
+ #c_map_pair_exact{key = K, val=V, anno = As}.
+
+
+ann_c_map_skel(As, Es) ->
+ #c_map{es = Es, anno = As}.
+
+update_c_map_skel(Old, Es) ->
+ #c_map{es = Es, anno = get_ann(Old)}.
+
+
%% ---------------------------------------------------------------------
%% @spec c_tuple(Elements::[cerl()]) -> cerl()
@@ -2945,6 +3009,10 @@ pat_vars(Node, Vs) ->
pat_vars(cons_hd(Node), pat_vars(cons_tl(Node), Vs));
tuple ->
pat_list_vars(tuple_es(Node), Vs);
+ map ->
+ pat_list_vars(map_es(Node), Vs);
+ map_pair_exact ->
+ pat_list_vars(map_pair_exact_es(Node), Vs);
binary ->
pat_list_vars(binary_segments(Node), Vs);
bitstr ->
@@ -3756,6 +3824,12 @@ is_data(#c_cons{}) ->
true;
is_data(#c_tuple{}) ->
true;
+is_data(#c_map{}) ->
+ true;
+is_data(#c_map_pair_assoc{}) ->
+ true;
+is_data(#c_map_pair_exact{}) ->
+ true;
is_data(_) ->
false.
@@ -3801,7 +3875,13 @@ data_type(#c_literal{val = V}) ->
data_type(#c_cons{}) ->
cons;
data_type(#c_tuple{}) ->
- tuple.
+ tuple;
+data_type(#c_map{}) ->
+ map;
+data_type(#c_map_pair_assoc{}) ->
+ map_pair_assoc;
+data_type(#c_map_pair_exact{}) ->
+ map_pair_exact.
%% @spec data_es(Node::cerl()) -> [cerl()]
@@ -3833,7 +3913,13 @@ data_es(#c_literal{val = V}) ->
data_es(#c_cons{hd = H, tl = T}) ->
[H, T];
data_es(#c_tuple{es = Es}) ->
- Es.
+ Es;
+data_es(#c_map{es=Es}) ->
+ Es;
+data_es(#c_map_pair_assoc{key=K,val=V}) ->
+ [K,V];
+data_es(#c_map_pair_exact{key=K,val=V}) ->
+ [K,V].
%% @spec data_arity(Node::cerl()) -> integer()
@@ -3890,7 +3976,10 @@ make_data(CType, Es) ->
ann_make_data(As, {atomic, V}, []) -> #c_literal{val = V, anno = As};
ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T);
-ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es).
+ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es);
+ann_make_data(As, map, Es) -> ann_c_map(As, Es);
+ann_make_data(As, map_pair_assoc, Es) -> ann_c_map_pair_assoc(As, Es);
+ann_make_data(As, map_pair_exact, Es) -> ann_c_map_pair_exact(As, Es).
%% @spec update_data(Old::cerl(), Type::dtype(),
@@ -4022,6 +4111,12 @@ subtrees(T) ->
[[cons_hd(T)], [cons_tl(T)]];
tuple ->
[tuple_es(T)];
+ map ->
+ [map_es(T)];
+ map_pair_assoc ->
+ [map_pair_assoc_es(T)];
+ map_pair_exact ->
+ [map_pair_exact_es(T)];
'let' ->
[let_vars(T), [let_arg(T)], [let_body(T)]];
seq ->
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 1e3755025f..dc1cc606b3 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -55,7 +55,15 @@
update_c_let/4, update_c_letrec/3, update_c_module/5,
update_c_primop/3, update_c_receive/4, update_c_seq/3,
update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2,
- update_c_values/2, values_es/1, var_name/1]).
+ update_c_values/2, values_es/1, var_name/1,
+
+ map_es/1,
+ update_c_map_skel/2,
+ update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2,
+ ann_c_map_skel/2,
+ ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2,
+ map_pair_assoc_es/1, map_pair_exact_es/1
+ ]).
%% ---------------------------------------------------------------------
@@ -129,6 +137,12 @@ map_1(F, T) ->
map(F, cons_tl(T)));
tuple ->
update_c_tuple_skel(T, map_list(F, tuple_es(T)));
+ map ->
+ update_c_map_skel(T, map_list(F, map_es(T)));
+ map_pair_assoc ->
+ update_c_map_pair_assoc_skel(T, map_list(F, map_pair_assoc_es(T)));
+ map_pair_exact ->
+ update_c_map_pair_exact_skel(T, map_list(F, map_pair_exact_es(T)));
'let' ->
update_c_let(T, map_list(F, let_vars(T)),
map(F, let_arg(T)),
@@ -235,6 +249,12 @@ fold_1(F, S, T) ->
fold(F, fold(F, S, cons_hd(T)), cons_tl(T));
tuple ->
fold_list(F, S, tuple_es(T));
+ map ->
+ fold_list(F, S, map_es(T));
+ map_pair_assoc ->
+ fold_list(F, S, map_pair_assoc_es(T));
+ map_pair_exact ->
+ fold_list(F, S, map_pair_exact_es(T));
'let' ->
fold(F, fold(F, fold_list(F, S, let_vars(T)),
let_arg(T)),
@@ -349,6 +369,15 @@ mapfold(F, S0, T) ->
tuple ->
{Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
F(update_c_tuple_skel(T, Ts), S1);
+ map ->
+ {Ts, S1} = mapfold_list(F, S0, map_es(T)),
+ F(update_c_map_skel(T, Ts), S1);
+ map_pair_assoc ->
+ {Ts, S1} = mapfold_list(F, S0, map_pair_assoc_es(T)),
+ F(update_c_map_pair_assoc_skel(T,Ts), S1);
+ map_pair_exact ->
+ {Ts, S1} = mapfold_list(F, S0, map_pair_exact_es(T)),
+ F(update_c_map_pair_exact_skel(T,Ts), S1);
'let' ->
{Vs, S1} = mapfold_list(F, S0, let_vars(T)),
{A, S2} = mapfold(F, S1, let_arg(T)),
@@ -488,6 +517,12 @@ variables(T, S) ->
variables(cons_tl(T), S));
tuple ->
vars_in_list(tuple_es(T), S);
+ map ->
+ vars_in_list(map_es(T), S);
+ map_pair_assoc ->
+ vars_in_list(map_pair_assoc_es(T), S);
+ map_pair_exact ->
+ vars_in_list(map_pair_exact_es(T), S);
'let' ->
Vs = variables(let_body(T), S),
Vs1 = var_list_names(let_vars(T)),
@@ -688,6 +723,18 @@ label(T, N, Env) ->
{Ts, N1} = label_list(tuple_es(T), N, Env),
{As, N2} = label_ann(T, N1),
{ann_c_tuple_skel(As, Ts), N2};
+ map ->
+ {Ts, N1} = label_list(map_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_map_skel(As, Ts), N2};
+ map_pair_assoc ->
+ {Ts, N1} = label_list(map_pair_assoc_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_map_pair_assoc_skel(As, Ts), N2};
+ map_pair_exact ->
+ {Ts, N1} = label_list(map_pair_exact_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_map_pair_exact_skel(As, Ts), N2};
'let' ->
{A, N1} = label(let_arg(T), N, Env),
{Vs, N2, Env1} = label_vars(let_vars(T), N1, Env),
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index 67d37ff1fc..185193e45d 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -254,6 +254,12 @@ gexpr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
gexpr_list([H,T], Def, St);
gexpr(#c_tuple{es=Es}, Def, _Rt, St) ->
gexpr_list(Es, Def, St);
+gexpr(#c_map{es=Es}, Def, _Rt, St) ->
+ gexpr_list(Es, Def, St);
+gexpr(#c_map_pair_assoc{key=K,val=V}, Def, _Rt, St) ->
+ gexpr_list([K,V], Def, St);
+gexpr(#c_map_pair_exact{key=K,val=V}, Def, _Rt, St) ->
+ gexpr_list([K,V], Def, St);
gexpr(#c_binary{segments=Ss}, Def, _Rt, St) ->
gbitstr_list(Ss, Def, St);
gexpr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
@@ -278,6 +284,7 @@ gexpr(#c_case{arg=Arg,clauses=Cs}, Def, Rt, St0) ->
St1 = gbody(Arg, Def, PatCount, St0),
clauses(Cs, Def, PatCount, Rt, St1);
gexpr(_Core, _, _, St) ->
+ %%io:fwrite("clint gexpr: ~p~n", [_Core]),
add_error({illegal_guard,St#lint.func}, St).
%% gexpr_list([Expr], Defined, State) -> State.
@@ -303,6 +310,12 @@ expr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
expr_list([H,T], Def, St);
expr(#c_tuple{es=Es}, Def, _Rt, St) ->
expr_list(Es, Def, St);
+expr(#c_map{es=Es}, Def, _Rt, St) ->
+ expr_list(Es, Def, St);
+expr(#c_map_pair_assoc{key=K,val=V},Def,_Rt,St) ->
+ expr_list([K,V],Def,St);
+expr(#c_map_pair_exact{key=K,val=V},Def,_Rt,St) ->
+ expr_list([K,V],Def,St);
expr(#c_binary{segments=Ss}, Def, _Rt, St) ->
bitstr_list(Ss, Def, St);
expr(#c_fun{vars=Vs,body=B}, Def, Rt, St0) ->
@@ -355,7 +368,7 @@ expr(#c_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Def, Rt, St0) ->
{Ens,St5} = variable_list(Evs, St4),
body(H, union(Ens, Def), Rt, St5);
expr(_Other, _, _, St) ->
- %%io:fwrite("clint: ~p~n", [_Other]),
+ %%io:fwrite("clint expr: ~p~n", [_Other]),
add_error({illegal_expr,St#lint.func}, St).
%% expr_list([Expr], Defined, State) -> State.
@@ -454,13 +467,19 @@ pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) ->
pattern_list([H,T], Def, Ps, St);
pattern(#c_tuple{es=Es}, Def, Ps, St) ->
pattern_list(Es, Def, Ps, St);
+pattern(#c_map{es=Es}, Def, Ps, St) ->
+ pattern_list(Es, Def, Ps, St);
+pattern(#c_map_pair_exact{key=K,val=V},Def,Ps,St) ->
+ pattern_list([K,V],Def,Ps,St);
pattern(#c_binary{segments=Ss}, Def, Ps, St0) ->
St = pat_bin_tail_check(Ss, St0),
pat_bin(Ss, Def, Ps, St);
pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) ->
{Vvs,St1} = variable(V, Ps, St0),
pattern(P, Def, union(Vvs, Ps), St1);
-pattern(_, _, Ps, St) -> {Ps,add_error({not_pattern,St#lint.func}, St)}.
+pattern(_Other, _, Ps, St) ->
+ %%io:fwrite("clint pattern: ~p~n", [_Other]),
+ {Ps,add_error({not_pattern,St#lint.func}, St)}.
pat_var(N, _Def, Ps, St) ->
case is_element(N, Ps) of
--
cgit v1.2.3
From 1014059ea438798960cf858e6f37b16e321152ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 5 Nov 2013 15:42:27 +0100
Subject: compiler: Test Maps inlining
---
lib/compiler/test/compile_SUITE.erl | 11 ++--
lib/compiler/test/inline_SUITE.erl | 3 +-
.../test/inline_SUITE_data/maps_inline_test.erl | 60 ++++++++++++++++++++++
3 files changed, 68 insertions(+), 6 deletions(-)
create mode 100644 lib/compiler/test/inline_SUITE_data/maps_inline_test.erl
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 4ec75d015e..de35ebc7bd 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -415,11 +415,11 @@ encrypted_abstr(Config) when is_list(Config) ->
?line {Simple,Target} = files(Config, "encrypted_abstr"),
Res = case has_crypto() of
- no ->
+ false ->
%% No crypto.
?line encrypted_abstr_no_crypto(Simple, Target),
{comment,"The crypto application is missing or broken"};
- yes ->
+ true ->
%% Simulate not having crypto by removing
%% the crypto application from the path.
?line OldPath = code:get_path(),
@@ -511,6 +511,7 @@ write_crypt_file(Contents0) ->
ok = file:write_file(".erlang.crypt", Contents).
encrypted_abstr_no_crypto(Simple, Target) ->
+ io:format("simpe: ~p~n", [Simple]),
?line TargetDir = filename:dirname(Target),
?line Key = "ablurf123BX#$;3",
?line error = compile:file(Simple,
@@ -525,11 +526,11 @@ verify_abstract(Target) ->
has_crypto() ->
try
crypto:start(),
- crypto:info(),
+ <<_,_,_,_,_>> = crypto:rand_bytes(5),
crypto:stop(),
- yes
+ true
catch
- error:_ -> no
+ error:_ -> false
end.
install_crypto_key(Key) ->
diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl
index e5c2d4f73a..47851e680b 100644
--- a/lib/compiler/test/inline_SUITE.erl
+++ b/lib/compiler/test/inline_SUITE.erl
@@ -37,7 +37,7 @@ all() ->
groups() ->
[{p,test_lib:parallel(),
[attribute,bsdecode,bsdes,barnes2,decode1,smith,fname,
- itracer,pseudoknot,comma_splitter,lists,really_inlined,otp_7223,
+ itracer,pseudoknot,maps_inline_test,comma_splitter,lists,really_inlined,otp_7223,
coverage]}].
init_per_suite(Config) ->
@@ -85,6 +85,7 @@ attribute(Config) when is_list(Config) ->
?comp(pseudoknot).
?comp(comma_splitter).
?comp(fname).
+?comp(maps_inline_test).
try_inline(Mod, Config) ->
Node = ?config(testing_node, Config),
diff --git a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl
new file mode 100644
index 0000000000..87a8f86b47
--- /dev/null
+++ b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl
@@ -0,0 +1,60 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(maps_inline_test).
+
+-export([?MODULE/0]).
+
+?MODULE() ->
+ 21 = mval(#{val => 1}) +
+ mval(#{val => 2}) +
+ mval(#{val => 3}) +
+ mval(#{val => 4}) +
+ mval(#{val => 5}) +
+ mval(#{val => 6}),
+
+ 21 = gval(#{id => 1}) +
+ gval(#{id => 2}) +
+ gval(#{id => 3}) +
+ gval(#{id => 4}) +
+ gval(#{id => 5}) +
+ gval(#{id => 6}),
+
+ 21 = sval(#{id => 1}) +
+ sval(#{id => 2}) +
+ sval(#{id => 3}) +
+ sval(#{id => 4}) +
+ sval(#{id => 5}) +
+ sval(#{id => 6}).
+
+mval(#{val := V}) -> V.
+
+sval(#{id := 1}) -> 6;
+sval(#{id := 2}) -> 5;
+sval(#{id := 3}) -> 4;
+sval(#{id := 4}) -> 3;
+sval(#{id := 5}) -> 2;
+sval(#{id := 6}) -> 1.
+
+gval(M) when is_map(M) andalso M =:= #{ id => 1} -> 1;
+gval(M) when is_map(M) andalso M =:= #{ id => 2} -> 4;
+gval(M) when is_map(M) andalso M =:= #{ id => 3} -> 2;
+gval(M) when is_map(M) andalso M =:= #{ id => 4} -> 5;
+gval(M) when is_map(M) andalso M =:= #{ id => 5} -> 3;
+gval(M) when is_map(M) andalso M =:= #{ id => 6} -> 6.
--
cgit v1.2.3
From f3ea2236bc8d11dd09b7eaaec3f175361e4771d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Sat, 26 Oct 2013 16:04:29 +0200
Subject: compiler: Fix get_map_element bug with allocate
The instruction get_map_element has a faillabel so you may not
use the instruction within a allocate/deallocate block.
---
lib/compiler/src/beam_block.erl | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index d5f2ffc444..3723cc19e1 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -240,6 +240,7 @@ move_allocates_2(Alloc, [], Acc) ->
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
+alloc_may_pass({set,_,_,{get_map_element,_,_}}) -> false;
alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
--
cgit v1.2.3
From 03588c989909b9bca00a3d685f7486d567a55e19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 30 Oct 2013 17:07:22 +0100
Subject: compiler: Fix v3_core for map update syntax
Make map update expressions safe, i.e. (foo())#{ k1 := 1 }
---
lib/compiler/src/v3_core.erl | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index b33799275b..b5fb65ff08 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -493,11 +493,11 @@ expr({map,L,Es0}, St0) ->
{Es1,Eps,St1} = map_pair_list(Es0, St0),
A = lineno_anno(L, St1),
{#c_map{anno=A,es=Es1},Eps,St1};
-expr({map,L,{var,Vl,Map},Es0}, St0) ->
- {Es1,Eps,St1} = map_pair_list(Es0, St0),
- A = lineno_anno(L, St1),
- Av = lineno_anno(Vl, St1),
- {#c_map{anno=A,var=#c_var{anno=Av,name=Map},es=Es1},Eps,St1};
+expr({map,L,M0,Es0}, St0) ->
+ {M1,Mps,St1} = safe(M0, St0),
+ {Es1,Eps,St2} = map_pair_list(Es0, St1),
+ A = lineno_anno(L, St2),
+ {#c_map{anno=A,var=M1,es=Es1},Mps++Eps,St2};
expr({bin,L,Es0}, St0) ->
try expr_bin(Es0, lineno_anno(L, St0), St0) of
{_,_,_}=Res -> Res
@@ -1505,7 +1505,7 @@ pattern({cons,L,H,T}, St) ->
pattern({tuple,L,Ps}, St) ->
ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St));
pattern({map,L,Ps}, St) ->
- #c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))};
+ #c_map{anno=lineno_anno(L, St), es=sort(pattern_list(Ps, St))};
pattern({map_field_exact,L,K,V}, St) ->
%% FIXME: Better way to construct literals? or missing case
%% {Key,_,_} = expr(K, St),
--
cgit v1.2.3
From 05b06b217755bd0903677d650b364084f3a063ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 4 Nov 2013 17:29:58 +0100
Subject: compiler: Teach Maps to beam_disasm
---
lib/compiler/src/beam_disasm.erl | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 1a8bbcee22..e0d0d0fd1d 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -365,6 +365,10 @@ disasm_instr(B, Bs, Atoms, Literals) ->
disasm_select_inst(select_val, Bs, Atoms, Literals);
select_tuple_arity ->
disasm_select_inst(select_tuple_arity, Bs, Atoms, Literals);
+ put_map_assoc ->
+ disasm_map_inst(put_map_assoc, Bs, Atoms, Literals);
+ put_map_exact ->
+ disasm_map_inst(put_map_exact, Bs, Atoms, Literals);
_ ->
try decode_n_args(Arity, Bs, Atoms, Literals) of
{Args, RestBs} ->
@@ -395,6 +399,17 @@ disasm_select_inst(Inst, Bs, Atoms, Literals) ->
{List, RestBs} = decode_n_args(Len, Bs4, Atoms, Literals),
{{Inst, [X,F,{Z,U,List}]}, RestBs}.
+disasm_map_inst(Inst, Bs0, Atoms, Literals) ->
+ {F, Bs1} = decode_arg(Bs0, Atoms, Literals),
+ {S, Bs2} = decode_arg(Bs1, Atoms, Literals),
+ {X, Bs3} = decode_arg(Bs2, Atoms, Literals),
+ {N, Bs4} = decode_arg(Bs3, Atoms, Literals),
+ {Z, Bs5} = decode_arg(Bs4, Atoms, Literals),
+ {U, Bs6} = decode_arg(Bs5, Atoms, Literals),
+ {u, Len} = U,
+ {List, RestBs} = decode_n_args(Len, Bs6, Atoms, Literals),
+ {{Inst, [F,S,X,N,{Z,U,List}]}, RestBs}.
+
%%-----------------------------------------------------------------------
%% decode_arg([Byte]) -> {Arg, [Byte]}
%%
@@ -421,7 +436,7 @@ decode_arg([B|Bs]) ->
decode_arg([B|Bs0], Atoms, Literals) ->
Tag = decode_tag(B band 2#111),
- ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n', [Tag, B, Bs]),
+ ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n', [Tag, B, Bs0]),
case Tag of
z ->
decode_z_tagged(Tag, B, Bs0, Literals);
@@ -1118,6 +1133,27 @@ resolve_inst({recv_set,[Lbl]},_,_,_) ->
resolve_inst({line,[Index]},_,_,_) ->
{line,resolve_arg(Index)};
+%%
+%% R17A.
+%%
+resolve_inst({put_map_assoc,Args},_,_,_) ->
+ [FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
+ List = resolve_args(List0),
+ {put_map_assoc,FLbl,Src,Dst,N,{list,List}};
+
+resolve_inst({put_map_exact,Args},_,_,_) ->
+ [FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
+ List = resolve_args(List0),
+ {put_map_exact,FLbl,Src,Dst,N,{list,List}};
+
+resolve_inst({is_map,Args0},_,_,_) ->
+ [FLbl|Args] = resolve_args(Args0),
+ {test, is_map, FLbl, Args};
+
+resolve_inst({get_map_element,Args},_,_,_) ->
+ [FLbl,Src,Key,Dst] = resolve_args(Args),
+ {get_map_element,FLbl,Src,Key,Dst};
+
%%
%% Catches instructions that are not yet handled.
%%
--
cgit v1.2.3
From b10a3b3aa5efa08488623ecba1e24a0de48d4b51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 12 Nov 2013 19:05:22 +0100
Subject: compiler: Format stacktrace on error
---
lib/compiler/src/compile.erl | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 3db7ffc4d2..0bb4de6f17 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -230,12 +230,25 @@ format_error({undef_parse_transform,M}) ->
format_error({core_transform,M,R}) ->
io_lib:format("error in core transform '~s': ~tp", [M, R]);
format_error({crash,Pass,Reason}) ->
- io_lib:format("internal error in ~p;\ncrash reason: ~tp", [Pass,Reason]);
+ io_lib:format("internal error in ~p;\ncrash reason: ~ts", [Pass,format_error_reason(Reason)]);
format_error({bad_return,Pass,Reason}) ->
- io_lib:format("internal error in ~p;\nbad return value: ~tp", [Pass,Reason]);
+ io_lib:format("internal error in ~p;\nbad return value: ~ts", [Pass,format_error_reason(Reason)]);
format_error({module_name,Mod,Filename}) ->
- io_lib:format("Module name '~s' does not match file name '~ts'",
- [Mod,Filename]).
+ io_lib:format("Module name '~s' does not match file name '~ts'", [Mod,Filename]).
+
+format_error_reason({Reason, Stack}) when is_list(Stack) ->
+ StackFun = fun
+ (escript, run, 2) -> true;
+ (escript, start, 1) -> true;
+ (init, start_it, 1) -> true;
+ (init, start_em, 1) -> true;
+ (_Mod, _Fun, _Arity) -> false
+ end,
+ FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end,
+ [io_lib:format("~tp", [Reason]),"\n\n",
+ lib:format_stacktrace(1, erlang:get_stacktrace(), StackFun, FormatFun)];
+format_error_reason(Reason) ->
+ io_lib:format("~tp", [Reason]).
%% The compile state record.
-record(compile, {filename="" :: file:filename(),
--
cgit v1.2.3
From 64e5677c883990e1d5b5666d857413293910972a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 12 Nov 2013 12:19:30 +0100
Subject: compiler: Add Maps types to cerl
---
lib/compiler/src/cerl.erl | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 30bb4c5d96..c6bf046c4d 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -154,6 +154,9 @@
-type c_let() :: #c_let{}.
-type c_letrec() :: #c_letrec{}.
-type c_literal() :: #c_literal{}.
+-type c_map() :: #c_map{}.
+-type c_map_pair_assoc() :: #c_map_pair_assoc{}.
+-type c_map_pair_exact() :: #c_map_pair_exact{}.
-type c_module() :: #c_module{}.
-type c_primop() :: #c_primop{}.
-type c_receive() :: #c_receive{}.
@@ -164,9 +167,10 @@
-type c_var() :: #c_var{}.
-type cerl() :: c_alias() | c_apply() | c_binary() | c_bitstr()
- | c_call() | c_case() | c_catch() | c_clause() | c_cons()
+ | c_call() | c_case() | c_catch() | c_clause() | c_cons()
| c_fun() | c_let() | c_letrec() | c_literal()
- | c_module() | c_primop() | c_receive() | c_seq()
+ | c_map() | c_map_pair_assoc() | c_map_pair_exact()
+ | c_module() | c_primop() | c_receive() | c_seq()
| c_try() | c_tuple() | c_values() | c_var().
%% =====================================================================
@@ -1572,6 +1576,8 @@ ann_make_list(_, [], Node) ->
%% ---------------------------------------------------------------------
%% maps
+-spec map_es(c_map()) -> [cerl()].
+
map_es(#c_map{es = Es}) ->
Es.
--
cgit v1.2.3
From b6f6b19cc114af6f88458624c1470f0f70e24d0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 12 Nov 2013 19:18:22 +0100
Subject: compiler: Rename map pattern to proper name
---
lib/compiler/src/v3_kernel.erl | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 14e776fd3c..a5a94c1294 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -668,14 +668,10 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
{#k_map{anno=A,es=Kes},Osub1,St1};
-pattern(#c_map_pair_assoc{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
- {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
- {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
- {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2};
pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
{Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
{Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
- {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2};
+ {#k_map_pair_exact{anno=A,key=Kk,val=Kv},Osub2,St2};
pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->
{Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),
{#k_binary{anno=A,segs=Kv},Osub1,St1};
@@ -1351,9 +1347,10 @@ get_match(#k_tuple{es=Es}, St0) ->
{#k_tuple{es=Mes},Mes,St1};
get_match(#k_map{es=Es0}, St0) ->
{Mes,St1} = new_vars(length(Es0), St0),
- {Es,_} = mapfoldl(fun(#k_map_pair_assoc{}=Pair, [V|Vs]) ->
- {Pair#k_map_pair_assoc{val=V},Vs}
- end, Mes, Es0),
+ {Es,_} = mapfoldl(fun
+ (#k_map_pair_exact{}=Pair, [V|Vs]) ->
+ {Pair#k_map_pair_exact{val=V},Vs}
+ end, Mes, Es0),
{#k_map{es=Es},Mes,St1};
get_match(M, St) ->
{M,[],St}.
@@ -1373,7 +1370,7 @@ new_clauses(Cs0, U, St) ->
[N|As];
#k_map{es=Es} ->
Vals = [V ||
- #k_map_pair_assoc{val=V} <- Es],
+ #k_map_pair_exact{val=V} <- Es],
Vals ++ As;
_Other ->
As
@@ -1475,7 +1472,7 @@ arg_val(Arg, C) ->
end;
#k_map{es=Es} ->
Keys = [begin
- #k_map_pair_assoc{key=#k_literal{val=Key}} = Pair,
+ #k_map_pair_exact{key=#k_literal{val=Key}} = Pair,
Key
end || Pair <- Es],
%% multiple keys may have the same name
@@ -1893,7 +1890,7 @@ pat_vars(#k_tuple{es=Es}) ->
pat_list_vars(Es);
pat_vars(#k_map{es=Es}) ->
pat_list_vars(Es);
-pat_vars(#k_map_pair_assoc{val=V}) ->
+pat_vars(#k_map_pair_exact{val=V}) ->
pat_vars(V).
pat_list_vars(Ps) ->
--
cgit v1.2.3
From eb218eb375d353bcd78ae895805d1d45f927b3a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 2 Oct 2013 16:19:22 +0200
Subject: erts: Teach term_to_binary/1 and binary_to_term/1 Map encoding
---
erts/emulator/beam/external.c | 197 ++++++++++++++++++++++++++++++++++++++----
erts/emulator/beam/external.h | 1 +
2 files changed, 183 insertions(+), 15 deletions(-)
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 5e7a5cab6e..a31b2731be 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -42,6 +42,7 @@
#include "erl_binary.h"
#include "erl_bits.h"
#include "erl_zlib.h"
+#include "erl_map.h"
#ifdef HIPE
#include "hipe_mode_switch.h"
@@ -2555,6 +2556,38 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
}
break;
+ case MAP_DEF:
+ {
+ map_t *mp = (map_t*)map_val(obj);
+ Uint size = map_get_size(mp);
+ Eterm *mptr;
+
+ *ep++ = MAP_EXT;
+ put_int32(size, ep); ep += 4;
+
+ /* Push values first */
+ if (size > 0) {
+ mptr = map_get_values(mp);
+ for (i = size-1; i >= 1; i--) {
+ WSTACK_PUSH(s, ENC_TERM);
+ WSTACK_PUSH(s, (UWord) mptr[i]);
+ }
+
+ WSTACK_PUSH(s, ENC_TERM);
+ WSTACK_PUSH(s, (UWord) mptr[0]);
+
+ mptr = map_get_keys(mp);
+ for (i = size-1; i >= 1; i--) {
+ WSTACK_PUSH(s, ENC_TERM);
+ WSTACK_PUSH(s, (UWord) mptr[i]);
+ }
+
+ obj = mptr[0];
+ goto L_jump_start;
+ }
+ }
+ break;
+
case FLOAT_DEF:
GET_DOUBLE(obj, f);
if (dflags & DFLAG_NEW_FLOATS) {
@@ -2845,6 +2878,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap,
int n;
ErtsAtomEncoding char_enc;
register Eterm* hp; /* Please don't take the address of hp */
+ Eterm *maps_head = NULL; /* for validation of maps */
Eterm* next;
SWord reds;
@@ -3469,6 +3503,65 @@ dec_term_atom_common:
break;
}
break;
+ case MAP_EXT:
+ {
+ map_t *mp;
+ Uint32 size,n;
+ Eterm *kptr,*vptr;
+ Eterm keys;
+
+ size = get_int32(ep); ep += 4;
+
+ keys = make_tuple(hp);
+ *hp++ = make_arityval(size);
+ kptr = hp;
+ hp += size;
+
+ mp = (map_t*)hp;
+ hp += MAP_HEADER_SIZE;
+ vptr = hp;
+ hp += size;
+
+ /* kptr, first word for keys
+ * vptr, first word for values
+ */
+
+ /*
+ * Use thing_word to link through decoded maps.
+ * The list of maps is for later validation.
+ */
+
+ mp->thing_word = (Eterm) COMPRESS_POINTER(maps_head);
+ maps_head = (Eterm *) mp;
+
+ mp->size = size;
+ mp->keys = keys;
+ *objp = make_map(mp);
+
+ /* We assume the map is wellformed, meaning:
+ * - ascending key order
+ * - unique keys
+ */
+
+ objp = vptr + size - 1;
+ n = size;
+
+ while (n-- > 0) {
+ *objp = (Eterm) COMPRESS_POINTER(next);
+ next = objp;
+ objp--;
+ }
+
+ objp = kptr + size - 1;
+ n = size;
+
+ while (n-- > 0) {
+ *objp = (Eterm) COMPRESS_POINTER(next);
+ next = objp;
+ objp--;
+ }
+ }
+ break;
case NEW_FUN_EXT:
{
ErlFunThing* funp = (ErlFunThing *) hp;
@@ -3678,21 +3771,7 @@ dec_term_atom_common:
}
default:
- error:
- /* UNDO:
- * Must unlink all off-heap objects that may have been
- * linked into the process.
- */
- if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */
- hp = *hpp; /* the largest must be the freshest */
- }
- undo_offheap_in_area(off_heap, hp_saved, hp);
- *hpp = hp_saved;
- if (ctx) {
- ctx->state = B2TDecodeFail;
- ctx->reds = reds;
- }
- return NULL;
+ goto error;
}
if (--reds <= 0) {
@@ -3710,12 +3789,53 @@ dec_term_atom_common:
}
}
}
+
+ /* Iterate through all the maps and check for validity
+ * - done here for when we know it is complete.
+ */
+
+ while (maps_head) {
+ Eterm *keys;
+ Sint arity;
+
+ next = (Eterm *)(EXPAND_POINTER(*maps_head));
+ keys = tuple_val(*(maps_head + 2));
+ arity = arityval(*keys++);
+
+ while(arity-- > 1) {
+ if (CMP(keys[arity-1],keys[arity]) >= 0) {
+ goto error;
+ }
+ }
+
+ *maps_head = MAP_HEADER;
+ maps_head = next;
+ }
+
if (ctx) {
ctx->state = B2TDone;
ctx->reds = reds;
}
+
*hpp = hp;
return ep;
+
+error:
+ /* UNDO:
+ * Must unlink all off-heap objects that may have been
+ * linked into the process.
+ */
+ if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */
+ hp = *hpp; /* the largest must be the freshest */
+ }
+ undo_offheap_in_area(off_heap, hp_saved, hp);
+ *hpp = hp_saved;
+ if (ctx) {
+ ctx->state = B2TDecodeFail;
+ ctx->reds = reds;
+ }
+
+ return NULL;
}
/* returns the number of bytes needed to encode an object
@@ -3885,6 +4005,46 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
goto outer_loop;
}
break;
+ case MAP_DEF:
+ {
+ map_t *mp = (map_t*)map_val(obj);
+ Uint size = map_get_size(mp);
+ Uint i;
+ Eterm *ptr;
+
+ result += 1 + 4; /* tag + 4 bytes size */
+
+ /* push values first */
+ ptr = map_get_values(mp);
+ i = size;
+ while(i--) {
+ if (is_list(*ptr)) {
+ if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) {
+ result += m + 2 + 1;
+ } else {
+ result += 5;
+ }
+ }
+ ESTACK_PUSH(s,*ptr);
+ ++ptr;
+ }
+
+ ptr = map_get_keys(mp);
+ i = size;
+ while(i--) {
+ if (is_list(*ptr)) {
+ if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) {
+ result += m + 2 + 1;
+ } else {
+ result += 5;
+ }
+ }
+ ESTACK_PUSH(s,*ptr);
+ ++ptr;
+ }
+ goto outer_loop;
+ }
+ break;
case FLOAT_DEF:
if (dflags & DFLAG_NEW_FLOATS) {
result += 9;
@@ -4175,6 +4335,13 @@ init_done:
ADDTERMS(n);
heap_size += n + 1;
break;
+ case MAP_EXT:
+ CHKSIZE(4);
+ n = get_int32(ep);
+ ep += 4;
+ ADDTERMS(2*n);
+ heap_size += 3 + n + 1 + n;
+ break;
case STRING_EXT:
CHKSIZE(2);
n = get_int16(ep);
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index 83001b2c7e..bf00958eb1 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -50,6 +50,7 @@
#define LARGE_BIG_EXT 'o'
#define NEW_FUN_EXT 'p'
#define EXPORT_EXT 'q'
+#define MAP_EXT 't'
#define FUN_EXT 'u'
#define ATOM_UTF8_EXT 'v'
#define SMALL_ATOM_UTF8_EXT 'w'
--
cgit v1.2.3
From 97d087263e1836f3efb4267e3990219fe3e3c045 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 3 Oct 2013 00:35:45 +0200
Subject: erts: Add Map tests for hashing and external format
- Update map_SUITE with hash and encode tests
- Update map_SUITE with external format decode tests
- Update map_SUITE with map:to_list/1 and map:from_list/1 tests
---
erts/emulator/test/map_SUITE.erl | 143 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 140 insertions(+), 3 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 81d39fc97a..27f35ccd29 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -30,7 +30,7 @@
t_map_sort_literals/1,
t_size/1, t_map_size/1,
- %% BIFs
+ %% Specific Map BIFs
t_bif_map_get/1,
t_bif_map_find/1,
t_bif_map_is_key/1,
@@ -38,7 +38,13 @@
t_bif_map_new/1,
t_bif_map_put/1,
t_bif_map_remove/1,
- t_bif_map_values/1
+ t_bif_map_values/1,
+ t_bif_map_to_list/1,
+ t_bif_map_from_list/1,
+
+ %% erlang
+ t_erlang_hash/1,
+ t_map_encode_decode/1
]).
-include_lib("test_server/include/test_server.hrl").
@@ -57,7 +63,11 @@ all() -> [
%% Specific Map BIFs
t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
t_bif_map_keys,t_bif_map_new,t_bif_map_put,
- t_bif_map_remove,t_bif_map_values
+ t_bif_map_remove,t_bif_map_values,
+ t_bif_map_to_list, t_bif_map_from_list,
+
+ %% erlang
+ t_erlang_hash, t_map_encode_decode
].
groups() -> [].
@@ -562,7 +572,134 @@ t_bif_map_values(Config) when is_list(Config) ->
{'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)),
{'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])),
{'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)),
+ ok.
+
+t_erlang_hash(Config) when is_list(Config) ->
+
+ 39679005 = erlang:phash2(#{}),
+ 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
+ 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
+ 87559846 = erlang:phash2(#{ 1 => a }),
+ 76038729 = erlang:phash2(#{ a => 1 }),
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = map:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 67968757 = erlang:phash2(M0),
+ 76038729 = erlang:phash2(M1),
+ 67968757 = erlang:phash2(M2),
+ ok.
+
+t_map_encode_decode(Config) when is_list(Config) ->
+ <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}),
+ Pairs = [
+ {a,b},{"key","values"},{<<"key">>,<<"value">>},
+ {1,b},{[atom,1],{<<"wat">>,1,2,3}},
+ {aa,"values"},
+ {1 bsl 64 + (1 bsl 50 - 1), sc1},
+ {99, sc2},
+ {1 bsl 65 + (1 bsl 51 - 1), sc3},
+ {88, sc4},
+ {1 bsl 66 + (1 bsl 52 - 1), sc5},
+ {77, sc6},
+ {1 bsl 67 + (1 bsl 53 - 1), sc3},
+ {75, sc6}, {-10,sc8},
+ {<<>>, sc9}, {3.14158, sc10},
+ {[3.14158], sc11}, {more_atoms, sc12},
+ {{more_tuples}, sc13}, {self(), sc14},
+ {{},{}},{[],[]}
+ ],
+ ok = map_encode_decode_and_match(Pairs,[],#{}),
+
+ %% error cases
+ %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>>
+ %% which is: #{ a=>1, b=>1 }
+
+ %% order violation
+ %% literally #{ b=>1, a=>1 } in the internal order (bad)
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)),
+
+ %% uniqueness violation
+ %% literally #{ a=>1, a=>1 }
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)),
+
+ %% bad size (too large)
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)),
+
+ %% bad size (too small) .. should fail just truncate it .. weird.
+ %% possibly change external format so truncated will be #{a:=1}
+ #{ a:=b } =
+ erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>),
+
+ ok.
+
+map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
+ M1 = map:put(K,V,M0),
+ B0 = erlang:term_to_binary(M1),
+ Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
+ %% sort Ks and Vs according to term spec, then match it
+ ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]),
+ %% decode and match it
+ M1 = erlang:binary_to_term(B0),
+ map_encode_decode_and_match(Pairs,Ls,M1);
+map_encode_decode_and_match([],_,_) -> ok.
+
+match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) ->
+ match_encoded_map(Encoded,Items);
+match_encoded_map(_,_,_) -> no_match_size.
+
+match_encoded_map(<<>>,[]) -> ok;
+match_encoded_map(Bin,[<<131,Item/binary>>|Items]) ->
+ Size = erlang:byte_size(Item),
+ <> = Bin,
+ EncodedTerm = Item, %% Asssert
+ match_encoded_map(Bin1,Items).
+
+
+t_bif_map_to_list(Config) when is_list(Config) ->
+ [] = map:to_list(#{}),
+ [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}),
+ [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}),
+ [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}),
+
+ [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = map:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
+ <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}),
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch map:to_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch map:to_list(id(42))),
+ ok.
+
+
+t_bif_map_from_list(Config) when is_list(Config) ->
+ #{} = map:from_list([]),
+ A = map:from_list([]),
+ 0 = erlang:map_size(A),
+
+ #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]),
+ #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]),
+ #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]),
+
+ #{a:=2} = map:from_list([{a,1},{a,3},{a,2}]),
+
+ #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} =
+ map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]),
+
+ #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} =
+ map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2},
+ {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]),
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch map:from_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch map:from_list(id(42))),
ok.
--
cgit v1.2.3
From ac2954982d6089b0970798581b2230bdd1a065d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 15 Oct 2013 17:21:30 +0200
Subject: erts: Add Maps to erlang:phash/2 and erlang:hash/2
The hashing a map in these functions uses the same strategy
as the other terms. The exception being a prime number with size
so we do not get erlang:phash(#{}) -> 1 which would be the same
as erlang:phash({}) and erlang:phash(<<>>). Same argument for
erlang:hash/1.
---
erts/emulator/beam/utils.c | 53 ++++++++++++++++++++++++++++++++++++++++------
1 file changed, 47 insertions(+), 6 deletions(-)
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index bc7e91295d..3774f379cc 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -735,6 +735,8 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length,
#define FUNNY_NUMBER10 268440479
#define FUNNY_NUMBER11 268440577
#define FUNNY_NUMBER12 268440581
+#define FUNNY_NUMBER13 268440593
+#define FUNNY_NUMBER14 268440611
static Uint32
hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash)
@@ -787,7 +789,7 @@ Uint32 make_hash(Eterm term_arg)
/* Must not collide with the real tag_val_def's: */
#define MAKE_HASH_TUPLE_OP 0x11
-#define MAKE_HASH_FUN_OP 0x12
+#define MAKE_HASH_TERM_ARRAY_OP 0x12
#define MAKE_HASH_CDR_PRE_OP 0x13
#define MAKE_HASH_CDR_POST_OP 0x14
@@ -878,7 +880,7 @@ tail_recur:
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
if (num_free > 0) {
if (num_free > 1) {
- WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP);
+ WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP);
}
term = funp->env[0];
goto tail_recur;
@@ -968,6 +970,24 @@ tail_recur:
hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3;
break;
}
+ case MAP_DEF:
+ {
+ map_t *mp = (map_t *)map_val(term);
+ int size = map_get_size(mp);
+ Eterm *ks = map_get_keys(mp);
+ Eterm *vs = map_get_values(mp);
+
+ /* Use a prime with size to remedy some of
+ * the {} and <<>> hash problems */
+ hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size;
+ if (size == 0)
+ break;
+
+ /* push values first */
+ WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
+ WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
+ break;
+ }
case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
@@ -977,7 +997,7 @@ tail_recur:
op = MAKE_HASH_TUPLE_OP;
}/*fall through*/
case MAKE_HASH_TUPLE_OP:
- case MAKE_HASH_FUN_OP:
+ case MAKE_HASH_TERM_ARRAY_OP:
{
Uint i = (Uint) WSTACK_POP(stack);
Eterm* ptr = (Eterm*) WSTACK_POP(stack);
@@ -1206,6 +1226,9 @@ make_hash2(Eterm term)
tmp = vs[i];
ESTACK_PUSH(s, tmp);
}
+ /* We do not want to expose the tuple representation.
+ * Do not push the keys as a tuple.
+ */
for (i = size - 1; i >= 1; i--) {
tmp = ks[i];
ESTACK_PUSH(s, tmp);
@@ -1514,7 +1537,7 @@ tail_recur:
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
if (num_free > 0) {
if (num_free > 1) {
- WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP);
+ WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP);
}
term = funp->env[0];
goto tail_recur;
@@ -1627,6 +1650,24 @@ tail_recur:
}
break;
+ case MAP_DEF:
+ {
+ map_t *mp = (map_t *)map_val(term);
+ int size = map_get_size(mp);
+ Eterm *ks = map_get_keys(mp);
+ Eterm *vs = map_get_values(mp);
+
+ /* Use a prime with size to remedy some of
+ * the {} and <<>> hash problems */
+ hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size;
+ if (size == 0)
+ break;
+
+ /* push values first */
+ WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
+ WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP);
+ break;
+ }
case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
@@ -1636,7 +1677,7 @@ tail_recur:
op = MAKE_HASH_TUPLE_OP;
}/*fall through*/
case MAKE_HASH_TUPLE_OP:
- case MAKE_HASH_FUN_OP:
+ case MAKE_HASH_TERM_ARRAY_OP:
{
Uint i = (Uint) WSTACK_POP(stack);
Eterm* ptr = (Eterm*) WSTACK_POP(stack);
@@ -1664,7 +1705,7 @@ tail_recur:
return hash;
#undef MAKE_HASH_TUPLE_OP
-#undef MAKE_HASH_FUN_OP
+#undef MAKE_HASH_TERM_ARRAY_OP
#undef MAKE_HASH_CDR_PRE_OP
#undef MAKE_HASH_CDR_POST_OP
}
--
cgit v1.2.3
From 912f0f2aae82ee4316a2ee1863775bcf1d8a3ba2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 15 Oct 2013 18:06:34 +0200
Subject: erts: Add Map tests for erlang:phash/2
---
erts/emulator/test/map_SUITE.erl | 60 +++++++++++++++++++++++++++++++++++++---
1 file changed, 56 insertions(+), 4 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 27f35ccd29..70b5c33fb2 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -23,7 +23,7 @@
-export([
t_build_and_match_literals/1,
t_update_literals/1,t_match_and_update_literals/1,
- update_assoc/1,update_exact/1,
+ t_update_assoc/1,t_update_exact/1,
t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
t_guard_receive/1, t_guard_fun/1,
t_list_comprehension/1,
@@ -55,7 +55,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() -> [
t_build_and_match_literals,
t_update_literals, t_match_and_update_literals,
- update_assoc,update_exact,
+ t_update_assoc,t_update_exact,
t_guard_bifs, t_guard_sequence, t_guard_update,
t_guard_receive,t_guard_fun, t_list_comprehension,
t_map_sort_literals,
@@ -178,7 +178,7 @@ loop_match_and_update_literals_x_q(Map, []) -> Map;
loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
-update_assoc(Config) when is_list(Config) ->
+t_update_assoc(Config) when is_list(Config) ->
M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
@@ -195,7 +195,7 @@ update_assoc(Config) when is_list(Config) ->
ok.
-update_exact(Config) when is_list(Config) ->
+t_update_exact(Config) when is_list(Config) ->
M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
@@ -576,12 +576,23 @@ t_bif_map_values(Config) when is_list(Config) ->
t_erlang_hash(Config) when is_list(Config) ->
+ ok = t_bif_erlang_phash2(),
+ ok = t_bif_erlang_phash(),
+ ok = t_bif_erlang_hash(),
+
+ ok.
+
+t_bif_erlang_phash2() ->
+
39679005 = erlang:phash2(#{}),
106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
87559846 = erlang:phash2(#{ 1 => a }),
76038729 = erlang:phash2(#{ a => 1 }),
+ 79950245 = erlang:phash2(#{{} => <<>>}),
+ 11244490 = erlang:phash2(#{<<>> => {}}),
+
M0 = #{ a => 1, "key" => <<"value">> },
M1 = map:remove("key",M0),
M2 = M1#{ "key" => <<"value">> },
@@ -591,6 +602,47 @@ t_erlang_hash(Config) when is_list(Config) ->
67968757 = erlang:phash2(M2),
ok.
+t_bif_erlang_phash() ->
+ Sz = 1 bsl 32,
+ 268440612 = erlang:phash(#{},Sz),
+ 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 1394238263 = erlang:phash(#{ 1 => a },Sz),
+ 4066388227 = erlang:phash(#{ a => 1 },Sz),
+
+ 1578050717 = erlang:phash(#{{} => <<>>},Sz),
+ 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = map:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 3590546636 = erlang:phash(M0,Sz),
+ 4066388227 = erlang:phash(M1,Sz),
+ 3590546636 = erlang:phash(M2,Sz),
+ ok.
+
+t_bif_erlang_hash() ->
+ Sz = 1 bsl 27 - 1,
+ 5158 = erlang:hash(#{},Sz),
+ 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 126071654 = erlang:hash(#{ 1 => a },Sz),
+ 126426236 = erlang:hash(#{ a => 1 },Sz),
+
+ 101655720 = erlang:hash(#{{} => <<>>},Sz),
+ 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = map:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 38260486 = erlang:hash(M0,Sz),
+ 126426236 = erlang:hash(M1,Sz),
+ 38260486 = erlang:hash(M2,Sz),
+ ok.
+
+
t_map_encode_decode(Config) when is_list(Config) ->
<<131,116,0,0,0,0>> = erlang:term_to_binary(#{}),
Pairs = [
--
cgit v1.2.3
From ab54731c41f28a21bce526249e582b56106d4965 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 16 Dec 2013 17:07:54 +0100
Subject: erts: erlang:phash2 should hash Maps independent of order
---
erts/emulator/beam/utils.c | 63 ++++++++++++++++++++++++++++++++++++++++------
1 file changed, 55 insertions(+), 8 deletions(-)
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 3774f379cc..8958d334ae 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1091,9 +1091,11 @@ Uint32
make_hash2(Eterm term)
{
Uint32 hash;
+ Uint32 hash_xor_keys = 0;
+ Uint32 hash_xor_values = 0;
DeclareTmpHeapNoproc(tmp_big,2);
-/* (HCONST * {2, ..., 14}) mod 2^32 */
+/* (HCONST * {2, ..., 16}) mod 2^32 */
#define HCONST_2 0x3c6ef372UL
#define HCONST_3 0xdaa66d2bUL
#define HCONST_4 0x78dde6e4UL
@@ -1110,6 +1112,10 @@ make_hash2(Eterm term)
#define HCONST_15 0x454021d7UL
#define HCONST_16 0xe3779b90UL
+#define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF))
+#define HASH_MAP_KEY (_make_header(2,_TAG_HEADER_REF))
+#define HASH_MAP_VAL (_make_header(3,_TAG_HEADER_REF))
+
#define UINT32_HASH_2(Expr1, Expr2, AConst) \
do { \
Uint32 a,b; \
@@ -1204,36 +1210,45 @@ make_hash2(Eterm term)
UINT32_HASH(arity, HCONST_9);
if (arity == 0) /* Empty tuple */
goto hash2_common;
- for (i = arity; i >= 2; i--) {
+ for (i = arity; i >= 1; i--) {
tmp = elem[i];
ESTACK_PUSH(s, tmp);
}
- term = elem[1];
+ goto hash2_common;
}
break;
case MAP_SUBTAG:
{
map_t *mp = (map_t *)map_val(term);
int i;
- int size = map_get_size(mp);
+ int size = map_get_size(mp);
Eterm *ks = map_get_keys(mp);
Eterm *vs = map_get_values(mp);
UINT32_HASH(size, HCONST_16);
- if (size == 0) /* Empty Map */
+ if (size == 0) {
goto hash2_common;
-
+ }
+ ESTACK_PUSH(s, hash_xor_values);
+ ESTACK_PUSH(s, hash_xor_keys);
+ ESTACK_PUSH(s, hash);
+ ESTACK_PUSH(s, HASH_MAP_TAIL);
+ hash = 0;
+ hash_xor_keys = 0;
+ hash_xor_values = 0;
for (i = size - 1; i >= 0; i--) {
tmp = vs[i];
+ ESTACK_PUSH(s, HASH_MAP_VAL);
ESTACK_PUSH(s, tmp);
}
/* We do not want to expose the tuple representation.
* Do not push the keys as a tuple.
*/
- for (i = size - 1; i >= 1; i--) {
+ for (i = size - 1; i >= 0; i--) {
tmp = ks[i];
+ ESTACK_PUSH(s, HASH_MAP_KEY);
ESTACK_PUSH(s, tmp);
}
- term = ks[0];
+ goto hash2_common;
}
break;
case EXPORT_SUBTAG:
@@ -1427,15 +1442,47 @@ make_hash2(Eterm term)
default:
erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term);
hash2_common:
+
+ /* Uint32 hash always has the hash value of the previous term,
+ * compounded or otherwise.
+ */
+
if (ESTACK_ISEMPTY(s)) {
DESTROY_ESTACK(s);
UnUseTmpHeapNoproc(2);
return hash;
}
+
term = ESTACK_POP(s);
+
+ switch (term) {
+ case HASH_MAP_TAIL: {
+ hash = (Uint32) ESTACK_POP(s);
+ UINT32_HASH(hash_xor_keys, HCONST_16);
+ UINT32_HASH(hash_xor_values, HCONST_16);
+ hash_xor_keys = (Uint32) ESTACK_POP(s);
+ hash_xor_values = (Uint32) ESTACK_POP(s);
+ goto hash2_common;
+ }
+ case HASH_MAP_KEY:
+ hash_xor_keys ^= hash;
+ hash = 0;
+ goto hash2_common;
+ case HASH_MAP_VAL:
+ hash_xor_values ^= hash;
+ hash = 0;
+ goto hash2_common;
+ default:
+ break;
+ }
}
}
}
+
+#undef HASH_MAP_TAIL
+#undef HASH_MAP_KEY
+#undef HASH_MAP_VAL
+
#undef UINT32_HASH_2
#undef UINT32_HASH
#undef SINT32_HASH
--
cgit v1.2.3
From 9344e8ccf846af62576657244e3d526f88fdeb0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 16 Dec 2013 18:01:58 +0100
Subject: erts: Update Maps erlang:phash2/1 tests
---
erts/emulator/test/map_SUITE.erl | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 70b5c33fb2..1f4476defa 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -584,22 +584,22 @@ t_erlang_hash(Config) when is_list(Config) ->
t_bif_erlang_phash2() ->
- 39679005 = erlang:phash2(#{}),
- 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
- 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
- 87559846 = erlang:phash2(#{ 1 => a }),
- 76038729 = erlang:phash2(#{ a => 1 }),
+ 39679005 = erlang:phash2(#{}),
+ 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
+ 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
+ 14363616 = erlang:phash2(#{ 1 => a }),
+ 51612236 = erlang:phash2(#{ a => 1 }),
- 79950245 = erlang:phash2(#{{} => <<>>}),
- 11244490 = erlang:phash2(#{<<>> => {}}),
+ 37468437 = erlang:phash2(#{{} => <<>>}),
+ 44049159 = erlang:phash2(#{<<>> => {}}),
M0 = #{ a => 1, "key" => <<"value">> },
M1 = map:remove("key",M0),
M2 = M1#{ "key" => <<"value">> },
- 67968757 = erlang:phash2(M0),
- 76038729 = erlang:phash2(M1),
- 67968757 = erlang:phash2(M2),
+ 118679416 = erlang:phash2(M0),
+ 51612236 = erlang:phash2(M1),
+ 118679416 = erlang:phash2(M2),
ok.
t_bif_erlang_phash() ->
--
cgit v1.2.3
From e9a3d0b6533b72cba3d4e7ea129f835e80bd2647 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 2 Oct 2013 16:40:33 +0200
Subject: stdlib: Teach erl_eval Maps ':=' exact operator
---
lib/stdlib/src/erl_eval.erl | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 335134d1f0..fcd1945c63 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -241,18 +241,26 @@ expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, {undef_record,Name}, stacktrace());
%% map
-expr({map_field,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
+expr({map_field_assoc,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
{value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none),
{value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none),
- ret_expr({K,V}, merge_bindings(Bs1,Bs2), RBs);
+ ret_expr({map_assoc,K,V}, merge_bindings(Bs1,Bs2), RBs);
+expr({map_field_exact,_,EK, EV}, Bs0, Lf, Ef, RBs) ->
+ {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none),
+ {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none),
+ ret_expr({map_exact,K,V}, merge_bindings(Bs1,Bs2), RBs);
expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) ->
{value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs),
{Vs,Bs} = expr_list(Es, Bs1, Lf, Ef),
- ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, Map0, Vs), Bs, RBs);
+ ret_expr(lists:foldl(fun
+ ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi);
+ ({map_exact,K,V}, Mi) -> map:update(K,V,Mi)
+ end, Map0, Vs), Bs, RBs);
expr({map,_,Es}, Bs0, Lf, Ef, RBs) ->
{Vs,Bs} = expr_list(Es, Bs0, Lf, Ef),
- ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, map:new(), Vs), Bs, RBs);
-
+ ret_expr(lists:foldl(fun
+ ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi)
+ end, map:new(), Vs), Bs, RBs);
expr({block,_,Es}, Bs, Lf, Ef, RBs) ->
exprs(Es, Bs, Lf, Ef, RBs);
@@ -1137,9 +1145,12 @@ match_tuple([E|Es], Tuple, I, Bs0, BBs) ->
match_tuple([], _, _, Bs, _BBs) ->
{match,Bs}.
-match_map([{map_field, _, K, V}|Fs], Map, Bs0, BBs) ->
- Vm = try map:get(element(3,K),Map)
- catch error:_ -> throw(nomatch)
+match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs) ->
+ Vm = try
+ {value, Ke, _} = expr(K, new_bindings()),
+ map:get(Ke,Map)
+ catch error:_ ->
+ throw(nomatch)
end,
{match, Bs} = match1(V, Vm, Bs0, BBs),
match_map(Fs, Map, Bs, BBs);
--
cgit v1.2.3
From f00675d3c682f53824e23f1599cbb09ff2d57daa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 9 Oct 2013 16:37:58 +0200
Subject: stdlib: Deny variables as keys and disallow ':=' in map construction
In the current iteration of Maps we should deny *any* variables in
Map keys.
---
erts/emulator/test/map_SUITE.erl | 2 +-
lib/stdlib/src/erl_lint.erl | 57 ++++++++++++++++++++++++++++++++--------
lib/stdlib/src/erl_parse.yrl | 2 --
3 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 1f4476defa..327e9b8060 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -327,7 +327,7 @@ guard_receive_loop() ->
t_list_comprehension(Config) when is_list(Config) ->
- [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]],
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
ok.
t_guard_fun(Config) when is_list(Config) ->
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 86ebe82acc..a41bcb91cc 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -225,6 +225,8 @@ format_error({too_many_arguments,Arity}) ->
"maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]);
%% --- patterns and guards ---
format_error(illegal_pattern) -> "illegal pattern";
+format_error({illegal_map_key_variable,K}) ->
+ io_lib:format("illegal use of variable ~w in map",[K]);
format_error(illegal_bin_pattern) ->
"binary patterns cannot be matched in parallel using '='";
format_error(illegal_expr) -> "illegal expression";
@@ -232,6 +234,9 @@ format_error({illegal_guard_local_call, {F,A}}) ->
io_lib:format("call to local/imported function ~w/~w is illegal in guard",
[F,A]);
format_error(illegal_guard_expr) -> "illegal guard expression";
+%% --- maps ---
+format_error(illegal_map_construction) ->
+ "only association operators '=>' are allowed in map construction";
%% --- records ---
format_error({undefined_record,T}) ->
io_lib:format("record ~w undefined", [T]);
@@ -1368,11 +1373,17 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
-pattern({map_field_assoc,_Line,_,_}=Pat, _, _, _, St) ->
- {[],[],add_error(element(2, Pat), illegal_pattern, St)};
-pattern({map_field_exact,_Line,_,P}, Vt, Old, Bvt0, St0) ->
- {Pvt,Bvt,St1} = pattern(P, Vt, Old, Bvt0, St0),
- {Pvt,Bvt,St1};
+pattern({map_field_assoc,Line,_,_}, _, _, _, St) ->
+ {[],[],add_error(Line, illegal_pattern, St)};
+pattern({map_field_exact,Line,KP,VP}, Vt, Old, Bvt0, St0) ->
+ %% if the key pattern has variables we should fail
+ case expr(KP,[],St0) of
+ {[],_} ->
+ pattern(VP, Vt, Old, Bvt0, St0);
+ {[Var|_],_} ->
+ %% found variables in key expression
+ {Vt,Old,add_error(Line,{illegal_map_key_variable,element(1,Var)},St0)}
+ end;
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
@@ -1985,14 +1996,24 @@ expr({bc,_Line,E,Qs}, Vt, St) ->
handle_comprehension(E, Qs, Vt, St);
expr({tuple,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
-expr({map,_Line,Es}, Vt, St) ->
- expr_list(Es, Vt, St);
+expr({map,Line,Es}, Vt, St) ->
+ {Rvt,St1} = expr_list(Es,Vt,St),
+ case is_valid_map_construction(Es) of
+ true -> {Rvt,St1};
+ false -> {[],add_error(Line,illegal_map_construction,St1)}
+ end;
expr({map,_Line,Src,Es}, Vt, St) ->
expr_list([Src|Es], Vt, St);
-expr({map_field_assoc,_Line,K,V}, Vt, St) ->
- expr_list([K,V], Vt, St);
-expr({map_field_exact,_Line,K,V}, Vt, St) ->
- expr_list([K,V], Vt, St);
+expr({map_field_assoc,Line,K,V}, Vt, St) ->
+ case is_valid_map_key(K,St) of
+ true -> expr_list([K,V], Vt, St);
+ {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
+ end;
+expr({map_field_exact,Line,K,V}, Vt, St) ->
+ case is_valid_map_key(K,St) of
+ true -> expr_list([K,V], Vt, St);
+ {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
+ end;
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
@@ -2252,6 +2273,20 @@ is_valid_call(Call) ->
_ -> true
end.
+%% check_map_construction
+%% Only #{ K => V }, i.e. assoc is a valid construction
+is_valid_map_construction([{map_field_assoc,_,_,_}|Es]) ->
+ is_valid_map_construction(Es);
+is_valid_map_construction([]) -> true;
+is_valid_map_construction(_) -> false.
+
+is_valid_map_key(K,St) ->
+ case expr(K,[],St) of
+ {[],_} -> true;
+ {[Var|_],_} ->
+ {false,element(1,Var)}
+ end.
+
%% record_def(Line, RecordName, [RecField], State) -> State.
%% Add a record definition if it does not already exist. Normalise
%% so that all fields have explicit initial value.
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index e9a813e098..c04a1aeb89 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -335,8 +335,6 @@ map_expr -> expr_max '#' map_tuple :
{map, ?line('$2'),'$1','$3'}.
map_expr -> map_expr '#' map_tuple :
{map, ?line('$2'),'$1','$3'}.
-map_expr -> map_expr '.' atom :
- {map_field, ?line('$2'),'$1','$3'}.
map_tuple -> '{' '}' : [].
map_tuple -> '{' map_fields '}' : '$2'.
--
cgit v1.2.3
From 34b043379ee6952136db98c782f69e670c93f9fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 17 Oct 2013 15:01:52 +0200
Subject: stdlib: Update erl_scan_SUITE for ':=' and '=>' tokens
---
lib/stdlib/test/erl_scan_SUITE.erl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index e628f7248d..447e159cd4 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -224,8 +224,8 @@ atoms() ->
punctuations() ->
L = ["<<", "<-", "<=", "<", ">>", ">=", ">", "->", "--",
- "-", "++", "+", "=:=", "=/=", "=<", "==", "=", "/=",
- "/", "||", "|", ":-", "::", ":"],
+ "-", "++", "+", "=:=", "=/=", "=<", "=>", "==", "=", "/=",
+ "/", "||", "|", ":=", ":-", "::", ":"],
%% One token at a time:
[begin
W = list_to_atom(S),
--
cgit v1.2.3
From bbdec77e0749a81f5e1e94a00631249cbb420fe6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 15 Oct 2013 19:40:25 +0200
Subject: erts: Remove erlang:size/1 test from map_SUITE
---
erts/emulator/test/map_SUITE.erl | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 327e9b8060..3b52295039 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -28,7 +28,8 @@
t_guard_receive/1, t_guard_fun/1,
t_list_comprehension/1,
t_map_sort_literals/1,
- t_size/1, t_map_size/1,
+ %t_size/1,
+ t_map_size/1,
%% Specific Map BIFs
t_bif_map_get/1,
@@ -67,7 +68,9 @@ all() -> [
t_bif_map_to_list, t_bif_map_from_list,
%% erlang
- t_erlang_hash, t_map_encode_decode
+ t_erlang_hash, t_map_encode_decode,
+ %t_size,
+ t_map_size
].
groups() -> [].
@@ -115,14 +118,15 @@ t_build_and_match_literals(Config) when is_list(Config) ->
ok.
%% Tests size(Map).
+%% not implemented, perhaps it shouldn't be either
-t_size(Config) when is_list(Config) ->
+%t_size(Config) when is_list(Config) ->
% 0 = size(#{}),
% 1 = size(#{a=>1}),
% 1 = size(#{a=>#{a=>1}}),
% 2 = size(#{a=>1, b=>2}),
% 3 = size(#{a=>1, b=>2, b=>"3"}),
- ok.
+% ok.
t_map_size(Config) when is_list(Config) ->
0 = map_size(id(#{})),
@@ -132,10 +136,12 @@ t_map_size(Config) when is_list(Config) ->
3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
true = map_is_size(#{a=>1}, 1),
- true = map_is_size(#{a=>1, a=>2}, 2),
+ true = map_is_size(#{a=>1, a=>2}, 1),
M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M, 2),
+ false = map_is_size(M, 3),
true = map_is_size(M#{ "a" => 2}, 2),
- false = map_is_size(M#{ "a" => 2}, 3),
+ false = map_is_size(M#{ "c" => 2}, 2),
%% Error cases.
{'EXIT',{badarg,_}} = (catch map_size([])),
--
cgit v1.2.3
From c3a45ed9ae4f84018a63df18666d0752beb8458a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 10 Oct 2013 20:13:49 +0200
Subject: stdlib: Add Map module
The map type is set to term.
---
lib/stdlib/src/Makefile | 1 +
lib/stdlib/src/map.erl | 215 ++++++++++++++++++++++++++++++++++++++++++
lib/stdlib/src/stdlib.app.src | 1 +
3 files changed, 217 insertions(+)
create mode 100644 lib/stdlib/src/map.erl
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index f3387d669b..376083c7d6 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -91,6 +91,7 @@ MODULES= \
lib \
lists \
log_mf_h \
+ map \
math \
ms_transform \
otp_internal \
diff --git a/lib/stdlib/src/map.erl b/lib/stdlib/src/map.erl
new file mode 100644
index 0000000000..6257a90180
--- /dev/null
+++ b/lib/stdlib/src/map.erl
@@ -0,0 +1,215 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(map).
+
+-export([
+ foldl/3,
+ foldr/3,
+ map/2,
+ size/1,
+ without/2
+ ]).
+
+
+%%% BIFs
+-export([
+ get/2,
+ find/2,
+ from_list/1,
+ is_key/2,
+ keys/1,
+ merge/2,
+ new/0,
+ put/3,
+ remove/2,
+ to_list/1,
+ update/3,
+ values/1
+ ]).
+
+-type map() :: term(). %% FIXME: remove when erl_bif_types knows map().
+
+%% Shadowed by erl_bif_types: map:get/3
+-spec get(Key,Map) -> Value when
+ Key :: term(),
+ Map :: map(),
+ Value :: term().
+
+get(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:find/3
+-spec find(Key,Map) -> {ok, Value} | error when
+ Key :: term(),
+ Map :: map(),
+ Value :: term().
+
+find(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:from_list/1
+-spec from_list([{Key,Value}]) -> Map when
+ Key :: term(),
+ Value :: term(),
+ Map :: map().
+
+from_list(_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:is_key/2
+-spec is_key(Key,Map) -> boolean() when
+ Key :: term(),
+ Map :: map().
+
+is_key(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:keys/1
+-spec keys(Map) -> Keys when
+ Map :: map(),
+ Keys :: [Key],
+ Key :: term().
+
+keys(_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:merge/2
+-spec merge(Map1,Map2) -> Map3 when
+ Map1 :: map(),
+ Map2 :: map(),
+ Map3 :: map().
+
+merge(_,_) -> erlang:nif_error(undef).
+
+
+
+%% Shadowed by erl_bif_types: map:new/0
+-spec new() -> Map when
+ Map :: map().
+
+new() -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:put/3
+-spec put(Key,Value,Map1) -> Map2 when
+ Key :: term(),
+ Value :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+put(_,_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:put/3
+-spec remove(Key,Map1) -> Map2 when
+ Key :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+remove(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:to_list/1
+-spec to_list(Map) -> [{Key,Value}] when
+ Map :: map(),
+ Key :: term(),
+ Value :: term().
+
+to_list(_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:update/3
+-spec update(Key,Value,Map1) -> Map2 when
+ Key :: term(),
+ Value :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+update(_,_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: map:values/1
+-spec values(Map) -> Keys when
+ Map :: map(),
+ Keys :: [Key],
+ Key :: term().
+
+values(_) -> erlang:nif_error(undef).
+
+
+%%% End of BIFs
+
+-spec foldl(Fun,Init,Map) -> Acc when
+ Fun :: fun((K, V, AccIn) -> AccOut),
+ Init :: term(),
+ Acc :: term(),
+ AccIn :: term(),
+ AccOut :: term(),
+ Map :: map(),
+ K :: term(),
+ V :: term().
+
+foldl(Fun, Init, Map) ->
+ lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)).
+
+-spec foldr(Fun,Init,Map) -> Acc when
+ Fun :: fun((K,V,AccIn) -> AccOut),
+ Init :: term(),
+ Acc :: term(),
+ AccIn :: term(),
+ AccOut :: term(),
+ Map :: map(),
+ K :: term(),
+ V :: term().
+
+
+foldr(Fun, Init, Map) ->
+ lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)).
+
+
+-spec map(Fun,Map1) -> Map2 when
+ Fun :: fun((K, V1) -> V2),
+ Map1 :: map(),
+ Map2 :: map(),
+ K :: term(),
+ V1 :: term(),
+ V2 :: term().
+
+
+map(Fun, Map) ->
+ map:from_list(lists:map(fun
+ ({K,V}) ->
+ {K,Fun(K,V)}
+ end,map:to_list(Map))).
+
+-spec size(Map) -> non_neg_integer() when
+ Map :: map().
+
+size(Map) ->
+ erlang:map_size(Map).
+
+-spec without(Ks,Map1) -> Map2 when
+ Ks :: [K],
+ Map1 :: map(),
+ Map2 :: map(),
+ K :: term().
+
+without(Ks, M) ->
+ map:from_list([{K,V}||{K,V} <- map:to_list(M), not lists:member(K, Ks)]).
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index a30685e830..9a77ae9d66 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -71,6 +71,7 @@
lib,
lists,
log_mf_h,
+ map,
math,
ms_transform,
orddict,
--
cgit v1.2.3
From 72146c6675aaff02b2452c2fd2026c111e641f35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 11 Oct 2013 18:11:12 +0200
Subject: erts: Specs for Map BIFs
---
erts/preloaded/src/erlang.erl | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index ee5bd3e515..ff0f9d0ead 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -60,6 +60,7 @@
-export_type([timestamp/0]).
-type ext_binary() :: binary().
+-type map() :: term(). %% FIXME: remove when handled internally.
-type timestamp() :: {MegaSecs :: non_neg_integer(),
Secs :: non_neg_integer(),
MicroSecs :: non_neg_integer()}.
@@ -104,10 +105,9 @@
-export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]).
-export([list_to_integer/1, list_to_integer/2]).
-export([list_to_pid/1, list_to_tuple/1, loaded/0]).
--export([localtime/0, make_ref/0, match_spec_test/3, md5/1, md5_final/1]).
+-export([localtime/0, make_ref/0, map_size/1, match_spec_test/3, md5/1, md5_final/1]).
-export([md5_init/0, md5_update/2, module_loaded/1, monitor/2]).
--export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2
-]).
+-export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2]).
-export([node/0, node/1, now/0, phash/2, phash2/1, phash2/2]).
-export([pid_to_list/1, port_close/1, port_command/2, port_command/3]).
-export([port_connect/2, port_control/3, port_get_data/1]).
@@ -128,7 +128,7 @@
-export([abs/1, append/2, element/2, get_module_info/2, hd/1,
is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1,
is_float/1, is_function/1, is_function/2, is_integer/1,
- is_list/1, is_number/1, is_pid/1, is_port/1, is_record/2,
+ is_list/1, is_map/1, is_number/1, is_pid/1, is_port/1, is_record/2,
is_record/3, is_reference/1, is_tuple/1, load_module/2,
load_nif/2, localtime_to_universaltime/2, make_fun/3,
make_tuple/2, make_tuple/3, nodes/1, open_port/2,
@@ -1149,6 +1149,12 @@ localtime() ->
make_ref() ->
erlang:nif_error(undefined).
+%% Shadowed by erl_bif_types: erlang:map_size/1
+-spec map_size(Map) -> non_neg_integer() when
+ Map :: map().
+map_size(_Map) ->
+ erlang:nif_error(undefined).
+
%% match_spec_test/3
-spec erlang:match_spec_test(P1, P2, P3) -> TestResult when
P1 :: [term()] | tuple(),
@@ -1739,6 +1745,12 @@ is_number(_Term) ->
is_pid(_Term) ->
erlang:nif_error(undefined).
+%% Shadowed by erl_bif_types: erlang:is_map/1
+-spec is_map(Map) -> boolean() when
+ Map :: map().
+is_map(_Map) ->
+ erlang:nif_error(undefined).
+
%% Shadowed by erl_bif_types: erlang:is_port/1
-spec is_port(Term) -> boolean() when
Term :: term().
--
cgit v1.2.3
From 351987be44e11e6eb9e5841adb197ec8e49bc49d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 6 Nov 2013 09:01:30 +0100
Subject: dialyzer,hipe,stdlib: Add Maps understanding to Dialyzer
---
lib/dialyzer/src/dialyzer_cl.erl | 2 +-
lib/dialyzer/src/dialyzer_dataflow.erl | 56 ++++++++++++++++-
lib/dialyzer/src/dialyzer_dep.erl | 12 ++++
lib/dialyzer/src/dialyzer_typesig.erl | 34 ++++++++++-
lib/hipe/cerl/cerl_prettypr.erl | 29 ++++++++-
lib/hipe/cerl/erl_bif_types.erl | 17 +++++-
lib/hipe/cerl/erl_types.erl | 108 ++++++++++++++++++++++++---------
lib/stdlib/src/erl_lint.erl | 8 +++
lib/stdlib/src/erl_parse.yrl | 9 +++
9 files changed, 239 insertions(+), 36 deletions(-)
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index a7be6e0d05..cda801bf6c 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -603,7 +603,7 @@ cl_loop(State, LogCache) ->
Msg = failed_anal_msg(Reason, LogCache),
cl_error(State, Msg);
{'EXIT', BackendPid, Reason} when Reason =/= 'normal' ->
- Msg = failed_anal_msg(io_lib:format("~P", [Reason, 12]), LogCache),
+ Msg = failed_anal_msg(io_lib:format("~p", [Reason]), LogCache),
cl_error(State, Msg);
_Other ->
%% io:format("Received ~p\n", [_Other]),
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 3591d5be8e..4d614320c2 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -67,7 +67,9 @@
t_to_string/2, t_to_tlist/1,
t_tuple/0, t_tuple/1, t_tuple_args/1, t_tuple_args/2,
t_tuple_subtypes/2,
- t_unit/0, t_unopaque/2]).
+ t_unit/0, t_unopaque/2,
+ t_map/1
+ ]).
%%-define(DEBUG, true).
%%-define(DEBUG_PP, true).
@@ -305,6 +307,12 @@ traverse(Tree, Map, State) ->
handle_try(Tree, Map, State);
tuple ->
handle_tuple(Tree, Map, State);
+ map ->
+ handle_map(Tree, Map, State);
+ map_pair_assoc ->
+ handle_map_pair_assoc(Tree, Map, State);
+ map_pair_exact ->
+ handle_map_pair_exact(Tree, Map, State);
values ->
Elements = cerl:values_es(Tree),
{State1, Map1, EsType} = traverse_list(Elements, Map, State),
@@ -657,7 +665,8 @@ is_opaque_type_test_problem(Fun, Args, ArgTypes, State) ->
FN =:= is_float; FN =:= is_function;
FN =:= is_integer; FN =:= is_list;
FN =:= is_number; FN =:= is_pid; FN =:= is_port;
- FN =:= is_reference; FN =:= is_tuple ->
+ FN =:= is_reference; FN =:= is_tuple;
+ FN =:= is_map ->
type_test_opaque_arg(Args, ArgTypes, State#state.opaques);
{erlang, FN, 2} when FN =:= is_function ->
type_test_opaque_arg(Args, ArgTypes, State#state.opaques);
@@ -1054,6 +1063,23 @@ handle_try(Tree, Map, State) ->
%%----------------------------------------
+handle_map(Tree,Map,State) ->
+ Pairs = cerl:map_es(Tree),
+ {State1, Map1, TypePairs} = traverse_list(Pairs,Map,State),
+ {State1, Map1, t_map(TypePairs)}.
+
+handle_map_pair_assoc(Tree,Map,State) ->
+ Elements = cerl:map_pair_assoc_es(Tree),
+ {State1, Map1, [K,V]} = traverse_list(Elements,Map,State),
+ {State1, Map1, {K,V}}.
+
+handle_map_pair_exact(Tree,Map,State) ->
+ Elements = cerl:map_pair_exact_es(Tree),
+ {State1, Map1, [K,V]} = traverse_list(Elements,Map,State),
+ {State1, Map1, {K,V}}.
+
+%%----------------------------------------
+
handle_tuple(Tree, Map, State) ->
Elements = cerl:tuple_es(Tree),
{State1, Map1, EsType} = traverse_list(Elements, Map, State),
@@ -1431,6 +1457,30 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
bind_opaque_pats(Literal, Type, Pat, Map, State, Rev);
false -> {Map, Literal}
end;
+ map ->
+ {Map, t_map([])};
+% Pairs = [ cerl:map_pair_es(Pair) || Pair <- cerl:map_es(Pat) ],
+% MapType = t_inf(t_map([]), Type),
+% case t_is_none(MapType) of
+% true ->
+% bind_opaque_pats(MapType, Type, Pat, Map, State, Rev);
+% false ->
+% MapJ = join_maps_begin(Map),
+% Results = case Rev of
+% true ->
+% [bind_pat_vars_reverse(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs];
+% false ->
+% [bind_pat_vars(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs]
+% end,
+% case [M || {M, _} <- Results, M =/= error] of
+% [] -> bind_error([Pat], MapType, t_none(), bind);
+% Maps ->
+% Map1 = join_maps_end(Maps, MapJ),
+% _PairTypes = [{Ktype,Vtype} || {M, [Ktype,Vtype]} <- Results, M =/= error],
+% % add t_sup
+% {Map1, t_map([])}
+% end
+% end;
tuple ->
Es = cerl:tuple_es(Pat),
{TypedRecord, Prototype} =
@@ -1641,6 +1691,8 @@ bind_guard(Guard, Map, Env, Eval, State) ->
Es0 = cerl:tuple_es(Guard),
{Map1, Es} = bind_guard_list(Es0, Map, Env, dont_know, State),
{Map1, t_tuple(Es)};
+ map ->
+ {Map, t_map([])};
'let' ->
Arg = cerl:let_arg(Guard),
[Var] = cerl:let_vars(Guard),
diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl
index 05f81399fb..12833b49c7 100644
--- a/lib/dialyzer/src/dialyzer_dep.erl
+++ b/lib/dialyzer/src/dialyzer_dep.erl
@@ -182,6 +182,18 @@ traverse(Tree, Out, State, CurrentFun) ->
Args = cerl:tuple_es(Tree),
{List, State1} = traverse_list(Args, Out, State, CurrentFun),
{merge_outs(List), State1};
+ map ->
+ Args = cerl:map_es(Tree),
+ {List, State1} = traverse_list(Args, Out, State, CurrentFun),
+ {merge_outs(List), State1};
+ map_pair_assoc ->
+ Args = cerl:map_pair_assoc_es(Tree),
+ {List, State1} = traverse_list(Args, Out, State, CurrentFun),
+ {merge_outs(List), State1};
+ map_pair_exact ->
+ Args = cerl:map_pair_exact_es(Tree),
+ {List, State1} = traverse_list(Args, Out, State, CurrentFun),
+ {merge_outs(List), State1};
values ->
traverse_list(cerl:values_es(Tree), Out, State, CurrentFun);
var ->
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index db7875704a..4e18e9c7d2 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -55,7 +55,9 @@
t_subst/2,
t_timeout/0, t_tuple/0, t_tuple/1,
t_var/1, t_var_name/1,
- t_none/0, t_unit/0]).
+ t_none/0, t_unit/0,
+ t_map/1
+ ]).
-include("dialyzer.hrl").
@@ -470,6 +472,33 @@ traverse(Tree, DefinedVars, State) ->
end;
[] -> {State2, TupleType}
end;
+ map ->
+ {State, t_map([])};
+% Pairs = cerl:map_es(Tree),
+% {State1, EVars} = traverse_list(Pairs, DefinedVars, State),
+% case cerl:is_literal(cerl:fold_literal(Tree)) of
+% true ->
+% %% We do not need to do anything more here.
+% {State, t_map([])};
+% false ->
+% Fun = fun(Var, AccState) ->
+% case t_has_var(Var) of
+% true ->
+% {AccState1, NewVar} = state__mk_var(AccState),
+% {NewVar,
+% state__store_conj(Var, eq, NewVar, AccState1)};
+% false ->
+% {Var, AccState}
+% end
+% end,
+% {_NewEvars, State2} = lists:mapfoldl(Fun, State1, EVars),
+% {State2, t_map([])}
+% end;
+% map_pair_assoc ->
+% [K,V] = cerl:map_pair_assoc_es(Tree),
+% {State1,_} = traverse(K,DefinedVars,State),
+% {State2,_} = traverse(V,DefinedVars,State),
+% {_,State2};
values ->
%% We can get into trouble when unifying products that have the
%% same element appearing several times. Handle these cases by
@@ -1037,6 +1066,9 @@ get_safe_underapprox_1([Pat|Left], Acc, Map) ->
{Ts, Map1} = get_safe_underapprox_1(Es, [], Map),
Type = t_tuple(Ts),
get_safe_underapprox_1(Left, [Type|Acc], Map1);
+ map ->
+ %% TODO: Can maybe do something here
+ throw(dont_know);
values ->
Es = cerl:values_es(Pat),
{Ts, Map1} = get_safe_underapprox_1(Es, [], Map),
diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl
index fba9a48cda..60926e16e1 100644
--- a/lib/hipe/cerl/cerl_prettypr.erl
+++ b/lib/hipe/cerl/cerl_prettypr.erl
@@ -62,7 +62,10 @@
receive_action/1, receive_clauses/1, receive_timeout/1,
seq_arg/1, seq_body/1, string_lit/1, try_arg/1,
try_body/1, try_vars/1, try_evars/1, try_handler/1,
- tuple_es/1, type/1, values_es/1, var_name/1]).
+ tuple_es/1, type/1, values_es/1, var_name/1,
+
+ map_es/1, map_pair_assoc_es/1, map_pair_exact_es/1
+ ]).
-define(PAPER, 76).
-define(RIBBON, 45).
@@ -424,6 +427,12 @@ lay_1(Node, Ctxt) ->
lay_cons(Node, Ctxt);
tuple ->
lay_tuple(Node, Ctxt);
+ map ->
+ lay_map(Node, Ctxt);
+ map_pair_assoc ->
+ lay_map_pair_assoc(Node, Ctxt);
+ map_pair_exact ->
+ lay_map_pair_exact(Node, Ctxt);
'let' ->
lay_let(Node, Ctxt);
seq ->
@@ -589,6 +598,24 @@ lay_tuple(Node, Ctxt) ->
Ctxt, fun lay/2)),
floating(text("}")))).
+lay_map(Node, Ctxt) ->
+ beside(floating(text("~{")),
+ beside(par(seq(map_es(Node), floating(text(",")),
+ Ctxt, fun lay/2)),
+ floating(text("}~")))).
+
+lay_map_pair_assoc(Node, Ctxt) ->
+ [K,V] = map_pair_assoc_es(Node),
+ beside(floating(text("::<")),
+ beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
+ floating(text(">")))))).
+
+lay_map_pair_exact(Node, Ctxt) ->
+ [K,V] = map_pair_exact_es(Node),
+ beside(floating(text("~<")),
+ beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
+ floating(text(">")))))).
+
lay_let(Node, Ctxt) ->
V = lay_value_list(let_vars(Node), Ctxt),
D1 = par([follow(text("let"),
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 32a502e212..8b610ac893 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -114,7 +114,9 @@
t_tuple/1,
t_tuple_args/2,
t_tuple_size/2,
- t_tuple_subtypes/2
+ t_tuple_subtypes/2,
+ t_is_map/2,
+ t_map/0
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -654,6 +656,11 @@ type(erlang, is_list, 1, Xs, Opaques) ->
check_guard(X, Fun2, t_maybe_improper_list(), Opaques)
end,
strict(erlang, is_list, 1, Xs, Fun, Opaques);
+type(erlang, is_map, 1, Xs, Opaques) ->
+ Fun = fun (X) ->
+ check_guard(X, fun (Y) -> t_is_map(Y, Opaques) end,
+ t_map(), Opaques) end,
+ strict(erlang, is_map, 1, Xs, Fun, Opaques);
type(erlang, is_number, 1, Xs, Opaques) ->
Fun = fun (X) ->
check_guard(X, fun (Y) -> t_is_number(Y, Opaques) end,
@@ -756,6 +763,9 @@ type(erlang, is_tuple, 1, Xs, Opaques) ->
%% Guard bif, needs to be here.
type(erlang, length, 1, Xs, Opaques) ->
strict(erlang, length, 1, Xs, fun (_) -> t_non_neg_fixnum() end, Opaques);
+%% Guard bif, needs to be here.
+type(erlang, map_size, 1, Xs, Opaques) ->
+ strict(erlang, map_size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques);
type(erlang, make_tuple, 2, Xs, Opaques) ->
strict(erlang, make_tuple, 2, Xs,
fun ([Int, _]) ->
@@ -2297,6 +2307,8 @@ arg_types(erlang, is_integer, 1) ->
[t_any()];
arg_types(erlang, is_list, 1) ->
[t_any()];
+arg_types(erlang, is_map, 1) ->
+ [t_any()];
arg_types(erlang, is_number, 1) ->
[t_any()];
arg_types(erlang, is_pid, 1) ->
@@ -2314,6 +2326,9 @@ arg_types(erlang, is_tuple, 1) ->
%% Guard bif, needs to be here.
arg_types(erlang, length, 1) ->
[t_list()];
+%% Guard bif, needs to be here.
+arg_types(erlang, map_size, 1) ->
+ [t_map()];
arg_types(erlang, make_tuple, 2) ->
[t_non_neg_fixnum(), t_any()]; % the value 0 is OK as first argument
arg_types(erlang, make_tuple, 3) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index cfa72d85b7..af34e355bb 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -208,7 +208,11 @@
lift_list_to_pos_empty/1,
is_opaque_type/2,
is_erl_type/1,
- atom_to_string/1
+ atom_to_string/1,
+
+ t_is_map/2,
+ t_map/1,
+ t_map/0
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -266,6 +270,7 @@
-define(function_tag, function).
-define(identifier_tag, identifier).
-define(list_tag, list).
+-define(map_tag, map).
-define(matchstate_tag, matchstate).
-define(nil_tag, nil).
-define(number_tag, number).
@@ -278,7 +283,7 @@
-define(var_tag, var).
-type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag
- | ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag
+ | ?list_tag | ?map_tag | ?matchstate_tag | ?nil_tag | ?number_tag
| ?opaque_tag | ?product_tag | ?remote_tag
| ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag.
@@ -338,6 +343,7 @@
-define(nonempty_list(Types, Term),?list(Types, Term, ?nonempty_qual)).
-define(number(Set, Qualifier), #c{tag=?number_tag, elements=Set,
qualifier=Qualifier}).
+-define(map(Pairs), #c{tag=?map_tag, elements=Pairs}).
-define(opaque(Optypes), #c{tag=?opaque_tag, elements=Optypes}).
-define(product(Types), #c{tag=?product_tag, elements=Types}).
-define(remote(RemTypes), #c{tag=?remote_tag, elements=RemTypes}).
@@ -361,18 +367,19 @@
%% Unions
%%
--define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_]=List}).
-
--define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
--define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none])).
--define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none])).
--define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none])).
--define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none])).
--define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none])).
--define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none])).
--define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none])).
--define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none])).
--define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T])).
+-define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_,_]=List}).
+
+-define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none,?none])).
+-define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none,?none])).
+-define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none,?none])).
+-define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none,?none])).
+-define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none,?none])).
+-define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none,?none])).
+-define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T,?none])).
+-define(map_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,?none,T])).
-define(integer_union(T), ?number_union(T)).
-define(float_union(T), ?number_union(T)).
-define(nil_union(T), ?list_union(T)).
@@ -482,6 +489,9 @@ t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
t_contains_opaque(?list(Type, Tail, _), Opaques) ->
t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques);
+t_contains_opaque(?map(Pairs), Opaques) ->
+ list_contains_opaque([V||{_,V}<-Pairs], Opaques) orelse
+ list_contains_opaque([K||{K,_}<-Pairs], Opaques);
t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false;
t_contains_opaque(?nil, _Opaques) -> false;
t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false;
@@ -655,9 +665,9 @@ list_decorate(List, L, Opaques) ->
union_decorate(U1, U2, Opaques) ->
Union = union_decorate(U1, U2, Opaques, 0, []),
- [A,B,F,I,L,N,T,M,_,_R] = U1,
- [_,_,_,_,_,_,_,_,Opaque,_] = U2,
- List = [A,B,F,I,L,N,T,M],
+ [A,B,F,I,L,N,T,M,_,_R,Map] = U1,
+ [_,_,_,_,_,_,_,_,Opaque,_,_] = U2,
+ List = [A,B,F,I,L,N,T,M,Map],
DecList = [Dec ||
E <- List,
not t_is_none(Dec = decorate(E, Opaque, Opaques))],
@@ -1694,6 +1704,29 @@ lift_list_to_pos_empty(?nil) -> ?nil;
lift_list_to_pos_empty(?list(Content, Termination, _)) ->
?list(Content, Termination, ?unknown_qual).
+%%-----------------------------------------------------------------------------
+%% Maps
+%%
+
+-spec t_map() -> erl_type().
+
+t_map() ->
+ ?map([]).
+
+-spec t_map([{erl_type(),erl_type()}]) -> erl_type().
+
+t_map(_) ->
+ ?map([]).
+
+-spec t_is_map(erl_type(), opaques()) -> boolean().
+
+t_is_map(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_map1/1).
+
+is_map1(?map(_)) -> true;
+is_map1(_) -> false.
+
+
%%-----------------------------------------------------------------------------
%% Tuples
%%
@@ -2517,6 +2550,7 @@ force_union(T = ?nil) -> ?list_union(T);
force_union(T = ?number(_,_)) -> ?number_union(T);
force_union(T = ?opaque(_)) -> ?opaque_union(T);
force_union(T = ?remote(_)) -> ?remote_union(T);
+force_union(T = ?map(_)) -> ?map_union(T);
force_union(T = ?tuple(_, _, _)) -> ?tuple_union(T);
force_union(T = ?tuple_set(_)) -> ?tuple_union(T);
force_union(T = ?matchstate(_, _)) -> ?matchstate_union(T);
@@ -2553,6 +2587,7 @@ t_elements(?number(_, _) = T) ->
end;
t_elements(?opaque(_) = T) ->
do_elements(T);
+t_elements(?map(_) = T) -> [T];
t_elements(?tuple(_, _, _) = T) -> [T];
t_elements(?tuple_set(_) = TS) ->
case t_tuple_subtypes(TS) of
@@ -2914,9 +2949,9 @@ inf_tuples_in_sets2(_, [], Acc, _Opaques) -> lists:reverse(Acc).
inf_union(U1, U2, Opaques) ->
OpaqueFun =
fun(Union1, Union2, InfFun) ->
- [_,_,_,_,_,_,_,_,Opaque,_] = Union1,
- [A,B,F,I,L,N,T,M,_,_R] = Union2,
- List = [A,B,F,I,L,N,T,M],
+ [_,_,_,_,_,_,_,_,Opaque,_,_] = Union1,
+ [A,B,F,I,L,N,T,M,_,_R,Map] = Union2,
+ List = [A,B,F,I,L,N,T,M,Map],
inf_union_collect(List, Opaque, InfFun, [], [])
end,
O1 = OpaqueFun(U1, U2, fun(E, Opaque) -> t_inf(Opaque, E, Opaques) end),
@@ -3182,11 +3217,11 @@ unify_union1(?union(List), T1, T2) ->
end.
unify_union(List) ->
- [A,B,F,I,L,N,T,M,O,R] = List,
+ [A,B,F,I,L,N,T,M,O,R,Map] = List,
if O =:= ?none -> no;
true ->
S = t_opaque_structure(O),
- {yes, t_sup([A,B,F,I,L,N,T,M,S,R])}
+ {yes, t_sup([A,B,F,I,L,N,T,M,S,R,Map])}
end.
-spec is_opaque_type(erl_type(), [erl_type()]) -> boolean().
@@ -3537,10 +3572,10 @@ t_subtract_lists([], [], Acc) ->
-spec subtract_union([erl_type(),...], [erl_type(),...]) -> erl_type().
subtract_union(U1, U2) ->
- [A1,B1,F1,I1,L1,N1,T1,M1,O1,R1] = U1,
- [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2] = U2,
- List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1],
- List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2],
+ [A1,B1,F1,I1,L1,N1,T1,M1,O1,R1,Map1] = U1,
+ [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2,Map2] = U2,
+ List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1,Map1],
+ List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2,Map2],
Sub1 = subtract_union(List1, List2, 0, []),
O = if O1 =:= ?none -> O1;
true -> t_subtract(O1, ?union(U2))
@@ -3656,7 +3691,7 @@ t_unopaque(?product(Types), Opaques) ->
?product([t_unopaque(T, Opaques) || T <- Types]);
t_unopaque(?function(Domain, Range), Opaques) ->
?function(t_unopaque(Domain, Opaques), t_unopaque(Range, Opaques));
-t_unopaque(?union([A,B,F,I,L,N,T,M,O,R]), Opaques) ->
+t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) ->
UL = t_unopaque(L, Opaques),
UT = t_unopaque(T, Opaques),
UF = t_unopaque(F, Opaques),
@@ -3664,7 +3699,7 @@ t_unopaque(?union([A,B,F,I,L,N,T,M,O,R]), Opaques) ->
?opaque(_) = O1 -> {O1, []};
Type -> {?none, [Type]}
end,
- t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R])|UO]);
+ t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,Map])|UO]);
t_unopaque(T, _) ->
T.
@@ -3941,6 +3976,8 @@ t_to_string(?remote(Set), RecDict) ->
|| #remote{mod = Mod, name = Name, args = Args} <-
set_to_list(Set)],
" | ");
+t_to_string(?map(Pairs), RecDict) ->
+ "#{" ++ map_pairs_to_string(Pairs,RecDict) ++ "}";
t_to_string(?tuple(?any, ?any, ?any), _RecDict) -> "tuple()";
t_to_string(?tuple(Elements, _Arity, ?any), RecDict) ->
"{" ++ comma_sequence(Elements, RecDict) ++ "}";
@@ -3960,6 +3997,13 @@ t_to_string(?var(Id), _RecDict) when is_atom(Id) ->
t_to_string(?var(Id), _RecDict) when is_integer(Id) ->
flat_format("var(~w)", [Id]).
+
+map_pairs_to_string([],_) -> [];
+map_pairs_to_string(Pairs,RecDict) ->
+ StrPairs = [{t_to_string(K,RecDict),t_to_string(V,RecDict)}||{K,V}<-Pairs],
+ string:join([K ++ "=>" ++ V||{K,V}<-StrPairs], ", ").
+
+
record_to_string(Tag, [_|Fields], FieldNames, RecDict) ->
FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []),
"#" ++ atom_to_string(Tag) ++ "{" ++ string:join(FieldStrings, ",") ++ "}".
@@ -4153,6 +4197,8 @@ t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) ->
t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) ->
{T, R} = t_from_form(Type, TypeNames, RecDict, VarDict),
{t_list(T), R};
+t_from_form({type, _L, map, _}, _TypeNames, _RecDict, _VarDict) ->
+ {t_map([]), []};
t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) ->
{t_mfa(), []};
t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4437,6 +4483,8 @@ t_form_to_string({type, _L, iodata, []}) -> "iodata()";
t_form_to_string({type, _L, iolist, []}) -> "iolist()";
t_form_to_string({type, _L, list, [Type]}) ->
"[" ++ t_form_to_string(Type) ++ "]";
+t_form_to_string({type, _L, map, _}) ->
+ "#{}";
t_form_to_string({type, _L, mfa, []}) -> "mfa()";
t_form_to_string({type, _L, module, []}) -> "module()";
t_form_to_string({type, _L, node, []}) -> "node()";
@@ -4566,13 +4614,13 @@ do_opaque(?opaque(_) = Type, Opaques, Pred) ->
false -> Pred(Type)
end;
do_opaque(?union(List) = Type, Opaques, Pred) ->
- [A,B,F,I,L,N,T,M,O,R] = List,
+ [A,B,F,I,L,N,T,M,O,R,Map] = List,
if O =:= ?none -> Pred(Type);
true ->
case Opaques =:= 'universe' orelse is_opaque_type(O, Opaques) of
true ->
S = t_opaque_structure(O),
- do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R]), Opaques, Pred);
+ do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R,Map]), Opaques, Pred);
false -> Pred(Type)
end
end;
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index a41bcb91cc..f630db6032 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2607,6 +2607,13 @@ check_type({type, L, range, [From, To]}, SeenVars, St) ->
_ -> add_error(L, {type_syntax, range}, St)
end,
{SeenVars, St1};
+check_type({type, _L, map, any}, SeenVars, St) -> {SeenVars, St};
+check_type({type, _L, map, Pairs}, SeenVars, St) ->
+ lists:foldl(fun(Pair, {AccSeenVars, AccSt}) ->
+ check_type(Pair, AccSeenVars, AccSt)
+ end, {SeenVars, St}, Pairs);
+check_type({type, _L, map_field_assoc, Dom, Range}, SeenVars, St) ->
+ check_type({type, -1, product, [Dom, Range]}, SeenVars, St);
check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, L, binary, [Base, Unit]}, SeenVars, St) ->
@@ -2712,6 +2719,7 @@ is_default_type({iodata, 0}) -> true;
is_default_type({iolist, 0}) -> true;
is_default_type({list, 0}) -> true;
is_default_type({list, 1}) -> true;
+is_default_type({map, 0}) -> true;
is_default_type({maybe_improper_list, 0}) -> true;
is_default_type({maybe_improper_list, 2}) -> true;
is_default_type({mfa, 0}) -> true;
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index c04a1aeb89..6d9aafa980 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -48,6 +48,7 @@ opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type
top_type top_type_100 top_types type typed_expr typed_attr_val
type_sig type_sigs type_guard type_guards fun_type fun_type_100 binary_type
type_spec spec_fun typed_exprs typed_record_fields field_types field_type
+map_pair_types map_pair_type
bin_base_type bin_unit_type type_200 type_300 type_400 type_500.
Terminals
@@ -155,6 +156,8 @@ type -> '[' ']' : {type, ?line('$1'), nil, []}.
type -> '[' top_type ']' : {type, ?line('$1'), list, ['$2']}.
type -> '[' top_type ',' '...' ']' : {type, ?line('$1'),
nonempty_list, ['$2']}.
+type -> '#' '{' '}' : {type, ?line('$1'), map, []}.
+type -> '#' '{' map_pair_types '}' : {type, ?line('$1'), map, '$3'}.
type -> '{' '}' : {type, ?line('$1'), tuple, []}.
type -> '{' top_types '}' : {type, ?line('$1'), tuple, '$2'}.
type -> '#' atom '{' '}' : {type, ?line('$1'), record, ['$2']}.
@@ -176,6 +179,10 @@ fun_type -> '(' top_types ')' '->' top_type
: {type, ?line('$1'), 'fun',
[{type, ?line('$1'), product, '$2'},'$5']}.
+map_pair_types -> map_pair_type : ['$1'].
+map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3'].
+map_pair_type -> top_type '=>' top_type : {type, ?line('$2'), map_field_assoc,'$1','$3'}.
+
field_types -> field_type : ['$1'].
field_types -> field_type ',' field_types : ['$1'|'$3'].
@@ -674,6 +681,8 @@ skip_paren(Type) ->
build_gen_type({atom, La, tuple}) ->
{type, La, tuple, any};
+build_gen_type({atom, La, map}) ->
+ {type, La, map, any};
build_gen_type({atom, La, Name}) ->
{type, La, Name, []}.
--
cgit v1.2.3
From b42453915e4c6aa1133e88c06a60dfbc5fe5e1f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 24 Oct 2013 21:17:11 +0200
Subject: edoc,syntax_tools: Teach Map understanding
---
lib/edoc/src/edoc_doclet.erl | 2 +-
lib/edoc/src/edoc_layout.erl | 10 +++++++++
lib/edoc/src/edoc_lib.erl | 2 +-
lib/edoc/src/edoc_parser.yrl | 16 +++++++++++++--
lib/edoc/src/edoc_scanner.erl | 2 ++
lib/edoc/src/edoc_specs.erl | 13 ++++++++++++
lib/edoc/src/edoc_tags.erl | 2 ++
lib/edoc/src/edoc_types.erl | 4 ++++
lib/edoc/src/edoc_types.hrl | 4 ++++
lib/erl_docgen/src/docgen_otp_specs.erl | 5 +++++
lib/syntax_tools/src/erl_syntax.erl | 36 ++++++++++++++++++++++++++++++++-
11 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index ce1e94a26a..5653b5894b 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -200,7 +200,7 @@ source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden,
{Set, Error}
end;
R ->
- report("skipping source file '~ts': ~W.", [File, R, 15]),
+ report("skipping source file '~ts': ~P.", [File, R, 15]),
{Set, true}
end.
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
index 7bd0615f5c..e164ff060f 100644
--- a/lib/edoc/src/edoc_layout.erl
+++ b/lib/edoc/src/edoc_layout.erl
@@ -829,6 +829,10 @@ t_type([#xmlElement{name = list, content = Es}]) ->
t_list(Es);
t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
t_nonempty_list(Es);
+t_type([#xmlElement{name = map, content = Es}]) ->
+ t_map(Es);
+t_type([#xmlElement{name = map_field, content=Es}]) ->
+ t_map_field(Es);
t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
@@ -877,6 +881,12 @@ t_fun(Es) ->
["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
[") -> "] ++ t_utype(get_elem(type, Es))).
+t_map(Es) ->
+ ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+
+t_map_field([K,V]) ->
+ [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
+
t_record(E, Es) ->
Name = ["#"] ++ t_type(get_elem(atom, Es)),
case get_elem(field, Es) of
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index ca9df2b632..c46338a2e1 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -1032,7 +1032,7 @@ run_plugin(Name, Key, Default, Fun, Opts) when is_atom(Name) ->
{ok, Value} ->
Value;
R ->
- report("error in ~ts '~w': ~W.", [Name, Module, R, 20]),
+ report("error in ~ts '~w': ~P.", [Name, Module, R, 20]),
exit(error)
end.
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
index 7762f2da7d..c6f8a04775 100644
--- a/lib/edoc/src/edoc_parser.yrl
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -29,13 +29,14 @@ Nonterminals
start spec func_type utype_list utype_tuple utypes utype ptypes ptype
nutype function_name where_defs defs defs2 def typedef etype
throws qname ref aref mref lref pref var_list vars fields field
+utype_map utype_map_fields utype_map_field
futype_list bin_base_type bin_unit_type.
Terminals
atom float integer var an_var string start_spec start_typedef start_throws
start_ref
-'(' ')' ',' '.' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*'
+'(' ')' ',' '.' '=>' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*'
'#' 'where' '<<' '>>' '..' '...'.
Rootsymbol start.
@@ -69,6 +70,14 @@ utype_list -> '(' utypes ')' : {lists:reverse('$2'), tok_line('$1')}.
futype_list -> utype_list : '$1'.
futype_list -> '(' '...' ')' : {[#t_var{name = '...'}], tok_line('$1')}.
+utype_map -> '#' '{' utype_map_fields '}' : lists:reverse('$3').
+
+utype_map_fields -> '$empty' : [].
+utype_map_fields -> utype_map_field : ['$1'].
+utype_map_fields -> utype_map_fields ',' utype_map_field : ['$3' | '$1'].
+
+utype_map_field -> utype '=>' utype : #t_map_field{ k_type = '$1', v_type = '$3'}.
+
utype_tuple -> '{' utypes '}' : lists:reverse('$2').
%% Produced in reverse order.
@@ -91,9 +100,10 @@ ptype -> var : #t_var{name = tok_val('$1')}.
ptype -> atom : #t_atom{val = tok_val('$1')}.
ptype -> integer: #t_integer{val = tok_val('$1')}.
ptype -> integer '..' integer: #t_integer_range{from = tok_val('$1'),
- to = tok_val('$3')}.
+ to = tok_val('$3')}.
ptype -> float: #t_float{val = tok_val('$1')}.
ptype -> utype_tuple : #t_tuple{types = '$1'}.
+ptype -> utype_map : #t_map{types = '$1'}.
ptype -> '[' ']' : #t_nil{}.
ptype -> '[' utype ']' : #t_list{type = '$2'}.
ptype -> '[' utype ',' '...' ']' : #t_nonempty_list{type = '$2'}.
@@ -462,3 +472,5 @@ throw_error(parse_param, L) ->
throw({error, L, "missing parameter name"});
throw_error({Where, E}, L) when is_list(Where) ->
throw({error,L,{"unknown error parsing ~ts: ~P.",[Where,E,15]}}).
+
+%% vim: ft=erlang
diff --git a/lib/edoc/src/edoc_scanner.erl b/lib/edoc/src/edoc_scanner.erl
index 754fcef643..6ff97a134c 100644
--- a/lib/edoc/src/edoc_scanner.erl
+++ b/lib/edoc/src/edoc_scanner.erl
@@ -137,6 +137,8 @@ scan1([$"|Cs0], Toks, Pos) -> % String
scan_error({illegal, string}, Pos)
end;
%% Punctuation characters and operators, first recognise multiples.
+scan1([$=,$>|Cs], Toks, Pos) ->
+ scan1(Cs, [{'=>',Pos}|Toks], Pos);
scan1([$<,$<|Cs], Toks, Pos) ->
scan1(Cs, [{'<<',Pos}|Toks], Pos);
scan1([$>,$>|Cs], Toks, Pos) ->
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl
index 5acf8ac0d5..466c9df951 100644
--- a/lib/edoc/src/edoc_specs.erl
+++ b/lib/edoc/src/edoc_specs.erl
@@ -358,6 +358,14 @@ d2e({type,_,tuple,any}) ->
d2e({type,_,binary,[Base,Unit]}) ->
#t_binary{base_size = element(3, Base),
unit_size = element(3, Unit)};
+d2e({type,_,map,any}) ->
+ #t_map{ types = []};
+d2e({type,_,map,Es}) ->
+ #t_map{ types = d2e(Es) };
+d2e({type,_,map_field_assoc,K,V}) ->
+ #t_map_field{ k_type = d2e(K), v_type=d2e(V) };
+d2e({type,_,map_field_exact,K,V}) ->
+ #t_map_field{ k_type = d2e(K), v_type=d2e(V) };
d2e({type,_,tuple,Ts0}) ->
Ts = d2e(Ts0),
typevar_anno(#t_tuple{types = Ts}, Ts);
@@ -476,6 +484,11 @@ xrecs(#t_fun{args = Args0, range = Range0}=T, P) ->
Args = xrecs(Args0, P),
Range = xrecs(Range0, P),
T#t_fun{args = Args, range = Range};
+xrecs(#t_map{ types = Ts0 }=T,P) ->
+ Ts = xrecs(Ts0, P),
+ T#t_map{ types = Ts };
+xrecs(#t_map_field{ k_type=Kt, v_type=Vt}=T, P) ->
+ T#t_map_field{ k_type=xrecs(Kt,P), v_type=xrecs(Vt,P)};
xrecs(#t_tuple{types = Types0}=T, P) ->
Types = xrecs(Types0, P),
T#t_tuple{types = Types};
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index eb41f1922a..74702102f0 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -460,6 +460,8 @@ check_type(#t_var{}, P, Ls, Ts) ->
check_types3(Ts, P, Ls);
check_type(#t_fun{args = Args, range = Range}, P, Ls, Ts) ->
check_type(Range, P, Ls, Args++Ts);
+check_type(#t_map{}, P, Ls, Ts) ->
+ check_types3(Ts, P, Ls);
check_type(#t_tuple{types = Types}, P, Ls, Ts) ->
check_types3(Types ++Ts, P, Ls);
check_type(#t_list{type = Type}, P, Ls, Ts) ->
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
index 60c6cecb97..af8f1230fb 100644
--- a/lib/edoc/src/edoc_types.erl
+++ b/lib/edoc/src/edoc_types.erl
@@ -141,6 +141,10 @@ to_xml(#t_type{name = N, args = As}, Env) ->
to_xml(#t_fun{args = As, range = T}, Env) ->
{'fun', [{argtypes, map(fun wrap_utype/2, As, Env)},
wrap_utype(T, Env)]};
+to_xml(#t_map{ types = Ts}, Env) ->
+ {map, map(fun wrap_utype/2, Ts, Env)};
+to_xml(#t_map_field{ k_type=K, v_type=V}, Env) ->
+ {map_field, [wrap_utype(K,Env), wrap_utype(V, Env)]};
to_xml(#t_tuple{types = Ts}, Env) ->
{tuple, map(fun wrap_utype/2, Ts, Env)};
to_xml(#t_list{type = T}, Env) ->
diff --git a/lib/edoc/src/edoc_types.hrl b/lib/edoc/src/edoc_types.hrl
index 05c61d70ff..7fec10d936 100644
--- a/lib/edoc/src/edoc_types.hrl
+++ b/lib/edoc/src/edoc_types.hrl
@@ -155,3 +155,7 @@
%% @type t_paren() = #t_paren{a = list(), type = type()}
-record(t_paren, {a=[], type}). % parentheses
+
+-record(t_map, {a=[], types=[]}).
+-record(t_map_field, {a=[], k_type, v_type}).
+
diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl
index 3929e66515..3240edd68e 100644
--- a/lib/erl_docgen/src/docgen_otp_specs.erl
+++ b/lib/erl_docgen/src/docgen_otp_specs.erl
@@ -388,6 +388,8 @@ t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
t_nonempty_list(Es);
t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
+t_type([#xmlElement{name = map}]) ->
+ t_map();
t_type([#xmlElement{name = 'fun', content = Es}]) ->
["fun("] ++ t_fun(Es) ++ [")"];
t_type([E = #xmlElement{name = record, content = Es}]) ->
@@ -430,6 +432,9 @@ t_nonempty_list(Es) ->
t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+t_map() ->
+ ["#{}"].
+
t_fun(Es) ->
["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
[") -> "] ++ t_utype(get_elem(type, Es))).
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 4f7f9e83ac..78a37445ed 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -582,6 +582,9 @@ type(Node) ->
{match, _, _, _} -> match_expr;
{op, _, _, _, _} -> infix_expr;
{op, _, _, _} -> prefix_expr;
+ {map,_,_} -> map;
+ {map_field_assoc,_,_,_} -> map_field_assoc;
+ {map_field_exact,_,_,_} -> map_field_exact;
{record, _, _, _, _} -> record_expr;
{record, _, _, _} -> record_expr;
{record_field, _, _, _, _} -> record_access;
@@ -1909,6 +1912,28 @@ atom_literal(Node) ->
io_lib:write_atom(atom_value(Node)).
+%% =====================================================================
+
+map_elements(Node) ->
+ case unwrap(Node) of
+ {map, _, List} ->
+ List;
+ Node1 ->
+ data(Node1)
+ end.
+
+map_field_elements({_,_,K,V}) ->
+ [K,V].
+
+map(List) ->
+ tree(map, List).
+
+map_field_assoc(List) ->
+ tree(map_field_assoc, List).
+
+map_field_exact(List) ->
+ tree(map_field_exact, List).
+
%% =====================================================================
%% @doc Creates an abstract tuple. If `Elements' is
%% `[X1, ..., Xn]', the result represents
@@ -6396,6 +6421,12 @@ subtrees(T) ->
try_expr_clauses(T),
try_expr_handlers(T),
try_expr_after(T)];
+ map ->
+ [map_elements(T)];
+ map_field_assoc ->
+ [map_field_elements(T)];
+ map_field_exact ->
+ [map_field_elements(T)];
tuple ->
[tuple_elements(T)]
end
@@ -6491,7 +6522,10 @@ make_tree(record_index_expr, [[T], [F]]) ->
make_tree(rule, [[N], C]) -> rule(N, C);
make_tree(size_qualifier, [[N], [A]]) -> size_qualifier(N, A);
make_tree(try_expr, [B, C, H, A]) -> try_expr(B, C, H, A);
-make_tree(tuple, [E]) -> tuple(E).
+make_tree(tuple, [E]) -> tuple(E);
+make_tree(map, [E]) -> map(E);
+make_tree(map_field_assoc, [E]) -> map_field_assoc(E);
+make_tree(map_field_exact, [E]) -> map_field_exact(E).
%% =====================================================================
--
cgit v1.2.3
From 40706594b498291d57528ccb3e966febccb959b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 7 Nov 2013 15:27:56 +0100
Subject: edoc: Add test for Maps in EDoc
---
lib/edoc/test/edoc_SUITE.erl | 39 +++++++++++++++-------------
lib/edoc/test/edoc_SUITE_data/map_module.erl | 27 +++++++++++++++++++
2 files changed, 48 insertions(+), 18 deletions(-)
create mode 100644 lib/edoc/test/edoc_SUITE_data/map_module.erl
diff --git a/lib/edoc/test/edoc_SUITE.erl b/lib/edoc/test/edoc_SUITE.erl
index 5b95c35756..b649971e99 100644
--- a/lib/edoc/test/edoc_SUITE.erl
+++ b/lib/edoc/test/edoc_SUITE.erl
@@ -22,12 +22,12 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([build_std/1]).
+-export([build_std/1,build_map_module/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [build_std].
+ [build_std,build_map_module].
groups() ->
[].
@@ -45,25 +45,28 @@ end_per_group(_GroupName, Config) ->
Config.
-build_std(suite) ->
- [];
-build_std(doc) ->
- ["Build some documentation using standard EDoc layout"];
+build_std(suite) -> [];
+build_std(doc) -> ["Build some documentation using standard EDoc layout"];
build_std(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Overview1 = filename:join(DataDir, "overview.edoc"),
+ Overview2 = filename:join(DataDir, "overview.syntax_tools"),
+ PrivDir = ?config(priv_dir, Config),
- ?line DataDir = ?config(data_dir, Config),
- ?line Overview1 = filename:join(DataDir, "overview.edoc"),
- ?line Overview2 = filename:join(DataDir, "overview.syntax_tools"),
- ?line PrivDir = ?config(priv_dir, Config),
+ ok = edoc:application(edoc, [{overview, Overview1},
+ {def, {vsn,"TEST"}},
+ {dir, PrivDir}]),
- ?line ok = edoc:application(edoc, [{overview, Overview1},
- {def, {vsn,"TEST"}},
- {dir, PrivDir}]),
+ ok = edoc:application(syntax_tools, [{overview, Overview2},
+ {def, {vsn,"TEST"}},
+ {dir, PrivDir}]),
- ?line ok = edoc:application(syntax_tools, [{overview, Overview2},
- {def, {vsn,"TEST"}},
- {dir, PrivDir}]),
-
- ?line ok = edoc:application(xmerl, [{dir, PrivDir}]),
+ ok = edoc:application(xmerl, [{dir, PrivDir}]),
+ ok.
+build_map_module(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Filename = filename:join(DataDir, "map_module.erl"),
+ ok = edoc:file(Filename, [{dir, PrivDir}]),
ok.
diff --git a/lib/edoc/test/edoc_SUITE_data/map_module.erl b/lib/edoc/test/edoc_SUITE_data/map_module.erl
new file mode 100644
index 0000000000..94ee7e6f26
--- /dev/null
+++ b/lib/edoc/test/edoc_SUITE_data/map_module.erl
@@ -0,0 +1,27 @@
+-module(map_module).
+
+-export([foo1/1,foo2/3]).
+
+%% @type wazzup() = integer()
+%% @type some_type() = map()
+%% @type some_other_type() = {a, #{ list() => term()}}
+
+-type some_type() :: map().
+-type some_other_type() :: {'a', #{ list() => term()} }.
+-type wazzup() :: integer().
+
+-spec foo1(Map :: #{ 'a' => integer(), 'b' => term()}) -> term().
+
+%% @doc Gets value from map.
+
+foo1(#{ a:= 1, b := V}) -> V.
+
+%% @spec foo2(some_type(), Type2 :: some_other_type(), map()) -> Value
+%% @doc Gets value from map.
+
+-spec foo2(
+ Type1 :: some_type(),
+ Type2 :: some_other_type(),
+ Map :: #{ get => 'value', 'value' => binary()}) -> binary().
+
+foo2(Type1, {a,#{ "a" := _}}, #{get := value, value := B}) when is_map(Type1) -> B.
--
cgit v1.2.3
From bd524e53cc258e11785bfc8bd42348b471479878 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 12 Nov 2013 14:34:51 +0100
Subject: typer: Rename map() type to map_dict()
The name conflicted with builtin type map().
---
lib/typer/src/typer.erl | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index 0ace1d5fb8..20ae4d6066 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -63,10 +63,10 @@
%% Files in 'fms' are compilable with option 'to_pp'; we keep them
%% as {FileName, ModuleName} in case the ModuleName is different
fms = [] :: [{file:filename(), module()}],
- ex_func = map__new() :: map(),
- record = map__new() :: map(),
- func = map__new() :: map(),
- inc_func = map__new() :: map(),
+ ex_func = map__new() :: map_dict(),
+ record = map__new() :: map_dict(),
+ func = map__new() :: map_dict(),
+ inc_func = map__new() :: map_dict(),
trust_plt = dialyzer_plt:new() :: plt()}).
-type analysis() :: #analysis{}.
@@ -220,11 +220,11 @@ get_external(Exts, Plt) ->
-type fa() :: {atom(), arity()}.
-type func_info() :: {line(), atom(), arity()}.
--record(info, {records = map__new() :: map(),
+-record(info, {records = map__new() :: map_dict(),
functions = [] :: [func_info()],
- types = map__new() :: map(),
+ types = map__new() :: map_dict(),
edoc = false :: boolean()}).
--record(inc, {map = map__new() :: map(), filter = [] :: files()}).
+-record(inc, {map = map__new() :: map_dict(), filter = [] :: files()}).
-type inc() :: #inc{}.
-spec show_or_annotate(analysis()) -> 'ok'.
@@ -1094,29 +1094,29 @@ rcv_ext_types(Self, ExtTypes) ->
%% specialized for the uses in this module
%%--------------------------------------------------------------------
--type map() :: dict().
+-type map_dict() :: dict().
--spec map__new() -> map().
+-spec map__new() -> map_dict().
map__new() ->
dict:new().
--spec map__insert({term(), term()}, map()) -> map().
+-spec map__insert({term(), term()}, map_dict()) -> map_dict().
map__insert(Object, Map) ->
{Key, Value} = Object,
dict:store(Key, Value, Map).
--spec map__lookup(term(), map()) -> term().
+-spec map__lookup(term(), map_dict()) -> term().
map__lookup(Key, Map) ->
try dict:fetch(Key, Map) catch error:_ -> none end.
--spec map__from_list([{fa(), term()}]) -> map().
+-spec map__from_list([{fa(), term()}]) -> map_dict().
map__from_list(List) ->
dict:from_list(List).
--spec map__remove(term(), map()) -> map().
+-spec map__remove(term(), map_dict()) -> map_dict().
map__remove(Key, Dict) ->
dict:erase(Key, Dict).
--spec map__fold(fun((term(), term(), term()) -> map()), map(), map()) -> map().
+-spec map__fold(fun((term(), term(), term()) -> map_dict()), map_dict(), map_dict()) -> map_dict().
map__fold(Fun, Acc0, Dict) ->
dict:fold(Fun, Acc0, Dict).
--
cgit v1.2.3
From 6868557c0244140ea7709822e8f7a0ba26029b21 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 25 Oct 2013 03:57:38 +0200
Subject: stdlib: Fixup id_transform_SUITE
---
lib/stdlib/examples/erl_id_trans.erl | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index 2c842fafc7..5fcb74310e 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -144,6 +144,13 @@ pattern({cons,Line,H0,T0}) ->
pattern({tuple,Line,Ps0}) ->
Ps1 = pattern_list(Ps0),
{tuple,Line,Ps1};
+pattern({map,Line,Ps0}) ->
+ Ps1 = pattern_list(Ps0),
+ {map,Line,Ps1};
+pattern({map_field_exact,Line,K,V}) ->
+ Ke = pattern(K),
+ Ve = pattern(V),
+ {map_field_exact,Line,Ke,Ve};
%%pattern({struct,Line,Tag,Ps0}) ->
%% Ps1 = pattern_list(Ps0),
%% {struct,Line,Tag,Ps1};
@@ -251,6 +258,20 @@ gexpr({float,Line,F}) -> {float,Line,F};
gexpr({atom,Line,A}) -> {atom,Line,A};
gexpr({string,Line,S}) -> {string,Line,S};
gexpr({nil,Line}) -> {nil,Line};
+gexpr({map,Line,Map0,Es0}) ->
+ [Map1|Es1] = gexpr_list([Map0|Es0]),
+ {map,Line,Map1,Es1};
+gexpr({map,Line,Es0}) ->
+ Es1 = gexpr_list(Es0),
+ {map,Line,Es1};
+gexpr({map_field_assoc,Line,K,V}) ->
+ Ke = gexpr(K),
+ Ve = gexpr(V),
+ {map_field_assoc,Line,Ke,Ve};
+gexpr({map_field_exact,Line,K,V}) ->
+ Ke = gexpr(K),
+ Ve = gexpr(V),
+ {map_field_exact,Line,Ke,Ve};
gexpr({cons,Line,H0,T0}) ->
H1 = gexpr(H0),
T1 = gexpr(T0), %They see the same variables
@@ -356,6 +377,20 @@ expr({bc,Line,E0,Qs0}) ->
expr({tuple,Line,Es0}) ->
Es1 = expr_list(Es0),
{tuple,Line,Es1};
+expr({map,Line,Map0,Es0}) ->
+ [Map1|Es1] = exprs([Map0|Es0]),
+ {map,Line,Map1,Es1};
+expr({map,Line,Es0}) ->
+ Es1 = exprs(Es0),
+ {map,Line,Es1};
+expr({map_field_assoc,Line,K,V}) ->
+ Ke = expr(K),
+ Ve = expr(V),
+ {map_field_assoc,Line,Ke,Ve};
+expr({map_field_exact,Line,K,V}) ->
+ Ke = expr(K),
+ Ve = expr(V),
+ {map_field_exact,Line,Ke,Ve};
%%expr({struct,Line,Tag,Es0}) ->
%% Es1 = pattern_list(Es0),
%% {struct,Line,Tag,Es1};
--
cgit v1.2.3
From 457b29c9ddd2b338e80916a5e7bad8dfe4b36ffc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 25 Oct 2013 16:28:50 +0200
Subject: erts,stdlib: Teach matchspec compiler map guards
---
erts/emulator/beam/erl_db_util.c | 12 ++++++++++++
lib/stdlib/src/ms_transform.erl | 2 ++
2 files changed, 14 insertions(+)
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index d903053aa4..3986ccd4d3 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -565,6 +565,12 @@ static DMCGuardBif guard_tab[] =
1,
DBIF_ALL
},
+ {
+ am_is_map,
+ &is_map_1,
+ 1,
+ DBIF_ALL
+ },
{
am_is_binary,
&is_binary_1,
@@ -631,6 +637,12 @@ static DMCGuardBif guard_tab[] =
1,
DBIF_ALL
},
+ {
+ am_map_size,
+ &map_size_1,
+ 1,
+ DBIF_ALL
+ },
{
am_bit_size,
&bit_size_1,
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index 25b04fe45e..27dfcf52e1 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -910,6 +910,7 @@ bool_test(is_pid,1) -> true;
bool_test(is_port,1) -> true;
bool_test(is_reference,1) -> true;
bool_test(is_tuple,1) -> true;
+bool_test(is_map,1) -> true;
bool_test(is_binary,1) -> true;
bool_test(is_function,1) -> true;
bool_test(is_record,2) -> true;
@@ -924,6 +925,7 @@ real_guard_function(node,0) -> true;
real_guard_function(node,1) -> true;
real_guard_function(round,1) -> true;
real_guard_function(size,1) -> true;
+real_guard_function(map_size,1) -> true;
real_guard_function(tl,1) -> true;
real_guard_function(trunc,1) -> true;
real_guard_function(self,0) -> true;
--
cgit v1.2.3
From e1095860776d76294c1577079b2f24e0688c8e04 Mon Sep 17 00:00:00 2001
From: Lukas Larsson
Date: Fri, 25 Oct 2013 14:38:50 +0200
Subject: Add tests for pditc, ets and tracing
---
erts/emulator/test/map_SUITE.erl | 143 +++++++++++++++++++++++++++++++++++++--
1 file changed, 138 insertions(+), 5 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 3b52295039..ea3483d09e 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -45,13 +45,18 @@
%% erlang
t_erlang_hash/1,
- t_map_encode_decode/1
- ]).
+ t_map_encode_decode/1,
--include_lib("test_server/include/test_server.hrl").
+ %% misc
+ t_pdict/1,
+ t_ets/1,
+ t_dets/1,
+ t_tracing/1
+ ]).
+-include_lib("stdlib/include/ms_transform.hrl").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [].
all() -> [
t_build_and_match_literals,
@@ -70,7 +75,12 @@ all() -> [
%% erlang
t_erlang_hash, t_map_encode_decode,
%t_size,
- t_map_size
+ t_map_size,
+
+ %% Other functions
+ t_pdict,
+ t_ets,
+ t_tracing
].
groups() -> [].
@@ -760,6 +770,129 @@ t_bif_map_from_list(Config) when is_list(Config) ->
{'EXIT', {badarg,_}} = (catch map:from_list(id(42))),
ok.
+%% MISC
+t_pdict(_Config) ->
+
+ put(#{ a => b, b => a},#{ c => d}),
+ put(get(#{ a => b, b => a}),1),
+ 1 = get(#{ c => d}),
+ #{ c := d } = get(#{ a => b, b => a}).
+
+t_ets(_Config) ->
+
+ Tid = ets:new(map_table,[]),
+
+ [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)],
+
+
+ [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }),
+
+ %% Test equal
+ [3,4] = lists:sort(
+ ets:select(Tid,[{{'$1','$2'},
+ [{'or',{'==','$1',#{ 3 => -3 }},
+ {'==','$1',#{ 4 => -4 }}}],
+ ['$2']}])),
+ %% Test match
+ [30,50] = lists:sort(
+ ets:select(Tid,
+ [{{#{ 30 => -30}, '$1'},[],['$1']},
+ {{#{ 50 => -50}, '$1'},[],['$1']}]
+ )),
+
+ ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}),
+
+ %% Test equal with map of different size
+ [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]),
+
+ %% Test match with map of different size
+ [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]),
+
+ %% Test match with don't care value
+ [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]),
+
+ %% Test is_map bif
+ ets:insert(Tid,{not_a_map,2}),
+ 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$_']}])),
+
+ %% Test map_size bif
+ [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}],
+ [{map_size,'$1'}]}]),
+
+ true = ets:delete(Tid,#{50 => -50}),
+ [] = ets:lookup(Tid,#{50 => -50}),
+
+ ets:delete(Tid),
+ ok.
+
+t_dets(_Config) ->
+ ok.
+
+t_tracing(_Config) ->
+
+ dbg:stop_clear(),
+ {ok,Tracer} = dbg:tracer(process,{fun trace_collector/2, self()}),
+ dbg:p(self(),c),
+
+ %% Test basic map call
+ {ok,_} = dbg:tpl(?MODULE,id,x),
+ id(#{ a => b }),
+ {trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
+ {trace,_,return_from,{?MODULE,id,1},#{ a := b }} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test equals in argument list
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==','$1',#{ b => c}}],
+ [{return_trace}]}]),
+ id(#{ a => b }),
+ id(#{ b => c }),
+ {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
+ {trace,_,return_from,{?MODULE,id,1},#{ b := c }} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test match in head
+ {ok,_} = dbg:tpl(?MODULE,id,[{[#{b => c}],[],[]}]),
+ id(#{ a => b }),
+ id(#{ b => c }),
+ {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test map guard bifs
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'or',{is_map,{element,1,'$1'}},
+ {'==',{map_size,'$1'},2}}],[]}]),
+ id(#{ a => b }),
+ id({1,2}),
+ id({#{ a => b},2}),
+ id(#{ a => b, b => c}),
+ {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer),
+ {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test fun2ms, DOES NOT COMPILE!!
+ %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end),
+ %dbg:tpl(?MODULE,id,MS),
+ %id(#{ a => b }),
+ %id(#{ b => c }),
+ %{trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
+ %dbg:ctpl(),
+
+ %% Check to extra messages
+ timeout = getmsg(Tracer),
+
+ dbg:stop_clear(),
+ ok.
+
+getmsg(_Tracer) ->
+ receive
+ V -> V
+ after 50 ->
+ timeout
+ end.
+
+trace_collector(Msg,Parent) ->
+ io:format("~p~n",[Msg]),
+ Parent ! Msg,
+ Parent.
%% Use this function to avoid compile-time evaluation of an expression.
id(I) -> I.
--
cgit v1.2.3
From f8345dc86a1787c3c8deb06befbb697f37db1c0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 25 Oct 2013 17:34:19 +0200
Subject: erts: Fix map_SUITE match spec tests
---
erts/emulator/test/map_SUITE.erl | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index ea3483d09e..69b68c4b67 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -806,14 +806,17 @@ t_ets(_Config) ->
[] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]),
%% Test match with map of different size
- [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]),
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]),
- %% Test match with don't care value
- [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]),
+ %%% Test match with don't care value
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]),
%% Test is_map bif
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
ets:insert(Tid,{not_a_map,2}),
- 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$_']}])),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+ ets:insert(Tid,{{nope,a,tuple},2}),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
%% Test map_size bif
[3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}],
@@ -857,18 +860,22 @@ t_tracing(_Config) ->
{trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
dbg:ctpl(),
- %% Test map guard bifs
- {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'or',{is_map,{element,1,'$1'}},
- {'==',{map_size,'$1'},2}}],[]}]),
+ % Test map guard bifs
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{is_map,{element,1,'$1'}}],[]}]),
id(#{ a => b }),
id({1,2}),
id({#{ a => b},2}),
- id(#{ a => b, b => c}),
{trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer),
- {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer),
dbg:ctpl(),
- %% Test fun2ms, DOES NOT COMPILE!!
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==',{map_size,{element,1,'$1'}},2}],[]}]),
+ id(#{ a => b }),
+ id({1,2}),
+ id({#{ a => b},2}),
+ id({#{ a => b, b => c},atom}),
+ {trace,_,call,{?MODULE,id,[{#{ a := b, b := c },atom}]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
%MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end),
%dbg:tpl(?MODULE,id,MS),
%id(#{ a => b }),
@@ -883,11 +890,7 @@ t_tracing(_Config) ->
ok.
getmsg(_Tracer) ->
- receive
- V -> V
- after 50 ->
- timeout
- end.
+ receive V -> V after 100 -> timeout end.
trace_collector(Msg,Parent) ->
io:format("~p~n",[Msg]),
--
cgit v1.2.3
From 6fdad74f41803089a0f9026c98f319daecda9a50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 24 Oct 2013 19:06:34 +0200
Subject: erts,stdlib: Change map module name to maps
Name conforms to EEP.
---
erts/emulator/beam/bif.tab | 24 ++--
erts/emulator/beam/erl_map.c | 82 ++++++-------
erts/emulator/test/map_SUITE.erl | 248 +++++++++++++++++++--------------------
lib/stdlib/src/Makefile | 2 +-
lib/stdlib/src/erl_eval.erl | 10 +-
lib/stdlib/src/erl_parse.yrl | 2 +-
lib/stdlib/src/io_lib.erl | 2 +-
lib/stdlib/src/map.erl | 215 ---------------------------------
lib/stdlib/src/maps.erl | 214 +++++++++++++++++++++++++++++++++
lib/stdlib/src/stdlib.app.src | 2 +-
10 files changed, 400 insertions(+), 401 deletions(-)
delete mode 100644 lib/stdlib/src/map.erl
create mode 100644 lib/stdlib/src/maps.erl
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 306861a4c5..b623e47b9a 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -580,18 +580,18 @@ bif re:inspect/2
ubif erlang:is_map/1
ubif erlang:map_size/1
-bif map:to_list/1
-bif map:find/2
-bif map:get/2
-bif map:from_list/1
-bif map:is_key/2
-bif map:keys/1
-bif map:merge/2
-bif map:new/0
-bif map:put/3
-bif map:remove/2
-bif map:update/3
-bif map:values/1
+bif maps:to_list/1
+bif maps:find/2
+bif maps:get/2
+bif maps:from_list/1
+bif maps:is_key/2
+bif maps:keys/1
+bif maps:merge/2
+bif maps:new/0
+bif maps:put/3
+bif maps:remove/2
+bif maps:update/3
+bif maps:values/1
#
# Obsolete
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 6fd60f0689..27734276f9 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -38,25 +38,25 @@
* - erlang:is_map/1
* - erlang:map_size/1
*
- * - map:find/2
- * - map:from_list/1
- * - map:get/2
- * - map:is_key/2
- * - map:keys/1
- * - map:merge/2
- * - map:new/0
- * - map:put/3
- * - map:remove/2
- * - map:to_list/1
- * - map:update/3
- * - map:values/1
+ * - maps:find/2
+ * - maps:from_list/1
+ * - maps:get/2
+ * - maps:is_key/2
+ * - maps:keys/1
+ * - maps:merge/2
+ * - maps:new/0
+ * - maps:put/3
+ * - maps:remove/2
+ * - maps:to_list/1
+ * - maps:update/3
+ * - maps:values/1
*
* TODO:
- * - map:foldl/3
- * - map:foldr/3
- * - map:map/3
- * - map:size/1
- * - map:without/2
+ * - maps:foldl/3
+ * - maps:foldr/3
+ * - maps:map/3
+ * - maps:size/1
+ * - maps:without/2
*
*/
@@ -80,10 +80,10 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) {
BIF_ERROR(BIF_P, BADARG);
}
-/* map:to_list/1
+/* maps:to_list/1
*/
-BIF_RETTYPE map_to_list_1(BIF_ALIST_1) {
+BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
if (is_map(BIF_ARG_1)) {
Uint n;
Eterm* hp;
@@ -107,11 +107,11 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) {
BIF_ERROR(BIF_P, BADARG);
}
-/* map:find/2
+/* maps:find/2
* return value if key *equals* a key in the map
*/
-BIF_RETTYPE map_find_2(BIF_ALIST_2) {
+BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
if (is_map(BIF_ARG_2)) {
Eterm *hp, *ks,*vs, key, res;
map_t *mp;
@@ -137,12 +137,12 @@ BIF_RETTYPE map_find_2(BIF_ALIST_2) {
}
BIF_ERROR(BIF_P, BADARG);
}
-/* map:get/2
+/* maps:get/2
* return value if key *matches* a key in the map
* exception bad_key if none matches
*/
-BIF_RETTYPE map_get_2(BIF_ALIST_2) {
+BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
if (is_map(BIF_ARG_2)) {
Eterm *hp, *ks,*vs, key, error;
map_t *mp;
@@ -184,11 +184,11 @@ error:
BIF_ERROR(BIF_P, BADARG);
}
-/* map:from_list/1
+/* maps:from_list/1
* List may be unsorted [{K,V}]
*/
-BIF_RETTYPE map_from_list_1(BIF_ALIST_1) {
+BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) {
Eterm *kv, item = BIF_ARG_1;
Eterm *hp, *thp,*vs, *ks, keys, res;
map_t *mp;
@@ -305,10 +305,10 @@ error:
BIF_ERROR(BIF_P, BADARG);
}
-/* map:is_key/2
+/* maps:is_key/2
*/
-BIF_RETTYPE map_is_key_2(BIF_ALIST_2) {
+BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
if (is_map(BIF_ARG_2)) {
Eterm *ks, key;
map_t *mp;
@@ -340,10 +340,10 @@ BIF_RETTYPE map_is_key_2(BIF_ALIST_2) {
BIF_ERROR(BIF_P, BADARG);
}
-/* map:keys/1
+/* maps:keys/1
*/
-BIF_RETTYPE map_keys_1(BIF_ALIST_1) {
+BIF_RETTYPE maps_keys_1(BIF_ALIST_1) {
if (is_map(BIF_ARG_1)) {
Eterm *hp, *ks, res = NIL;
map_t *mp;
@@ -366,10 +366,10 @@ BIF_RETTYPE map_keys_1(BIF_ALIST_1) {
}
BIF_ERROR(BIF_P, BADARG);
}
-/* map:merge/2
+/* maps:merge/2
*/
-BIF_RETTYPE map_merge_2(BIF_ALIST_2) {
+BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) {
Eterm *hp,*thp;
Eterm tup;
@@ -452,10 +452,10 @@ BIF_RETTYPE map_merge_2(BIF_ALIST_2) {
}
BIF_ERROR(BIF_P, BADARG);
}
-/* map:new/2
+/* maps:new/2
*/
-BIF_RETTYPE map_new_0(BIF_ALIST_0) {
+BIF_RETTYPE maps_new_0(BIF_ALIST_0) {
Eterm* hp;
Eterm tup;
map_t *mp;
@@ -472,10 +472,10 @@ BIF_RETTYPE map_new_0(BIF_ALIST_0) {
BIF_RET(make_map(mp));
}
-/* map:put/3
+/* maps:put/3
*/
-BIF_RETTYPE map_put_3(BIF_ALIST_3) {
+BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
if (is_map(BIF_ARG_3)) {
Sint n,i;
Sint c = 0;
@@ -582,10 +582,10 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) {
BIF_ERROR(BIF_P, BADARG);
}
-/* map:remove/3
+/* maps:remove/3
*/
-BIF_RETTYPE map_remove_2(BIF_ALIST_2) {
+BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
if (is_map(BIF_ARG_2)) {
Sint n;
Sint found = 0;
@@ -656,10 +656,10 @@ BIF_RETTYPE map_remove_2(BIF_ALIST_2) {
BIF_ERROR(BIF_P, BADARG);
}
-/* map:update/3
+/* maps:update/3
*/
-BIF_RETTYPE map_update_3(BIF_ALIST_3) {
+BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
if (is_map(BIF_ARG_3)) {
Sint n,i;
Sint found = 0;
@@ -719,10 +719,10 @@ BIF_RETTYPE map_update_3(BIF_ALIST_3) {
}
-/* map:values/1
+/* maps:values/1
*/
-BIF_RETTYPE map_values_1(BIF_ALIST_1) {
+BIF_RETTYPE maps_values_1(BIF_ALIST_1) {
if (is_map(BIF_ARG_1)) {
Eterm *hp, *vs, res = NIL;
map_t *mp;
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 69b68c4b67..59fdb82f50 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -412,182 +412,182 @@ t_map_sort_literals(Config) when is_list(Config) ->
%% BIFs
t_bif_map_get(Config) when is_list(Config) ->
- 1 = map:get(a, #{ a=> 1}),
- 2 = map:get(b, #{ a=> 1, b => 2}),
- "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}),
- "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+ 1 = maps:get(a, #{ a=> 1}),
+ 2 = maps:get(b, #{ a=> 1, b => 2}),
+ "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}),
+ "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
- "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }),
+ "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }),
%% error case
- {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])),
- {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)),
- {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})),
- {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})),
- {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})),
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])),
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})),
ok.
t_bif_map_find(Config) when is_list(Config) ->
- {ok, 1} = map:find(a, #{ a=> 1}),
- {ok, 2} = map:find(b, #{ a=> 1, b => 2}),
- {ok, "int"} = map:find(1, #{ 1 => "int"}),
- {ok, "int"} = map:find(1.0, #{ 1 => "int"}),
- {ok, "float"} = map:find(1, #{ 1.0 => "float"}),
- {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}),
+ {ok, 1} = maps:find(a, #{ a=> 1}),
+ {ok, 2} = maps:find(b, #{ a=> 1, b => 2}),
+ {ok, "int"} = maps:find(1, #{ 1 => "int"}),
+ {ok, "int"} = maps:find(1.0, #{ 1 => "int"}),
+ {ok, "float"} = maps:find(1, #{ 1.0 => "float"}),
+ {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}),
- {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}),
- {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
+ {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}),
+ {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
- {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
+ {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
%% error case
- error = map:find(a,#{}),
- error = map:find(a,#{b=>1, c=>2}),
+ error = maps:find(a,#{}),
+ error = maps:find(a,#{b=>1, c=>2}),
- {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])),
- {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)),
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])),
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)),
ok.
t_bif_map_is_key(Config) when is_list(Config) ->
M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
- true = map:is_key("hi", M1),
- true = map:is_key(int, M1),
- true = map:is_key(<<"key">>, M1),
- true = map:is_key(4, M1),
-
- false = map:is_key(5, M1),
- false = map:is_key(<<"key2">>, M1),
- false = map:is_key("h", M1),
- false = map:is_key("hello", M1),
- false = map:is_key(atom, M1),
-
- false = map:is_key("hi", map:remove("hi", M1)),
- true = map:is_key("hi", M1),
- true = map:is_key(1, map:put(1, "number", M1)),
- false = map:is_key(1.0, map:put(1, "number", M1)),
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(int, M1),
+ true = maps:is_key(<<"key">>, M1),
+ true = maps:is_key(4, M1),
+
+ false = maps:is_key(5, M1),
+ false = maps:is_key(<<"key2">>, M1),
+ false = maps:is_key("h", M1),
+ false = maps:is_key("hello", M1),
+ false = maps:is_key(atom, M1),
+
+ false = maps:is_key("hi", maps:remove("hi", M1)),
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(1, maps:put(1, "number", M1)),
+ false = maps:is_key(1.0, maps:put(1, "number", M1)),
ok.
t_bif_map_keys(Config) when is_list(Config) ->
- [] = map:keys(#{}),
+ [] = maps:keys(#{}),
- [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
- [1,2,3,4,5] = map:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+ [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
% values in key order: [4,int,"hi",<<"key">>]
M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
- [4,int,"hi",<<"key">>] = map:keys(M1),
+ [4,int,"hi",<<"key">>] = maps:keys(M1),
%% error case
- {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)),
- {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)),
- {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)),
- {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])),
- {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)),
ok.
t_bif_map_new(Config) when is_list(Config) ->
- #{} = map:new(),
- 0 = erlang:map_size(map:new()),
+ #{} = maps:new(),
+ 0 = erlang:map_size(maps:new()),
ok.
t_bif_map_put(Config) when is_list(Config) ->
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
4 => number, 18446744073709551629 => wat},
- M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}),
+ M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}),
- ["hi"] = map:keys(M1),
- ["hello"] = map:values(M1),
+ ["hi"] = maps:keys(M1),
+ ["hello"] = maps:values(M1),
- M2 = #{ int := 3 } = map:put(int, 3, M1),
+ M2 = #{ int := 3 } = maps:put(int, 3, M1),
- [int,"hi"] = map:keys(M2),
- [3,"hello"] = map:values(M2),
+ [int,"hi"] = maps:keys(M2),
+ [3,"hello"] = maps:values(M2),
- M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2),
+ M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2),
- [int,"hi",<<"key">>] = map:keys(M3),
- [3,"hello",<<"value">>] = map:values(M3),
+ [int,"hi",<<"key">>] = maps:keys(M3),
+ [3,"hello",<<"value">>] = maps:values(M3),
- M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3),
+ M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3),
- [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4),
- [wat,3,"hello",<<"value">>] = map:values(M4),
+ [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4),
+ [wat,3,"hello",<<"value">>] = maps:values(M4),
- M0 = #{ 4 := number } = M5 = map:put(4, number, M4),
+ M0 = #{ 4 := number } = M5 = maps:put(4, number, M4),
- [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5),
- [number,wat,3,"hello",<<"value">>] = map:values(M5),
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5),
+ [number,wat,3,"hello",<<"value">>] = maps:values(M5),
%% error case
- {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)),
- {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)),
- {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)),
- {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])),
- {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)),
ok.
t_bif_map_remove(Config) when is_list(Config) ->
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
4 => number, 18446744073709551629 => wat},
- M1 = map:remove("hi", M0),
- [4,18446744073709551629,int,<<"key">>] = map:keys(M1),
- [number,wat,3,<<"value">>] = map:values(M1),
+ M1 = maps:remove("hi", M0),
+ [4,18446744073709551629,int,<<"key">>] = maps:keys(M1),
+ [number,wat,3,<<"value">>] = maps:values(M1),
- M2 = map:remove(int, M1),
- [4,18446744073709551629,<<"key">>] = map:keys(M2),
- [number,wat,<<"value">>] = map:values(M2),
+ M2 = maps:remove(int, M1),
+ [4,18446744073709551629,<<"key">>] = maps:keys(M2),
+ [number,wat,<<"value">>] = maps:values(M2),
- M3 = map:remove(<<"key">>, M2),
- [4,18446744073709551629] = map:keys(M3),
- [number,wat] = map:values(M3),
+ M3 = maps:remove(<<"key">>, M2),
+ [4,18446744073709551629] = maps:keys(M3),
+ [number,wat] = maps:values(M3),
- M4 = map:remove(18446744073709551629, M3),
- [4] = map:keys(M4),
- [number] = map:values(M4),
+ M4 = maps:remove(18446744073709551629, M3),
+ [4] = maps:keys(M4),
+ [number] = maps:values(M4),
- M5 = map:remove(4, M4),
- [] = map:keys(M5),
- [] = map:values(M5),
+ M5 = maps:remove(4, M4),
+ [] = maps:keys(M5),
+ [] = maps:values(M5),
- M0 = map:remove(5,M0),
- M0 = map:remove("hi there",M0),
+ M0 = maps:remove(5,M0),
+ M0 = maps:remove("hi there",M0),
- #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)),
+ #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)),
%% error case
- {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)),
- {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)),
- {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)),
- {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])),
- {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)),
ok.
t_bif_map_values(Config) when is_list(Config) ->
- [] = map:values(#{}),
+ [] = maps:values(#{}),
- [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
- [a,b,c,d,e] = map:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+ [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
% values in key order: [4,int,"hi",<<"key">>]
M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
- [number,3,"hello2",<<"value2">>] = map:values(M2),
- [number,3,"hello",<<"value">>] = map:values(M1),
+ [number,3,"hello2",<<"value2">>] = maps:values(M2),
+ [number,3,"hello",<<"value">>] = maps:values(M1),
%% error case
- {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)),
- {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)),
- {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])),
- {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)),
ok.
t_erlang_hash(Config) when is_list(Config) ->
@@ -610,7 +610,7 @@ t_bif_erlang_phash2() ->
44049159 = erlang:phash2(#{<<>> => {}}),
M0 = #{ a => 1, "key" => <<"value">> },
- M1 = map:remove("key",M0),
+ M1 = maps:remove("key",M0),
M2 = M1#{ "key" => <<"value">> },
118679416 = erlang:phash2(M0),
@@ -630,7 +630,7 @@ t_bif_erlang_phash() ->
1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken
M0 = #{ a => 1, "key" => <<"value">> },
- M1 = map:remove("key",M0),
+ M1 = maps:remove("key",M0),
M2 = M1#{ "key" => <<"value">> },
3590546636 = erlang:phash(M0,Sz),
@@ -650,7 +650,7 @@ t_bif_erlang_hash() ->
101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken
M0 = #{ a => 1, "key" => <<"value">> },
- M1 = map:remove("key",M0),
+ M1 = maps:remove("key",M0),
M2 = M1#{ "key" => <<"value">> },
38260486 = erlang:hash(M0,Sz),
@@ -706,7 +706,7 @@ t_map_encode_decode(Config) when is_list(Config) ->
ok.
map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
- M1 = map:put(K,V,M0),
+ M1 = maps:put(K,V,M0),
B0 = erlang:term_to_binary(M1),
Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
%% sort Ks and Vs according to term spec, then match it
@@ -729,45 +729,45 @@ match_encoded_map(Bin,[<<131,Item/binary>>|Items]) ->
t_bif_map_to_list(Config) when is_list(Config) ->
- [] = map:to_list(#{}),
- [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}),
- [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}),
- [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}),
- [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}),
- [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{
+ [] = maps:to_list(#{}),
+ [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}),
+ [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}),
+ [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{
<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}),
- [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = map:to_list(#{
+ [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{
<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
<<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}),
%% error cases
- {'EXIT', {badarg,_}} = (catch map:to_list(id(a))),
- {'EXIT', {badarg,_}} = (catch map:to_list(id(42))),
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))),
ok.
t_bif_map_from_list(Config) when is_list(Config) ->
- #{} = map:from_list([]),
- A = map:from_list([]),
+ #{} = maps:from_list([]),
+ A = maps:from_list([]),
0 = erlang:map_size(A),
- #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]),
- #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]),
- #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]),
+ #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]),
+ #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]),
+ #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]),
- #{a:=2} = map:from_list([{a,1},{a,3},{a,2}]),
+ #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]),
#{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} =
- map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]),
+ maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]),
#{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} =
- map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2},
+ maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2},
{<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]),
%% error cases
- {'EXIT', {badarg,_}} = (catch map:from_list(id(a))),
- {'EXIT', {badarg,_}} = (catch map:from_list(id(42))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
ok.
%% MISC
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 376083c7d6..9ab2cd4134 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -91,7 +91,7 @@ MODULES= \
lib \
lists \
log_mf_h \
- map \
+ maps \
math \
ms_transform \
otp_internal \
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index fcd1945c63..5f96795d92 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -253,14 +253,14 @@ expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) ->
{value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs),
{Vs,Bs} = expr_list(Es, Bs1, Lf, Ef),
ret_expr(lists:foldl(fun
- ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi);
- ({map_exact,K,V}, Mi) -> map:update(K,V,Mi)
+ ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
+ ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi)
end, Map0, Vs), Bs, RBs);
expr({map,_,Es}, Bs0, Lf, Ef, RBs) ->
{Vs,Bs} = expr_list(Es, Bs0, Lf, Ef),
ret_expr(lists:foldl(fun
- ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi)
- end, map:new(), Vs), Bs, RBs);
+ ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi)
+ end, maps:new(), Vs), Bs, RBs);
expr({block,_,Es}, Bs, Lf, Ef, RBs) ->
exprs(Es, Bs, Lf, Ef, RBs);
@@ -1148,7 +1148,7 @@ match_tuple([], _, _, Bs, _BBs) ->
match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs) ->
Vm = try
{value, Ke, _} = expr(K, new_bindings()),
- map:get(Ke,Map)
+ maps:get(Ke,Map)
catch error:_ ->
throw(nomatch)
end,
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 6d9aafa980..6316db7054 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -896,7 +896,7 @@ normalise({cons,_,Head,Tail}) ->
normalise({tuple,_,Args}) ->
list_to_tuple(normalise_list(Args));
normalise({map,_,Pairs}=M) ->
- map:from_list(lists:map(fun
+ maps:from_list(lists:map(fun
%% only allow '=>'
({map_field_assoc,_,K,V}) -> {normalise(K),normalise(V)};
(_) -> erlang:error({badarg,M})
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 886939761c..adc9a0cf5f 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -278,7 +278,7 @@ write_ref(Ref) ->
erlang:ref_to_list(Ref).
write_map(Map, D) when is_integer(D) ->
- [$#,${,write_map_body(map:to_list(Map), D),$}].
+ [$#,${,write_map_body(maps:to_list(Map), D),$}].
write_map_body(_, 0) -> "...";
write_map_body([],_) -> [];
diff --git a/lib/stdlib/src/map.erl b/lib/stdlib/src/map.erl
deleted file mode 100644
index 6257a90180..0000000000
--- a/lib/stdlib/src/map.erl
+++ /dev/null
@@ -1,215 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(map).
-
--export([
- foldl/3,
- foldr/3,
- map/2,
- size/1,
- without/2
- ]).
-
-
-%%% BIFs
--export([
- get/2,
- find/2,
- from_list/1,
- is_key/2,
- keys/1,
- merge/2,
- new/0,
- put/3,
- remove/2,
- to_list/1,
- update/3,
- values/1
- ]).
-
--type map() :: term(). %% FIXME: remove when erl_bif_types knows map().
-
-%% Shadowed by erl_bif_types: map:get/3
--spec get(Key,Map) -> Value when
- Key :: term(),
- Map :: map(),
- Value :: term().
-
-get(_,_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:find/3
--spec find(Key,Map) -> {ok, Value} | error when
- Key :: term(),
- Map :: map(),
- Value :: term().
-
-find(_,_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:from_list/1
--spec from_list([{Key,Value}]) -> Map when
- Key :: term(),
- Value :: term(),
- Map :: map().
-
-from_list(_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:is_key/2
--spec is_key(Key,Map) -> boolean() when
- Key :: term(),
- Map :: map().
-
-is_key(_,_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:keys/1
--spec keys(Map) -> Keys when
- Map :: map(),
- Keys :: [Key],
- Key :: term().
-
-keys(_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:merge/2
--spec merge(Map1,Map2) -> Map3 when
- Map1 :: map(),
- Map2 :: map(),
- Map3 :: map().
-
-merge(_,_) -> erlang:nif_error(undef).
-
-
-
-%% Shadowed by erl_bif_types: map:new/0
--spec new() -> Map when
- Map :: map().
-
-new() -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:put/3
--spec put(Key,Value,Map1) -> Map2 when
- Key :: term(),
- Value :: term(),
- Map1 :: map(),
- Map2 :: map().
-
-put(_,_,_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:put/3
--spec remove(Key,Map1) -> Map2 when
- Key :: term(),
- Map1 :: map(),
- Map2 :: map().
-
-remove(_,_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:to_list/1
--spec to_list(Map) -> [{Key,Value}] when
- Map :: map(),
- Key :: term(),
- Value :: term().
-
-to_list(_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:update/3
--spec update(Key,Value,Map1) -> Map2 when
- Key :: term(),
- Value :: term(),
- Map1 :: map(),
- Map2 :: map().
-
-update(_,_,_) -> erlang:nif_error(undef).
-
-
-%% Shadowed by erl_bif_types: map:values/1
--spec values(Map) -> Keys when
- Map :: map(),
- Keys :: [Key],
- Key :: term().
-
-values(_) -> erlang:nif_error(undef).
-
-
-%%% End of BIFs
-
--spec foldl(Fun,Init,Map) -> Acc when
- Fun :: fun((K, V, AccIn) -> AccOut),
- Init :: term(),
- Acc :: term(),
- AccIn :: term(),
- AccOut :: term(),
- Map :: map(),
- K :: term(),
- V :: term().
-
-foldl(Fun, Init, Map) ->
- lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)).
-
--spec foldr(Fun,Init,Map) -> Acc when
- Fun :: fun((K,V,AccIn) -> AccOut),
- Init :: term(),
- Acc :: term(),
- AccIn :: term(),
- AccOut :: term(),
- Map :: map(),
- K :: term(),
- V :: term().
-
-
-foldr(Fun, Init, Map) ->
- lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)).
-
-
--spec map(Fun,Map1) -> Map2 when
- Fun :: fun((K, V1) -> V2),
- Map1 :: map(),
- Map2 :: map(),
- K :: term(),
- V1 :: term(),
- V2 :: term().
-
-
-map(Fun, Map) ->
- map:from_list(lists:map(fun
- ({K,V}) ->
- {K,Fun(K,V)}
- end,map:to_list(Map))).
-
--spec size(Map) -> non_neg_integer() when
- Map :: map().
-
-size(Map) ->
- erlang:map_size(Map).
-
--spec without(Ks,Map1) -> Map2 when
- Ks :: [K],
- Map1 :: map(),
- Map2 :: map(),
- K :: term().
-
-without(Ks, M) ->
- map:from_list([{K,V}||{K,V} <- map:to_list(M), not lists:member(K, Ks)]).
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
new file mode 100644
index 0000000000..218dc28bec
--- /dev/null
+++ b/lib/stdlib/src/maps.erl
@@ -0,0 +1,214 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(maps).
+
+-export([
+ foldl/3,
+ foldr/3,
+ map/2,
+ size/1,
+ without/2
+ ]).
+
+
+%%% BIFs
+-export([
+ get/2,
+ find/2,
+ from_list/1,
+ is_key/2,
+ keys/1,
+ merge/2,
+ new/0,
+ put/3,
+ remove/2,
+ to_list/1,
+ update/3,
+ values/1
+ ]).
+
+-type map() :: term(). %% FIXME: remove when erl_bif_types knows map().
+
+%% Shadowed by erl_bif_types: maps:get/3
+-spec get(Key,Map) -> Value when
+ Key :: term(),
+ Map :: map(),
+ Value :: term().
+
+get(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:find/3
+-spec find(Key,Map) -> {ok, Value} | error when
+ Key :: term(),
+ Map :: map(),
+ Value :: term().
+
+find(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:from_list/1
+-spec from_list([{Key,Value}]) -> Map when
+ Key :: term(),
+ Value :: term(),
+ Map :: map().
+
+from_list(_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:is_key/2
+-spec is_key(Key,Map) -> boolean() when
+ Key :: term(),
+ Map :: map().
+
+is_key(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:keys/1
+-spec keys(Map) -> Keys when
+ Map :: map(),
+ Keys :: [Key],
+ Key :: term().
+
+keys(_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:merge/2
+-spec merge(Map1,Map2) -> Map3 when
+ Map1 :: map(),
+ Map2 :: map(),
+ Map3 :: map().
+
+merge(_,_) -> erlang:nif_error(undef).
+
+
+
+%% Shadowed by erl_bif_types: maps:new/0
+-spec new() -> Map when
+ Map :: map().
+
+new() -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:put/3
+-spec put(Key,Value,Map1) -> Map2 when
+ Key :: term(),
+ Value :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+put(_,_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:put/3
+-spec remove(Key,Map1) -> Map2 when
+ Key :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+remove(_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:to_list/1
+-spec to_list(Map) -> [{Key,Value}] when
+ Map :: map(),
+ Key :: term(),
+ Value :: term().
+
+to_list(_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:update/3
+-spec update(Key,Value,Map1) -> Map2 when
+ Key :: term(),
+ Value :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+update(_,_,_) -> erlang:nif_error(undef).
+
+
+%% Shadowed by erl_bif_types: maps:values/1
+-spec values(Map) -> Keys when
+ Map :: map(),
+ Keys :: [Key],
+ Key :: term().
+
+values(_) -> erlang:nif_error(undef).
+
+
+%%% End of BIFs
+
+-spec foldl(Fun,Init,Map) -> Acc when
+ Fun :: fun((K, V, AccIn) -> AccOut),
+ Init :: term(),
+ Acc :: term(),
+ AccIn :: term(),
+ AccOut :: term(),
+ Map :: map(),
+ K :: term(),
+ V :: term().
+
+foldl(Fun, Init, Map) ->
+ lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)).
+
+-spec foldr(Fun,Init,Map) -> Acc when
+ Fun :: fun((K,V,AccIn) -> AccOut),
+ Init :: term(),
+ Acc :: term(),
+ AccIn :: term(),
+ AccOut :: term(),
+ Map :: map(),
+ K :: term(),
+ V :: term().
+
+foldr(Fun, Init, Map) ->
+ lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)).
+
+
+-spec map(Fun,Map1) -> Map2 when
+ Fun :: fun((K, V1) -> V2),
+ Map1 :: map(),
+ Map2 :: map(),
+ K :: term(),
+ V1 :: term(),
+ V2 :: term().
+
+map(Fun, Map) ->
+ maps:from_list(lists:map(fun
+ ({K,V}) ->
+ {K,Fun(K,V)}
+ end,maps:to_list(Map))).
+
+
+-spec size(Map) -> non_neg_integer() when
+ Map :: map().
+
+size(Map) ->
+ erlang:map_size(Map).
+
+-spec without(Ks,Map1) -> Map2 when
+ Ks :: [K],
+ Map1 :: map(),
+ Map2 :: map(),
+ K :: term().
+
+without(Ks, M) ->
+ maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]).
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index 9a77ae9d66..a64b8e13c0 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -71,7 +71,7 @@
lib,
lists,
log_mf_h,
- map,
+ maps,
math,
ms_transform,
orddict,
--
cgit v1.2.3
From 7e42aefdecae6cc91bdaaef21924b4e7cb75861c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 30 Oct 2013 16:09:29 +0100
Subject: erts: Add tests for non BIFs in Maps module
* maps:without/2
* maps:foldl/3
* maps:foldr/3
* maps:map/2
* maps:size/1
---
erts/emulator/test/map_SUITE.erl | 55 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 54 insertions(+), 1 deletion(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 59fdb82f50..b6cfd668f1 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -47,6 +47,12 @@
t_erlang_hash/1,
t_map_encode_decode/1,
+ %% maps module not bifs
+ t_maps_fold/1,
+ t_maps_map/1,
+ t_maps_size/1,
+ t_maps_without/1,
+
%% misc
t_pdict/1,
t_ets/1,
@@ -74,9 +80,13 @@ all() -> [
%% erlang
t_erlang_hash, t_map_encode_decode,
- %t_size,
t_map_size,
+ %% maps module
+ t_maps_fold, t_maps_map,
+ t_maps_size, t_maps_without,
+
+
%% Other functions
t_pdict,
t_ets,
@@ -770,6 +780,49 @@ t_bif_map_from_list(Config) when is_list(Config) ->
{'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
ok.
+%% Maps module, not BIFs
+t_maps_fold(_Config) ->
+
+ Vs = lists:seq(1,100),
+ Rs = lists:reverse(Vs),
+ M = maps:from_list([{{k,I},{v,I}}||I<-Vs]),
+
+ %% foldl
+ 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M),
+ Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M),
+
+ %% foldr
+ 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M),
+ Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], M),
+
+ ok.
+
+t_maps_map(_Config) ->
+ Vs = lists:seq(1,100),
+ M1 = maps:from_list([{I,I}||I<-Vs]),
+ M2 = maps:from_list([{I,{token,I}}||I<-Vs]),
+
+ M2 = maps:map(fun(_K,V) -> {token,V} end, M1),
+ ok.
+
+t_maps_size(_Config) ->
+ Vs = lists:seq(1,100),
+ lists:foldl(fun(I,M) ->
+ M1 = maps:put(I,I,M),
+ I = maps:size(M1),
+ M1
+ end, #{}, Vs),
+ ok.
+
+
+t_maps_without(_Config) ->
+ Ki = [11,22,33,44,55,66,77,88,99],
+ M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]),
+ M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]),
+ M1 = maps:without([{k,I}||I <- Ki],M0),
+ ok.
+
+
%% MISC
t_pdict(_Config) ->
--
cgit v1.2.3
From 105dcb3eb7c6d5fd07cd76cd495230b0a1d1e7c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 30 Oct 2013 17:05:43 +0100
Subject: erts: Add tests for Map update on expressions
(foo())#{ k1 := V1, k2 => V2 }
---
erts/emulator/test/map_SUITE.erl | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index b6cfd668f1..a06cb43ac7 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -23,6 +23,7 @@
-export([
t_build_and_match_literals/1,
t_update_literals/1,t_match_and_update_literals/1,
+ t_update_map_expressions/1,
t_update_assoc/1,t_update_exact/1,
t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
t_guard_receive/1, t_guard_fun/1,
@@ -67,6 +68,7 @@ suite() -> [].
all() -> [
t_build_and_match_literals,
t_update_literals, t_match_and_update_literals,
+ t_update_map_expressions,
t_update_assoc,t_update_exact,
t_guard_bifs, t_guard_sequence, t_guard_update,
t_guard_receive,t_guard_fun, t_list_comprehension,
@@ -137,6 +139,7 @@ t_build_and_match_literals(Config) when is_list(Config) ->
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
ok.
+
%% Tests size(Map).
%% not implemented, perhaps it shouldn't be either
@@ -163,7 +166,7 @@ t_map_size(Config) when is_list(Config) ->
true = map_is_size(M#{ "a" => 2}, 2),
false = map_is_size(M#{ "c" => 2}, 2),
- %% Error cases.
+ %% Error cases.
{'EXIT',{badarg,_}} = (catch map_size([])),
{'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
{'EXIT',{badarg,_}} = (catch map_size(1)),
@@ -204,6 +207,22 @@ loop_match_and_update_literals_x_q(Map, []) -> Map;
loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+t_update_map_expressions(Config) when is_list(Config) ->
+ M = maps:new(),
+ #{ a := 1 } = M#{a => 1},
+
+ #{ b := 2 } = (maps:new())#{ b => 2 },
+
+ #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
+ #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
+
+ %% Error cases, FIXME: should be 'badmap'?
+ {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ ok.
+
+
t_update_assoc(Config) when is_list(Config) ->
M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
--
cgit v1.2.3
From 94963032a2bb7bd5bfe5ea05d99ca507e67e7ad7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 6 Nov 2013 11:56:58 +0100
Subject: stdlib: Remove bogus map() type
---
lib/stdlib/src/maps.erl | 2 --
1 file changed, 2 deletions(-)
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 218dc28bec..59ac80b46c 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -44,8 +44,6 @@
values/1
]).
--type map() :: term(). %% FIXME: remove when erl_bif_types knows map().
-
%% Shadowed by erl_bif_types: maps:get/3
-spec get(Key,Map) -> Value when
Key :: term(),
--
cgit v1.2.3
From 039d3166bbe8d81b2f3bf72e4ade708b9ae5f2f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 17 Dec 2013 16:57:40 +0100
Subject: Update primary bootstrap
---
bootstrap/bin/start.boot | Bin 5250 -> 5264 bytes
bootstrap/bin/start_clean.boot | Bin 5250 -> 5264 bytes
bootstrap/lib/compiler/ebin/beam_a.beam | Bin 2288 -> 2396 bytes
bootstrap/lib/compiler/ebin/beam_block.beam | Bin 15048 -> 15260 bytes
bootstrap/lib/compiler/ebin/beam_clean.beam | Bin 9476 -> 11256 bytes
bootstrap/lib/compiler/ebin/beam_disasm.beam | Bin 25124 -> 26140 bytes
bootstrap/lib/compiler/ebin/beam_flatten.beam | Bin 2924 -> 3092 bytes
bootstrap/lib/compiler/ebin/beam_jump.beam | Bin 8568 -> 8700 bytes
bootstrap/lib/compiler/ebin/beam_opcodes.beam | Bin 6812 -> 7064 bytes
bootstrap/lib/compiler/ebin/beam_split.beam | Bin 2048 -> 2380 bytes
bootstrap/lib/compiler/ebin/beam_utils.beam | Bin 13348 -> 13392 bytes
bootstrap/lib/compiler/ebin/beam_validator.beam | Bin 34488 -> 34984 bytes
bootstrap/lib/compiler/ebin/beam_z.beam | Bin 2080 -> 2232 bytes
bootstrap/lib/compiler/ebin/cerl.beam | Bin 30084 -> 31576 bytes
bootstrap/lib/compiler/ebin/cerl_trees.beam | Bin 18952 -> 20272 bytes
bootstrap/lib/compiler/ebin/compile.beam | Bin 38164 -> 38720 bytes
bootstrap/lib/compiler/ebin/compiler.appup | 2 +-
bootstrap/lib/compiler/ebin/core_lib.beam | Bin 5460 -> 5812 bytes
bootstrap/lib/compiler/ebin/core_lint.beam | Bin 11644 -> 12372 bytes
bootstrap/lib/compiler/ebin/core_parse.beam | Bin 37664 -> 42808 bytes
bootstrap/lib/compiler/ebin/core_pp.beam | Bin 12064 -> 12748 bytes
bootstrap/lib/compiler/ebin/sys_core_dsetel.beam | Bin 6896 -> 7400 bytes
bootstrap/lib/compiler/ebin/sys_core_fold.beam | Bin 47644 -> 48840 bytes
bootstrap/lib/compiler/ebin/sys_pre_expand.beam | Bin 13620 -> 14216 bytes
bootstrap/lib/compiler/ebin/v3_codegen.beam | Bin 51524 -> 55688 bytes
bootstrap/lib/compiler/ebin/v3_core.beam | Bin 51880 -> 53520 bytes
bootstrap/lib/compiler/ebin/v3_kernel.beam | Bin 42880 -> 44892 bytes
bootstrap/lib/compiler/ebin/v3_kernel_pp.beam | Bin 11780 -> 12320 bytes
bootstrap/lib/compiler/ebin/v3_life.beam | Bin 19992 -> 20856 bytes
bootstrap/lib/kernel/ebin/kernel.appup | 6 +++---
bootstrap/lib/stdlib/ebin/erl_eval.beam | Bin 29232 -> 30492 bytes
bootstrap/lib/stdlib/ebin/erl_expand_records.beam | Bin 21872 -> 22428 bytes
bootstrap/lib/stdlib/ebin/erl_internal.beam | Bin 5200 -> 5236 bytes
bootstrap/lib/stdlib/ebin/erl_lint.beam | Bin 84628 -> 85972 bytes
bootstrap/lib/stdlib/ebin/erl_parse.beam | Bin 69620 -> 74708 bytes
bootstrap/lib/stdlib/ebin/erl_scan.beam | Bin 30064 -> 30124 bytes
bootstrap/lib/stdlib/ebin/io_lib.beam | Bin 9472 -> 9856 bytes
bootstrap/lib/stdlib/ebin/maps.beam | Bin 0 -> 1816 bytes
bootstrap/lib/stdlib/ebin/ms_transform.beam | Bin 20360 -> 20384 bytes
bootstrap/lib/stdlib/ebin/stdlib.app | 1 +
bootstrap/lib/stdlib/ebin/stdlib.appup | 6 +++---
41 files changed, 8 insertions(+), 7 deletions(-)
create mode 100644 bootstrap/lib/stdlib/ebin/maps.beam
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index 6f7fb5d7ca..f5800105d3 100644
Binary files a/bootstrap/bin/start.boot and b/bootstrap/bin/start.boot differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index 6f7fb5d7ca..f5800105d3 100644
Binary files a/bootstrap/bin/start_clean.boot and b/bootstrap/bin/start_clean.boot differ
diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam
index a4965abd48..459db28f43 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_a.beam and b/bootstrap/lib/compiler/ebin/beam_a.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index 6513a0f33b..c161220c93 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_block.beam and b/bootstrap/lib/compiler/ebin/beam_block.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index abe390267e..f6e3059e86 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_clean.beam and b/bootstrap/lib/compiler/ebin/beam_clean.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index eba17a7bbd..c19e467785 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_disasm.beam and b/bootstrap/lib/compiler/ebin/beam_disasm.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_flatten.beam b/bootstrap/lib/compiler/ebin/beam_flatten.beam
index f0a3dfac91..bf2d341c05 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_flatten.beam and b/bootstrap/lib/compiler/ebin/beam_flatten.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 5c0f4a9996..75c02f082f 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_jump.beam and b/bootstrap/lib/compiler/ebin/beam_jump.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_opcodes.beam b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
index 8425c2b2b5..5047d223c2 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_opcodes.beam and b/bootstrap/lib/compiler/ebin/beam_opcodes.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_split.beam b/bootstrap/lib/compiler/ebin/beam_split.beam
index 64a946db25..874245d501 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_split.beam and b/bootstrap/lib/compiler/ebin/beam_split.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index ff455ce50f..7da67b85ee 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_utils.beam and b/bootstrap/lib/compiler/ebin/beam_utils.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index fed0bb3c48..9f154f58b8 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_validator.beam and b/bootstrap/lib/compiler/ebin/beam_validator.beam differ
diff --git a/bootstrap/lib/compiler/ebin/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam
index 4d28344035..2c4ca55563 100644
Binary files a/bootstrap/lib/compiler/ebin/beam_z.beam and b/bootstrap/lib/compiler/ebin/beam_z.beam differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index 2cba88e8c3..16c2e4ecb5 100644
Binary files a/bootstrap/lib/compiler/ebin/cerl.beam and b/bootstrap/lib/compiler/ebin/cerl.beam differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index ce81e67924..b25cfd07cf 100644
Binary files a/bootstrap/lib/compiler/ebin/cerl_trees.beam and b/bootstrap/lib/compiler/ebin/cerl_trees.beam differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index 34b3cc86a9..5dcacbacc8 100644
Binary files a/bootstrap/lib/compiler/ebin/compile.beam and b/bootstrap/lib/compiler/ebin/compile.beam differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.appup b/bootstrap/lib/compiler/ebin/compiler.appup
index 577ebb149d..2c0d46d5f5 100644
--- a/bootstrap/lib/compiler/ebin/compiler.appup
+++ b/bootstrap/lib/compiler/ebin/compiler.appup
@@ -1 +1 @@
-{"4.9.1",[],[]}.
+{"4.9.4",[],[]}.
diff --git a/bootstrap/lib/compiler/ebin/core_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam
index 5e40c086c2..84e8a8034d 100644
Binary files a/bootstrap/lib/compiler/ebin/core_lib.beam and b/bootstrap/lib/compiler/ebin/core_lib.beam differ
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index 831e2acc43..aeefed6fcb 100644
Binary files a/bootstrap/lib/compiler/ebin/core_lint.beam and b/bootstrap/lib/compiler/ebin/core_lint.beam differ
diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam
index 3f9c212fe7..4fd3f2fbdb 100644
Binary files a/bootstrap/lib/compiler/ebin/core_parse.beam and b/bootstrap/lib/compiler/ebin/core_parse.beam differ
diff --git a/bootstrap/lib/compiler/ebin/core_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index f7e0dc40ae..bc9decc9f7 100644
Binary files a/bootstrap/lib/compiler/ebin/core_pp.beam and b/bootstrap/lib/compiler/ebin/core_pp.beam differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
index 120c4f4a76..82868f66bc 100644
Binary files a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam and b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index 5c87bdd3dd..5516ecaaf9 100644
Binary files a/bootstrap/lib/compiler/ebin/sys_core_fold.beam and b/bootstrap/lib/compiler/ebin/sys_core_fold.beam differ
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
index d726c3ca07..6a6216e80f 100644
Binary files a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam and b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index c683fc6c46..e6bbc49fba 100644
Binary files a/bootstrap/lib/compiler/ebin/v3_codegen.beam and b/bootstrap/lib/compiler/ebin/v3_codegen.beam differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index 8004afb882..d0e6b3d97e 100644
Binary files a/bootstrap/lib/compiler/ebin/v3_core.beam and b/bootstrap/lib/compiler/ebin/v3_core.beam differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 0566a7ff51..8f7ba8803a 100644
Binary files a/bootstrap/lib/compiler/ebin/v3_kernel.beam and b/bootstrap/lib/compiler/ebin/v3_kernel.beam differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
index 1b2d02c1d5..d123c58593 100644
Binary files a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam and b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam differ
diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam
index bb0ceefdd1..89d1d70814 100644
Binary files a/bootstrap/lib/compiler/ebin/v3_life.beam and b/bootstrap/lib/compiler/ebin/v3_life.beam differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
index 382d9deea1..b58d066898 100644
--- a/bootstrap/lib/kernel/ebin/kernel.appup
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -15,13 +15,13 @@
%% under the License.
%%
%% %CopyrightEnd%
-{"2.17",
+{"3.0",
%% Up from - max two major revisions back
- [{<<"2\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
+ [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15
%% Down to - max two major revisions back
- [{<<"2\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
+ [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15
}.
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index d58dd86070..19fadc9585 100644
Binary files a/bootstrap/lib/stdlib/ebin/erl_eval.beam and b/bootstrap/lib/stdlib/ebin/erl_eval.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
index 0563699d5e..c8f45fd838 100644
Binary files a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam and b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam
index 01c6598cdc..b851ca484e 100644
Binary files a/bootstrap/lib/stdlib/ebin/erl_internal.beam and b/bootstrap/lib/stdlib/ebin/erl_internal.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 10d32c9865..c413a7fda1 100644
Binary files a/bootstrap/lib/stdlib/ebin/erl_lint.beam and b/bootstrap/lib/stdlib/ebin/erl_lint.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam
index 2815cad4a3..c1c114dd3c 100644
Binary files a/bootstrap/lib/stdlib/ebin/erl_parse.beam and b/bootstrap/lib/stdlib/ebin/erl_parse.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_scan.beam b/bootstrap/lib/stdlib/ebin/erl_scan.beam
index 851270bd37..fe45755343 100644
Binary files a/bootstrap/lib/stdlib/ebin/erl_scan.beam and b/bootstrap/lib/stdlib/ebin/erl_scan.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam
index d34f69097a..ad77021190 100644
Binary files a/bootstrap/lib/stdlib/ebin/io_lib.beam and b/bootstrap/lib/stdlib/ebin/io_lib.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam
new file mode 100644
index 0000000000..9a902a1ae4
Binary files /dev/null and b/bootstrap/lib/stdlib/ebin/maps.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index 6e7a19c78a..9a55c521f8 100644
Binary files a/bootstrap/lib/stdlib/ebin/ms_transform.beam and b/bootstrap/lib/stdlib/ebin/ms_transform.beam differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index d1da05048b..31c7222100 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -71,6 +71,7 @@
lib,
lists,
log_mf_h,
+ maps,
math,
ms_transform,
orddict,
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
index b8460d543c..0871dc5a98 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.appup
+++ b/bootstrap/lib/stdlib/ebin/stdlib.appup
@@ -15,13 +15,13 @@
%% under the License.
%%
%% %CopyrightEnd%
-{"1.20",
+{"2.0",
%% Up from - max two major revisions back
- [{<<"1\\.20(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
+ [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15
%% Down to - max two major revisions back
- [{<<"1\\.20(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
+ [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15
}.
--
cgit v1.2.3
From b313f455be0def48b85c905efd98308aefb5bbc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 24 Oct 2013 14:40:12 +0200
Subject: preloaded: Remove bogus map type
---
erts/preloaded/src/erlang.erl | 1 -
1 file changed, 1 deletion(-)
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index ff0f9d0ead..fbc37bd955 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -60,7 +60,6 @@
-export_type([timestamp/0]).
-type ext_binary() :: binary().
--type map() :: term(). %% FIXME: remove when handled internally.
-type timestamp() :: {MegaSecs :: non_neg_integer(),
Secs :: non_neg_integer(),
MicroSecs :: non_neg_integer()}.
--
cgit v1.2.3
From 38047efbbb91b02bd8894654458350b5a9813798 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 6 Nov 2013 16:44:55 +0100
Subject: Update preloaded erlang.beam
---
erts/preloaded/ebin/erlang.beam | Bin 98008 -> 98256 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index f38377647c..eb696bb32f 100644
Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ
--
cgit v1.2.3
From 5dda9e038891500a0849a3bf4d1079a355cdd30f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 24 Oct 2013 18:00:57 +0200
Subject: stdlib: Pretty print maps
Pretty printing behaviour mimic records.
This commit requires Map enabled bootstrap compiler.
---
lib/stdlib/src/io_lib_pretty.erl | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 7637ad7a3d..f02a7921f8 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -101,6 +101,7 @@ print(Term, Col, Ll, D, M, RecDefFun, Enc, Str) when Col =< 0 ->
print(Term, 1, Ll, D, M, RecDefFun, Enc, Str);
print(Term, Col, Ll, D, M0, RecDefFun, Enc, Str) when is_tuple(Term);
is_list(Term);
+ is_map(Term);
is_bitstring(Term) ->
If = {_S, Len} = print_length(Term, D, RecDefFun, Enc, Str),
M = max_cs(M0, Len),
@@ -137,6 +138,10 @@ pp({{tuple,true,L}, _Len}, Col, Ll, M, TInd, Ind, LD, W) ->
[${, pp_tag_tuple(L, Col, Ll, M, TInd, Ind, LD, W + 1), $}];
pp({{tuple,false,L}, _Len}, Col, Ll, M, TInd, Ind, LD, W) ->
[${, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $,, W + 1), $}];
+pp({{map,Pairs},_Len}, Col, Ll, M, TInd, Ind, LD, W) ->
+ [$#,${, pp_list(Pairs, Col + 2, Ll, M, TInd, indent(2, Ind), LD, $,, W + 1), $}];
+pp({{map_pair,K,V},_Len}, Col, Ll, M, TInd, Ind, LD, W) ->
+ [pp(K, Col, Ll, M, TInd, Ind, LD, W), " => ", pp(V, Col, Ll, M, TInd, Ind, LD, W)];
pp({{record,[{Name,NLen} | L]}, _Len}, Col, Ll, M, TInd, Ind, LD, W) ->
[Name, ${, pp_record(L, NLen, Col, Ll, M, TInd, Ind, LD, W + NLen+1), $}];
pp({{bin,S}, _Len}, Col, Ll, M, _TInd, Ind, LD, W) ->
@@ -283,6 +288,10 @@ write({{tuple, _IsTagged, L}, _}) ->
[${, write_list(L, $,), $}];
write({{list, L}, _}) ->
[$[, write_list(L, $|), $]];
+write({{map, Pairs}, _}) ->
+ [$#,${, write_list(Pairs, $,), $}];
+write({{map_pair, K, V}, _}) ->
+ [write(K)," => ",write(V)];
write({{record, [{Name,_} | L]}, _}) ->
[Name, ${, write_fields(L), $}];
write({{bin, S}, _}) ->
@@ -331,6 +340,8 @@ print_length([], _D, _RF, _Enc, _Str) ->
{"[]", 2};
print_length({}, _D, _RF, _Enc, _Str) ->
{"{}", 2};
+print_length(#{}=M, _D, _RF, _Enc, _Str) when map_size(M) =:= 0 ->
+ {"#{}", 3};
print_length(List, D, RF, Enc, Str) when is_list(List) ->
case Str andalso printable_list(List, D, Enc) of
true ->
@@ -356,6 +367,8 @@ print_length(R, D, RF, Enc, Str) when is_atom(element(1, R)),
end;
print_length(Tuple, D, RF, Enc, Str) when is_tuple(Tuple) ->
print_length_tuple(Tuple, D, RF, Enc, Str);
+print_length(Map, D, RF, Enc, Str) when is_map(Map) ->
+ print_length_map(Map, D, RF, Enc, Str);
print_length(<<>>, _D, _RF, _Enc, _Str) ->
{"<<>>", 4};
print_length(<<_/bitstring>>, 1, _RF, _Enc, _Str) ->
@@ -389,6 +402,25 @@ print_length(Term, _D, _RF, _Enc, _Str) ->
S = io_lib:write(Term),
{S, lists:flatlength(S)}.
+print_length_map(_Map, 1, _RF, _Enc, _Str) ->
+ {"#{...}", 6};
+print_length_map(Map, D, RF, Enc, Str) when is_map(Map) ->
+ Pairs = print_length_map_pairs(maps:to_list(Map), D, RF, Enc, Str),
+ {{map, Pairs}, list_length(Pairs, 3)}.
+
+print_length_map_pairs([], _D, _RF, _Enc, _Str) ->
+ [];
+print_length_map_pairs(_Pairs, 1, _RF, _Enc, _Str) ->
+ {dots, 3};
+print_length_map_pairs([{K,V}|Pairs], D, RF, Enc, Str) ->
+ [print_length_map_pair(K,V,D-1,RF,Enc,Str) |
+ print_length_map_pairs(Pairs,D-1,RF,Enc,Str)].
+
+print_length_map_pair(K, V, D, RF, Enc, Str) ->
+ {KS, KL} = print_length(K, D, RF, Enc, Str),
+ {VS, VL} = print_length(V, D, RF, Enc, Str),
+ {{map_pair, {KS,KL}, {VS,VL}}, KL + VL}.
+
print_length_tuple(_Tuple, 1, _RF, _Enc, _Str) ->
{"{...}", 5};
print_length_tuple(Tuple, D, RF, Enc, Str) ->
--
cgit v1.2.3
From 1a234c9eba8ac2c78f97e5f3e33521b8cc5d3748 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 24 Oct 2013 18:13:31 +0200
Subject: stdlib: Strengthen Map module with guards
This commit requires Map enabled bootstrap compiler.
---
lib/stdlib/src/maps.erl | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 59ac80b46c..0e6a1ddb1a 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -164,7 +164,7 @@ values(_) -> erlang:nif_error(undef).
K :: term(),
V :: term().
-foldl(Fun, Init, Map) ->
+foldl(Fun, Init, Map) when is_function(Fun,3), is_map(Map) ->
lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)).
-spec foldr(Fun,Init,Map) -> Acc when
@@ -177,7 +177,7 @@ foldl(Fun, Init, Map) ->
K :: term(),
V :: term().
-foldr(Fun, Init, Map) ->
+foldr(Fun, Init, Map) when is_function(Fun,3), is_map(Map) ->
lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)).
@@ -189,7 +189,7 @@ foldr(Fun, Init, Map) ->
V1 :: term(),
V2 :: term().
-map(Fun, Map) ->
+map(Fun, Map) when is_function(Fun, 2), is_map(Map) ->
maps:from_list(lists:map(fun
({K,V}) ->
{K,Fun(K,V)}
@@ -199,14 +199,15 @@ map(Fun, Map) ->
-spec size(Map) -> non_neg_integer() when
Map :: map().
-size(Map) ->
+size(Map) when is_map(Map) ->
erlang:map_size(Map).
+
-spec without(Ks,Map1) -> Map2 when
Ks :: [K],
Map1 :: map(),
Map2 :: map(),
K :: term().
-without(Ks, M) ->
+without(Ks, M) when is_list(Ks), is_map(M) ->
maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]).
--
cgit v1.2.3
From e0eb6d5bafcebc1c24b0a538e50a1d55a3724f01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 8 Nov 2013 15:34:05 +0100
Subject: erts: Add NIFs for Maps
- int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM map)
- int enif_get_map_size(ErlNifEnv *env, ERL_NIF_TERM, int*)
- ERL_NIF_TERM enif_make_new_map(ErlNifEnv *env)
- int enif_make_map_put(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)
- int enif_get_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)
- int enif_find_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)
- int enif_make_map_update(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)
- int enif_make_map_remove(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)
- int enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter)
- void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)
- int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
- int enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)
---
erts/emulator/beam/erl_map.c | 443 ++++++++++++++++++---------------
erts/emulator/beam/erl_map.h | 6 +
erts/emulator/beam/erl_nif.c | 209 ++++++++++++++++
erts/emulator/beam/erl_nif.h | 26 +-
erts/emulator/beam/erl_nif_api_funcs.h | 33 +++
5 files changed, 511 insertions(+), 206 deletions(-)
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 27734276f9..98aeee634b 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -111,28 +111,39 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
* return value if key *equals* a key in the map
*/
-BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
- if (is_map(BIF_ARG_2)) {
- Eterm *hp, *ks,*vs, key, res;
- map_t *mp;
- Uint n,i;
+int erts_maps_find(Eterm key, Eterm map, Eterm *value) {
- mp = (map_t*)map_val(BIF_ARG_2);
- key = BIF_ARG_1;
- n = map_get_size(mp);
- ks = map_get_keys(mp);
- vs = map_get_values(mp);
+ Eterm *ks,*vs;
+ map_t *mp;
+ Uint n,i;
- for( i = 0; i < n; i++) {
- if (CMP(ks[i], key)==0) {
- hp = HAlloc(BIF_P, 3);
- res = make_tuple(hp);
- *hp++ = make_arityval(2);
- *hp++ = am_ok;
- *hp++ = vs[i];
- BIF_RET(res);
- }
+ mp = (map_t*)map_val(map);
+ n = map_get_size(mp);
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+
+ for( i = 0; i < n; i++) {
+ if (CMP(ks[i], key)==0) {
+ *value = vs[i];
+ return 1;
+ }
+ }
+ return 0;
+}
+
+BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm *hp, value,res;
+
+ if (erts_maps_find(BIF_ARG_1, BIF_ARG_2, &value)) {
+ hp = HAlloc(BIF_P, 3);
+ res = make_tuple(hp);
+ *hp++ = make_arityval(2);
+ *hp++ = am_ok;
+ *hp++ = value;
+ BIF_RET(res);
}
+
BIF_RET(am_error);
}
BIF_ERROR(BIF_P, BADARG);
@@ -142,43 +153,54 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) {
* exception bad_key if none matches
*/
-BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
- if (is_map(BIF_ARG_2)) {
- Eterm *hp, *ks,*vs, key, error;
- map_t *mp;
- Uint n,i;
- char *s_error;
- mp = (map_t*)map_val(BIF_ARG_2);
- key = BIF_ARG_1;
- n = map_get_size(mp);
-
- if (n == 0)
- goto error;
+int erts_maps_get(Eterm key, Eterm map, Eterm *value) {
+ Eterm *ks,*vs;
+ map_t *mp;
+ Uint n,i;
- ks = map_get_keys(mp);
- vs = map_get_values(mp);
+ mp = (map_t*)map_val(map);
+ n = map_get_size(mp);
- if (is_immed(key)) {
- for( i = 0; i < n; i++) {
- if (ks[i] == key) {
- BIF_RET(vs[i]);
- }
- }
- }
+ if (n == 0)
+ return 0;
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+ if (is_immed(key)) {
for( i = 0; i < n; i++) {
- if (eq(ks[i], key)) {
- BIF_RET(vs[i]);
+ if (ks[i] == key) {
+ *value = vs[i];
+ return 1;
}
}
-error:
+ }
+
+ for( i = 0; i < n; i++) {
+ if (eq(ks[i], key)) {
+ *value = vs[i];
+ return 1;
+ }
+ }
+ return 0;
+}
+
+BIF_RETTYPE maps_get_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm *hp;
+ Eterm value, error;
+ char *s_error;
+
+ if (erts_maps_get(BIF_ARG_1, BIF_ARG_2, &value)) {
+ BIF_RET(value);
+ }
s_error = "bad_key";
error = am_atom_put(s_error, sys_strlen(s_error));
hp = HAlloc(BIF_P, 3);
- BIF_P->fvalue = TUPLE2(hp, error, key);
+ BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1);
BIF_ERROR(BIF_P, EXC_ERROR_2);
}
BIF_ERROR(BIF_P, BADARG);
@@ -460,7 +482,7 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) {
Eterm tup;
map_t *mp;
- hp = HAlloc(BIF_P, (3 + 1));
+ hp = HAlloc(BIF_P, (MAP_HEADER_SIZE + 1));
tup = make_tuple(hp);
*hp++ = make_arityval(0);
@@ -475,183 +497,197 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) {
/* maps:put/3
*/
-BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
- if (is_map(BIF_ARG_3)) {
- Sint n,i;
- Sint c = 0;
- Eterm* hp, *shp;
- Eterm *ks,*vs, res, key, tup;
- map_t *mp = (map_t*)map_val(BIF_ARG_3);
-
- key = BIF_ARG_1;
- n = map_get_size(mp);
-
- if (n == 0) {
- hp = HAlloc(BIF_P, 4 + 2);
- tup = make_tuple(hp);
- *hp++ = make_arityval(1);
- *hp++ = key;
- res = make_map(hp);
- *hp++ = MAP_HEADER;
- *hp++ = 1;
- *hp++ = tup;
- *hp++ = BIF_ARG_2;
+Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
+ Sint n,i;
+ Sint c = 0;
+ Eterm* hp, *shp;
+ Eterm *ks,*vs, res, tup;
+ map_t *mp = (map_t*)map_val(map);
- BIF_RET(res);
- }
-
- ks = map_get_keys(mp);
- vs = map_get_values(mp);
- /* only allocate for values,
- * assume key-tuple will be intact
- */
+ n = map_get_size(mp);
- hp = HAlloc(BIF_P, 3 + n);
- shp = hp; /* save hp, used if optimistic update fails */
- res = make_map(hp);
+ if (n == 0) {
+ hp = HAlloc(p, MAP_HEADER_SIZE + 1 + 2);
+ tup = make_tuple(hp);
+ *hp++ = make_arityval(1);
+ *hp++ = key;
+ res = make_map(hp);
*hp++ = MAP_HEADER;
- *hp++ = n;
- *hp++ = mp->keys;
+ *hp++ = 1;
+ *hp++ = tup;
+ *hp++ = value;
- if (is_immed(key)) {
- for( i = 0; i < n; i ++) {
- if (ks[i] == key) {
- *hp++ = BIF_ARG_2;
- vs++;
- c = 1;
- } else {
- *hp++ = *vs++;
- }
+ return res;
+ }
+
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
+
+ /* only allocate for values,
+ * assume key-tuple will be intact
+ */
+
+ hp = HAlloc(p, MAP_HEADER_SIZE + n);
+ shp = hp; /* save hp, used if optimistic update fails */
+ res = make_map(hp);
+ *hp++ = MAP_HEADER;
+ *hp++ = n;
+ *hp++ = mp->keys;
+
+ if (is_immed(key)) {
+ for( i = 0; i < n; i ++) {
+ if (ks[i] == key) {
+ *hp++ = value;
+ vs++;
+ c = 1;
+ } else {
+ *hp++ = *vs++;
}
- } else {
- for( i = 0; i < n; i ++) {
- if (eq(ks[i], key)) {
- *hp++ = BIF_ARG_2;
- vs++;
- c = 1;
- } else {
- *hp++ = *vs++;
- }
+ }
+ } else {
+ for( i = 0; i < n; i ++) {
+ if (eq(ks[i], key)) {
+ *hp++ = value;
+ vs++;
+ c = 1;
+ } else {
+ *hp++ = *vs++;
}
}
+ }
- if (c)
- BIF_RET(res);
+ if (c)
+ return res;
- /* need to make a new tuple,
- * use old hp since it needs to be recreated anyway.
- */
- tup = make_tuple(shp);
- *shp++ = make_arityval(n+1);
+ /* need to make a new tuple,
+ * use old hp since it needs to be recreated anyway.
+ */
+ tup = make_tuple(shp);
+ *shp++ = make_arityval(n+1);
- hp = HAlloc(BIF_P, 3 + n + 1);
- res = make_map(hp);
- *hp++ = MAP_HEADER;
- *hp++ = n + 1;
- *hp++ = tup;
+ hp = HAlloc(p, 3 + n + 1);
+ res = make_map(hp);
+ *hp++ = MAP_HEADER;
+ *hp++ = n + 1;
+ *hp++ = tup;
- ks = map_get_keys(mp);
- vs = map_get_values(mp);
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
- ASSERT(n >= 0);
+ ASSERT(n >= 0);
- /* copy map in order */
- while (n && ((c = CMP(*ks, key)) < 0)) {
- *shp++ = *ks++;
- *hp++ = *vs++;
- n--;
- }
+ /* copy map in order */
+ while (n && ((c = CMP(*ks, key)) < 0)) {
+ *shp++ = *ks++;
+ *hp++ = *vs++;
+ n--;
+ }
- *shp++ = key;
- *hp++ = BIF_ARG_2;
+ *shp++ = key;
+ *hp++ = value;
- ASSERT(n >= 0);
+ ASSERT(n >= 0);
- while(n--) {
- *shp++ = *ks++;
- *hp++ = *vs++;
- }
- /* we have one word remaining
- * this will work out fine once we get the size word
- * in the header.
- */
- *shp = make_pos_bignum_header(0);
- BIF_RET(res);
+ while(n--) {
+ *shp++ = *ks++;
+ *hp++ = *vs++;
}
+ /* we have one word remaining
+ * this will work out fine once we get the size word
+ * in the header.
+ */
+ *shp = make_pos_bignum_header(0);
+ return res;
+}
+BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
+ if (is_map(BIF_ARG_3)) {
+ BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3));
+ }
BIF_ERROR(BIF_P, BADARG);
}
/* maps:remove/3
*/
-BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
- if (is_map(BIF_ARG_2)) {
- Sint n;
- Sint found = 0;
- Uint need;
- Eterm *thp, *mhp;
- Eterm *ks, *vs, res, key,tup;
- map_t *mp = (map_t*)map_val(BIF_ARG_2);
+int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
+ Sint n;
+ Sint found = 0;
+ Uint need;
+ Eterm *thp, *mhp;
+ Eterm *ks, *vs, tup;
+ map_t *mp = (map_t*)map_val(map);
- key = BIF_ARG_1;
- n = map_get_size(mp);
+ n = map_get_size(mp);
- if (n == 0)
- BIF_RET(BIF_ARG_2);
+ if (n == 0) {
+ *res = map;
+ return 1;
+ }
- ks = map_get_keys(mp);
- vs = map_get_values(mp);
+ ks = map_get_keys(mp);
+ vs = map_get_values(mp);
- /* Assume key exists.
- * Release allocated if it didn't.
- * Allocate key tuple first.
- */
+ /* Assume key exists.
+ * Release allocated if it didn't.
+ * Allocate key tuple first.
+ */
- need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */
- thp = HAlloc(BIF_P, need);
- mhp = thp + n; /* offset with tuple heap size */
+ need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */
+ thp = HAlloc(p, need);
+ mhp = thp + n; /* offset with tuple heap size */
- tup = make_tuple(thp);
- *thp++ = make_arityval(n - 1);
+ tup = make_tuple(thp);
+ *thp++ = make_arityval(n - 1);
- res = make_map(mhp);
- *mhp++ = MAP_HEADER;
- *mhp++ = n - 1;
- *mhp++ = tup;
+ *res = make_map(mhp);
+ *mhp++ = MAP_HEADER;
+ *mhp++ = n - 1;
+ *mhp++ = tup;
- if (is_immed(key)) {
- while(n--) {
- if (*ks == key) {
- ks++;
- vs++;
- found = 1;
- } else {
- *mhp++ = *vs++;
- *thp++ = *ks++;
- }
+ if (is_immed(key)) {
+ while(n--) {
+ if (*ks == key) {
+ ks++;
+ vs++;
+ found = 1;
+ } else {
+ *mhp++ = *vs++;
+ *thp++ = *ks++;
}
- } else {
- while(n--) {
- if (eq(*ks, key)) {
- ks++;
- vs++;
- found = 1;
- } else {
- *mhp++ = *vs++;
- *thp++ = *ks++;
- }
+ }
+ } else {
+ while(n--) {
+ if (eq(*ks, key)) {
+ ks++;
+ vs++;
+ found = 1;
+ } else {
+ *mhp++ = *vs++;
+ *thp++ = *ks++;
}
}
+ }
- if (found)
- BIF_RET(res);
+ if (found) {
+ return 1;
+ }
- /* Not found, remove allocated memory
- * and return previous map.
- */
- HRelease(BIF_P, thp + need, thp);
- BIF_RET(BIF_ARG_2);
+ /* Not found, remove allocated memory
+ * and return previous map.
+ */
+ HRelease(p, thp + need, thp);
+
+ *res = map;
+ return 1;
+}
+
+BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
+ if (is_map(BIF_ARG_2)) {
+ Eterm res;
+ if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) {
+ BIF_RET(res);
+ }
}
BIF_ERROR(BIF_P, BADARG);
}
@@ -659,19 +695,15 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
/* maps:update/3
*/
-BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
- if (is_map(BIF_ARG_3)) {
+int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) {
Sint n,i;
Sint found = 0;
Eterm* hp,*shp;
- Eterm *ks,*vs, res, key;
- map_t *mp = (map_t*)map_val(BIF_ARG_3);
+ Eterm *ks,*vs;
+ map_t *mp = (map_t*)map_val(map);
- key = BIF_ARG_1;
- n = map_get_size(mp);
-
- if (n == 0) {
- BIF_ERROR(BIF_P, BADARG);
+ if ((n = map_get_size(mp)) == 0) {
+ return 0;
}
ks = map_get_keys(mp);
@@ -681,9 +713,9 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
* assume key-tuple will be intact
*/
- hp = HAlloc(BIF_P, 3 + n);
+ hp = HAlloc(p, MAP_HEADER_SIZE + n);
shp = hp;
- res = make_map(hp);
+ *res = make_map(hp);
*hp++ = MAP_HEADER;
*hp++ = n;
*hp++ = mp->keys;
@@ -691,7 +723,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
if (is_immed(key)) {
for( i = 0; i < n; i ++) {
if (ks[i] == key) {
- *hp++ = BIF_ARG_2;
+ *hp++ = value;
vs++;
found = 1;
} else {
@@ -701,7 +733,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
} else {
for( i = 0; i < n; i ++) {
if (eq(ks[i], key)) {
- *hp++ = BIF_ARG_2;
+ *hp++ = value;
vs++;
found = 1;
} else {
@@ -710,10 +742,19 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
}
}
- if (found)
- BIF_RET(res);
+ if (found) {
+ return 1;
+ }
+ HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
+ return 0;
+}
- HRelease(BIF_P, shp + 3 + n, shp);
+BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
+ if (is_map(BIF_ARG_3)) {
+ Eterm res;
+ if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) {
+ BIF_RET(res);
+ }
}
BIF_ERROR(BIF_P, BADARG);
}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 4f0d26e100..616ecd24ce 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -62,5 +62,11 @@ typedef struct map_s {
#define MAP_HEADER _make_header(1,_TAG_HEADER_MAP)
#define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm))
+Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map);
+int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res);
+int erts_maps_find(Eterm key, Eterm map, Eterm *value);
+int erts_maps_get(Eterm key, Eterm map, Eterm *value);
+int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
+
#endif
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index e1e213c4eb..c683847aaa 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -31,6 +31,7 @@
#include "bif.h"
#include "error.h"
#include "big.h"
+#include "erl_map.h"
#include "beam_bp.h"
#include "erl_thr_progress.h"
#include "dtrace-wrapper.h"
@@ -1602,6 +1603,214 @@ enif_have_dirty_schedulers()
#endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */
+/* Maps */
+
+int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)
+{
+ return is_map(term);
+}
+
+int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size)
+{
+ if (is_map(term)) {
+ map_t *mp;
+ mp = (map_t*)map_val(term);
+ *size = map_get_size(mp);
+ return 1;
+ }
+ return 0;
+}
+
+ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env)
+{
+ Eterm* hp = alloc_heap(env,MAP_HEADER_SIZE+1);
+ Eterm tup;
+ map_t *mp;
+
+ tup = make_tuple(hp);
+ *hp++ = make_arityval(0);
+ mp = (map_t*)hp;
+ mp->thing_word = MAP_HEADER;
+ mp->size = 0;
+ mp->keys = tup;
+
+ return make_map(mp);
+}
+
+int enif_make_map_put(ErlNifEnv* env,
+ Eterm map_in,
+ Eterm key,
+ Eterm value,
+ Eterm *map_out)
+{
+ if (is_not_map(map_in)) {
+ return 0;
+ }
+ flush_env(env);
+ *map_out = erts_maps_put(env->proc, key, value, map_in);
+ cache_env(env);
+ return 1;
+}
+
+int enif_get_map_value(ErlNifEnv* env,
+ Eterm map,
+ Eterm key,
+ Eterm *value)
+{
+ if (is_not_map(map)) {
+ return 0;
+ }
+ return erts_maps_get(key, map, value);
+}
+
+int enif_find_map_value(ErlNifEnv* env,
+ Eterm map,
+ Eterm key,
+ Eterm *value)
+{
+ if (is_not_map(map)) {
+ return 0;
+ }
+ return erts_maps_get(key, map, value);
+}
+
+int enif_make_map_update(ErlNifEnv* env,
+ Eterm map_in,
+ Eterm key,
+ Eterm value,
+ Eterm *map_out)
+{
+ int res;
+ if (is_not_map(map_in)) {
+ return 0;
+ }
+
+ flush_env(env);
+ res = erts_maps_update(env->proc, key, value, map_in, map_out);
+ cache_env(env);
+ return res;
+}
+
+int enif_make_map_remove(ErlNifEnv* env,
+ Eterm map_in,
+ Eterm key,
+ Eterm *map_out)
+{
+ int res;
+ if (is_not_map(map_in)) {
+ return 0;
+ }
+ flush_env(env);
+ res = erts_maps_remove(env->proc, key, map_in, map_out);
+ cache_env(env);
+ return res;
+}
+
+int enif_map_iterator_create(ErlNifEnv *env,
+ Eterm map,
+ ErlNifMapIterator *iter,
+ ErlNifMapIteratorEntry entry)
+{
+ if (is_map(map)) {
+ map_t *mp = (map_t*)map_val(map);
+ size_t offset;
+
+ switch (entry) {
+ case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break;
+ case ERL_NIF_MAP_ITERATOR_TAIL: offset = map_get_size(mp) - 1; break;
+ default: goto error;
+ }
+
+ /* empty maps are ok but will leave the iterator
+ * in bad shape.
+ */
+
+ iter->map = map;
+ iter->ks = ((Eterm *)map_get_keys(mp)) + offset;
+ iter->vs = ((Eterm *)map_get_values(mp)) + offset;
+ iter->t_limit = map_get_size(mp) + 1;
+ iter->h_limit = 0;
+ iter->idx = offset + 1;
+
+ return 1;
+ }
+
+error:
+ iter->map = THE_NON_VALUE;
+ return 0;
+}
+
+void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)
+{
+ /* not used */
+}
+
+int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)
+{
+ ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
+ if (is_map(iter->map) && (
+ (iter->t_limit - iter->h_limit) == 1 ||
+ iter->idx == iter->t_limit)) {
+ return 1;
+ }
+ return 0;
+}
+
+int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)
+{
+ ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
+ if (is_map(iter->map) && (
+ (iter->t_limit - iter->h_limit) == 1 ||
+ iter->idx == iter->h_limit)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
+{
+ if (is_map(iter->map) && iter->idx < iter->t_limit) {
+ iter->idx++;
+ if (iter->idx != iter->t_limit) {
+ iter->ks++;
+ iter->vs++;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
+{
+ if (is_map(iter->map) && iter->idx > iter->h_limit ) {
+ iter->idx--;
+ if (iter->idx != iter->h_limit ) {
+ iter->ks--;
+ iter->vs--;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int enif_map_iterator_get_pair(ErlNifEnv *env,
+ ErlNifMapIterator *iter,
+ Eterm *key,
+ Eterm *value)
+{
+ if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) {
+ ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) &&
+ iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map))));
+ ASSERT(iter->vs >= map_get_values(map_val(iter->map)) &&
+ iter->vs < (map_get_values(map_val(iter->map)) + map_get_size(map_val(iter->map))));
+ *key = *(iter->ks);
+ *value = *(iter->vs);
+ return 1;
+ }
+ return 0;
+}
+
/***************************************************************************
** load_nif/2 **
***************************************************************************/
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index fb3c359ec9..3c1e13f8a4 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -38,14 +38,11 @@
** 2.2: R14B03 enif_is_exception
** 2.3: R15 enif_make_reverse_list, enif_is_number
** 2.4: R16 enif_consume_timeslice
-** 2.5: R17 dirty schedulers
+** 2.5: R17 Maps API additions
+** R17 dirty schedulers
*/
#define ERL_NIF_MAJOR_VERSION 2
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
#define ERL_NIF_MINOR_VERSION 5
-#else
-#define ERL_NIF_MINOR_VERSION 4
-#endif
#include
@@ -168,6 +165,7 @@ typedef int ErlNifTSDKey;
typedef ErlDrvThreadOpts ErlNifThreadOpts;
+<<<<<<< HEAD
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
typedef enum
{
@@ -175,6 +173,24 @@ typedef enum
ERL_NIF_DIRTY_JOB_IO_BOUND = 2
}ErlNifDirtyTaskFlags;
#endif
+=======
+typedef struct
+{
+ /* use a lot of memory, structure may change */
+ ERL_NIF_TERM map;
+ ErlNifUInt64 h_limit;
+ ErlNifUInt64 t_limit;
+ ErlNifUInt64 idx;
+ ERL_NIF_TERM *ks;
+ ERL_NIF_TERM *vs;
+} ErlNifMapIterator;
+
+typedef enum {
+ ERL_NIF_MAP_ITERATOR_HEAD = 1,
+ ERL_NIF_MAP_ITERATOR_TAIL = 2
+} ErlNifMapIteratorEntry;
+
+>>>>>>> erts: Add NIFs for Maps
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
# define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index f5b27dfdfa..2cabfd4ce1 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -149,6 +149,23 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void));
#endif
+ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term));
+ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env));
+ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out));
+ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value));
+ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value));
+ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out));
+ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out));
+ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry));
+ERL_NIF_API_FUNC_DECL(void, enif_map_iterator_destroy, (ErlNifEnv *env, ErlNifMapIterator *iter));
+ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_head, (ErlNifEnv *env, ErlNifMapIterator *iter));
+ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_tail, (ErlNifEnv *env, ErlNifMapIterator *iter));
+ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIterator *iter));
+ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter));
+ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value));
+
+
/*
** Add new entries here to keep compatibility on Windows!!!
*/
@@ -281,6 +298,22 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void));
# define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers)
#endif
+# define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map)
+# define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size)
+# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map)
+# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put)
+# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value)
+# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value)
+# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update)
+# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove)
+# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create)
+# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy)
+# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head)
+# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail)
+# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next)
+# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev)
+# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair)
+
/*
** Add new entries here
*/
--
cgit v1.2.3
From c71408f7184090fee077e35d0220fc021d2817fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 19 Nov 2013 11:38:36 +0100
Subject: erts: Test Maps with NIFs
---
erts/emulator/test/nif_SUITE.erl | 25 +++++++++++-
erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 59 +++++++++++++++++++++++++++
2 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index affb66289b..5b7c310aa4 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -29,7 +29,8 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,
end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1,
- types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
+ types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
+ maps/1,
api_macros/1,
from_array/1, iolist_as_binary/1, resource/1, resource_binary/1,
resource_takeover/1,
@@ -58,7 +59,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, reload, upgrade, heap_frag, types, many_args,
- binaries, get_string, get_atom, api_macros, from_array,
+ binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
resource_takeover, threading, send, send2, send3,
send_threaded, neg, is_checks, get_length, make_atom,
@@ -435,6 +436,21 @@ get_atom(Config) when is_list(Config) ->
?line {0, <<>>} = atom_to_bin('',0),
ok.
+maps(doc) -> ["Test NIF maps handling."];
+maps(suite) -> [];
+maps(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ Pairs = [{adam, "bert"}] ++
+ [{I,I}||I <- lists:seq(1,10)] ++
+ [{a,value},{"a","value"},{<<"a">>,<<"value">>}],
+ ok = ensure_lib_loaded(Config, 1),
+ M = maps_from_list(Pairs),
+ R = {RIs,Is} = sorted_list_from_maps(M),
+ io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]),
+ Is = lists:sort(Pairs),
+ Is = lists:reverse(RIs),
+ ok.
+
api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"];
api_macros(suite) -> [];
api_macros(Config) when is_list(Config) ->
@@ -1488,5 +1504,10 @@ otp_9668_nif(_) -> ?nif_stub.
consume_timeslice_nif(_,_) -> ?nif_stub.
call_dirty_nif(_,_,_) -> ?nif_stub.
+%% maps
+maps_from_list(_) -> ?nif_stub.
+sorted_list_from_maps(_) -> ?nif_stub.
+
+
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 6f902e186d..a0316a7861 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1536,6 +1536,63 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
}
#endif
+/* maps */
+static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM cell = argv[0];
+ ERL_NIF_TERM map = enif_make_new_map(env);
+ ERL_NIF_TERM tuple;
+ const ERL_NIF_TERM *pair;
+ int arity = -1;
+
+ if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env);
+
+ /* assume sorted keys */
+
+ while (!enif_is_empty_list(env,cell)) {
+ if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env);
+ if (enif_get_tuple(env,tuple,&arity,&pair)) {
+ enif_make_map_put(env, map, pair[0], pair[1], &map);
+ }
+ }
+
+ return map;
+}
+
+static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+
+ ERL_NIF_TERM map = argv[0];
+ ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
+ ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */
+ ERL_NIF_TERM key, value;
+ ErlNifMapIterator iter;
+
+ if (argc != 1 && !enif_is_map(env, map))
+ return enif_make_badarg(env);
+
+ if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD))
+ return enif_make_badarg(env);
+
+ while(enif_map_iterator_get_pair(env,&iter,&key,&value)) {
+ list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f);
+ enif_map_iterator_next(env,&iter);
+ }
+
+ enif_map_iterator_destroy(env, &iter);
+
+ if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL))
+ return enif_make_badarg(env);
+
+ while(enif_map_iterator_get_pair(env,&iter,&key,&value)) {
+ list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
+ enif_map_iterator_prev(env,&iter);
+ }
+
+ enif_map_iterator_destroy(env, &iter);
+
+ return enif_make_tuple2(env, list_f, list_b);
+}
+
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
@@ -1589,6 +1646,8 @@ static ErlNifFunc nif_funcs[] =
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
{"call_dirty_nif", 3, call_dirty_nif},
#endif
+ {"maps_from_list", 1, maps_from_list},
+ {"sorted_list_from_maps", 1, sorted_list_from_maps}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload)
--
cgit v1.2.3
From faf92c5174c7b86d2b5829c928b187753b3918e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 29 Nov 2013 12:12:53 +0100
Subject: erts: NIFs Map API fixup
---
erts/emulator/beam/erl_nif_api_funcs.h | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 2cabfd4ce1..4a5aacad48 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -300,19 +300,19 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa
# define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map)
# define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size)
-# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map)
-# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put)
-# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value)
-# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value)
-# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update)
-# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove)
-# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create)
-# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy)
-# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head)
-# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail)
-# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next)
-# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev)
-# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair)
+# define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map)
+# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put)
+# define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value)
+# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value)
+# define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update)
+# define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove)
+# define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create)
+# define enif_map_iterator_destroy ERL_NIF_API_FUNC_MACRO(enif_map_iterator_destroy)
+# define enif_map_iterator_is_head ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_head)
+# define enif_map_iterator_is_tail ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_tail)
+# define enif_map_iterator_next ERL_NIF_API_FUNC_MACRO(enif_map_iterator_next)
+# define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev)
+# define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair)
/*
** Add new entries here
--
cgit v1.2.3
From d5c238473b9cec819d93faaef4ccc00ddb60465f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Sat, 26 Oct 2013 21:01:41 +0200
Subject: erts: Add cmp_term to compare
Uses total order of types meaning int < float
---
erts/emulator/beam/erl_utils.h | 46 +++++++++++++++++++++++++++---------------
erts/emulator/beam/utils.c | 13 ++++++++++--
2 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 292d135946..c6247c7246 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -202,23 +202,37 @@ int eq(Eterm, Eterm);
#define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y))))
#if HALFWORD_HEAP
-Sint cmp_rel(Eterm, Eterm*, Eterm, Eterm*);
-#define CMP(A,B) cmp_rel(A,NULL,B,NULL)
+Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int);
+#define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0)
+#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1)
+#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0)
+#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1)
#else
-Sint cmp(Eterm, Eterm);
-#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B)
-#define CMP(A,B) cmp(A,B)
+Sint cmp(Eterm, Eterm, int);
+#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0)
+#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp(A,B,1)
+#define CMP(A,B) cmp(A,B,0)
+#define CMP_TERM(A,B) cmp(A,B,1)
#endif
-#define cmp_lt(a,b) (CMP((a),(b)) < 0)
-#define cmp_le(a,b) (CMP((a),(b)) <= 0)
-#define cmp_eq(a,b) (CMP((a),(b)) == 0)
-#define cmp_ne(a,b) (CMP((a),(b)) != 0)
-#define cmp_ge(a,b) (CMP((a),(b)) >= 0)
-#define cmp_gt(a,b) (CMP((a),(b)) > 0)
-
-#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b)))
-#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b)))
-#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b)))
-#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b)))
+
+#define cmp_lt(a,b) (CMP((a),(b)) < 0)
+#define cmp_le(a,b) (CMP((a),(b)) <= 0)
+#define cmp_eq(a,b) (CMP((a),(b)) == 0)
+#define cmp_ne(a,b) (CMP((a),(b)) != 0)
+#define cmp_ge(a,b) (CMP((a),(b)) >= 0)
+#define cmp_gt(a,b) (CMP((a),(b)) > 0)
+
+#define cmp_lt_term(a,b) (CMP_TERM((a),(b)) < 0)
+#define cmp_le_term(a,b) (CMP_TERM((a),(b)) <= 0)
+#define cmp_ge_term(a,b) (CMP_TERM((a),(b)) >= 0)
+#define cmp_gt_term(a,b) (CMP_TERM((a),(b)) > 0)
+
+#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b)))
+#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b)))
+#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b)))
+#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b)))
+
+#define CMP_LT_TERM(a,b) ((a) != (b) && cmp_lt_term((a),(b)))
+#define CMP_GE_TERM(a,b) ((a) == (b) || cmp_ge_term((a),(b)))
#endif
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 8958d334ae..bc4a05d385 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2425,10 +2425,14 @@ static int cmp_atoms(Eterm a, Eterm b)
bb->name+3, bb->len-3);
}
+/* cmp(Eterm a, Eterm b, int exact)
+ * exact = 1 -> term-based compare
+ * exact = 0 -> arith-based compare
+ */
#if HALFWORD_HEAP
-Sint cmp_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base)
+Sint cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact)
#else
-Sint cmp(Eterm a, Eterm b)
+Sint cmp(Eterm a, Eterm b, int exact)
#endif
{
DECLARE_WSTACK(stack);
@@ -2852,6 +2856,7 @@ tailrecur_ne:
j = big_sign(aw) ? -1 : 1;
break;
case SMALL_FLOAT:
+ if (exact) goto exact_fall_through;
GET_DOUBLE(bw, f2);
if (f2.fd < MAX_LOSSLESS_FLOAT && f2.fd > MIN_LOSSLESS_FLOAT) {
/* Float is within the no loss limit */
@@ -2877,12 +2882,14 @@ tailrecur_ne:
#endif /* ERTS_SIZEOF_ETERM == 8 */
break;
case FLOAT_BIG:
+ if (exact) goto exact_fall_through;
{
Wterm tmp = aw;
aw = bw;
bw = tmp;
}/* fall through */
case BIG_FLOAT:
+ if (exact) goto exact_fall_through;
GET_DOUBLE(bw, f2);
if ((f2.fd < (double) (MAX_SMALL + 1))
&& (f2.fd > (double) (MIN_SMALL - 1))) {
@@ -2912,6 +2919,7 @@ tailrecur_ne:
}
break;
case FLOAT_SMALL:
+ if (exact) goto exact_fall_through;
GET_DOUBLE(aw, f1);
if (f1.fd < MAX_LOSSLESS_FLOAT && f1.fd > MIN_LOSSLESS_FLOAT) {
/* Float is within the no loss limit */
@@ -2936,6 +2944,7 @@ tailrecur_ne:
}
#endif /* ERTS_SIZEOF_ETERM == 8 */
break;
+exact_fall_through:
default:
j = b_tag - a_tag;
}
--
cgit v1.2.3
From 76b8ea8ab1eb4ce099f88ccb8d1721c438d0ada4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 12 Dec 2013 10:58:59 +0100
Subject: erts: Add BIF erts_internal:cmp_term/2
Compares terms where integer() < float().
---
erts/emulator/beam/bif.c | 11 +++++++++++
erts/emulator/beam/bif.tab | 2 ++
erts/preloaded/src/erts_internal.erl | 10 ++++++++++
3 files changed, 23 insertions(+)
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 61c1abedb5..9c4801041f 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -4615,6 +4615,17 @@ BIF_RETTYPE bump_reductions_1(BIF_ALIST_1)
BIF_RET2(am_true, reds);
}
+BIF_RETTYPE erts_internal_cmp_term_2(BIF_ALIST_2) {
+ int res = CMP_TERM(BIF_ARG_1,BIF_ARG_2);
+
+ /* ensure -1, 0, 1 result */
+ if (res < 0) {
+ BIF_RET(make_small(-1));
+ } else if (res > 0) {
+ BIF_RET(make_small(1));
+ }
+ BIF_RET(make_small(0));
+}
/*
* Processes doing yield on return in a bif ends up in bif_return_trap().
*/
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index b623e47b9a..2d888862bf 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -593,6 +593,8 @@ bif maps:remove/2
bif maps:update/3
bif maps:values/1
+bif erts_internal:cmp_term/2
+
#
# Obsolete
#
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index d6a185482e..88eb317f1d 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -170,3 +170,13 @@ binary_to_term(_Binary) ->
Opts :: [safe].
binary_to_term(_Binary, _Opts) ->
erlang:nif_error(undefined).
+
+%% term compare where integer() < float() = true
+
+-spec cmp_term(A,B) -> Result when
+ A :: term(),
+ B :: term(),
+ Result :: -1 | 0 | 1.
+
+cmp_term(_A,_B) ->
+ erlang:nif_error(undefined).
--
cgit v1.2.3
From 862728a458729f4a71630f4a8fa93f1f26744c7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 12 Dec 2013 11:15:20 +0100
Subject: erts: Update maps BIFs to use term order
Maps internally uses term order to store keys in an ordered fashion.
---
erts/emulator/beam/erl_map.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 98aeee634b..455ba25e11 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -108,7 +108,7 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) {
}
/* maps:find/2
- * return value if key *equals* a key in the map
+ * return value if key *matches* a key in the map
*/
int erts_maps_find(Eterm key, Eterm map, Eterm *value) {
@@ -123,7 +123,7 @@ int erts_maps_find(Eterm key, Eterm map, Eterm *value) {
vs = map_get_values(mp);
for( i = 0; i < n; i++) {
- if (CMP(ks[i], key)==0) {
+ if (EQ(ks[i], key)) {
*value = vs[i];
return 1;
}
@@ -178,7 +178,7 @@ int erts_maps_get(Eterm key, Eterm map, Eterm *value) {
}
for( i = 0; i < n; i++) {
- if (eq(ks[i], key)) {
+ if (EQ(ks[i], key)) {
*value = vs[i];
return 1;
}
@@ -283,7 +283,7 @@ BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) {
idx = size;
- while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; }
+ while(idx > 0 && (c = CMP_TERM(kv[1],ks[idx-1])) < 0) { idx--; }
if (c == 0) {
/* last compare was equal,
@@ -353,7 +353,7 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) {
}
for( i = 0; i < n; i++) {
- if (eq(ks[i], key)) {
+ if (EQ(ks[i], key)) {
BIF_RET(am_true);
}
}
@@ -425,7 +425,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) {
vs2 = map_get_values(mp2);
while(i1 < n1 && i2 < n2) {
- c = CMP(ks1[i1],ks2[i2]);
+ c = CMP_TERM(ks1[i1],ks2[i2]);
if ( c == 0) {
/* use righthand side arguments map value,
* but advance both maps */
@@ -546,7 +546,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
}
} else {
for( i = 0; i < n; i ++) {
- if (eq(ks[i], key)) {
+ if (EQ(ks[i], key)) {
*hp++ = value;
vs++;
c = 1;
@@ -577,7 +577,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
ASSERT(n >= 0);
/* copy map in order */
- while (n && ((c = CMP(*ks, key)) < 0)) {
+ while (n && ((c = CMP_TERM(*ks, key)) < 0)) {
*shp++ = *ks++;
*hp++ = *vs++;
n--;
@@ -658,7 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
}
} else {
while(n--) {
- if (eq(*ks, key)) {
+ if (EQ(*ks, key)) {
ks++;
vs++;
found = 1;
@@ -732,7 +732,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res)
}
} else {
for( i = 0; i < n; i ++) {
- if (eq(ks[i], key)) {
+ if (EQ(ks[i], key)) {
*hp++ = value;
vs++;
found = 1;
--
cgit v1.2.3
From f3821597383fa20d1093dab70fa75b4a1018a6b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 12 Dec 2013 11:20:04 +0100
Subject: erts: Update maps instructions to use term order
Maps internally uses term order to store keys in an ordered fashion.
---
erts/emulator/beam/beam_emu.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index fe2e196785..89d9442526 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -6316,7 +6316,7 @@ static int has_not_map_field(Eterm map, Eterm key)
}
} else {
for (i = 0; i < n; i++) {
- if (eq(keys[i], key)) {
+ if (EQ(keys[i], key)) {
return 0;
}
}
@@ -6343,7 +6343,7 @@ static Eterm get_map_element(Eterm map, Eterm key)
}
} else {
for (i = 0; i < n; i++) {
- if (eq(ks[i], key)) {
+ if (EQ(ks[i], key)) {
return vs[i];
}
}
@@ -6506,7 +6506,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
ASSERT(kp < (Eterm *)mp);
key = *old_keys;
- if ((c = CMP(key, new_key)) < 0) {
+ if ((c = CMP_TERM(key, new_key)) < 0) {
/* Copy old key and value */
*kp++ = key;
*hp++ = *old_vals;
--
cgit v1.2.3
From c334d4841d9d600237184233518ee13f7421f91c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 10 Jan 2014 18:04:00 +0100
Subject: erts: Update maps serializing to use term order
* erlang:term_to_binary/1
* erlang:binary_to_term/1
---
erts/emulator/beam/external.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index a31b2731be..e8b77b9f37 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -3803,7 +3803,7 @@ dec_term_atom_common:
arity = arityval(*keys++);
while(arity-- > 1) {
- if (CMP(keys[arity-1],keys[arity]) >= 0) {
+ if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) {
goto error;
}
}
--
cgit v1.2.3
From 0422247a9d8259ef1a28704500f07caf827f42f6 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Thu, 16 Jan 2014 22:00:04 +0100
Subject: erts: Fix bug in erts_maps_remove
HRelease was called with wrong arguments and left garbage on heap
when key was not found.
---
erts/emulator/beam/erl_map.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 455ba25e11..e68482be58 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2013. All Rights Reserved.
+ * Copyright Ericsson AB 2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -614,6 +614,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
Sint n;
Sint found = 0;
Uint need;
+ Eterm *hp_start;
Eterm *thp, *mhp;
Eterm *ks, *vs, tup;
map_t *mp = (map_t*)map_val(map);
@@ -634,7 +635,8 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
*/
need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */
- thp = HAlloc(p, need);
+ hp_start = HAlloc(p, need);
+ thp = hp_start;
mhp = thp + n; /* offset with tuple heap size */
tup = make_tuple(thp);
@@ -676,7 +678,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
/* Not found, remove allocated memory
* and return previous map.
*/
- HRelease(p, thp + need, thp);
+ HRelease(p, hp_start + need, hp_start);
*res = map;
return 1;
--
cgit v1.2.3
From f1003b26524c2321b1eef36fb2d3997aaf0b9e10 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Thu, 16 Jan 2014 23:03:33 +0100
Subject: erts: Optimize erts_map_update/remove
to not continue comparing keys once it has been found.
---
erts/emulator/beam/erl_map.c | 42 ++++++++++++++++++++----------------------
1 file changed, 20 insertions(+), 22 deletions(-)
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index e68482be58..57f156509c 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -612,7 +612,6 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) {
int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
Sint n;
- Sint found = 0;
Uint need;
Eterm *hp_start;
Eterm *thp, *mhp;
@@ -650,9 +649,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
if (is_immed(key)) {
while(n--) {
if (*ks == key) {
- ks++;
- vs++;
- found = 1;
+ goto found_key;
} else {
*mhp++ = *vs++;
*thp++ = *ks++;
@@ -661,9 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
} else {
while(n--) {
if (EQ(*ks, key)) {
- ks++;
- vs++;
- found = 1;
+ goto found_key;
} else {
*mhp++ = *vs++;
*thp++ = *ks++;
@@ -671,10 +666,6 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
}
}
- if (found) {
- return 1;
- }
-
/* Not found, remove allocated memory
* and return previous map.
*/
@@ -682,6 +673,14 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
*res = map;
return 1;
+
+found_key:
+ /* Copy rest of keys and values */
+ if (n) {
+ sys_memcpy(mhp, vs+1, n*sizeof(Eterm));
+ sys_memcpy(thp, ks+1, n*sizeof(Eterm));
+ }
+ return 1;
}
BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
@@ -699,7 +698,6 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) {
int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) {
Sint n,i;
- Sint found = 0;
Eterm* hp,*shp;
Eterm *ks,*vs;
map_t *mp = (map_t*)map_val(map);
@@ -717,7 +715,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res)
hp = HAlloc(p, MAP_HEADER_SIZE + n);
shp = hp;
- *res = make_map(hp);
*hp++ = MAP_HEADER;
*hp++ = n;
*hp++ = mp->keys;
@@ -725,9 +722,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res)
if (is_immed(key)) {
for( i = 0; i < n; i ++) {
if (ks[i] == key) {
- *hp++ = value;
- vs++;
- found = 1;
+ goto found_key;
} else {
*hp++ = *vs++;
}
@@ -735,20 +730,23 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res)
} else {
for( i = 0; i < n; i ++) {
if (EQ(ks[i], key)) {
- *hp++ = value;
- vs++;
- found = 1;
+ goto found_key;
} else {
*hp++ = *vs++;
}
}
}
- if (found) {
- return 1;
- }
HRelease(p, shp + MAP_HEADER_SIZE + n, shp);
return 0;
+
+found_key:
+ *hp++ = value;
+ vs++;
+ if (++i < n)
+ sys_memcpy(hp, vs, (n - i)*sizeof(Eterm));
+ *res = make_map(shp);
+ return 1;
}
BIF_RETTYPE maps_update_3(BIF_ALIST_3) {
--
cgit v1.2.3
From 701ed39cc53e40783c4b48c83f9f686eb2668d38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 12 Dec 2013 13:24:05 +0100
Subject: compiler: Fix term order compiler for maps
---
lib/compiler/src/v3_codegen.erl | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index db8ea04778..c8735a76e8 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1572,7 +1572,7 @@ cg_map_pairs(Es0) ->
end || {_,Vs} <- R2],
%% R3 is now [{Op,{Key,Value}}]
- R = lists:sort(R3),
+ R = termsort(R3),
%% R4 is now sorted with all alloc first in the list, followed by
%% all exact.
@@ -1611,6 +1611,9 @@ map_pair_op_and_key(L) ->
{assoc,K}
end.
+termsort(Ls) ->
+ lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Ls).
+
%%%
%%% Code generation for constructing binaries.
%%%
--
cgit v1.2.3
From 4db2458bba34884448d3b3752dce74c17d92c698 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 12 Dec 2013 13:24:20 +0100
Subject: Update map_SUITE to respect term order
---
erts/emulator/test/map_SUITE.erl | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index a06cb43ac7..081e1b852e 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -228,7 +228,7 @@ t_update_assoc(Config) when is_list(Config) ->
M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
#{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
- M1 = M0#{1.0=>wrong,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+ #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
M2 = M0#{3.0=>new},
#{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
@@ -462,12 +462,10 @@ t_bif_map_find(Config) when is_list(Config) ->
{ok, 1} = maps:find(a, #{ a=> 1}),
{ok, 2} = maps:find(b, #{ a=> 1, b => 2}),
{ok, "int"} = maps:find(1, #{ 1 => "int"}),
- {ok, "int"} = maps:find(1.0, #{ 1 => "int"}),
- {ok, "float"} = maps:find(1, #{ 1.0 => "float"}),
{ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}),
{ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}),
- {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
+ {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
{ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
@@ -475,6 +473,10 @@ t_bif_map_find(Config) when is_list(Config) ->
%% error case
error = maps:find(a,#{}),
error = maps:find(a,#{b=>1, c=>2}),
+ error = maps:find(1.0, #{ 1 => "int"}),
+ error = maps:find(1, #{ 1.0 => "float"}),
+ error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
+
{'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])),
{'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)),
@@ -737,7 +739,7 @@ t_map_encode_decode(Config) when is_list(Config) ->
map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
M1 = maps:put(K,V,M0),
B0 = erlang:term_to_binary(M1),
- Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
+ Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
%% sort Ks and Vs according to term spec, then match it
ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]),
%% decode and match it
--
cgit v1.2.3
From 39c35199f5118a59f337b695a934c6bfcbf0813b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 13 Jan 2014 16:23:56 +0100
Subject: erts: Let erlang:binary_to_term/1 handle unsorted Maps
Maps may be encoded with keys in arbitrary order. This is fine,
as long as keys are unique.
---
erts/emulator/beam/external.c | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index e8b77b9f37..57251286c8 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -3790,24 +3790,41 @@ dec_term_atom_common:
}
}
- /* Iterate through all the maps and check for validity
+ /* Iterate through all the maps and check for validity and sort keys
* - done here for when we know it is complete.
*/
while (maps_head) {
- Eterm *keys;
- Sint arity;
+ map_t *mp = (map_t*)maps_head;
+ Eterm *ks = map_get_keys(mp);
+ Eterm *vs = map_get_values(mp);
+ Uint sz = map_get_size(mp);
+ Uint ix,jx;
+ Eterm tmp;
+ int c;
next = (Eterm *)(EXPAND_POINTER(*maps_head));
- keys = tuple_val(*(maps_head + 2));
- arity = arityval(*keys++);
- while(arity-- > 1) {
- if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) {
- goto error;
+ /* sort */
+
+ for ( ix = 1; ix < sz; ix++) {
+ jx = ix;
+ while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) {
+ /* identical key -> error */
+ if (c == 0) goto error;
+
+ tmp = ks[jx];
+ ks[jx] = ks[jx - 1];
+ ks[jx - 1] = tmp;
+
+ tmp = vs[jx];
+ vs[jx] = vs[jx - 1];
+ vs[jx - 1] = tmp;
+
+ jx--;
}
- }
+ }
*maps_head = MAP_HEADER;
maps_head = next;
}
--
cgit v1.2.3
From 228ee23186639c8b66be8ed26def7c8e9cb09059 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 13 Jan 2014 16:26:23 +0100
Subject: Update map_SUITE to test "unsorted" encoded maps
---
erts/emulator/test/map_SUITE.erl | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 081e1b852e..75381de556 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -711,19 +711,32 @@ t_map_encode_decode(Config) when is_list(Config) ->
],
ok = map_encode_decode_and_match(Pairs,[],#{}),
+ %% check sorting
+
+ %% literally #{ b=>2, a=>1 } in the internal order
+ #{ a:=1, b:=2 } =
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>),
+
+
+ %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
+ #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
+ 107,0,2,104,105, % "hi" :: list()
+ 100,0,1,97, % a :: atom()
+ 100,0,1,98, % b :: atom()
+ 107,0,5,118,97,108,117,101, % "value" :: list()
+ 97,33, % 33 :: integer()
+ 97,55 % 55 :: integer()
+ >>),
+
+
%% error cases
%% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>>
%% which is: #{ a=>1, b=>1 }
- %% order violation
- %% literally #{ b=>1, a=>1 } in the internal order (bad)
- {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
- erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)),
-
%% uniqueness violation
- %% literally #{ a=>1, a=>1 }
+ %% literally #{ a=>1, "hi"=>"value", a=>2 }
{'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
- erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)),
+ erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)),
%% bad size (too large)
{'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
--
cgit v1.2.3
From c8ecb6962a923ceb0b7e599d22adef773d042e4a Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Mon, 13 Jan 2014 22:15:13 +0100
Subject: erts: Remove enif_find_map_value
as it does the same thing as enif_get_map_value.
Replace with placeholder to be ABI backward compatible on Windows
as long as enif_find_map_value is not called.
---
erts/emulator/beam/erl_nif.c | 13 +------------
erts/emulator/beam/erl_nif_api_funcs.h | 5 ++---
2 files changed, 3 insertions(+), 15 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index c683847aaa..1a539c730f 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -1663,17 +1663,6 @@ int enif_get_map_value(ErlNifEnv* env,
return erts_maps_get(key, map, value);
}
-int enif_find_map_value(ErlNifEnv* env,
- Eterm map,
- Eterm key,
- Eterm *value)
-{
- if (is_not_map(map)) {
- return 0;
- }
- return erts_maps_get(key, map, value);
-}
-
int enif_make_map_update(ErlNifEnv* env,
Eterm map_in,
Eterm key,
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 4a5aacad48..71abb5dc8b 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -154,7 +154,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env));
ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out));
ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value));
-ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value));
+ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void));
ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out));
ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out));
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry));
@@ -303,7 +303,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa
# define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map)
# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put)
# define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value)
-# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value)
# define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update)
# define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove)
# define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create)
--
cgit v1.2.3
From b8a7203f04a1ed6d2756e6b782cd4c95a8c7c491 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 14 Jan 2014 18:22:34 +0100
Subject: erts: Increase version for NIF API and reject experimental v2.5
---
erts/emulator/beam/erl_nif.c | 3 ++-
erts/emulator/beam/erl_nif.h | 9 ++++-----
erts/emulator/beam/erl_nif_api_funcs.h | 1 -
3 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 1a539c730f..1eca7822eb 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1996,7 +1996,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful");
}
else if (entry->major != ERL_NIF_MAJOR_VERSION
- || entry->minor > ERL_NIF_MINOR_VERSION) {
+ || entry->minor > ERL_NIF_MINOR_VERSION
+ || (entry->major==2 && entry->minor == 5)) { /* experimental maps */
ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).",
entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 3c1e13f8a4..ae7f63f923 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -38,11 +38,13 @@
** 2.2: R14B03 enif_is_exception
** 2.3: R15 enif_make_reverse_list, enif_is_number
** 2.4: R16 enif_consume_timeslice
+** 2.5: First experimental maps API additions (libs of this version is not compatible with any other VM)
** 2.5: R17 Maps API additions
+** 2.6: R17 with maps
** R17 dirty schedulers
*/
#define ERL_NIF_MAJOR_VERSION 2
-#define ERL_NIF_MINOR_VERSION 5
+#define ERL_NIF_MINOR_VERSION 6
#include
@@ -165,7 +167,6 @@ typedef int ErlNifTSDKey;
typedef ErlDrvThreadOpts ErlNifThreadOpts;
-<<<<<<< HEAD
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
typedef enum
{
@@ -173,7 +174,7 @@ typedef enum
ERL_NIF_DIRTY_JOB_IO_BOUND = 2
}ErlNifDirtyTaskFlags;
#endif
-=======
+
typedef struct
{
/* use a lot of memory, structure may change */
@@ -190,8 +191,6 @@ typedef enum {
ERL_NIF_MAP_ITERATOR_TAIL = 2
} ErlNifMapIteratorEntry;
->>>>>>> erts: Add NIFs for Maps
-
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
# define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS
typedef struct {
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 71abb5dc8b..9c386a4c41 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -154,7 +154,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env));
ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out));
ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value));
-ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void));
ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out));
ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out));
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry));
--
cgit v1.2.3
From f321ea89fecdb343e58e2485c057326d08fed69c Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Mon, 13 Jan 2014 22:16:40 +0100
Subject: erts: Do not allow map iterator created without map
---
erts/emulator/beam/erl_nif.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 1eca7822eb..ca82590a78 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1725,21 +1725,27 @@ int enif_map_iterator_create(ErlNifEnv *env,
}
error:
+#ifdef DEBUG
iter->map = THE_NON_VALUE;
+#endif
return 0;
}
void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)
{
/* not used */
+#ifdef DEBUG
+ iter->map = THE_NON_VALUE;
+#endif
+
}
int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)
{
+ ASSERT(iter && is_map(iter->map));
ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
- if (is_map(iter->map) && (
- (iter->t_limit - iter->h_limit) == 1 ||
- iter->idx == iter->t_limit)) {
+ if ((iter->t_limit - iter->h_limit) == 1
+ || iter->idx == iter->t_limit) {
return 1;
}
return 0;
@@ -1747,10 +1753,10 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)
int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)
{
+ ASSERT(iter && is_map(iter->map));
ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
- if (is_map(iter->map) && (
- (iter->t_limit - iter->h_limit) == 1 ||
- iter->idx == iter->h_limit)) {
+ if ((iter->t_limit - iter->h_limit) == 1
+ || iter->idx == iter->h_limit) {
return 1;
}
return 0;
@@ -1759,7 +1765,8 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)
int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
{
- if (is_map(iter->map) && iter->idx < iter->t_limit) {
+ ASSERT(iter && is_map(iter->map));
+ if (iter->idx < iter->t_limit) {
iter->idx++;
if (iter->idx != iter->t_limit) {
iter->ks++;
@@ -1772,7 +1779,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
{
- if (is_map(iter->map) && iter->idx > iter->h_limit ) {
+ ASSERT(iter && is_map(iter->map));
+ if (iter->idx > iter->h_limit ) {
iter->idx--;
if (iter->idx != iter->h_limit ) {
iter->ks--;
@@ -1788,7 +1796,8 @@ int enif_map_iterator_get_pair(ErlNifEnv *env,
Eterm *key,
Eterm *value)
{
- if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) {
+ ASSERT(iter && is_map(iter->map));
+ if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) {
ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) &&
iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map))));
ASSERT(iter->vs >= map_get_values(map_val(iter->map)) &&
--
cgit v1.2.3
From 609d835ce04a1f6320377780a11fb32382e1e419 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 14 Jan 2014 11:25:34 +0100
Subject: erts: Let enif_map_iterator_next/prev return 0 to signal end of map.
---
erts/emulator/beam/erl_nif.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index ca82590a78..def9a7efe1 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1771,8 +1771,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
if (iter->idx != iter->t_limit) {
iter->ks++;
iter->vs++;
+ return 1;
}
- return 1;
}
return 0;
}
@@ -1785,8 +1785,8 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
if (iter->idx != iter->h_limit ) {
iter->ks--;
iter->vs--;
+ return 1;
}
- return 1;
}
return 0;
}
--
cgit v1.2.3
From d39affeac223a7e2891b509395e7b19c931b5018 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 14 Jan 2014 20:32:46 +0100
Subject: erts: Remove use of h_limit which is always zero.
---
erts/emulator/beam/erl_nif.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index def9a7efe1..94c2512650 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1718,7 +1718,6 @@ int enif_map_iterator_create(ErlNifEnv *env,
iter->ks = ((Eterm *)map_get_keys(mp)) + offset;
iter->vs = ((Eterm *)map_get_values(mp)) + offset;
iter->t_limit = map_get_size(mp) + 1;
- iter->h_limit = 0;
iter->idx = offset + 1;
return 1;
@@ -1744,8 +1743,7 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)
{
ASSERT(iter && is_map(iter->map));
ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
- if ((iter->t_limit - iter->h_limit) == 1
- || iter->idx == iter->t_limit) {
+ if (iter->t_limit == 1 || iter->idx == iter->t_limit) {
return 1;
}
return 0;
@@ -1755,8 +1753,7 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)
{
ASSERT(iter && is_map(iter->map));
ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
- if ((iter->t_limit - iter->h_limit) == 1
- || iter->idx == iter->h_limit) {
+ if (iter->t_limit == 1 || iter->idx == 0) {
return 1;
}
return 0;
@@ -1780,9 +1777,9 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
{
ASSERT(iter && is_map(iter->map));
- if (iter->idx > iter->h_limit ) {
+ if (iter->idx > 0) {
iter->idx--;
- if (iter->idx != iter->h_limit ) {
+ if (iter->idx != 0) {
iter->ks--;
iter->vs--;
return 1;
@@ -1797,7 +1794,7 @@ int enif_map_iterator_get_pair(ErlNifEnv *env,
Eterm *value)
{
ASSERT(iter && is_map(iter->map));
- if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) {
+ if (iter->idx > 0 && iter->idx < iter->t_limit) {
ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) &&
iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map))));
ASSERT(iter->vs >= map_get_values(map_val(iter->map)) &&
--
cgit v1.2.3
From ada36d9024b4ceda974f32a78b5fe39b64f59319 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 14 Jan 2014 20:55:10 +0100
Subject: erts: Simplify some map iterator code
---
erts/emulator/beam/erl_nif.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 94c2512650..c6f7c8adb5 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1743,20 +1743,14 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)
{
ASSERT(iter && is_map(iter->map));
ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
- if (iter->t_limit == 1 || iter->idx == iter->t_limit) {
- return 1;
- }
- return 0;
+ return (iter->t_limit == 1 || iter->idx == iter->t_limit);
}
int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)
{
ASSERT(iter && is_map(iter->map));
ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1));
- if (iter->t_limit == 1 || iter->idx == 0) {
- return 1;
- }
- return 0;
+ return (iter->t_limit == 1 || iter->idx == 0);
}
--
cgit v1.2.3
From 00f9be42e43913bce9b110382e55bfbdaa9406d0 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 14 Jan 2014 18:13:45 +0100
Subject: erts: Fix map iterator bug when reverting from end of map position
and simplify code by ignoring h_limit which is always zero.
---
erts/emulator/beam/erl_nif.c | 18 +++------
erts/emulator/test/nif_SUITE.erl | 5 ++-
erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 57 ++++++++++++++++++++-------
3 files changed, 53 insertions(+), 27 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index c6f7c8adb5..6fceb8a0ba 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1759,13 +1759,10 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)
ASSERT(iter && is_map(iter->map));
if (iter->idx < iter->t_limit) {
iter->idx++;
- if (iter->idx != iter->t_limit) {
- iter->ks++;
- iter->vs++;
- return 1;
- }
+ iter->ks++;
+ iter->vs++;
}
- return 0;
+ return (iter->idx != iter->t_limit);
}
int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
@@ -1773,13 +1770,10 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)
ASSERT(iter && is_map(iter->map));
if (iter->idx > 0) {
iter->idx--;
- if (iter->idx != 0) {
- iter->ks--;
- iter->vs--;
- return 1;
- }
+ iter->ks--;
+ iter->vs--;
}
- return 0;
+ return (iter->idx > 0);
}
int enif_map_iterator_get_pair(ErlNifEnv *env,
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 5b7c310aa4..a34f70c618 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -449,6 +449,9 @@ maps(Config) when is_list(Config) ->
io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]),
Is = lists:sort(Pairs),
Is = lists:reverse(RIs),
+
+ #{} = maps_from_list([]),
+ {[],[]} = sorted_list_from_maps(#{}),
ok.
api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"];
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index a0316a7861..8549d277de 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -1564,31 +1564,60 @@ static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NI
ERL_NIF_TERM map = argv[0];
ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */
- ERL_NIF_TERM key, value;
- ErlNifMapIterator iter;
+ ERL_NIF_TERM key, value, k2, v2;
+ ErlNifMapIterator iter_f;
+ ErlNifMapIterator iter_b;
+ int cnt, next_ret, prev_ret;
if (argc != 1 && !enif_is_map(env, map))
- return enif_make_badarg(env);
+ return enif_make_int(env, __LINE__);
- if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD))
- return enif_make_badarg(env);
+ if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD))
+ return enif_make_int(env, __LINE__);
- while(enif_map_iterator_get_pair(env,&iter,&key,&value)) {
+ cnt = 0;
+ while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) {
+ if (cnt && !next_ret)
+ return enif_make_int(env, __LINE__);
list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f);
- enif_map_iterator_next(env,&iter);
+ next_ret = enif_map_iterator_next(env,&iter_f);
+ cnt++;
}
+ if (cnt && next_ret)
+ return enif_make_int(env, __LINE__);
- enif_map_iterator_destroy(env, &iter);
+ if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL))
+ return enif_make_int(env, __LINE__);
- if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL))
- return enif_make_badarg(env);
+ cnt = 0;
+ while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) {
+ if (cnt && !prev_ret)
+ return enif_make_int(env, __LINE__);
+
+ /* Test that iter_f can step "backwards" */
+ if (!enif_map_iterator_prev(env,&iter_f)
+ || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2)
+ || k2 != key || v2 != value) {
+ return enif_make_int(env, __LINE__);
+ }
- while(enif_map_iterator_get_pair(env,&iter,&key,&value)) {
list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
- enif_map_iterator_prev(env,&iter);
+ prev_ret = enif_map_iterator_prev(env,&iter_b);
+ }
+
+ if (cnt) {
+ if (prev_ret || enif_map_iterator_prev(env,&iter_f))
+ return enif_make_int(env, __LINE__);
+
+ /* Test that iter_b can step "backwards" one step */
+ if (!enif_map_iterator_next(env, &iter_b)
+ || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2)
+ || k2 != key || v2 != value)
+ return enif_make_int(env, __LINE__);
}
- enif_map_iterator_destroy(env, &iter);
+ enif_map_iterator_destroy(env, &iter_f);
+ enif_map_iterator_destroy(env, &iter_b);
return enif_make_tuple2(env, list_f, list_b);
}
--
cgit v1.2.3
From a18853e6814ab42188e56343e6363ff94c794bb8 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 14 Jan 2014 18:21:26 +0100
Subject: erts: Optimize struct ErlNifMapIterator
No need to use 64bit integers on 32bit machines.
---
erts/emulator/beam/erl_nif.h | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index ae7f63f923..7613446f64 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -103,6 +103,8 @@ typedef unsigned long long ERL_NIF_TERM;
# endif
#endif
+typedef ERL_NIF_TERM ERL_NIF_UINT;
+
struct enif_environment_t;
typedef struct enif_environment_t ErlNifEnv;
@@ -175,15 +177,14 @@ typedef enum
}ErlNifDirtyTaskFlags;
#endif
-typedef struct
+typedef struct /* All fields all internal and may change */
{
- /* use a lot of memory, structure may change */
ERL_NIF_TERM map;
- ErlNifUInt64 h_limit;
- ErlNifUInt64 t_limit;
- ErlNifUInt64 idx;
+ ERL_NIF_UINT t_limit;
+ ERL_NIF_UINT idx;
ERL_NIF_TERM *ks;
ERL_NIF_TERM *vs;
+ void* __spare__[2]; /* for future additions to be ABI compatible (same struct size) */
} ErlNifMapIterator;
typedef enum {
--
cgit v1.2.3
From e21989fdc834c9c9e75454bd06cb6ef29218bf9f Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Thu, 16 Jan 2014 23:07:01 +0100
Subject: erts: Add more tests for the NIF map API
---
erts/emulator/test/nif_SUITE.erl | 49 +++++++++++++++++++++---
erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 55 +++++++++++++++++++++++++--
2 files changed, 94 insertions(+), 10 deletions(-)
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index a34f70c618..bcc1f9e5af 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -444,14 +444,44 @@ maps(Config) when is_list(Config) ->
[{I,I}||I <- lists:seq(1,10)] ++
[{a,value},{"a","value"},{<<"a">>,<<"value">>}],
ok = ensure_lib_loaded(Config, 1),
- M = maps_from_list(Pairs),
- R = {RIs,Is} = sorted_list_from_maps(M),
+ M = maps_from_list_nif(Pairs),
+ R = {RIs,Is} = sorted_list_from_maps_nif(M),
io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]),
Is = lists:sort(Pairs),
Is = lists:reverse(RIs),
- #{} = maps_from_list([]),
- {[],[]} = sorted_list_from_maps(#{}),
+ #{} = maps_from_list_nif([]),
+ {[],[]} = sorted_list_from_maps_nif(#{}),
+
+ 1 = is_map_nif(M),
+ 0 = is_map_nif("no map"),
+
+ Msz = map_size(M),
+ {1,Msz} = get_map_size_nif(M),
+ {1,0} = get_map_size_nif(#{}),
+ {0,-123} = get_map_size_nif({#{}}),
+
+ #{} = M0 = make_new_map_nif(),
+
+ {1, #{key := value}=M1} = make_map_put_nif(M0, key, value),
+ {1, #{key := value, "key2" := "value2"}=M2} = make_map_put_nif(M1, "key2", "value2"),
+ {1, #{key := "value", "key2" := "value2"}=M3} = make_map_put_nif(M2, key, "value"),
+ {0, undefined} = make_map_put_nif(666, key, value),
+
+ {1, "value2"} = get_map_value_nif(M3,"key2"),
+ {0, undefined} = get_map_value_nif(M3,"key3"),
+ {0, undefined} = get_map_value_nif(false,key),
+
+ {0, undefined} = make_map_update_nif(M0, key, value),
+ {0, undefined} = make_map_update_nif(M1, "key2", "value2"),
+ {1, #{key := "value", "key2" := "value2"}} = make_map_update_nif(M2, key, "value"),
+ {0, undefined} = make_map_update_nif(666, key, value),
+
+ {1, #{}} = make_map_remove_nif(M1, key),
+ {1, M1} = make_map_remove_nif(M2, "key2"),
+ {1, M2} = make_map_remove_nif(M2, "key3"),
+ {0, undefined} = make_map_remove_nif(self(), key),
+
ok.
api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"];
@@ -1508,8 +1538,15 @@ consume_timeslice_nif(_,_) -> ?nif_stub.
call_dirty_nif(_,_,_) -> ?nif_stub.
%% maps
-maps_from_list(_) -> ?nif_stub.
-sorted_list_from_maps(_) -> ?nif_stub.
+is_map_nif(_) -> ?nif_stub.
+get_map_size_nif(_) -> ?nif_stub.
+make_new_map_nif() -> ?nif_stub.
+make_map_put_nif(_,_,_) -> ?nif_stub.
+get_map_value_nif(_,_) -> ?nif_stub.
+make_map_update_nif(_,_,_) -> ?nif_stub.
+make_map_remove_nif(_,_) -> ?nif_stub.
+maps_from_list_nif(_) -> ?nif_stub.
+sorted_list_from_maps_nif(_) -> ?nif_stub.
nif_stub_error(Line) ->
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 8549d277de..9ee89a49a4 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1536,8 +1536,48 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
}
#endif
+static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_int(env, enif_is_map(env,argv[0]));
+}
+static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int size = -123;
+ int ret = enif_get_map_size(env, argv[0], &size);
+ return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size));
+}
+static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_new_map(env);
+}
+static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
+ int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out);
+ return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
+}
+static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM value = enif_make_atom(env, "undefined");
+ int ret = enif_get_map_value(env, argv[0], argv[1], &value);
+ return enif_make_tuple2(env, enif_make_int(env,ret), value);
+
+}
+static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
+ int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out);
+ return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
+}
+static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
+ int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out);
+ return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
+}
+
/* maps */
-static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM cell = argv[0];
ERL_NIF_TERM map = enif_make_new_map(env);
@@ -1559,7 +1599,7 @@ static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM
return map;
}
-static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
ERL_NIF_TERM map = argv[0];
ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
@@ -1675,8 +1715,15 @@ static ErlNifFunc nif_funcs[] =
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
{"call_dirty_nif", 3, call_dirty_nif},
#endif
- {"maps_from_list", 1, maps_from_list},
- {"sorted_list_from_maps", 1, sorted_list_from_maps}
+ {"is_map_nif", 1, is_map_nif},
+ {"get_map_size_nif", 1, get_map_size_nif},
+ {"make_new_map_nif", 0, make_new_map_nif},
+ {"make_map_put_nif", 3, make_map_put_nif},
+ {"get_map_value_nif", 2, get_map_value_nif},
+ {"make_map_update_nif", 3, make_map_update_nif},
+ {"make_map_remove_nif", 2, make_map_remove_nif},
+ {"maps_from_list_nif", 1, maps_from_list_nif},
+ {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload)
--
cgit v1.2.3
From 9eddc8eb4c7f4ef45b51650750334a787c5f9ef7 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Thu, 16 Jan 2014 23:45:42 +0100
Subject: erts: Fix compile error for halfword emulator
---
erts/emulator/beam/erl_utils.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index c6247c7246..5b81d814c6 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -204,9 +204,9 @@ int eq(Eterm, Eterm);
#if HALFWORD_HEAP
Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int);
#define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0)
-#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1)
-#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0)
-#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1)
+#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1)
+#define CMP(A,B) cmp_rel_opt(A,NULL,B,NULL,0)
+#define CMP_TERM(A,B) cmp_rel_opt(A,NULL,B,NULL,1)
#else
Sint cmp(Eterm, Eterm, int);
#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0)
--
cgit v1.2.3
From f2b9a5623ec5c9a314560add82ef20a03ba91235 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Fri, 17 Jan 2014 12:32:42 +0100
Subject: erts: Change 'size' argument of enif_get_map_size from int* to
size_t*
---
erts/emulator/beam/erl_nif.c | 2 +-
erts/emulator/beam/erl_nif_api_funcs.h | 2 +-
erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 6fceb8a0ba..c35f1fc2c6 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1610,7 +1610,7 @@ int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)
return is_map(term);
}
-int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size)
+int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)
{
if (is_map(term)) {
map_t *mp;
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 9c386a4c41..4121378e1a 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -150,7 +150,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void));
#endif
ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term));
-ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size));
+ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, size_t *size));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env));
ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out));
ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value));
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 9ee89a49a4..b550d1f16d 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1542,9 +1542,9 @@ static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
}
static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- int size = -123;
+ size_t size = (size_t)-123;
int ret = enif_get_map_size(env, argv[0], &size);
- return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size));
+ return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size));
}
static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
--
cgit v1.2.3
From 3645dc6b15d4fdd79768b92bb598c264005a8689 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 14 Jan 2014 12:30:51 +0100
Subject: preloaded: Fixup export cmp_term in erts_internal
---
erts/preloaded/src/erts_internal.erl | 1 +
1 file changed, 1 insertion(+)
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 88eb317f1d..edcd50c77e 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -30,6 +30,7 @@
-export([await_port_send_result/3]).
-export([binary_to_term/1, binary_to_term/2]).
+-export([cmp_term/2]).
-export([port_command/3, port_connect/2, port_close/1,
port_control/3, port_call/3, port_info/1, port_info/2]).
--
cgit v1.2.3
From 1f6c5e88f4f387cc27c9affbdf1ceaae7cfdcfd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 13 Jan 2014 17:33:44 +0100
Subject: compiler: Squash #k_map_pair_*{} to #k_map_pair{}
Simplify compiler internals for kernel passes.
---
lib/compiler/src/v3_kernel.erl | 20 +++++++++-----------
lib/compiler/src/v3_kernel.hrl | 3 +--
lib/compiler/src/v3_kernel_pp.erl | 4 ++--
lib/compiler/src/v3_life.erl | 8 +++-----
4 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index a5a94c1294..1d7d168214 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -503,11 +503,11 @@ map_pairs(Es, Sub, St) ->
(#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) ->
{K,[],St1} = expr(K0, Sub, St0),
{V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair_assoc{key=K,val=V}|Kes],Ep ++ Esp,St2};
+ {[#k_map_pair{op=assoc,key=K,val=V}|Kes],Ep ++ Esp,St2};
(#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) ->
{K,[],St1} = expr(K0, Sub, St0),
{V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair_exact{key=K,val=V}|Kes],Ep ++ Esp,St2}
+ {[#k_map_pair{op=exact,key=K,val=V}|Kes],Ep ++ Esp,St2}
end, {[],[],St}, Es).
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
@@ -671,7 +671,7 @@ pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
{Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
{Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
- {#k_map_pair_exact{anno=A,key=Kk,val=Kv},Osub2,St2};
+ {#k_map_pair{anno=A,op=exact,key=Kk,val=Kv},Osub2,St2};
pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->
{Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),
{#k_binary{anno=A,segs=Kv},Osub1,St1};
@@ -1348,8 +1348,8 @@ get_match(#k_tuple{es=Es}, St0) ->
get_match(#k_map{es=Es0}, St0) ->
{Mes,St1} = new_vars(length(Es0), St0),
{Es,_} = mapfoldl(fun
- (#k_map_pair_exact{}=Pair, [V|Vs]) ->
- {Pair#k_map_pair_exact{val=V},Vs}
+ (#k_map_pair{}=Pair, [V|Vs]) ->
+ {Pair#k_map_pair{val=V},Vs}
end, Mes, Es0),
{#k_map{es=Es},Mes,St1};
get_match(M, St) ->
@@ -1370,7 +1370,7 @@ new_clauses(Cs0, U, St) ->
[N|As];
#k_map{es=Es} ->
Vals = [V ||
- #k_map_pair_exact{val=V} <- Es],
+ #k_map_pair{op=exact,val=V} <- Es],
Vals ++ As;
_Other ->
As
@@ -1472,7 +1472,7 @@ arg_val(Arg, C) ->
end;
#k_map{es=Es} ->
Keys = [begin
- #k_map_pair_exact{key=#k_literal{val=Key}} = Pair,
+ #k_map_pair{op=exact,key=#k_literal{val=Key}} = Pair,
Key
end || Pair <- Es],
%% multiple keys may have the same name
@@ -1848,9 +1848,7 @@ lit_vars(#k_cons{hd=H,tl=T}) ->
union(lit_vars(H), lit_vars(T));
lit_vars(#k_map{var=Var,es=Es}) ->
lit_list_vars([Var|Es]);
-lit_vars(#k_map_pair_assoc{key=K,val=V}) ->
- union(lit_vars(K), lit_vars(V));
-lit_vars(#k_map_pair_exact{key=K,val=V}) ->
+lit_vars(#k_map_pair{key=K,val=V}) ->
union(lit_vars(K), lit_vars(V));
lit_vars(#k_binary{segs=V}) -> lit_vars(V);
lit_vars(#k_bin_end{}) -> [];
@@ -1890,7 +1888,7 @@ pat_vars(#k_tuple{es=Es}) ->
pat_list_vars(Es);
pat_vars(#k_map{es=Es}) ->
pat_list_vars(Es);
-pat_vars(#k_map_pair_exact{val=V}) ->
+pat_vars(#k_map_pair{op=exact,val=V}) ->
pat_vars(V).
pat_list_vars(Ps) ->
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index 9da394d50d..c7886a070d 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -39,8 +39,7 @@
-record(k_tuple, {anno=[],es}).
-record(k_map, {anno=[],var,es}).
--record(k_map_pair_assoc, {anno=[],key,val}).
--record(k_map_pair_exact, {anno=[],key,val}).
+-record(k_map_pair, {anno=[],op,key,val}).
-record(k_cons, {anno=[],hd,tl}).
-record(k_binary, {anno=[],segs}).
-record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index e8d19336f6..edbd3f74f8 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -115,9 +115,9 @@ format_1(#k_map{es=Es}, Ctxt) ->
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
$},$~
];
-format_1(#k_map_pair_assoc{key=K,val=V}, Ctxt) ->
+format_1(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) ->
["~<",format(K, Ctxt),",",format(V, Ctxt),">"];
-format_1(#k_map_pair_exact{key=K,val=V}, Ctxt) ->
+format_1(#k_map_pair{op=exact,key=K,val=V}, Ctxt) ->
["::<",format(K, Ctxt),",",format(V, Ctxt),">"];
format_1(#k_binary{segs=S}, Ctxt) ->
["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index a3bd781c98..ae928e955c 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -369,9 +369,9 @@ literal(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list(Es, Ctxt)};
literal(#k_map{var=Var,es=Es}, Ctxt) ->
{map,literal(Var, Ctxt),literal_list(Es, Ctxt)};
-literal(#k_map_pair_assoc{key=K,val=V}, Ctxt) ->
+literal(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) ->
{map_pair_assoc,literal(K, Ctxt),literal(V, Ctxt)};
-literal(#k_map_pair_exact{key=K,val=V}, Ctxt) ->
+literal(#k_map_pair{op=exact,key=K,val=V}, Ctxt) ->
{map_pair_exact,literal(K, Ctxt),literal(V, Ctxt)};
literal(#k_literal{val=V}, _Ctxt) ->
{literal,V}.
@@ -404,9 +404,7 @@ literal2(#k_tuple{es=Es}, Ctxt) ->
{tuple,literal_list2(Es, Ctxt)};
literal2(#k_map{es=Es}, Ctxt) ->
{map,literal_list2(Es, Ctxt)};
-literal2(#k_map_pair_assoc{key=K,val=V}, Ctxt) ->
- {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)};
-literal2(#k_map_pair_exact{key=K,val=V}, Ctxt) ->
+literal2(#k_map_pair{key=K,val=V}, Ctxt) ->
{map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}.
literal_list2(Ks, Ctxt) ->
--
cgit v1.2.3
From 5b4b6c9b6bd1cf258841004f2e11bb2a82ebfe24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 13 Jan 2014 18:05:00 +0100
Subject: compiler: Squash #c_map_pair_*{} to #c_map_pair{}
Simplify compiler internals and parsing of core format.
---
lib/compiler/src/cerl.erl | 106 +++++++++--------------------------
lib/compiler/src/cerl_inline.erl | 46 ++++++++++++++-
lib/compiler/src/cerl_trees.erl | 65 ++++++++++-----------
lib/compiler/src/core_lib.erl | 4 +-
lib/compiler/src/core_lint.erl | 10 +---
lib/compiler/src/core_parse.hrl | 8 +--
lib/compiler/src/core_parse.yrl | 6 +-
lib/compiler/src/core_pp.erl | 4 +-
lib/compiler/src/sys_core_dsetel.erl | 10 +---
lib/compiler/src/sys_core_fold.erl | 16 ++----
lib/compiler/src/v3_core.erl | 15 +++--
lib/compiler/src/v3_kernel.erl | 11 ++--
12 files changed, 134 insertions(+), 167 deletions(-)
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index c6bf046c4d..60a8559950 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -124,11 +124,11 @@
%% keep map exports here for now
map_es/1,
- update_c_map_skel/2,
- update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2,
- ann_c_map_skel/2,
- ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2,
- map_pair_es/1, map_pair_assoc_es/1, map_pair_exact_es/1
+ update_c_map/2,
+ ann_c_map/2,
+ map_pair_op/1,map_pair_key/1,map_pair_val/1,
+ update_c_map_pair/4,
+ ann_c_map_pair/4
]).
-export_type([c_binary/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, c_literal/0,
@@ -155,8 +155,7 @@
-type c_letrec() :: #c_letrec{}.
-type c_literal() :: #c_literal{}.
-type c_map() :: #c_map{}.
--type c_map_pair_assoc() :: #c_map_pair_assoc{}.
--type c_map_pair_exact() :: #c_map_pair_exact{}.
+-type c_map_pair() :: #c_map_pair{}.
-type c_module() :: #c_module{}.
-type c_primop() :: #c_primop{}.
-type c_receive() :: #c_receive{}.
@@ -169,7 +168,7 @@
-type cerl() :: c_alias() | c_apply() | c_binary() | c_bitstr()
| c_call() | c_case() | c_catch() | c_clause() | c_cons()
| c_fun() | c_let() | c_letrec() | c_literal()
- | c_map() | c_map_pair_assoc() | c_map_pair_exact()
+ | c_map() | c_map_pair()
| c_module() | c_primop() | c_receive() | c_seq()
| c_try() | c_tuple() | c_values() | c_var().
@@ -282,8 +281,7 @@ type(#c_let{}) -> 'let';
type(#c_letrec{}) -> letrec;
type(#c_literal{}) -> literal;
type(#c_map{}) -> map;
-type(#c_map_pair_assoc{}) -> map_pair_assoc;
-type(#c_map_pair_exact{}) -> map_pair_exact;
+type(#c_map_pair{}) -> map_pair;
type(#c_module{}) -> module;
type(#c_primop{}) -> primop;
type(#c_receive{}) -> 'receive';
@@ -1581,50 +1579,24 @@ ann_make_list(_, [], Node) ->
map_es(#c_map{es = Es}) ->
Es.
-map_pair_assoc_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V].
-map_pair_exact_es(#c_map_pair_exact{key=K,val=V}) -> [K,V].
-map_pair_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V];
-map_pair_es(#c_map_pair_exact{key=K,val=V}) -> [K,V].
-
-update_c_map_pair_assoc_skel(Old, [K,V]) ->
- #c_map_pair_assoc{key=K, val=V, anno = get_ann(Old)}.
-
-update_c_map_pair_exact_skel(Old, [K,V]) ->
- #c_map_pair_exact{key=K, val=V, anno = get_ann(Old)}.
-
-ann_c_map_pair_assoc_skel(As, [K,V]) ->
- #c_map_pair_assoc{key = K, val=V, anno = As}.
-
-ann_c_map_pair_exact_skel(As, [K,V]) ->
- #c_map_pair_exact{key = K, val=V, anno = As}.
-
-%c_map_skel(Es) ->
-% #c_map{es = Es}.
-%
-
ann_c_map(As, Es) ->
#c_map{es = Es, anno = As }.
-%% TODO: when we have map literals use a variant of
-%% case is_lit_list(Es) of
-%% false ->
-%% #c_map{es = Es, anno = As};
-%% true ->
-%% #c_literal{val = maps:from_list(lit_list_vals(Es)), anno = As}
-%% end.
-
-ann_c_map_pair_assoc(As, [K,V]) ->
- #c_map_pair_assoc{key = K, val=V, anno = As}.
+update_c_map(Old, Es) ->
+ #c_map{es = Es, anno = get_ann(Old)}.
-ann_c_map_pair_exact(As, [K,V]) ->
- #c_map_pair_exact{key = K, val=V, anno = As}.
+map_pair_key(#c_map_pair{key=K}) -> K.
+map_pair_val(#c_map_pair{val=V}) -> V.
+map_pair_op(#c_map_pair{op=Op}) -> Op.
+-spec ann_c_map_pair([term()], cerl(), cerl(), cerl()) ->
+ c_map_pair().
-ann_c_map_skel(As, Es) ->
- #c_map{es = Es, anno = As}.
+ann_c_map_pair(As,Op,K,V) ->
+ #c_map_pair{op=Op, key = K, val=V, anno = As}.
-update_c_map_skel(Old, Es) ->
- #c_map{es = Es, anno = get_ann(Old)}.
+update_c_map_pair(Old,Op,K,V) ->
+ #c_map_pair{op=Op, key=K, val=V, anno = get_ann(Old)}.
%% ---------------------------------------------------------------------
@@ -3017,8 +2989,8 @@ pat_vars(Node, Vs) ->
pat_list_vars(tuple_es(Node), Vs);
map ->
pat_list_vars(map_es(Node), Vs);
- map_pair_exact ->
- pat_list_vars(map_pair_exact_es(Node), Vs);
+ map_pair ->
+ pat_list_vars([map_pair_op(Node),map_pair_key(Node),map_pair_val(Node)],Vs);
binary ->
pat_list_vars(binary_segments(Node), Vs);
bitstr ->
@@ -3830,12 +3802,6 @@ is_data(#c_cons{}) ->
true;
is_data(#c_tuple{}) ->
true;
-is_data(#c_map{}) ->
- true;
-is_data(#c_map_pair_assoc{}) ->
- true;
-is_data(#c_map_pair_exact{}) ->
- true;
is_data(_) ->
false.
@@ -3881,14 +3847,7 @@ data_type(#c_literal{val = V}) ->
data_type(#c_cons{}) ->
cons;
data_type(#c_tuple{}) ->
- tuple;
-data_type(#c_map{}) ->
- map;
-data_type(#c_map_pair_assoc{}) ->
- map_pair_assoc;
-data_type(#c_map_pair_exact{}) ->
- map_pair_exact.
-
+ tuple.
%% @spec data_es(Node::cerl()) -> [cerl()]
%%
@@ -3919,14 +3878,7 @@ data_es(#c_literal{val = V}) ->
data_es(#c_cons{hd = H, tl = T}) ->
[H, T];
data_es(#c_tuple{es = Es}) ->
- Es;
-data_es(#c_map{es=Es}) ->
- Es;
-data_es(#c_map_pair_assoc{key=K,val=V}) ->
- [K,V];
-data_es(#c_map_pair_exact{key=K,val=V}) ->
- [K,V].
-
+ Es.
%% @spec data_arity(Node::cerl()) -> integer()
%%
@@ -3982,11 +3934,7 @@ make_data(CType, Es) ->
ann_make_data(As, {atomic, V}, []) -> #c_literal{val = V, anno = As};
ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T);
-ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es);
-ann_make_data(As, map, Es) -> ann_c_map(As, Es);
-ann_make_data(As, map_pair_assoc, Es) -> ann_c_map_pair_assoc(As, Es);
-ann_make_data(As, map_pair_exact, Es) -> ann_c_map_pair_exact(As, Es).
-
+ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es).
%% @spec update_data(Old::cerl(), Type::dtype(),
%% Elements::[cerl()]) -> cerl()
@@ -4119,10 +4067,8 @@ subtrees(T) ->
[tuple_es(T)];
map ->
[map_es(T)];
- map_pair_assoc ->
- [map_pair_assoc_es(T)];
- map_pair_exact ->
- [map_pair_exact_es(T)];
+ map_pair ->
+ [[map_pair_op(T)],[map_pair_key(T)],[map_pair_val(T)]];
'let' ->
[let_vars(T), [let_arg(T)], [let_body(T)]];
seq ->
diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl
index c6de63c69f..3837b57750 100644
--- a/lib/compiler/src/cerl_inline.erl
+++ b/lib/compiler/src/cerl_inline.erl
@@ -63,7 +63,11 @@
receive_clauses/1, receive_timeout/1, seq_arg/1,
seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,
try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1,
- type/1, values_es/1, var_name/1]).
+ type/1, values_es/1, var_name/1,
+ map_es/1, update_c_map/2,
+ update_c_map_pair/4,
+ map_pair_op/1, map_pair_key/1, map_pair_val/1
+ ]).
-import(lists, [foldl/3, foldr/3, mapfoldl/3, reverse/1]).
@@ -128,6 +132,8 @@ weight(call) -> 3; % Assume remote-calls as efficient as `apply'.
weight(primop) -> 2; % Assume more efficient than `apply'.
weight(binary) -> 4; % Initialisation base cost.
weight(bitstr) -> 3; % Coding/decoding a value; like a primop.
+weight(map) -> 4; % Initialisation base cost.
+weight(map_pair) -> 3; % Coding/decoding a value; like a primop.
weight(module) -> 1. % Like a letrec with a constant body
%% These "reference" structures are used for variables and function
@@ -333,6 +339,8 @@ i(E, Ctxt, Ren, Env, S0) ->
i_catch(E, Ctxt, Ren, Env, S);
binary ->
i_binary(E, Ren, Env, S);
+ map ->
+ i_map(E, Ctxt, Ren, Env, S);
module ->
i_module(E, Ctxt, Ren, Env, S)
end
@@ -1324,6 +1332,25 @@ i_bitstr(E, Ren, Env, S) ->
S3 = count_size(weight(bitstr), S2),
{update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
+i_map(E, Ctx, Ren, Env, S) ->
+ %% Visit the segments for value.
+ {Es, S1} = mapfoldl(fun (E, S) ->
+ i_map_pair(E, Ctx, Ren, Env, S)
+ end,
+ S, map_es(E)),
+ S2 = count_size(weight(map), S1),
+ {update_c_map(E, Es), S2}.
+
+i_map_pair(E, Ctx, Ren, Env, S) ->
+ %% It is not necessary to visit the Op and Key fields,
+ %% since these are always literals.
+ {Val, S1} = i(map_pair_val(E), Ctx, Ren, Env, S),
+ Op = map_pair_op(E),
+ Key = map_pair_key(E),
+ S2 = count_size(weight(map_pair), S1),
+ {update_c_map_pair(E, Op, Key, Val), S2}.
+
+
%% This is a simplified version of `i_pattern', for lists of parameter
%% variables only. It does not modify the state.
@@ -1383,6 +1410,14 @@ i_pattern(E, Ren, Env, Ren0, Env0, S) ->
S, binary_segments(E)),
S2 = count_size(weight(binary), S1),
{update_c_binary(E, Es), S2};
+ map ->
+ {Es, S1} = mapfoldl(fun (E, S) ->
+ i_map_pair_pattern(E, Ren, Env,
+ Ren0, Env0, S)
+ end,
+ S, map_es(E)),
+ S2 = count_size(weight(map), S1),
+ {update_c_map(E, Es), S2};
_ ->
case is_literal(E) of
true ->
@@ -1416,6 +1451,15 @@ i_bitstr_pattern(E, Ren, Env, Ren0, Env0, S) ->
S3 = count_size(weight(bitstr), S2),
{update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
+i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S) ->
+ %% It is not necessary to visit the Op it is always a literal.
+ %% Same goes for Key
+ {Val, S1} = i_pattern(map_pair_val(E), Ren, Env, Ren0, Env0, S),
+ Op = map_pair_op(E), %% should be 'exact' literal
+ Key = map_pair_key(E),
+ S2 = count_size(weight(map_pair), S1),
+ {update_c_map_pair(E, Op, Key, Val), S2}.
+
%% ---------------------------------------------------------------------
%% Other central inlining functions
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index dc1cc606b3..2542841eef 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -58,11 +58,11 @@
update_c_values/2, values_es/1, var_name/1,
map_es/1,
- update_c_map_skel/2,
- update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2,
- ann_c_map_skel/2,
- ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2,
- map_pair_assoc_es/1, map_pair_exact_es/1
+ ann_c_map/2,
+ update_c_map/2,
+ map_pair_key/1,map_pair_val/1,map_pair_op/1,
+ ann_c_map_pair/4,
+ update_c_map_pair/4
]).
@@ -138,11 +138,11 @@ map_1(F, T) ->
tuple ->
update_c_tuple_skel(T, map_list(F, tuple_es(T)));
map ->
- update_c_map_skel(T, map_list(F, map_es(T)));
- map_pair_assoc ->
- update_c_map_pair_assoc_skel(T, map_list(F, map_pair_assoc_es(T)));
- map_pair_exact ->
- update_c_map_pair_exact_skel(T, map_list(F, map_pair_exact_es(T)));
+ update_c_map(T, map_list(F, map_es(T)));
+ map_pair ->
+ update_c_map_pair(T, map(F, map_pair_op(T)),
+ map(F, map_pair_key(T)),
+ map(F, map_pair_val(T)));
'let' ->
update_c_let(T, map_list(F, let_vars(T)),
map(F, let_arg(T)),
@@ -251,10 +251,12 @@ fold_1(F, S, T) ->
fold_list(F, S, tuple_es(T));
map ->
fold_list(F, S, map_es(T));
- map_pair_assoc ->
- fold_list(F, S, map_pair_assoc_es(T));
- map_pair_exact ->
- fold_list(F, S, map_pair_exact_es(T));
+ map_pair ->
+ fold(F,
+ fold(F,
+ fold(F, S, map_pair_op(T)),
+ map_pair_key(T)),
+ map_pair_val(T));
'let' ->
fold(F, fold(F, fold_list(F, S, let_vars(T)),
let_arg(T)),
@@ -371,13 +373,12 @@ mapfold(F, S0, T) ->
F(update_c_tuple_skel(T, Ts), S1);
map ->
{Ts, S1} = mapfold_list(F, S0, map_es(T)),
- F(update_c_map_skel(T, Ts), S1);
- map_pair_assoc ->
- {Ts, S1} = mapfold_list(F, S0, map_pair_assoc_es(T)),
- F(update_c_map_pair_assoc_skel(T,Ts), S1);
- map_pair_exact ->
- {Ts, S1} = mapfold_list(F, S0, map_pair_exact_es(T)),
- F(update_c_map_pair_exact_skel(T,Ts), S1);
+ F(update_c_map(T, Ts), S1);
+ map_pair ->
+ {Op, S1} = mapfold(F, S0, map_pair_op(T)),
+ {Key, S2} = mapfold(F, S1, map_pair_key(T)),
+ {Val, S3} = mapfold(F, S2, map_pair_val(T)),
+ F(update_c_map_pair(T,Op,Key,Val), S3);
'let' ->
{Vs, S1} = mapfold_list(F, S0, let_vars(T)),
{A, S2} = mapfold(F, S1, let_arg(T)),
@@ -519,10 +520,8 @@ variables(T, S) ->
vars_in_list(tuple_es(T), S);
map ->
vars_in_list(map_es(T), S);
- map_pair_assoc ->
- vars_in_list(map_pair_assoc_es(T), S);
- map_pair_exact ->
- vars_in_list(map_pair_exact_es(T), S);
+ map_pair ->
+ vars_in_list([map_pair_op(T),map_pair_key(T), map_pair_val(T)], S);
'let' ->
Vs = variables(let_body(T), S),
Vs1 = var_list_names(let_vars(T)),
@@ -726,15 +725,13 @@ label(T, N, Env) ->
map ->
{Ts, N1} = label_list(map_es(T), N, Env),
{As, N2} = label_ann(T, N1),
- {ann_c_map_skel(As, Ts), N2};
- map_pair_assoc ->
- {Ts, N1} = label_list(map_pair_assoc_es(T), N, Env),
- {As, N2} = label_ann(T, N1),
- {ann_c_map_pair_assoc_skel(As, Ts), N2};
- map_pair_exact ->
- {Ts, N1} = label_list(map_pair_exact_es(T), N, Env),
- {As, N2} = label_ann(T, N1),
- {ann_c_map_pair_exact_skel(As, Ts), N2};
+ {ann_c_map(As, Ts), N2};
+ map_pair ->
+ {Op, N1} = label(map_pair_op(T), N, Env),
+ {Val, N2} = label(map_pair_key(T), N1, Env),
+ {Key, N3} = label(map_pair_val(T), N2, Env),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_map_pair(As,Op,Key,Val), N4};
'let' ->
{A, N1} = label(let_arg(T), N, Env),
{Vs, N2, Env1} = label_vars(let_vars(T), N1, Env),
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index d6221a22bb..f506901099 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -107,9 +107,7 @@ vu_expr(V, #c_tuple{es=Es}) ->
vu_expr_list(V, Es);
vu_expr(V, #c_map{es=Es}) ->
vu_expr_list(V, Es);
-vu_expr(V, #c_map_pair_assoc{key=Key,val=Val}) ->
- vu_expr_list(V, [Key,Val]);
-vu_expr(V, #c_map_pair_exact{key=Key,val=Val}) ->
+vu_expr(V, #c_map_pair{key=Key,val=Val}) ->
vu_expr_list(V, [Key,Val]);
vu_expr(V, #c_binary{segments=Ss}) ->
vu_seg_list(V, Ss);
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index 185193e45d..36165245a6 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -256,9 +256,7 @@ gexpr(#c_tuple{es=Es}, Def, _Rt, St) ->
gexpr_list(Es, Def, St);
gexpr(#c_map{es=Es}, Def, _Rt, St) ->
gexpr_list(Es, Def, St);
-gexpr(#c_map_pair_assoc{key=K,val=V}, Def, _Rt, St) ->
- gexpr_list([K,V], Def, St);
-gexpr(#c_map_pair_exact{key=K,val=V}, Def, _Rt, St) ->
+gexpr(#c_map_pair{key=K,val=V}, Def, _Rt, St) ->
gexpr_list([K,V], Def, St);
gexpr(#c_binary{segments=Ss}, Def, _Rt, St) ->
gbitstr_list(Ss, Def, St);
@@ -312,9 +310,7 @@ expr(#c_tuple{es=Es}, Def, _Rt, St) ->
expr_list(Es, Def, St);
expr(#c_map{es=Es}, Def, _Rt, St) ->
expr_list(Es, Def, St);
-expr(#c_map_pair_assoc{key=K,val=V},Def,_Rt,St) ->
- expr_list([K,V],Def,St);
-expr(#c_map_pair_exact{key=K,val=V},Def,_Rt,St) ->
+expr(#c_map_pair{key=K,val=V},Def,_Rt,St) ->
expr_list([K,V],Def,St);
expr(#c_binary{segments=Ss}, Def, _Rt, St) ->
bitstr_list(Ss, Def, St);
@@ -469,7 +465,7 @@ pattern(#c_tuple{es=Es}, Def, Ps, St) ->
pattern_list(Es, Def, Ps, St);
pattern(#c_map{es=Es}, Def, Ps, St) ->
pattern_list(Es, Def, Ps, St);
-pattern(#c_map_pair_exact{key=K,val=V},Def,Ps,St) ->
+pattern(#c_map_pair{op=#c_literal{val=exact},key=K,val=V},Def,Ps,St) ->
pattern_list([K,V],Def,Ps,St);
pattern(#c_binary{segments=Ss}, Def, Ps, St0) ->
St = pat_bin_tail_check(Ss, St0),
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index 40c3ae4582..067a1ae895 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -97,10 +97,10 @@
-record(c_var, {anno=[], name :: cerl:var_name()}).
--record(c_map_pair_assoc, {anno=[], key, val}).
-
--record(c_map_pair_exact, {anno=[], key, val}).
+-record(c_map_pair, {anno=[],
+ op :: 'assoc' | 'exact',
+ key, val}).
-record(c_map, {anno=[],
var=#c_literal{val=[]} :: #c_var{} | #c_literal{},
- es :: [#c_map_pair_assoc{}|#c_map_pair_exact{}]}).
+ es :: [#c_map_pair{}]}).
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index 82ba11b0fc..67acb0856c 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -190,7 +190,7 @@ map_pair_patterns -> map_pair_pattern : ['$1'].
map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' :
- #c_map_pair_exact{key='$3',val='$5'}.
+ #c_map_pair{op=exact,key='$3',val='$5'}.
cons_pattern -> '[' anno_pattern tail_pattern :
#c_cons{hd='$2',tl='$3'}.
@@ -296,9 +296,9 @@ map_pair -> map_pair_assoc : '$1'.
map_pair -> map_pair_exact : '$1'.
map_pair_assoc -> '::' '<' anno_expression ',' anno_expression'>' :
- #c_map_pair_assoc{key='$3',val='$5'}.
+ #c_map_pair{op=#c_literal{val=assoc},key='$3',val='$5'}.
map_pair_exact -> '~' '<' anno_expression ',' anno_expression'>' :
- #c_map_pair_exact{key='$3',val='$5'}.
+ #c_map_pair{op=#c_literal{val=exact},key='$3',val='$5'}.
cons -> '[' anno_expression tail : c_cons('$2', '$3').
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index f775d87507..faa26ec6df 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -172,12 +172,12 @@ format_1(#c_map{es=Es}, Ctxt) ->
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
"}~"
];
-format_1(#c_map_pair_assoc{key=K,val=V}, Ctxt) ->
+format_1(#c_map_pair{op=#c_literal{val=assoc},key=K,val=V}, Ctxt) ->
["::<",
format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2),
">"
];
-format_1(#c_map_pair_exact{key=K,val=V}, Ctxt) ->
+format_1(#c_map_pair{op=#c_literal{val=exact},key=K,val=V}, Ctxt) ->
["~<",
format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2),
">"
diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl
index f921429409..60d83763f8 100644
--- a/lib/compiler/src/sys_core_dsetel.erl
+++ b/lib/compiler/src/sys_core_dsetel.erl
@@ -105,14 +105,10 @@ visit(Env0, #c_tuple{es=Es0}=R) ->
visit(Env0, #c_map{es=Es0}=R) ->
{Es1,Env1} = visit_list(Env0, Es0),
{R#c_map{es=Es1}, Env1};
-visit(Env0, #c_map_pair_assoc{key=K0,val=V0}=R) ->
+visit(Env0, #c_map_pair{key=K0,val=V0}=R) ->
{K,Env1} = visit(Env0, K0),
{V,Env2} = visit(Env1, V0),
- {R#c_map_pair_assoc{key=K,val=V}, Env2};
-visit(Env0, #c_map_pair_exact{key=K0,val=V0}=R) ->
- {K,Env1} = visit(Env0, K0),
- {V,Env2} = visit(Env1, V0),
- {R#c_map_pair_exact{key=K,val=V}, Env2};
+ {R#c_map_pair{key=K,val=V}, Env2};
visit(Env0, #c_cons{hd=H0,tl=T0}=R) ->
{H1,Env1} = visit(Env0, H0),
{T1,Env2} = visit(Env1, T0),
@@ -225,7 +221,7 @@ visit_pat(Env0, #c_tuple{es=Es}, Vs) ->
visit_pats(Es, Env0, Vs);
visit_pat(Env0, #c_map{es=Es}, Vs) ->
visit_pats(Es, Env0, Vs);
-visit_pat(Env0, #c_map_pair_exact{key=V,val=K}, Vs0) ->
+visit_pat(Env0, #c_map_pair{op=#c_literal{val=exact},key=V,val=K}, Vs0) ->
{Vs1, Env1} = visit_pat(Env0, V, Vs0),
visit_pat(Env1, K, Vs1);
visit_pat(Env0, #c_cons{hd=H,tl=T}, Vs0) ->
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 6792e2c894..1cdbac5693 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -421,18 +421,12 @@ expr_list(Es, Ctxt, Sub) ->
pair_list(Es, Ctxt, Sub) ->
[pair(E, Ctxt, Sub) || E <- Es].
-pair(#c_map_pair_assoc{key=K,val=V}, effect, Sub) ->
+pair(#c_map_pair{key=K,val=V}, effect, Sub) ->
make_effect_seq([K,V], Sub);
-pair(#c_map_pair_exact{key=K,val=V}, effect, Sub) ->
- make_effect_seq([K,V], Sub);
-pair(#c_map_pair_assoc{key=K0,val=V0}=Pair, value=Ctxt, Sub) ->
- K = expr(K0, Ctxt, Sub),
- V = expr(V0, Ctxt, Sub),
- Pair#c_map_pair_assoc{key=K,val=V};
-pair(#c_map_pair_exact{key=K0,val=V0}=Pair, value=Ctxt, Sub) ->
+pair(#c_map_pair{key=K0,val=V0}=Pair, value=Ctxt, Sub) ->
K = expr(K0, Ctxt, Sub),
V = expr(V0, Ctxt, Sub),
- Pair#c_map_pair_exact{key=K,val=V}.
+ Pair#c_map_pair{key=K,val=V}.
bitstr_list(Es, Sub) ->
[bitstr(E, Sub) || E <- Es].
@@ -1542,10 +1536,10 @@ map_pair_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0),
{Ps,Osub}.
-map_pair_pattern(#c_map_pair_exact{key=K0,val=V0}=Pair, {Isub,Osub0}) ->
+map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair, {Isub,Osub0}) ->
{K,Osub1} = pattern(K0, Isub, Osub0),
{V,Osub} = pattern(V0, Isub, Osub1),
- {Pair#c_map_pair_exact{key=K,val=V},{Isub,Osub}}.
+ {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.
bin_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0),
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index b5fb65ff08..e30bfa729c 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -712,13 +712,13 @@ map_pair_list(Es, St) ->
{K,Ep0,St1} = safe(K0, St0),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
- Pair = #c_map_pair_assoc{anno=A,key=K,val=V},
+ Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2};
({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) ->
{K,Ep0,St1} = safe(K0, St0),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
- Pair = #c_map_pair_exact{anno=A,key=K,val=V},
+ Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
end, {[],[],St}, Es).
@@ -1520,7 +1520,8 @@ pattern({map_field_exact,L,K,V}, St) ->
_ ->
pattern(K,St)
end,
- #c_map_pair_exact{anno=lineno_anno(L, St),
+ #c_map_pair{anno=lineno_anno(L, St),
+ op=#c_literal{val=exact},
key=Key,
val=pattern(V, St)};
pattern({bin,L,Ps}, St) ->
@@ -1871,9 +1872,9 @@ upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
upattern(#c_map{es=Es0}=Map, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
{Map#c_map{es=Es1},Esg,Esv,Eus,St1};
-upattern(#c_map_pair_exact{val=V0}=MapPair, Ks, St0) ->
+upattern(#c_map_pair{op=#c_literal{val=exact},val=V0}=MapPair, Ks, St0) ->
{V,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
- {MapPair#c_map_pair_exact{val=V},Vg,Vv,Vu,St1};
+ {MapPair#c_map_pair{val=V},Vg,Vv,Vu,St1};
upattern(#c_binary{segments=Es0}=Bin, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0),
{Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1};
@@ -2204,9 +2205,7 @@ is_simple(#c_cons{hd=H,tl=T}) ->
is_simple(H) andalso is_simple(T);
is_simple(#c_tuple{es=Es}) -> is_simple_list(Es);
is_simple(#c_map{es=Es}) -> is_simple_list(Es);
-is_simple(#c_map_pair_assoc{key=K,val=V}) ->
- is_simple(K) andalso is_simple(V);
-is_simple(#c_map_pair_exact{key=K,val=V}) ->
+is_simple(#c_map_pair{key=K,val=V}) ->
is_simple(K) andalso is_simple(V);
is_simple(_) -> false.
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 1d7d168214..9a2b1605ad 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -500,14 +500,11 @@ translate_fc(Args) ->
%% FIXME: Not completed
map_pairs(Es, Sub, St) ->
foldr(fun
- (#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) ->
+ (#c_map_pair{op=#c_literal{val=Op},key=K0,val=V0}, {Kes,Esp,St0}) when
+ Op =:= assoc; Op =:= exact -> %% assert Op
{K,[],St1} = expr(K0, Sub, St0),
{V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair{op=assoc,key=K,val=V}|Kes],Ep ++ Esp,St2};
- (#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) ->
- {K,[],St1} = expr(K0, Sub, St0),
- {V,Ep,St2} = atomic(V0, Sub, St1),
- {[#k_map_pair{op=exact,key=K,val=V}|Kes],Ep ++ Esp,St2}
+ {[#k_map_pair{op=Op,key=K,val=V}|Kes],Ep ++ Esp,St2}
end, {[],[],St}, Es).
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
@@ -668,7 +665,7 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->
pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->
{Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0),
{#k_map{anno=A,es=Kes},Osub1,St1};
-pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
+pattern(#c_map_pair{op=#c_literal{val=exact},anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->
{Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),
{Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1),
{#k_map_pair{anno=A,op=exact,key=Kk,val=Kv},Osub2,St2};
--
cgit v1.2.3
From 1ebfc08a004c530faa592a09cf101036329b18fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 17 Jan 2014 18:39:47 +0100
Subject: compiler: Update inliner tests
---
lib/compiler/test/inline_SUITE_data/maps_inline_test.erl | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl
index 87a8f86b47..d9762e2647 100644
--- a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl
+++ b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl
@@ -41,7 +41,17 @@
sval(#{id => 3}) +
sval(#{id => 4}) +
sval(#{id => 5}) +
- sval(#{id => 6}).
+ sval(#{id => 6}),
+
+ M = #{v => 1, m => #{v => 21, m => #{v => 7, m => 13}}},
+
+ 42 = decompose(M).
+
+% switch key orders
+decompose(#{ m := M, v := V}) when is_map(M) ->
+ V + decompose(M);
+decompose(#{ v := V, m := M}) -> V + M.
+
mval(#{val := V}) -> V.
--
cgit v1.2.3
From f95c907a0fda5d185c895bfb43cd2a76e46b7e81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Wed, 15 Jan 2014 17:59:26 +0100
Subject: hipe: Update cerl pretty printer
---
lib/hipe/cerl/cerl_prettypr.erl | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl
index 60926e16e1..ee5b41207b 100644
--- a/lib/hipe/cerl/cerl_prettypr.erl
+++ b/lib/hipe/cerl/cerl_prettypr.erl
@@ -64,7 +64,7 @@
try_body/1, try_vars/1, try_evars/1, try_handler/1,
tuple_es/1, type/1, values_es/1, var_name/1,
- map_es/1, map_pair_assoc_es/1, map_pair_exact_es/1
+ map_es/1, map_pair_es/1
]).
-define(PAPER, 76).
@@ -605,13 +605,13 @@ lay_map(Node, Ctxt) ->
floating(text("}~")))).
lay_map_pair_assoc(Node, Ctxt) ->
- [K,V] = map_pair_assoc_es(Node),
+ [K,V] = map_pair_es(Node),
beside(floating(text("::<")),
beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
floating(text(">")))))).
lay_map_pair_exact(Node, Ctxt) ->
- [K,V] = map_pair_exact_es(Node),
+ [K,V] = map_pair_es(Node),
beside(floating(text("~<")),
beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
floating(text(">")))))).
--
cgit v1.2.3
From 23e728cd0f68de11feca27358bedb0a6b71e4715 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Thu, 16 Jan 2014 16:19:45 +0100
Subject: dialyzer: Reflect map_pair core changes in dialyzer
---
lib/dialyzer/src/dialyzer_dataflow.erl | 18 ++++++------------
lib/dialyzer/src/dialyzer_dep.erl | 11 ++++-------
2 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 4d614320c2..239df1b8bd 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -309,10 +309,8 @@ traverse(Tree, Map, State) ->
handle_tuple(Tree, Map, State);
map ->
handle_map(Tree, Map, State);
- map_pair_assoc ->
- handle_map_pair_assoc(Tree, Map, State);
- map_pair_exact ->
- handle_map_pair_exact(Tree, Map, State);
+ map_pair ->
+ handle_map_pair(Tree, Map, State);
values ->
Elements = cerl:values_es(Tree),
{State1, Map1, EsType} = traverse_list(Elements, Map, State),
@@ -1068,14 +1066,10 @@ handle_map(Tree,Map,State) ->
{State1, Map1, TypePairs} = traverse_list(Pairs,Map,State),
{State1, Map1, t_map(TypePairs)}.
-handle_map_pair_assoc(Tree,Map,State) ->
- Elements = cerl:map_pair_assoc_es(Tree),
- {State1, Map1, [K,V]} = traverse_list(Elements,Map,State),
- {State1, Map1, {K,V}}.
-
-handle_map_pair_exact(Tree,Map,State) ->
- Elements = cerl:map_pair_exact_es(Tree),
- {State1, Map1, [K,V]} = traverse_list(Elements,Map,State),
+handle_map_pair(Tree,Map,State) ->
+ Key = cerl:map_pair_key(Tree),
+ Val = cerl:map_pair_val(Tree),
+ {State1, Map1, [K,V]} = traverse_list([Key,Val],Map,State),
{State1, Map1, {K,V}}.
%%----------------------------------------
diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl
index 12833b49c7..a81ea1a98b 100644
--- a/lib/dialyzer/src/dialyzer_dep.erl
+++ b/lib/dialyzer/src/dialyzer_dep.erl
@@ -186,13 +186,10 @@ traverse(Tree, Out, State, CurrentFun) ->
Args = cerl:map_es(Tree),
{List, State1} = traverse_list(Args, Out, State, CurrentFun),
{merge_outs(List), State1};
- map_pair_assoc ->
- Args = cerl:map_pair_assoc_es(Tree),
- {List, State1} = traverse_list(Args, Out, State, CurrentFun),
- {merge_outs(List), State1};
- map_pair_exact ->
- Args = cerl:map_pair_exact_es(Tree),
- {List, State1} = traverse_list(Args, Out, State, CurrentFun),
+ map_pair ->
+ Key = cerl:map_pair_key(Tree),
+ Val = cerl:map_pair_val(Tree),
+ {List, State1} = traverse_list([Key,Val], Out, State, CurrentFun),
{merge_outs(List), State1};
values ->
traverse_list(cerl:values_es(Tree), Out, State, CurrentFun);
--
cgit v1.2.3
From b7b79aaf0453995ef2145647c5c50acc5fae811b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 17 Jan 2014 15:33:29 +0100
Subject: dialyzer: Remove dead code
---
lib/dialyzer/src/dialyzer_dataflow.erl | 22 ----------------------
lib/dialyzer/src/dialyzer_typesig.erl | 25 -------------------------
2 files changed, 47 deletions(-)
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 239df1b8bd..33fa107019 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -1453,28 +1453,6 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
end;
map ->
{Map, t_map([])};
-% Pairs = [ cerl:map_pair_es(Pair) || Pair <- cerl:map_es(Pat) ],
-% MapType = t_inf(t_map([]), Type),
-% case t_is_none(MapType) of
-% true ->
-% bind_opaque_pats(MapType, Type, Pat, Map, State, Rev);
-% false ->
-% MapJ = join_maps_begin(Map),
-% Results = case Rev of
-% true ->
-% [bind_pat_vars_reverse(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs];
-% false ->
-% [bind_pat_vars(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs]
-% end,
-% case [M || {M, _} <- Results, M =/= error] of
-% [] -> bind_error([Pat], MapType, t_none(), bind);
-% Maps ->
-% Map1 = join_maps_end(Maps, MapJ),
-% _PairTypes = [{Ktype,Vtype} || {M, [Ktype,Vtype]} <- Results, M =/= error],
-% % add t_sup
-% {Map1, t_map([])}
-% end
-% end;
tuple ->
Es = cerl:tuple_es(Pat),
{TypedRecord, Prototype} =
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 4e18e9c7d2..b4b3d5a092 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -474,31 +474,6 @@ traverse(Tree, DefinedVars, State) ->
end;
map ->
{State, t_map([])};
-% Pairs = cerl:map_es(Tree),
-% {State1, EVars} = traverse_list(Pairs, DefinedVars, State),
-% case cerl:is_literal(cerl:fold_literal(Tree)) of
-% true ->
-% %% We do not need to do anything more here.
-% {State, t_map([])};
-% false ->
-% Fun = fun(Var, AccState) ->
-% case t_has_var(Var) of
-% true ->
-% {AccState1, NewVar} = state__mk_var(AccState),
-% {NewVar,
-% state__store_conj(Var, eq, NewVar, AccState1)};
-% false ->
-% {Var, AccState}
-% end
-% end,
-% {_NewEvars, State2} = lists:mapfoldl(Fun, State1, EVars),
-% {State2, t_map([])}
-% end;
-% map_pair_assoc ->
-% [K,V] = cerl:map_pair_assoc_es(Tree),
-% {State1,_} = traverse(K,DefinedVars,State),
-% {State2,_} = traverse(V,DefinedVars,State),
-% {_,State2};
values ->
%% We can get into trouble when unifying products that have the
%% same element appearing several times. Handle these cases by
--
cgit v1.2.3
From fa92c876756032bc7f824ed81bb9ba4667f14805 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Fri, 17 Jan 2014 17:29:16 +0100
Subject: dialyzer: Add maps tests
---
lib/dialyzer/test/small_SUITE_data/src/maps1.erl | 41 ++++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 lib/dialyzer/test/small_SUITE_data/src/maps1.erl
diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
new file mode 100644
index 0000000000..06ced5b69e
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
@@ -0,0 +1,41 @@
+%%
+%% File: maps1.erl
+%% Author: Björn-Egil Dahlberg
+%% Created: 2014-01-17
+%%
+
+-module(maps1).
+
+-compile([export_all]).
+
+
+-export([recv/3, decode/1]).
+
+%-record(can_pkt, {id, data :: binary(), timestamp}).
+
+-type can_pkt() :: #{ id => term(), data => binary(), timestamp => term() }.
+-type channel() :: atom() | pid() | {atom(),_}.
+
+-spec recv(<<_:64,_:_*8>>, fun((can_pkt()) -> R), channel()) -> R.
+recv(Packet, Fun, Chan) ->
+ #{id := Can_id, data := Can_data} = P = decode(Packet),
+ Fun(P).
+
+-spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char()}.
+decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18,
+ Data:Len/binary, _/binary>>) ->
+ #{id => Id, data => Data, timestamp => Timestamp}.
+
+
+
+t1() ->
+ #{bar=>fun t2/0}.
+
+t2() -> ok.
+
+-type map_state() :: #{ id => integer(), val => term() }.
+
+-spec update(map_state(), term()) -> map_state().
+
+update(#{ id := Id, val := Val } = M, X) when is_integer(Id) ->
+ M#{ val := [Val,X] }.
--
cgit v1.2.3
From bc34b5dafaebf07d7185900310246f5752c0a6c4 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson
Date: Tue, 21 Jan 2014 12:21:11 +0100
Subject: erts: Add map construction to driver API
erl_drv_output_term() and erl_drv_send_term() can send messages
containing maps with the use of the new ERL_DRV_MAP.
The driver API minor version is updated as new functionality is added.
---
erts/doc/src/erl_driver.xml | 29 ++++++++-
erts/emulator/beam/erl_driver.h | 4 +-
erts/emulator/beam/erl_map.c | 31 ++++++++++
erts/emulator/beam/erl_map.h | 4 +-
erts/emulator/beam/external.c | 31 +---------
erts/emulator/beam/io.c | 44 ++++++++++++-
erts/emulator/test/send_term_SUITE.erl | 19 +++++-
.../test/send_term_SUITE_data/send_term_drv.c | 72 ++++++++++++++++++++--
8 files changed, 191 insertions(+), 43 deletions(-)
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index c2f7fa4588..710c9b19cf 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -4,7 +4,7 @@
- 20012013
+ 20012014
Ericsson AB. All Rights Reserved.
@@ -1742,15 +1742,19 @@ typedef struct ErlIOVec {
term consists of one to four elements in the array. The
term first has a term type, and then arguments. The
port parameter specifies the sending port.
- Tuple and lists (with the exception of strings, see below),
+
Tuples, maps and lists (with the exception of strings, see below),
are built in reverse polish notation, so that to build a
tuple, the elements are given first, and then the tuple
- term, with a count. Likewise for lists.
+ term, with a count. Likewise for lists and maps.
A tuple must be specified with the number of elements. (The
elements precede the ERL_DRV_TUPLE term.)
A list must be specified with the number of elements,
including the tail, which is the last term preceding
ERL_DRV_LIST.
+ A map must be specified with the number of key-value pairs N.
+ The key-value pairs must precede the ERL_DRV_MAP in this order:
+ key1,value1,key2,value2,...,keyN,valueN.
+ Duplicate keys are not allowed.
The special term ERL_DRV_STRING_CONS is used to
"splice" in a string in a list, a string given this way is
not a list per se, but the elements are elements of the
@@ -1774,6 +1778,7 @@ ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port)
ERL_DRV_STRING_CONS char *str, int len
ERL_DRV_FLOAT double *dbl
ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
+ERL_DRV_MAP int sz
The unsigned integer data type ErlDrvUInt and the
signed integer data type ErlDrvSInt are 64 bits wide
@@ -1856,6 +1861,24 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
]]>
+
+
To build the map #{key1 => 100, key2 => {200, 300}}, the
+ following call could be made.
+
+
+
If you want to pass a binary and don't already have the content
of the binary in an ErlDrvBinary, you can benefit from using
ERL_DRV_BUF2BINARY instead of creating an ErlDrvBinary
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 2bd3181bdc..ab9ee63104 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -605,6 +605,8 @@ EXTERN int null_func(void);
#define ERL_DRV_INT64 ((ErlDrvTermData) 15) /* ErlDrvSInt64 * */
#define ERL_DRV_UINT64 ((ErlDrvTermData) 16) /* ErlDrvUInt64 * */
+#define ERL_DRV_MAP ((ErlDrvTermData) 17) /* ErlDrvUInt */
+
#ifndef ERL_DRIVER_TYPES_ONLY
/* make terms for driver_output_term and driver_send_term */
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 57f156509c..2fff7f9390 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -786,3 +786,34 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) {
}
BIF_ERROR(BIF_P, BADARG);
}
+
+int erts_validate_and_sort_map(map_t* mp)
+{
+ Eterm *ks = map_get_keys(mp);
+ Eterm *vs = map_get_values(mp);
+ Uint sz = map_get_size(mp);
+ Uint ix,jx;
+ Eterm tmp;
+ int c;
+
+ /* sort */
+
+ for (ix = 1; ix < sz; ix++) {
+ jx = ix;
+ while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) {
+ /* identical key -> error */
+ if (c == 0) return 0;
+
+ tmp = ks[jx];
+ ks[jx] = ks[jx - 1];
+ ks[jx - 1] = tmp;
+
+ tmp = vs[jx];
+ vs[jx] = vs[jx - 1];
+ vs[jx - 1] = tmp;
+
+ jx--;
+ }
+ }
+ return 1;
+}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 616ecd24ce..cfacb2ec28 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2013. All Rights Reserved.
+ * Copyright Ericsson AB 2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -67,6 +67,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res
int erts_maps_find(Eterm key, Eterm map, Eterm *value);
int erts_maps_get(Eterm key, Eterm map, Eterm *value);
int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res);
-
+int erts_validate_and_sort_map(map_t* map);
#endif
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 57251286c8..a4cc3435c3 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -3795,37 +3795,10 @@ dec_term_atom_common:
*/
while (maps_head) {
- map_t *mp = (map_t*)maps_head;
- Eterm *ks = map_get_keys(mp);
- Eterm *vs = map_get_values(mp);
- Uint sz = map_get_size(mp);
- Uint ix,jx;
- Eterm tmp;
- int c;
-
next = (Eterm *)(EXPAND_POINTER(*maps_head));
-
- /* sort */
-
- for ( ix = 1; ix < sz; ix++) {
- jx = ix;
- while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) {
- /* identical key -> error */
- if (c == 0) goto error;
-
- tmp = ks[jx];
- ks[jx] = ks[jx - 1];
- ks[jx - 1] = tmp;
-
- tmp = vs[jx];
- vs[jx] = vs[jx - 1];
- vs[jx - 1] = tmp;
-
- jx--;
- }
-
- }
*maps_head = MAP_HEADER;
+ if (!erts_validate_and_sort_map((map_t*)maps_head))
+ goto error;
maps_head = next;
}
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 49af86b36a..3b16cdeb4a 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -46,6 +46,7 @@
#define ERTS_WANT_EXTERNAL_TAGS
#include "external.h"
#include "dtrace-wrapper.h"
+#include "erl_map.h"
extern ErlDrvEntry fd_driver_entry;
extern ErlDrvEntry vanilla_driver_entry;
@@ -5293,6 +5294,17 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
depth++;
break;
}
+ case ERL_DRV_MAP: { /* int */
+ ERTS_DDT_CHK_ENOUGH_ARGS(1);
+ if ((int) ptr[0] < 0) ERTS_DDT_FAIL;
+ need += MAP_HEADER_SIZE + 1 + 2*ptr[0];
+ depth -= 2*ptr[0];
+ if (depth < 0) ERTS_DDT_FAIL;
+ ptr++;
+ depth++;
+ break;
+ }
+
default:
ERTS_DDT_FAIL;
}
@@ -5529,6 +5541,36 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
ptr += 2;
break;
+ case ERL_DRV_MAP: { /* int */
+ int size = (int)ptr[0];
+ Eterm* tp = hp;
+ Eterm* vp;
+ map_t *mp;
+
+ *tp = make_arityval(size);
+
+ hp += 1 + size;
+ mp = (map_t*)hp;
+ mp->thing_word = MAP_HEADER;
+ mp->size = size;
+ mp->keys = make_tuple(tp);
+ mess = make_map(mp);
+
+ hp += MAP_HEADER_SIZE + size; /* advance "heap" pointer */
+
+ tp += size; /* point at last key */
+ vp = hp - 1; /* point at last value */
+
+ while(size--) {
+ *vp-- = ESTACK_POP(stack);
+ *tp-- = ESTACK_POP(stack);
+ }
+ if (!erts_validate_and_sort_map(mp))
+ ERTS_DDT_FAIL;
+ ptr++;
+ break;
+ }
+
}
ESTACK_PUSH(stack, mess);
}
diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl
index b631f55a03..8e1f8df43a 100644
--- a/erts/emulator/test/send_term_SUITE.erl
+++ b/erts/emulator/test/send_term_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -62,7 +62,19 @@ basic(Config) when is_list(Config) ->
?line [] = term(P, 0),
?line Self = self(),
- ?line {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self} = term(P, 1),
+ {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1),
+
+ Map41 = maps:from_list([{blurf, 42},
+ {[], [-42,{}|"abc"++P]},
+ {"kalle", 3.1416},
+ {Self, #{}}]),
+ Map41 = term(P, 41),
+
+ Map42 = maps:from_list([{42, []},
+ {[-42,{}|"abc"++P], "kalle"},
+ {3.1416, Self},
+ {#{}, blurf}]),
+ Map42 = term(P, 42),
?line Deep = lists:seq(0, 199),
?line Deep = term(P, 2),
?line {B1,B2} = term(P, 3),
@@ -125,7 +137,8 @@ basic(Config) when is_list(Config) ->
{-1, 36}, % ERL_DRV_INT64
{-4711, 37}, % ERL_DRV_INT64
{-20233590931456, 38}, % ERL_DRV_INT64
- {-9223372036854775808, 39}], % ERL_DRV_INT64
+ {-9223372036854775808, 39},
+ {#{}, 40}], % ERL_DRV_MAP
?line {Terms, Ops} = lists:unzip(Singles),
?line Terms = term(P,Ops),
diff --git a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
index f8613487b0..381a4f20d5 100644
--- a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
+++ b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
@@ -104,7 +104,7 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
double f = 3.1416;
msg[0] = ERL_DRV_ATOM;
- msg[1] = driver_mk_atom("blurf"),
+ msg[1] = driver_mk_atom("blurf");
msg[2] = ERL_DRV_INT;
msg[3] = (ErlDrvTermData) 42;
msg[4] = ERL_DRV_NIL;
@@ -126,9 +126,11 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
msg[20] = (ErlDrvTermData) &f;
msg[21] = ERL_DRV_PID;
msg[22] = driver_connected(erlang_port);
- msg[23] = ERL_DRV_TUPLE;
- msg[24] = (ErlDrvTermData) 7;
- msg += 25;
+ msg[23] = ERL_DRV_MAP;
+ msg[24] = (ErlDrvTermData) 0;
+ msg[25] = ERL_DRV_TUPLE;
+ msg[26] = (ErlDrvTermData) 8;
+ msg += 27;
}
break;
@@ -481,6 +483,52 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
break;
}
+ case 40: {
+ msg[0] = ERL_DRV_MAP;
+ msg[1] = (ErlDrvTermData) 0;
+ msg += 2;
+ break;
+ }
+
+ case 41: /* Most term types inside a map */
+ case 42: {
+ double f = 3.1416;
+
+ if (buf[i] == 41) {
+ *msg++ = ERL_DRV_ATOM;
+ *msg++ = driver_mk_atom("blurf");
+ }
+ *msg++ = ERL_DRV_INT;
+ *msg++ = (ErlDrvTermData)42;
+ *msg++ = ERL_DRV_NIL;
+ *msg++ = ERL_DRV_INT;
+ *msg++ = (ErlDrvTermData)-42;
+ *msg++ = ERL_DRV_TUPLE;
+ *msg++ = (ErlDrvTermData)0;
+ *msg++ = ERL_DRV_PORT;
+ *msg++ = driver_mk_port(erlang_port);
+ *msg++ = ERL_DRV_STRING_CONS;
+ *msg++ = (ErlDrvTermData)"abc";
+ *msg++ = (ErlDrvTermData)3;
+ *msg++ = ERL_DRV_LIST;
+ *msg++ = (ErlDrvTermData)3;
+ *msg++ = ERL_DRV_STRING;
+ *msg++ = (ErlDrvTermData)"kalle";
+ *msg++ = (ErlDrvTermData)5;
+ *msg++ = ERL_DRV_FLOAT;
+ *msg++ = (ErlDrvTermData)&f;
+ *msg++ = ERL_DRV_PID;
+ *msg++ = driver_connected(erlang_port);
+ *msg++ = ERL_DRV_MAP;
+ *msg++ = (ErlDrvTermData)0;
+ if (buf[i] == 42) {
+ *msg++ = ERL_DRV_ATOM;
+ *msg++ = driver_mk_atom("blurf");
+ }
+ *msg++ = ERL_DRV_MAP;
+ *msg++ = (ErlDrvTermData)4;
+ break;
+ }
case 127: /* Error cases */
{
@@ -662,6 +710,22 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
FAIL_TERM(msg, 2);
}
+ msg[0] = ERL_DRV_MAP;
+ msg[1] = (ErlDrvTermData) 0;
+ FAIL_TERM(msg, 1);
+
+ /* map with duplicate key */
+ msg[0] = ERL_DRV_ATOM;
+ msg[1] = driver_mk_atom("key");
+ msg[2] = ERL_DRV_NIL;
+ msg[3] = ERL_DRV_ATOM;
+ msg[4] = driver_mk_atom("key");
+ msg[5] = ERL_DRV_INT;
+ msg[6] = (ErlDrvTermData) -4711;
+ msg[7] = ERL_DRV_MAP;
+ msg[8] = 2;
+ FAIL_TERM(msg, 9);
+
/* Signal end of test case */
msg[0] = ERL_DRV_NIL;
erl_drv_output_term(driver_mk_port(erlang_port), msg, 1);
--
cgit v1.2.3
From f6370a6d73406e11d4a7664e566502c005062b0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 27 Jan 2014 11:52:00 +0100
Subject: hipe: Fixup update cerl pretty printer
---
lib/hipe/cerl/cerl_prettypr.erl | 26 +++++++++++---------------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl
index ee5b41207b..22f5b8945a 100644
--- a/lib/hipe/cerl/cerl_prettypr.erl
+++ b/lib/hipe/cerl/cerl_prettypr.erl
@@ -63,8 +63,7 @@
seq_arg/1, seq_body/1, string_lit/1, try_arg/1,
try_body/1, try_vars/1, try_evars/1, try_handler/1,
tuple_es/1, type/1, values_es/1, var_name/1,
-
- map_es/1, map_pair_es/1
+ map_es/1, map_pair_key/1, map_pair_val/1, map_pair_op/1
]).
-define(PAPER, 76).
@@ -429,10 +428,8 @@ lay_1(Node, Ctxt) ->
lay_tuple(Node, Ctxt);
map ->
lay_map(Node, Ctxt);
- map_pair_assoc ->
- lay_map_pair_assoc(Node, Ctxt);
- map_pair_exact ->
- lay_map_pair_exact(Node, Ctxt);
+ map_pair ->
+ lay_map_pair(Node, Ctxt);
'let' ->
lay_let(Node, Ctxt);
seq ->
@@ -604,15 +601,14 @@ lay_map(Node, Ctxt) ->
Ctxt, fun lay/2)),
floating(text("}~")))).
-lay_map_pair_assoc(Node, Ctxt) ->
- [K,V] = map_pair_es(Node),
- beside(floating(text("::<")),
- beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
- floating(text(">")))))).
-
-lay_map_pair_exact(Node, Ctxt) ->
- [K,V] = map_pair_es(Node),
- beside(floating(text("~<")),
+lay_map_pair(Node, Ctxt) ->
+ K = map_pair_key(Node),
+ V = map_pair_val(Node),
+ OpTxt = case concrete(map_pair_op(Node)) of
+ assoc -> "::<";
+ exact -> "~<"
+ end,
+ beside(floating(text(OpTxt)),
beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
floating(text(">")))))).
--
cgit v1.2.3
From dfce3c3ea8ed4064856c29a678c83fc277a05bc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 27 Jan 2014 14:18:14 +0100
Subject: erts: Update preloaded erts_internal.beam
---
erts/preloaded/ebin/erts_internal.beam | Bin 4096 -> 4276 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 12b36913a9..4a84b3945a 100644
Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ
--
cgit v1.2.3
From 543074e7aab966a597e405bc52c94e57358663a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 27 Jan 2014 14:47:42 +0100
Subject: erts: Fixup enif_make_map_put on windows
---
erts/emulator/beam/erl_nif_api_funcs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 4121378e1a..d7c554e60b 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -300,7 +300,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa
# define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map)
# define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size)
# define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map)
-# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put)
+# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_make_map_put)
# define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value)
# define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update)
# define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove)
--
cgit v1.2.3
From 335e6bf3e4987e5f549136c1f692c628b1dfc360 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 27 Jan 2014 16:00:34 +0100
Subject: stdlib: Make maps:fold/3 order-independent
This means replacing maps:foldl/3 and maps:foldr/3 with maps:fold/3.
---
lib/stdlib/src/maps.erl | 21 +++------------------
1 file changed, 3 insertions(+), 18 deletions(-)
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 0e6a1ddb1a..41de174e7d 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -20,8 +20,7 @@
-module(maps).
-export([
- foldl/3,
- foldr/3,
+ fold/3,
map/2,
size/1,
without/2
@@ -154,7 +153,7 @@ values(_) -> erlang:nif_error(undef).
%%% End of BIFs
--spec foldl(Fun,Init,Map) -> Acc when
+-spec fold(Fun,Init,Map) -> Acc when
Fun :: fun((K, V, AccIn) -> AccOut),
Init :: term(),
Acc :: term(),
@@ -164,23 +163,9 @@ values(_) -> erlang:nif_error(undef).
K :: term(),
V :: term().
-foldl(Fun, Init, Map) when is_function(Fun,3), is_map(Map) ->
+fold(Fun, Init, Map) when is_function(Fun,3), is_map(Map) ->
lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)).
--spec foldr(Fun,Init,Map) -> Acc when
- Fun :: fun((K,V,AccIn) -> AccOut),
- Init :: term(),
- Acc :: term(),
- AccIn :: term(),
- AccOut :: term(),
- Map :: map(),
- K :: term(),
- V :: term().
-
-foldr(Fun, Init, Map) when is_function(Fun,3), is_map(Map) ->
- lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)).
-
-
-spec map(Fun,Map1) -> Map2 when
Fun :: fun((K, V1) -> V2),
Map1 :: map(),
--
cgit v1.2.3
From a7ff048b6b5d8649d1709a90f7514fccbb5e9235 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 27 Jan 2014 16:04:49 +0100
Subject: erts: Update maps_fold test to respect maps:fold/3
---
erts/emulator/test/map_SUITE.erl | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 75381de556..faaafb33bc 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -816,18 +816,11 @@ t_bif_map_from_list(Config) when is_list(Config) ->
%% Maps module, not BIFs
t_maps_fold(_Config) ->
-
Vs = lists:seq(1,100),
- Rs = lists:reverse(Vs),
M = maps:from_list([{{k,I},{v,I}}||I<-Vs]),
- %% foldl
- 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M),
- Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M),
-
- %% foldr
- 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M),
- Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], M),
+ %% fold
+ 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M),
ok.
--
cgit v1.2.3
From 7849ea4d398717bd48bad76c551dcc5322fe39cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Mon, 27 Jan 2014 17:39:37 +0100
Subject: erts: Strengthen map_SUITE tests
* Add tests for maps:merge/2
* Add tests for maps:update/3
* Test more corner cases
---
erts/emulator/test/map_SUITE.erl | 83 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 79 insertions(+), 4 deletions(-)
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index faaafb33bc..31c1486f1c 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -37,9 +37,11 @@
t_bif_map_find/1,
t_bif_map_is_key/1,
t_bif_map_keys/1,
+ t_bif_map_merge/1,
t_bif_map_new/1,
t_bif_map_put/1,
t_bif_map_remove/1,
+ t_bif_map_update/1,
t_bif_map_values/1,
t_bif_map_to_list/1,
t_bif_map_from_list/1,
@@ -76,8 +78,10 @@ all() -> [
%% Specific Map BIFs
t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
- t_bif_map_keys,t_bif_map_new,t_bif_map_put,
- t_bif_map_remove,t_bif_map_values,
+ t_bif_map_keys, t_bif_map_merge, t_bif_map_new,
+ t_bif_map_put,
+ t_bif_map_remove, t_bif_map_update,
+ t_bif_map_values,
t_bif_map_to_list, t_bif_map_from_list,
%% erlang
@@ -478,8 +482,8 @@ t_bif_map_find(Config) when is_list(Config) ->
error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
- {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])),
- {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)),
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))),
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))),
ok.
@@ -496,11 +500,16 @@ t_bif_map_is_key(Config) when is_list(Config) ->
false = maps:is_key("h", M1),
false = maps:is_key("hello", M1),
false = maps:is_key(atom, M1),
+ false = maps:is_key(any, id(#{})),
false = maps:is_key("hi", maps:remove("hi", M1)),
true = maps:is_key("hi", M1),
true = maps:is_key(1, maps:put(1, "number", M1)),
false = maps:is_key(1.0, maps:put(1, "number", M1)),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))),
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))),
ok.
t_bif_map_keys(Config) when is_list(Config) ->
@@ -526,6 +535,34 @@ t_bif_map_new(Config) when is_list(Config) ->
0 = erlang:map_size(maps:new()),
ok.
+t_bif_map_merge(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:merge(#{},#{})),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}),
+
+ M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer },
+
+ #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0),
+
+ #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )),
+
+ ok.
+
+
t_bif_map_put(Config) when is_list(Config) ->
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
4 => number, 18446744073709551629 => wat},
@@ -555,6 +592,11 @@ t_bif_map_put(Config) when is_list(Config) ->
[4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5),
[number,wat,3,"hello",<<"value">>] = maps:values(M5),
+ M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6),
+ [number,wat,3,"hello",<<"other value">>] = maps:values(M6),
+
%% error case
{'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)),
{'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)),
@@ -564,6 +606,8 @@ t_bif_map_put(Config) when is_list(Config) ->
ok.
t_bif_map_remove(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:remove(some_key, #{})),
+
M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
4 => number, 18446744073709551629 => wat},
@@ -600,6 +644,33 @@ t_bif_map_remove(Config) when is_list(Config) ->
{'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)),
ok.
+t_bif_map_update(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0),
+
+ #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)),
+
+ ok.
+
+
t_bif_map_values(Config) when is_list(Config) ->
@@ -810,6 +881,10 @@ t_bif_map_from_list(Config) when is_list(Config) ->
{<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]),
%% error cases
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))),
{'EXIT', {badarg,_}} = (catch maps:from_list(id(a))),
{'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
ok.
--
cgit v1.2.3
From 8bd7113e5b9c53eb29b7211d43f6d8bc2f58eb81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 28 Jan 2014 10:39:14 +0100
Subject: compiler: Fixup #map_pair{} spec
---
lib/compiler/src/core_parse.hrl | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index 067a1ae895..d54715ef59 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -98,8 +98,9 @@
-record(c_var, {anno=[], name :: cerl:var_name()}).
-record(c_map_pair, {anno=[],
- op :: 'assoc' | 'exact',
- key, val}).
+ op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'},
+ key,
+ val}).
-record(c_map, {anno=[],
var=#c_literal{val=[]} :: #c_var{} | #c_literal{},
--
cgit v1.2.3
From 54e921a5944394665acab44b210fdb677b4d15e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 28 Jan 2014 11:17:33 +0100
Subject: compiler: Fix core parse for Maps
---
lib/compiler/src/core_parse.yrl | 2 +-
lib/compiler/src/core_scan.erl | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index 67acb0856c..b8db0f683a 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -190,7 +190,7 @@ map_pair_patterns -> map_pair_pattern : ['$1'].
map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' :
- #c_map_pair{op=exact,key='$3',val='$5'}.
+ #c_map_pair{op=#c_literal{val=exact},key='$3',val='$5'}.
cons_pattern -> '[' anno_pattern tail_pattern :
#c_cons{hd='$2',tl='$3'}.
diff --git a/lib/compiler/src/core_scan.erl b/lib/compiler/src/core_scan.erl
index a4fe920258..b7799b373a 100644
--- a/lib/compiler/src/core_scan.erl
+++ b/lib/compiler/src/core_scan.erl
@@ -271,6 +271,8 @@ scan1("->" ++ Cs, Toks, Pos) ->
scan1(Cs, [{'->',Pos}|Toks], Pos);
scan1("-|" ++ Cs, Toks, Pos) ->
scan1(Cs, [{'-|',Pos}|Toks], Pos);
+scan1("::" ++ Cs, Toks, Pos) ->
+ scan1(Cs, [{'::',Pos}|Toks], Pos);
scan1([C|Cs], Toks, Pos) -> %Punctuation character
P = list_to_atom([C]),
scan1(Cs, [{P,Pos}|Toks], Pos);
--
cgit v1.2.3
From cf5bc2e917dbcb2c2841bf07b995efe105bea4be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?=
Date: Tue, 28 Jan 2014 14:46:16 +0100
Subject: compiler: Add core compile test for maps
---
lib/compiler/test/core_SUITE.erl | 8 +-
.../test/core_SUITE_data/map_core_test.core | 95 ++++++++++++++++++++++
2 files changed, 101 insertions(+), 2 deletions(-)
create mode 100644 lib/compiler/test/core_SUITE_data/map_core_test.core
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index a40dc32d59..1a521c3591 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -23,7 +23,8 @@
init_per_testcase/2,end_per_testcase/2,
dehydrated_itracer/1,nested_tries/1,
seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1,
- unsafe_case/1,nomatch_shadow/1,reversed_annos/1]).
+ unsafe_case/1,nomatch_shadow/1,reversed_annos/1,
+ map_core_test/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -48,7 +49,9 @@ all() ->
groups() ->
[{p,test_lib:parallel(),
[dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
- eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos]}].
+ eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos,
+ map_core_test
+ ]}].
init_per_suite(Config) ->
@@ -72,6 +75,7 @@ end_per_group(_GroupName, Config) ->
?comp(unsafe_case).
?comp(nomatch_shadow).
?comp(reversed_annos).
+?comp(map_core_test).
try_it(Mod, Conf) ->
Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)),
diff --git a/lib/compiler/test/core_SUITE_data/map_core_test.core b/lib/compiler/test/core_SUITE_data/map_core_test.core
new file mode 100644
index 0000000000..7ece8a8bbd
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/map_core_test.core
@@ -0,0 +1,95 @@
+module 'map_core_test' ['map_core_test'/0,
+ 'module_info'/0,
+ 'module_info'/1]
+ attributes []
+'map_core_test'/0 =
+ %% Line 14
+ fun () ->
+ let <_cor0> =
+ %% Line 15
+ ~{::<'check','ok'>,::<1337,#{#<104>(8,1,'integer',['unsigned'|['big']]),
+ #<101>(8,1,'integer',['unsigned'|['big']]),
+ #<108>(8,1,'integer',['unsigned'|['big']]),
+ #<108>(8,1,'integer',['unsigned'|['big']]),
+ #<111>(8,1,'integer',['unsigned'|['big']])}#>,::<'val',0>}~
+ in let =
+ %% Line 15
+ apply 'id'/1
+ (_cor0)
+ in let <_cor2> =
+ %% Line 16
+ apply 'id'/1
+ ([1|[2|[3|[4|[5|[6]]]]]])
+ in %% Line 16
+ case apply 'call'/2
+ (M, _cor2) of
+ <~{~<1337,#{#<104>(8,1,'integer',['unsigned'|['big']]),
+ #<101>(8,1,'integer',['unsigned'|['big']]),
+ #<108>(8,1,'integer',['unsigned'|['big']]),
+ #<108>(8,1,'integer',['unsigned'|['big']]),
+ #<111>(8,1,'integer',['unsigned'|['big']]),
+ #<32>(8,1,'integer',['unsigned'|['big']]),
+ #<49>(8,1,'integer',['unsigned'|['big']]),
+ #<32>(8,1,'integer',['unsigned'|['big']]),
+ #<50>(8,1,'integer',['unsigned'|['big']]),
+ #<32>(8,1,'integer',['unsigned'|['big']]),
+ #<51>(8,1,'integer',['unsigned'|['big']]),
+ #<32>(8,1,'integer',['unsigned'|['big']]),
+ #<52>(8,1,'integer',['unsigned'|['big']]),
+ #<32>(8,1,'integer',['unsigned'|['big']]),
+ #<53>(8,1,'integer',['unsigned'|['big']]),
+ #<32>(8,1,'integer',['unsigned'|['big']]),
+ #<54>(8,1,'integer',['unsigned'|['big']])}#>,~<'check','ok'>,~<'val',21>}~> when 'true' ->
+ %% Line 17
+ 'ok'
+ ( <_cor3> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_cor3})
+ -| ['compiler_generated'] )
+ end
+'call'/2 =
+ %% Line 20
+ fun (_cor1,_cor0) ->
+ case <_cor1,_cor0> of
+ ,~<'check',_cor8>,~<'val',Val>}~,[V|Vs]> when 'true' ->
+ let <_cor3> =
+ %% Line 21
+ call 'erlang':'+'
+ (V, 48)
+ in let <_cor4> =
+ %% Line 21
+ #{#('all',8,'binary',['unsigned'|['big']]),
+ #<32>(8,1,'integer',['unsigned'|['big']]),
+ #<_cor3>(8,1,'integer',['unsigned'|['big']])}#
+ in let <_cor2> =
+ %% Line 21
+ call 'erlang':'+'
+ (Val, V)
+ in let <_cor5> =
+ %% Line 21
+ M~{~<1337,_cor4>,~<'val',_cor2>}~
+ in %% Line 21
+ apply 'call'/2
+ (_cor5, Vs)
+ %% Line 22
+ when 'true' ->
+ M
+ ( <_cor7,_cor6> when 'true' ->
+ ( primop 'match_fail'
+ ({'function_clause',_cor7,_cor6})
+ -| [{'function_name',{'call',2}}] )
+ -| ['compiler_generated'] )
+ end
+'id'/1 =
+ %% Line 24
+ fun (_cor0) ->
+ _cor0
+'module_info'/0 =
+ fun () ->
+ call 'erlang':'get_module_info'
+ ('map_core_test')
+'module_info'/1 =
+ fun (_cor0) ->
+ call 'erlang':'get_module_info'
+ ('map_core_test', _cor0)
+end
\ No newline at end of file
--
cgit v1.2.3