diff options
author | Guilherme Andrade <[email protected]> | 2017-02-25 22:00:17 +0000 |
---|---|---|
committer | Guilherme Andrade <[email protected]> | 2017-03-22 23:57:54 +0000 |
commit | ed71ea35bad9a511125c82ce42160cad9fa8311f (patch) | |
tree | 26e1d5a3e58246f7b44c193b8d1441e278ac63c8 /erts/emulator/beam/erl_db_util.c | |
parent | 36d93952f6ca64192f05e0482fa55270103c8d97 (diff) | |
download | otp-ed71ea35bad9a511125c82ce42160cad9fa8311f.tar.gz otp-ed71ea35bad9a511125c82ce42160cad9fa8311f.tar.bz2 otp-ed71ea35bad9a511125c82ce42160cad9fa8311f.zip |
Reject unsafe matchspecs on ets:select_replace/2
Preemptively fail operation with badarg if the replacement object
might have a different key.
Diffstat (limited to 'erts/emulator/beam/erl_db_util.c')
-rw-r--r-- | erts/emulator/beam/erl_db_util.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 6f30b1d3dd..3b2e7f4407 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1119,6 +1119,100 @@ error: return NULL; } +/* This is used by select_replace */ +int db_match_keeps_key(int keypos, Eterm match, Eterm guard, Eterm body) { + Eterm match_key = NIL; + int match_key_variable = -1; + Eterm* body_list = NULL; + Eterm single_body_term = NIL; + Eterm* single_body_term_tpl = NULL; + Eterm single_body_subterm = NIL; + Eterm single_body_subterm_key = NIL; + int single_body_subterm_key_variable = -1; + Eterm* single_body_subterm_key_tpl = NULL; + + if (!is_list(body)) { + return 0; + } + + body_list = list_val(body); + if (CDR(body_list) != NIL) { + return 0; + } + + single_body_term = CAR(body_list); + if (single_body_term == am_DollarUnderscore) { + /* same tuple is returned */ + return 1; + } + + if (!is_tuple(single_body_term)) { + return 0; + } + + single_body_term_tpl = tuple_val(single_body_term); + if (arityval(*single_body_term_tpl) != 1) { + // not the 1-element tuple we're expecting + return 0; + } + + match_key = db_getkey(keypos, match); + if (!is_value(match_key)) { + // can't get key out of match + return 0; + } + + single_body_subterm = single_body_term_tpl[1]; + single_body_subterm_key = db_getkey(keypos, single_body_subterm); + if (!is_value(single_body_subterm_key)) { + // can't get key out of single body subterm + return 0; + } + + match_key_variable = db_is_variable(match_key); + single_body_subterm_key_variable = db_is_variable(single_body_subterm_key); + if (match_key_variable != -1 && match_key_variable == single_body_subterm_key_variable) { + /* tuple with same key is returned */ + return 1; + } + + if (!is_tuple(single_body_subterm_key)) { + /* can't possibly be an element instruction */ + return 0; + } + + single_body_subterm_key_tpl = tuple_val(single_body_subterm_key); + if (arityval(*single_body_subterm_key_tpl) != 3) { + /* can't possibly be an element instruction */ + return 0; + } + + if (single_body_subterm_key_tpl[1] != am_element) { + /* tag is not of an element instruction */ + return 0; + } + if (single_body_subterm_key_tpl[3] != am_DollarUnderscore) { + /* even if it's an element instruction, it's not fetching from the original tuple */ + return 0; + } + + if (is_big(single_body_subterm_key_tpl[2]) + && (big_to_uint32(single_body_subterm_key_tpl[2]) != keypos)) + { + /* the key comes from a different position */ + return 0; + } + + if (is_small(single_body_subterm_key_tpl[2]) + && (unsigned_val(single_body_subterm_key_tpl[2]) != keypos)) + { + /* the key comes from a different position */ + return 0; + } + + return 1; +} + /* This is used when tracing */ Eterm erts_match_set_lint(Process *p, Eterm matchexpr) { return db_match_set_lint(p, matchexpr, DCOMP_TRACE); |