Make {**{}} return unfrozen empty hash
Previously, method call keyword splats and hash keyword splats were compiled exactly the same. This is because parse-wise, they operate on indentical nodes when it comes to compiling the **{}. Fix this by using an ugly hack of temporarily modifying the nd_brace flag in the method call keyword splat case. Inside compile_hash, only optimize the **{} case for hashes where the nd_brace flag has been modified to reflect we are in the method call keyword splat case and it is safe to do so. Since compile_keyword_args is only called in one place, move the keyword_node_p call out of that method to the single caller to avoid duplicating the code.
This commit is contained in:
parent
3325194ac0
commit
ac04b778c1
Notes:
git
2020-03-18 04:10:24 +09:00
24
compile.c
24
compile.c
@ -3898,10 +3898,14 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *co
|
|||||||
return COMPILE_OK;
|
return COMPILE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define HASH_NO_BRACE 0
|
||||||
|
#define HASH_BRACE 1
|
||||||
|
#define METHOD_CALL_KEYWORDS 2
|
||||||
|
|
||||||
static int
|
static int
|
||||||
keyword_node_p(const NODE *const node)
|
keyword_node_p(const NODE *const node)
|
||||||
{
|
{
|
||||||
return nd_type(node) == NODE_HASH && node->nd_brace == FALSE;
|
return nd_type(node) == NODE_HASH && (node->nd_brace & HASH_BRACE) != HASH_BRACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -3912,7 +3916,7 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
|
|||||||
{
|
{
|
||||||
if (kw_arg_ptr == NULL) return FALSE;
|
if (kw_arg_ptr == NULL) return FALSE;
|
||||||
|
|
||||||
if (keyword_node_p(root_node) && root_node->nd_head && nd_type(root_node->nd_head) == NODE_LIST) {
|
if (root_node->nd_head && nd_type(root_node->nd_head) == NODE_LIST) {
|
||||||
const NODE *node = root_node->nd_head;
|
const NODE *node = root_node->nd_head;
|
||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
@ -3970,10 +3974,19 @@ compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node,
|
|||||||
EXPECT_NODE("compile_args", node, NODE_LIST, -1);
|
EXPECT_NODE("compile_args", node, NODE_LIST, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->nd_next == NULL /* last node */ &&
|
if (node->nd_next == NULL && keyword_node_p(node->nd_head)) { /* last node */
|
||||||
compile_keyword_arg(iseq, ret, node->nd_head, keywords_ptr, flag)) {
|
if (compile_keyword_arg(iseq, ret, node->nd_head, keywords_ptr, flag)) {
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* Bad Hack: temporarily mark hash node with flag so compile_hash
|
||||||
|
* can compile call differently.
|
||||||
|
*/
|
||||||
|
node->nd_head->nd_brace = METHOD_CALL_KEYWORDS;
|
||||||
|
NO_CHECK(COMPILE_(ret, "array element", node->nd_head, FALSE));
|
||||||
|
node->nd_head->nd_brace = HASH_NO_BRACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
NO_CHECK(COMPILE_(ret, "array element", node->nd_head, FALSE));
|
NO_CHECK(COMPILE_(ret, "array element", node->nd_head, FALSE));
|
||||||
}
|
}
|
||||||
@ -4165,6 +4178,7 @@ static int
|
|||||||
compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
|
compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
|
||||||
{
|
{
|
||||||
int line = (int)nd_line(node);
|
int line = (int)nd_line(node);
|
||||||
|
int method_call_keywords = node->nd_brace == METHOD_CALL_KEYWORDS;
|
||||||
|
|
||||||
node = node->nd_head;
|
node = node->nd_head;
|
||||||
|
|
||||||
@ -4297,7 +4311,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popp
|
|||||||
int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
|
int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
|
||||||
|
|
||||||
if (empty_kw) {
|
if (empty_kw) {
|
||||||
if (only_kw) {
|
if (only_kw && method_call_keywords) {
|
||||||
/* **{} appears at the last, so it won't be modified.
|
/* **{} appears at the last, so it won't be modified.
|
||||||
* kw is a special NODE_LIT that contains a special empty hash,
|
* kw is a special NODE_LIT that contains a special empty hash,
|
||||||
* so this emits: putobject {}
|
* so this emits: putobject {}
|
||||||
|
@ -93,6 +93,17 @@ class TestSyntax < Test::Unit::TestCase
|
|||||||
assert_valid_syntax("tap (proc do end)", __FILE__, bug9726)
|
assert_valid_syntax("tap (proc do end)", __FILE__, bug9726)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_hash_kwsplat_hash
|
||||||
|
kw = {}
|
||||||
|
h = {a: 1}
|
||||||
|
assert_equal({}, {**{}})
|
||||||
|
assert_equal({}, {**kw})
|
||||||
|
assert_equal(h, {**h})
|
||||||
|
assert_equal(false, {**{}}.frozen?)
|
||||||
|
assert_equal(false, {**kw}.equal?(kw))
|
||||||
|
assert_equal(false, {**h}.equal?(h))
|
||||||
|
end
|
||||||
|
|
||||||
def test_array_kwsplat_hash
|
def test_array_kwsplat_hash
|
||||||
kw = {}
|
kw = {}
|
||||||
h = {a: 1}
|
h = {a: 1}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user