diff --git a/common.mk b/common.mk index 173b4fb2a7..3fce9df761 100644 --- a/common.mk +++ b/common.mk @@ -3534,6 +3534,7 @@ compile.$(OBJEXT): {$(VPATH)}prism/prism.h compile.$(OBJEXT): {$(VPATH)}prism/version.h compile.$(OBJEXT): {$(VPATH)}prism_compile.c compile.$(OBJEXT): {$(VPATH)}prism_compile.h +compile.$(OBJEXT): {$(VPATH)}ractor.h compile.$(OBJEXT): {$(VPATH)}re.h compile.$(OBJEXT): {$(VPATH)}regex.h compile.$(OBJEXT): {$(VPATH)}ruby_assert.h diff --git a/compile.c b/compile.c index a66d5e1b93..d3f83cfbd3 100644 --- a/compile.c +++ b/compile.c @@ -36,6 +36,7 @@ #include "internal/thread.h" #include "internal/variable.h" #include "iseq.h" +#include "ruby/ractor.h" #include "ruby/re.h" #include "ruby/util.h" #include "vm_core.h" @@ -9375,6 +9376,8 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node return COMPILE_OK; } +static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value); + static int compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { @@ -9418,7 +9421,7 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node /* cref [obj] */ if (!popped) ADD_INSN(ret, node, pop); /* cref */ if (lassign) ADD_LABEL(ret, lassign); - CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", RNODE_OP_CDECL(node)->nd_value)); + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value)); /* cref value */ if (popped) ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */ @@ -9432,7 +9435,7 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_INSN(ret, node, pop); /* [value] */ } else { - CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", RNODE_OP_CDECL(node)->nd_value)); + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value)); /* cref obj value */ ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1)); /* cref value */ @@ -9948,6 +9951,368 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node return COMPILE_OK; } +static int +compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy) +{ + ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore); + ADD_SEQ(ret, sub); + + if (copy) { + /* + * NEW_CALL(fcore, rb_intern("make_shareable_copy"), + * NEW_LIST(value, loc), loc); + */ + ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + else { + /* + * NEW_CALL(fcore, rb_intern("make_shareable"), + * NEW_LIST(value, loc), loc); + */ + ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + + return COMPILE_OK; +} + +static VALUE +node_const_decl_val(const NODE *node) +{ + VALUE path; + switch (nd_type(node)) { + case NODE_CDECL: + if (RNODE_CDECL(node)->nd_vid) { + path = rb_id2str(RNODE_CDECL(node)->nd_vid); + goto end; + } + else { + node = RNODE_CDECL(node)->nd_else; + } + break; + case NODE_COLON2: + break; + case NODE_COLON3: + // ::Const + path = rb_str_new_cstr("::"); + rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid)); + goto end; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + UNREACHABLE_RETURN(0); + } + + path = rb_ary_new(); + if (node) { + for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) { + rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid)); + } + if (node && nd_type_p(node, NODE_CONST)) { + // Const::Name + rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid)); + } + else if (node && nd_type_p(node, NODE_COLON3)) { + // ::Const::Name + rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid)); + rb_ary_push(path, rb_str_new(0, 0)); + } + else { + // expression::Name + rb_ary_push(path, rb_str_new_cstr("...")); + } + path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::")); + } + end: + path = rb_fstring(path); + return path; +} + +static VALUE +const_decl_path(NODE *dest) +{ + VALUE path = Qnil; + if (!nd_type_p(dest, NODE_CALL)) { + path = node_const_decl_val(dest); + } + return path; +} + +static int +compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value) +{ + /* + *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest)) + */ + VALUE path = const_decl_path(dest); + ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore); + CHECK(COMPILE(ret, "compile_ensure_shareable_node", value)); + ADD_INSN1(ret, value, putobject, path); + RB_OBJ_WRITTEN(iseq, Qundef, path); + ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE)); + + return COMPILE_OK; +} + +#ifndef SHAREABLE_BARE_EXPRESSION +#define SHAREABLE_BARE_EXPRESSION 1 +#endif + +static int +compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, NODE *dest, const NODE *node, size_t level, VALUE *value_p, int *shareable_literal_p) +{ +# define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \ + compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p) + VALUE lit = Qnil; + DECL_ANCHOR(anchor); + + enum node_type type = nd_type(node); + switch (type) { + case NODE_TRUE: + *value_p = Qtrue; + goto compile; + case NODE_FALSE: + *value_p = Qfalse; + goto compile; + case NODE_NIL: + *value_p = Qnil; + goto compile; + case NODE_LIT: + *value_p = RNODE_LIT(node)->nd_lit; + goto compile; + case NODE_SYM: + *value_p = rb_node_sym_string_val(node); + goto compile; + case NODE_REGX: + *value_p = rb_node_regx_string_val(node); + goto compile; + case NODE_LINE: + *value_p = rb_node_line_lineno_val(node); + goto compile; + case NODE_INTEGER: + *value_p = rb_node_integer_literal_val(node); + goto compile; + case NODE_FLOAT: + *value_p = rb_node_float_literal_val(node); + goto compile; + case NODE_RATIONAL: + *value_p = rb_node_rational_literal_val(node); + goto compile; + case NODE_IMAGINARY: + *value_p = rb_node_imaginary_literal_val(node); + goto compile; + case NODE_ENCODING: + *value_p = rb_node_encoding_val(node); + + compile: + CHECK(COMPILE(ret, "shareable_literal_constant", node)); + *shareable_literal_p = 1; + return COMPILE_OK; + + case NODE_DSTR: + CHECK(COMPILE(ret, "shareable_literal_constant", node)); + if (shareable == rb_parser_shareable_literal) { + /* + * NEW_CALL(node, idUMinus, 0, loc); + * + * -"#{var}" + */ + ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + *value_p = Qundef; + *shareable_literal_p = 1; + return COMPILE_OK; + + case NODE_STR:{ + VALUE lit = rb_fstring(rb_node_str_string_val(node)); + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + *value_p = lit; + *shareable_literal_p = 1; + + return COMPILE_OK; + } + + case NODE_FILE:{ + VALUE lit = rb_fstring(rb_node_file_path_val(node)); + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + *value_p = lit; + *shareable_literal_p = 1; + + return COMPILE_OK; + } + + case NODE_ZLIST:{ + VALUE lit = rb_ary_new(); + OBJ_FREEZE_RAW(lit); + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + *value_p = lit; + *shareable_literal_p = 1; + + return COMPILE_OK; + } + + case NODE_LIST:{ + INIT_ANCHOR(anchor); + lit = rb_ary_new(); + for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) { + VALUE val; + int shareable_literal_p2; + NODE *elt = RNODE_LIST(n)->nd_head; + if (elt) { + CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2)); + if (shareable_literal_p2) { + /* noop */ + } + else if (RTEST(lit)) { + rb_ary_clear(lit); + lit = Qfalse; + } + } + if (RTEST(lit)) { + if (!UNDEF_P(val)) { + rb_ary_push(lit, val); + } + else { + rb_ary_clear(lit); + lit = Qnil; /* make shareable at runtime */ + } + } + } + break; + } + case NODE_HASH:{ + if (!RNODE_HASH(node)->nd_brace) { + *value_p = Qundef; + *shareable_literal_p = 0; + return COMPILE_OK; + } + + INIT_ANCHOR(anchor); + lit = rb_hash_new(); + for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) { + VALUE key_val; + VALUE value_val; + int shareable_literal_p2; + NODE *key = RNODE_LIST(n)->nd_head; + NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head; + if (key) { + CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2)); + if (shareable_literal_p2) { + /* noop */ + } + else if (RTEST(lit)) { + rb_hash_clear(lit); + lit = Qfalse; + } + } + if (val) { + CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2)); + if (shareable_literal_p2) { + /* noop */ + } + else if (RTEST(lit)) { + rb_hash_clear(lit); + lit = Qfalse; + } + } + if (RTEST(lit)) { + if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) { + rb_hash_aset(lit, key_val, value_val); + } + else { + rb_hash_clear(lit); + lit = Qnil; /* make shareable at runtime */ + } + } + } + break; + } + + default: + if (shareable == rb_parser_shareable_literal && + (SHAREABLE_BARE_EXPRESSION || level > 0)) { + CHECK(compile_ensure_shareable_node(iseq, ret, dest, node)); + *value_p = Qundef; + *shareable_literal_p = 1; + return COMPILE_OK; + } + CHECK(COMPILE(ret, "shareable_literal_constant", node)); + *value_p = Qundef; + *shareable_literal_p = 0; + return COMPILE_OK; + } + + /* Array or Hash */ + if (!lit) { + if (nd_type(node) == NODE_LIST) { + ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen)); + } + else if (nd_type(node) == NODE_HASH) { + int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen; + ADD_INSN1(anchor, node, newhash, INT2FIX(len)); + } + *value_p = Qundef; + *shareable_literal_p = 0; + ADD_SEQ(ret, anchor); + return COMPILE_OK; + } + if (NIL_P(lit)) { + // if shareable_literal, all elements should have been ensured + // as shareable + if (nd_type(node) == NODE_LIST) { + ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen)); + } + else if (nd_type(node) == NODE_HASH) { + int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen; + ADD_INSN1(anchor, node, newhash, INT2FIX(len)); + } + CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false)); + *value_p = Qundef; + *shareable_literal_p = 1; + } + else { + VALUE val = rb_ractor_make_shareable(lit); + ADD_INSN1(ret, node, putobject, val); + RB_OBJ_WRITTEN(iseq, Qundef, val); + *value_p = val; + *shareable_literal_p = 1; + } + + return COMPILE_OK; +} + +static int +compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value) +{ + int literal_p = 0; + VALUE val; + DECL_ANCHOR(anchor); + INIT_ANCHOR(anchor); + + switch (shareable) { + case rb_parser_shareable_none: + CHECK(COMPILE(ret, "compile_shareable_constant_value", value)); + return COMPILE_OK; + + case rb_parser_shareable_literal: + CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p)); + ADD_SEQ(ret, anchor); + return COMPILE_OK; + + case rb_parser_shareable_copy: + case rb_parser_shareable_everything: + CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p)); + if (!literal_p) { + CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy)); + } + else { + ADD_SEQ(ret, anchor); + } + return COMPILE_OK; + } +} + static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped); /** compile each node @@ -10130,7 +10495,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_CDECL:{ if (RNODE_CDECL(node)->nd_vid) { - CHECK(COMPILE(ret, "lvalue", RNODE_CDECL(node)->nd_value)); + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value)); if (!popped) { ADD_INSN(ret, node, dup); @@ -10142,7 +10507,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } else { compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else); - CHECK(COMPILE(ret, "lvalue", RNODE_CDECL(node)->nd_value)); + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value)); ADD_INSN(ret, node, swap); if (!popped) { diff --git a/internal/ruby_parser.h b/internal/ruby_parser.h index 6a2dcab5ca..7dc6a46d4f 100644 --- a/internal/ruby_parser.h +++ b/internal/ruby_parser.h @@ -28,7 +28,6 @@ VALUE rb_node_dregx_string_val(const NODE *); VALUE rb_node_line_lineno_val(const NODE *); VALUE rb_node_file_path_val(const NODE *); VALUE rb_node_encoding_val(const NODE *); -VALUE rb_node_const_decl_val(const NODE *node); VALUE rb_node_integer_literal_val(const NODE *); VALUE rb_node_float_literal_val(const NODE *); diff --git a/node_dump.c b/node_dump.c index c4195d571c..d9f188d727 100644 --- a/node_dump.c +++ b/node_dump.c @@ -60,6 +60,22 @@ field_flag; /* should be optimized away */ \ reset, field_flag = 0) +#define A_SHAREABILITY(shareability) \ + switch (shareability) { \ + case rb_parser_shareable_none: \ + rb_str_cat_cstr(buf, "none"); \ + break; \ + case rb_parser_shareable_literal: \ + rb_str_cat_cstr(buf, "literal"); \ + break; \ + case rb_parser_shareable_copy: \ + rb_str_cat_cstr(buf, "experimental_copy"); \ + break; \ + case rb_parser_shareable_everything: \ + rb_str_cat_cstr(buf, "experimental_everything"); \ + break; \ + } + #define SIMPLE_FIELD1(name, ann) SIMPLE_FIELD(FIELD_NAME_LEN(name, ann), FIELD_NAME_DESC(name, ann)) #define F_CUSTOM1(name, ann) SIMPLE_FIELD1(#name, ann) #define F_ID(name, type, ann) SIMPLE_FIELD1(#name, ann) A_ID(type(node)->name) @@ -68,6 +84,7 @@ #define F_LIT(name, type, ann) SIMPLE_FIELD1(#name, ann) A_LIT(type(node)->name) #define F_VALUE(name, val, ann) SIMPLE_FIELD1(#name, ann) A_LIT(val) #define F_MSG(name, ann, desc) SIMPLE_FIELD1(#name, ann) A(desc) +#define F_SHAREABILITY(name, type, ann) SIMPLE_FIELD1(#name, ann) A_SHAREABILITY(type(node)->name) #define F_NODE(name, type, ann) \ COMPOUND_FIELD1(#name, ann) {dump_node(buf, indent, comment, RNODE(type(node)->name));} @@ -463,6 +480,7 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node) F_MSG(nd_vid, "constant", "0 (see extension field)"); F_NODE(nd_else, RNODE_CDECL, "extension"); } + F_SHAREABILITY(shareability, RNODE_CDECL, "shareability"); LAST_NODE; F_NODE(nd_value, RNODE_CDECL, "rvalue"); return; @@ -513,6 +531,7 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node) ANN("example: A::B ||= 1"); F_NODE(nd_head, RNODE_OP_CDECL, "constant"); F_ID(nd_aid, RNODE_OP_CDECL, "operator"); + F_SHAREABILITY(shareability, RNODE_OP_CDECL, "shareability"); LAST_NODE; F_NODE(nd_value, RNODE_OP_CDECL, "rvalue"); return; diff --git a/parse.y b/parse.y index 7bea55cb2b..67a0fcc778 100644 --- a/parse.y +++ b/parse.y @@ -302,13 +302,6 @@ VALUE rb_ripper_none; #include "ripper_init.h" #endif -enum shareability { - shareable_none, - shareable_literal, - shareable_copy, - shareable_everything, -}; - enum rescue_context { before_rescue, after_rescue, @@ -322,7 +315,7 @@ struct lex_context { unsigned int in_argdef: 1; unsigned int in_def: 1; unsigned int in_class: 1; - BITFIELD(enum shareability, shareable_constant_value, 2); + BITFIELD(enum rb_parser_shareability, shareable_constant_value, 2); BITFIELD(enum rescue_context, in_rescue, 2); }; @@ -1095,13 +1088,13 @@ static rb_node_lasgn_t *rb_node_lasgn_new(struct parser_params *p, ID nd_vid, NO static rb_node_dasgn_t *rb_node_dasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc); static rb_node_gasgn_t *rb_node_gasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc); static rb_node_iasgn_t *rb_node_iasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc); -static rb_node_cdecl_t *rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, const YYLTYPE *loc); +static rb_node_cdecl_t *rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, enum rb_parser_shareability shareability, const YYLTYPE *loc); static rb_node_cvasgn_t *rb_node_cvasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc); static rb_node_op_asgn1_t *rb_node_op_asgn1_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *index, NODE *rvalue, const YYLTYPE *loc); static rb_node_op_asgn2_t *rb_node_op_asgn2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, ID nd_vid, ID nd_mid, bool nd_aid, const YYLTYPE *loc); static rb_node_op_asgn_or_t *rb_node_op_asgn_or_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc); static rb_node_op_asgn_and_t *rb_node_op_asgn_and_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc); -static rb_node_op_cdecl_t *rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, const YYLTYPE *loc); +static rb_node_op_cdecl_t *rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, enum rb_parser_shareability shareability, const YYLTYPE *loc); static rb_node_call_t *rb_node_call_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc); static rb_node_opcall_t *rb_node_opcall_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc); static rb_node_fcall_t *rb_node_fcall_new(struct parser_params *p, ID nd_mid, NODE *nd_args, const YYLTYPE *loc); @@ -1125,7 +1118,6 @@ static rb_node_nth_ref_t *rb_node_nth_ref_new(struct parser_params *p, long nd_n static rb_node_back_ref_t *rb_node_back_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc); static rb_node_match2_t *rb_node_match2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc); static rb_node_match3_t *rb_node_match3_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc); -static rb_node_lit_t *rb_node_lit_new(struct parser_params *p, VALUE nd_lit, const YYLTYPE *loc); static rb_node_integer_t * rb_node_integer_new(struct parser_params *p, char* val, int base, const YYLTYPE *loc); static rb_node_float_t * rb_node_float_new(struct parser_params *p, char* val, const YYLTYPE *loc); static rb_node_rational_t * rb_node_rational_new(struct parser_params *p, char* val, int base, int seen_point, const YYLTYPE *loc); @@ -1204,13 +1196,13 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE #define NEW_DASGN(v,val,loc) (NODE *)rb_node_dasgn_new(p,v,val,loc) #define NEW_GASGN(v,val,loc) (NODE *)rb_node_gasgn_new(p,v,val,loc) #define NEW_IASGN(v,val,loc) (NODE *)rb_node_iasgn_new(p,v,val,loc) -#define NEW_CDECL(v,val,path,loc) (NODE *)rb_node_cdecl_new(p,v,val,path,loc) +#define NEW_CDECL(v,val,path,share,loc) (NODE *)rb_node_cdecl_new(p,v,val,path,share,loc) #define NEW_CVASGN(v,val,loc) (NODE *)rb_node_cvasgn_new(p,v,val,loc) #define NEW_OP_ASGN1(r,id,idx,rval,loc) (NODE *)rb_node_op_asgn1_new(p,r,id,idx,rval,loc) #define NEW_OP_ASGN2(r,t,i,o,val,loc) (NODE *)rb_node_op_asgn2_new(p,r,val,i,o,t,loc) #define NEW_OP_ASGN_OR(i,val,loc) (NODE *)rb_node_op_asgn_or_new(p,i,val,loc) #define NEW_OP_ASGN_AND(i,val,loc) (NODE *)rb_node_op_asgn_and_new(p,i,val,loc) -#define NEW_OP_CDECL(v,op,val,loc) (NODE *)rb_node_op_cdecl_new(p,v,val,op,loc) +#define NEW_OP_CDECL(v,op,val,share,loc) (NODE *)rb_node_op_cdecl_new(p,v,val,op,share,loc) #define NEW_CALL(r,m,a,loc) (NODE *)rb_node_call_new(p,r,m,a,loc) #define NEW_OPCALL(r,m,a,loc) (NODE *)rb_node_opcall_new(p,r,m,a,loc) #define NEW_FCALL(m,a,loc) rb_node_fcall_new(p,m,a,loc) @@ -1234,7 +1226,6 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE #define NEW_BACK_REF(n,loc) (NODE *)rb_node_back_ref_new(p,n,loc) #define NEW_MATCH2(n1,n2,loc) (NODE *)rb_node_match2_new(p,n1,n2,loc) #define NEW_MATCH3(r,n2,loc) (NODE *)rb_node_match3_new(p,r,n2,loc) -#define NEW_LIT(l,loc) (NODE *)rb_node_lit_new(p,l,loc) #define NEW_INTEGER(val, base,loc) (NODE *)rb_node_integer_new(p,val,base,loc) #define NEW_FLOAT(val,loc) (NODE *)rb_node_float_new(p,val,loc) #define NEW_RATIONAL(val,base,seen_point,loc) (NODE *)rb_node_rational_new(p,val,base,seen_point,loc) @@ -9751,23 +9742,23 @@ parser_set_shareable_constant_value(struct parser_params *p, const char *name, c switch (*val) { case 'n': case 'N': if (STRCASECMP(val, "none") == 0) { - p->ctxt.shareable_constant_value = shareable_none; + p->ctxt.shareable_constant_value = rb_parser_shareable_none; return; } break; case 'l': case 'L': if (STRCASECMP(val, "literal") == 0) { - p->ctxt.shareable_constant_value = shareable_literal; + p->ctxt.shareable_constant_value = rb_parser_shareable_literal; return; } break; case 'e': case 'E': if (STRCASECMP(val, "experimental_copy") == 0) { - p->ctxt.shareable_constant_value = shareable_copy; + p->ctxt.shareable_constant_value = rb_parser_shareable_copy; return; } if (STRCASECMP(val, "experimental_everything") == 0) { - p->ctxt.shareable_constant_value = shareable_everything; + p->ctxt.shareable_constant_value = rb_parser_shareable_everything; return; } break; @@ -12233,15 +12224,6 @@ rb_node_back_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc) return n; } -static rb_node_lit_t * -rb_node_lit_new(struct parser_params *p, VALUE nd_lit, const YYLTYPE *loc) -{ - rb_node_lit_t *n = NODE_NEWNODE(NODE_LIT, rb_node_lit_t, loc); - n->nd_lit = nd_lit; - - return n; -} - static rb_node_integer_t * rb_node_integer_new(struct parser_params *p, char* val, int base, const YYLTYPE *loc) { @@ -12652,23 +12634,25 @@ rb_node_encoding_new(struct parser_params *p, const YYLTYPE *loc) } static rb_node_cdecl_t * -rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, const YYLTYPE *loc) +rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, enum rb_parser_shareability shareability, const YYLTYPE *loc) { rb_node_cdecl_t *n = NODE_NEWNODE(NODE_CDECL, rb_node_cdecl_t, loc); n->nd_vid = nd_vid; n->nd_value = nd_value; n->nd_else = nd_else; + n->shareability = shareability; return n; } static rb_node_op_cdecl_t * -rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, const YYLTYPE *loc) +rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, enum rb_parser_shareability shareability, const YYLTYPE *loc) { rb_node_op_cdecl_t *n = NODE_NEWNODE(NODE_OP_CDECL, rb_node_op_cdecl_t, loc); n->nd_head = nd_head; n->nd_value = nd_value; n->nd_aid = nd_aid; + n->shareability = shareability; return n; } @@ -13772,7 +13756,7 @@ assignable(struct parser_params *p, ID id, NODE *val, const YYLTYPE *loc) case NODE_LASGN: return NEW_LASGN(id, val, loc); case NODE_GASGN: return NEW_GASGN(id, val, loc); case NODE_IASGN: return NEW_IASGN(id, val, loc); - case NODE_CDECL: return NEW_CDECL(id, val, 0, loc); + case NODE_CDECL: return NEW_CDECL(id, val, 0, p->ctxt.shareable_constant_value, loc); case NODE_CVASGN: return NEW_CVASGN(id, val, loc); } /* TODO: FIXME */ @@ -14043,256 +14027,8 @@ mark_lvar_used(struct parser_params *p, NODE *rhs) } } -static NODE * -const_decl_path(struct parser_params *p, NODE *dest) -{ - NODE *n = dest; - if (!nd_type_p(dest, NODE_CALL)) { - const YYLTYPE *loc = &dest->nd_loc; - VALUE path = rb_node_const_decl_val(dest); - n = NEW_LIT(path, loc); - RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(n)->nd_lit); - } - return n; -} - -static NODE * -make_shareable_node(struct parser_params *p, NODE *value, bool copy, const YYLTYPE *loc) -{ - NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc); - - if (copy) { - return NEW_CALL(fcore, rb_intern("make_shareable_copy"), - NEW_LIST(value, loc), loc); - } - else { - return NEW_CALL(fcore, rb_intern("make_shareable"), - NEW_LIST(value, loc), loc); - } -} - -static NODE * -ensure_shareable_node(struct parser_params *p, NODE *dest, NODE *value, const YYLTYPE *loc) -{ - NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc); - NODE *args = NEW_LIST(value, loc); - args = list_append(p, args, const_decl_path(p, dest)); - return NEW_CALL(fcore, rb_intern("ensure_shareable"), args, loc); -} - static int is_static_content(NODE *node); -static VALUE -shareable_literal_value(struct parser_params *p, NODE *node) -{ - if (!node) return Qnil; - enum node_type type = nd_type(node); - switch (type) { - case NODE_TRUE: - return Qtrue; - case NODE_FALSE: - return Qfalse; - case NODE_NIL: - return Qnil; - case NODE_SYM: - return rb_node_sym_string_val(node); - case NODE_LINE: - return rb_node_line_lineno_val(node); - case NODE_INTEGER: - return rb_node_integer_literal_val(node); - case NODE_FLOAT: - return rb_node_float_literal_val(node); - case NODE_RATIONAL: - return rb_node_rational_literal_val(node); - case NODE_IMAGINARY: - return rb_node_imaginary_literal_val(node); - case NODE_ENCODING: - return rb_node_encoding_val(node); - case NODE_REGX: - return rb_node_regx_string_val(node); - case NODE_LIT: - return RNODE_LIT(node)->nd_lit; - default: - return Qundef; - } -} - -#ifndef SHAREABLE_BARE_EXPRESSION -#define SHAREABLE_BARE_EXPRESSION 1 -#endif - -static NODE * -shareable_literal_constant(struct parser_params *p, enum shareability shareable, - NODE *dest, NODE *value, const YYLTYPE *loc, size_t level) -{ -# define shareable_literal_constant_next(n) \ - shareable_literal_constant(p, shareable, dest, (n), &(n)->nd_loc, level+1) - VALUE lit = Qnil; - - if (!value) return 0; - enum node_type type = nd_type(value); - switch (type) { - case NODE_TRUE: - case NODE_FALSE: - case NODE_NIL: - case NODE_LIT: - case NODE_SYM: - case NODE_REGX: - case NODE_LINE: - case NODE_INTEGER: - case NODE_FLOAT: - case NODE_RATIONAL: - case NODE_IMAGINARY: - case NODE_ENCODING: - return value; - - case NODE_DSTR: - if (shareable == shareable_literal) { - value = NEW_CALL(value, idUMinus, 0, loc); - } - return value; - - case NODE_STR: - lit = rb_str_to_interned_str(rb_node_str_string_val(value)); - value = NEW_LIT(lit, loc); - RB_OBJ_WRITE(p->ast, &RNODE_LIT(value)->nd_lit, lit); - return value; - - case NODE_FILE: - lit = rb_str_to_interned_str(rb_node_file_path_val(value)); - value = NEW_LIT(lit, loc); - RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(value)->nd_lit); - return value; - - case NODE_ZLIST: - lit = rb_ary_new(); - OBJ_FREEZE_RAW(lit); - NODE *n = NEW_LIT(lit, loc); - RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(n)->nd_lit); - return n; - - case NODE_LIST: - lit = rb_ary_new(); - for (NODE *n = value; n; n = RNODE_LIST(n)->nd_next) { - NODE *elt = RNODE_LIST(n)->nd_head; - if (elt) { - elt = shareable_literal_constant_next(elt); - if (elt) { - RNODE_LIST(n)->nd_head = elt; - } - else if (RTEST(lit)) { - rb_ary_clear(lit); - lit = Qfalse; - } - } - if (RTEST(lit)) { - VALUE e = shareable_literal_value(p, elt); - if (!UNDEF_P(e)) { - rb_ary_push(lit, e); - } - else { - rb_ary_clear(lit); - lit = Qnil; /* make shareable at runtime */ - } - } - } - break; - - case NODE_HASH: - if (!RNODE_HASH(value)->nd_brace) return 0; - lit = rb_hash_new(); - for (NODE *n = RNODE_HASH(value)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) { - NODE *key = RNODE_LIST(n)->nd_head; - NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head; - if (key) { - key = shareable_literal_constant_next(key); - if (key) { - RNODE_LIST(n)->nd_head = key; - } - else if (RTEST(lit)) { - rb_hash_clear(lit); - lit = Qfalse; - } - } - if (val) { - val = shareable_literal_constant_next(val); - if (val) { - RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head = val; - } - else if (RTEST(lit)) { - rb_hash_clear(lit); - lit = Qfalse; - } - } - if (RTEST(lit)) { - VALUE k = shareable_literal_value(p, key); - VALUE v = shareable_literal_value(p, val); - if (!UNDEF_P(k) && !UNDEF_P(v)) { - rb_hash_aset(lit, k, v); - } - else { - rb_hash_clear(lit); - lit = Qnil; /* make shareable at runtime */ - } - } - } - break; - - default: - if (shareable == shareable_literal && - (SHAREABLE_BARE_EXPRESSION || level > 0)) { - return ensure_shareable_node(p, dest, value, loc); - } - return 0; - } - - /* Array or Hash */ - if (!lit) return 0; - if (NIL_P(lit)) { - // if shareable_literal, all elements should have been ensured - // as shareable - value = make_shareable_node(p, value, false, loc); - } - else { - value = NEW_LIT(rb_ractor_make_shareable(lit), loc); - RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(value)->nd_lit); - } - - return value; -# undef shareable_literal_constant_next -} - -static NODE * -shareable_constant_value(struct parser_params *p, enum shareability shareable, - NODE *lhs, NODE *value, const YYLTYPE *loc) -{ - if (!value) return 0; - switch (shareable) { - case shareable_none: - return value; - - case shareable_literal: - { - NODE *lit = shareable_literal_constant(p, shareable, lhs, value, loc, 0); - if (lit) return lit; - return value; - } - break; - - case shareable_copy: - case shareable_everything: - { - NODE *lit = shareable_literal_constant(p, shareable, lhs, value, loc, 0); - if (lit) return lit; - return make_shareable_node(p, value, shareable == shareable_copy, loc); - } - break; - - default: - UNREACHABLE_RETURN(0); - } -} - static NODE * node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ctxt, const YYLTYPE *loc) { @@ -14300,9 +14036,6 @@ node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ct switch (nd_type(lhs)) { case NODE_CDECL: - rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc); - /* fallthru */ - case NODE_GASGN: case NODE_IASGN: case NODE_LASGN: @@ -15281,28 +15014,12 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c if (lhs) { ID vid = get_nd_vid(p, lhs); YYLTYPE lhs_loc = lhs->nd_loc; - int shareable = ctxt.shareable_constant_value; - if (shareable) { - switch (nd_type(lhs)) { - case NODE_CDECL: - case NODE_COLON2: - case NODE_COLON3: - break; - default: - shareable = 0; - break; - } - } if (op == tOROP) { - rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc); set_nd_value(p, lhs, rhs); nd_set_loc(lhs, loc); asgn = NEW_OP_ASGN_OR(gettable(p, vid, &lhs_loc), lhs, loc); } else if (op == tANDOP) { - if (shareable) { - rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc); - } set_nd_value(p, lhs, rhs); nd_set_loc(lhs, loc); asgn = NEW_OP_ASGN_AND(gettable(p, vid, &lhs_loc), lhs, loc); @@ -15310,9 +15027,6 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c else { asgn = lhs; rhs = NEW_CALL(gettable(p, vid, &lhs_loc), op, NEW_LIST(rhs, &rhs->nd_loc), loc); - if (shareable) { - rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc); - } set_nd_value(p, asgn, rhs); nd_set_loc(asgn, loc); } @@ -15353,8 +15067,7 @@ new_const_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct NODE *asgn; if (lhs) { - rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc); - asgn = NEW_OP_CDECL(lhs, op, rhs, loc); + asgn = NEW_OP_CDECL(lhs, op, rhs, ctxt.shareable_constant_value, loc); } else { asgn = NEW_ERROR(loc); @@ -15369,7 +15082,7 @@ const_decl(struct parser_params *p, NODE *path, const YYLTYPE *loc) if (p->ctxt.in_def) { yyerror1(loc, "dynamic constant assignment"); } - return NEW_CDECL(0, 0, (path), loc); + return NEW_CDECL(0, 0, (path), p->ctxt.shareable_constant_value, loc); } #ifdef RIPPER static VALUE diff --git a/ruby_parser.c b/ruby_parser.c index b674474f8c..60acf642a6 100644 --- a/ruby_parser.c +++ b/ruby_parser.c @@ -994,54 +994,3 @@ rb_node_encoding_val(const NODE *node) { return rb_enc_from_encoding(RNODE_ENCODING(node)->enc); } - -VALUE -rb_node_const_decl_val(const NODE *node) -{ - VALUE path; - switch (nd_type(node)) { - case NODE_CDECL: - if (RNODE_CDECL(node)->nd_vid) { - path = rb_id2str(RNODE_CDECL(node)->nd_vid); - goto end; - } - else { - node = RNODE_CDECL(node)->nd_else; - } - break; - case NODE_COLON2: - break; - case NODE_COLON3: - // ::Const - path = rb_str_new_cstr("::"); - rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid)); - goto end; - default: - rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); - UNREACHABLE_RETURN(0); - } - - path = rb_ary_new(); - if (node) { - for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) { - rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid)); - } - if (node && nd_type_p(node, NODE_CONST)) { - // Const::Name - rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid)); - } - else if (node && nd_type_p(node, NODE_COLON3)) { - // ::Const::Name - rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid)); - rb_ary_push(path, rb_str_new(0, 0)); - } - else { - // expression::Name - rb_ary_push(path, rb_str_new_cstr("...")); - } - path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::")); - } - end: - path = rb_fstring(path); - return path; -} diff --git a/rubyparser.h b/rubyparser.h index 138de47f0d..b51ca63372 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -66,6 +66,13 @@ typedef struct rb_parser_string { char *ptr; } rb_parser_string_t; +enum rb_parser_shareability { + rb_parser_shareable_none, + rb_parser_shareable_literal, + rb_parser_shareable_copy, + rb_parser_shareable_everything, +}; + /* * AST Node */ @@ -444,6 +451,7 @@ typedef struct RNode_CDECL { ID nd_vid; struct RNode *nd_value; struct RNode *nd_else; + enum rb_parser_shareability shareability; } rb_node_cdecl_t; typedef struct RNode_CVASGN { @@ -492,6 +500,7 @@ typedef struct RNode_OP_CDECL { struct RNode *nd_head; struct RNode *nd_value; ID nd_aid; + enum rb_parser_shareability shareability; } rb_node_op_cdecl_t; typedef struct RNode_CALL {