-
-
Save takaokouji/924911 to your computer and use it in GitHub Desktop.
| diff --git a/compiler.cpp b/compiler.cpp | |
| index f5ccc8a..e996c91 100644 | |
| --- a/compiler.cpp | |
| +++ b/compiler.cpp | |
| @@ -76,7 +76,8 @@ RoxorCompiler *RoxorCompiler::shared = NULL; | |
| __save_state(NODE *, ensure_node);\ | |
| __save_state(bool, block_declaration);\ | |
| __save_state(AllocaInst *, dispatch_argv);\ | |
| - __save_state(uint64_t, outer_mask); | |
| + __save_state(uint64_t, outer_mask);\ | |
| + __save_state(GlobalVariable *, outer_stack); | |
| #define restore_compiler_state() \ | |
| __restore_state(current_line);\ | |
| @@ -111,7 +112,8 @@ RoxorCompiler *RoxorCompiler::shared = NULL; | |
| __restore_state(ensure_node);\ | |
| __restore_state(block_declaration);\ | |
| __restore_state(dispatch_argv);\ | |
| - __restore_state(outer_mask); | |
| + __restore_state(outer_mask);\ | |
| + __restore_state(outer_stack); | |
| #define reset_compiler_state() \ | |
| bb = NULL;\ | |
| @@ -145,7 +147,8 @@ RoxorCompiler *RoxorCompiler::shared = NULL; | |
| ensure_node = NULL;\ | |
| block_declaration = false;\ | |
| dispatch_argv = NULL;\ | |
| - outer_mask = 0; | |
| + outer_mask = 0;\ | |
| + outer_stack = NULL; | |
| RoxorCompiler::RoxorCompiler(bool _debug_mode) | |
| { | |
| @@ -249,6 +252,9 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode) | |
| setHasEnsureFunc = NULL; | |
| setScopeFunc = get_function("vm_set_current_scope"); | |
| setCurrentClassFunc = NULL; | |
| + pushOuterFunc = NULL; | |
| + popOuterFunc = NULL; | |
| + setCurrentOuterFunc = NULL; | |
| debugTrapFunc = NULL; | |
| getFFStateFunc = NULL; | |
| setFFStateFunc = NULL; | |
| @@ -1627,15 +1633,19 @@ RoxorCompiler::compile_const(ID id, Value *outer) | |
| if (dynamic_class) { | |
| flags |= CONST_LOOKUP_DYNAMIC_CLASS; | |
| } | |
| + if (inside_eval) { | |
| + flags |= CONST_LOOKUP_INSIDE_EVAL; | |
| + } | |
| Value *args[] = { | |
| outer, | |
| ConstantInt::get(Int64Ty, outer_mask), | |
| compile_ccache(id), | |
| compile_id(id), | |
| - ConstantInt::get(Int32Ty, flags) | |
| + ConstantInt::get(Int32Ty, flags), | |
| + compile_outer_stack() | |
| }; | |
| - Instruction *insn = compile_protected_call(getConstFunc, args, args + 5); | |
| + Instruction *insn = compile_protected_call(getConstFunc, args, args + 6); | |
| attach_current_line_metadata(insn); | |
| return insn; | |
| } | |
| @@ -1790,10 +1800,10 @@ RoxorCompiler::compile_defined_expression(NODE *node) | |
| } | |
| if (definedFunc == NULL) { | |
| - // VALUE rb_vm_defined(VALUE self, int type, VALUE what, VALUE what2); | |
| + // VALUE rb_vm_defined(VALUE self, int type, VALUE what, VALUE what2, rb_vm_outer_t *outer_stack); | |
| definedFunc = cast<Function>(module->getOrInsertFunction( | |
| "rb_vm_defined", | |
| - RubyObjTy, RubyObjTy, Int32Ty, RubyObjTy, RubyObjTy, | |
| + RubyObjTy, RubyObjTy, Int32Ty, RubyObjTy, RubyObjTy, PtrTy, | |
| NULL)); | |
| } | |
| @@ -1804,9 +1814,10 @@ RoxorCompiler::compile_defined_expression(NODE *node) | |
| self, | |
| ConstantInt::get(Int32Ty, type), | |
| what1 == NULL ? nilVal : what1, | |
| - what2 == NULL ? nilVal : what2 | |
| + what2 == NULL ? nilVal : what2, | |
| + compile_outer_stack() | |
| }; | |
| - val = compile_protected_call(definedFunc, args, args + 4); | |
| + val = compile_protected_call(definedFunc, args, args + 5); | |
| } | |
| else { | |
| val = ConstantInt::get(RubyObjTy, (long)CFSTR("expression")); | |
| @@ -2576,6 +2587,59 @@ RoxorCompiler::compile_set_current_class(Value *klass) | |
| return CallInst::Create(setCurrentClassFunc, klass, "", bb); | |
| } | |
| +Value * | |
| +RoxorCompiler::compile_push_outer(Value *klass) | |
| +{ | |
| + if (pushOuterFunc == NULL) { | |
| + // rb_vm_outer_t *rb_vm_push_outer(Class klass) | |
| + pushOuterFunc = cast<Function>( | |
| + module->getOrInsertFunction("rb_vm_push_outer", | |
| + PtrTy, RubyObjTy, NULL)); | |
| + } | |
| + | |
| + Value *val = CallInst::Create(pushOuterFunc, klass, "", bb); | |
| + outer_stack = new GlobalVariable(*RoxorCompiler::module, PtrTy, false, | |
| + GlobalValue::InternalLinkage, | |
| + Constant::getNullValue(PtrTy), ""); | |
| + assert(outer_stack != NULL); | |
| + new StoreInst(val, outer_stack, "", bb); | |
| + return val; | |
| +} | |
| + | |
| +Value * | |
| +RoxorCompiler::compile_pop_outer(void) | |
| +{ | |
| + if (popOuterFunc == NULL) { | |
| + // rb_vm_outer_t *rb_vm_pop_outer(void) | |
| + popOuterFunc = cast<Function>( | |
| + module->getOrInsertFunction("rb_vm_pop_outer", PtrTy, NULL)); | |
| + } | |
| + | |
| + return CallInst::Create(popOuterFunc, "", bb); | |
| +} | |
| + | |
| +Value * | |
| +RoxorCompiler::compile_outer_stack(void) | |
| +{ | |
| + if (outer_stack == NULL) { | |
| + return compile_const_pointer(NULL); | |
| + } | |
| + return new LoadInst(outer_stack, "", bb); | |
| +} | |
| + | |
| +Value * | |
| +RoxorCompiler::compile_set_current_outer(void) | |
| +{ | |
| + if (setCurrentOuterFunc == NULL) { | |
| + // rb_vm_outer_t *rb_vm_set_current_outer(rb_vm_outer_t *outer) | |
| + setCurrentOuterFunc = cast<Function>( | |
| + module->getOrInsertFunction("rb_vm_set_current_outer", | |
| + PtrTy, PtrTy, NULL)); | |
| + } | |
| + | |
| + return CallInst::Create(setCurrentOuterFunc, compile_outer_stack(), "", bb); | |
| +} | |
| + | |
| void | |
| RoxorCompiler::compile_set_current_scope(Value *klass, Value *scope) | |
| { | |
| @@ -2886,6 +2950,8 @@ RoxorCompiler::compile_scope(NODE *node) | |
| current_arity = arity; | |
| } | |
| + compile_set_current_outer(); | |
| + | |
| DEBUG_LEVEL_INC(); | |
| val = compile_node(node->nd_body); | |
| DEBUG_LEVEL_DEC(); | |
| @@ -3932,12 +3998,12 @@ RoxorCompiler::compile_node0(NODE *node) | |
| if (defineClassFunc == NULL) { | |
| // VALUE rb_vm_define_class(ID path, VALUE outer, | |
| // VALUE super, int flags, | |
| - // unsigned char dynamic_class); | |
| + // unsigned char dynamic_class, rb_vm_outer_t *outer_stack); | |
| defineClassFunc = cast<Function>( | |
| module->getOrInsertFunction( | |
| "rb_vm_define_class", | |
| RubyObjTy, IntTy, RubyObjTy, RubyObjTy, | |
| - Int32Ty, Int8Ty, NULL)); | |
| + Int32Ty, Int8Ty, PtrTy, NULL)); | |
| } | |
| int flags = 0; | |
| @@ -3954,10 +4020,11 @@ RoxorCompiler::compile_node0(NODE *node) | |
| ConstantInt::get(Int32Ty, flags), | |
| ConstantInt::get(Int8Ty, | |
| (flags & DEFINE_OUTER) && dynamic_class | |
| - ? 1 : 0) | |
| + ? 1 : 0), | |
| + compile_outer_stack() | |
| }; | |
| Instruction *insn = compile_protected_call(defineClassFunc, | |
| - args, args + 5); | |
| + args, args + 6); | |
| attach_current_line_metadata(insn); | |
| classVal = insn; | |
| } | |
| @@ -3977,6 +4044,10 @@ RoxorCompiler::compile_node0(NODE *node) | |
| bool old_current_block_chain = current_block_chain; | |
| bool old_dynamic_class = dynamic_class; | |
| + GlobalVariable *old_outer_stack = outer_stack; | |
| + compile_push_outer(classVal); | |
| + compile_set_current_outer(); | |
| + | |
| current_block_chain = false; | |
| dynamic_class = false; | |
| @@ -4016,6 +4087,10 @@ RoxorCompiler::compile_node0(NODE *node) | |
| params.push_back(compile_const_pointer(NULL)); | |
| val = compile_protected_call(f, params); | |
| + compile_pop_outer(); | |
| + outer_stack = old_outer_stack; | |
| + compile_set_current_outer(); | |
| + | |
| dynamic_class = old_dynamic_class; | |
| compile_set_current_scope(classVal, defaultScope); | |
| diff --git a/compiler.h b/compiler.h | |
| index f442222..0c999ff 100644 | |
| --- a/compiler.h | |
| +++ b/compiler.h | |
| @@ -19,6 +19,7 @@ | |
| // For const lookup. | |
| #define CONST_LOOKUP_LEXICAL 1 | |
| #define CONST_LOOKUP_DYNAMIC_CLASS 2 | |
| +#define CONST_LOOKUP_INSIDE_EVAL 4 | |
| // For defined? | |
| #define DEFINED_IVAR 1 | |
| @@ -148,6 +149,7 @@ class RoxorCompiler { | |
| bool block_declaration; | |
| AllocaInst *dispatch_argv; | |
| long outer_mask; | |
| + GlobalVariable *outer_stack; | |
| Function *writeBarrierFunc; | |
| Function *dispatchFunc; | |
| @@ -234,6 +236,9 @@ class RoxorCompiler { | |
| Function *setHasEnsureFunc; | |
| Function *setScopeFunc; | |
| Function *setCurrentClassFunc; | |
| + Function *pushOuterFunc; | |
| + Function *popOuterFunc; | |
| + Function *setCurrentOuterFunc; | |
| Function *debugTrapFunc; | |
| Function *getFFStateFunc; | |
| Function *setFFStateFunc; | |
| @@ -421,6 +426,10 @@ class RoxorCompiler { | |
| void compile_set_current_scope(Value *klass, Value *scope); | |
| Value *compile_set_current_class(Value *klass); | |
| + Value *compile_push_outer(Value *klass); | |
| + Value *compile_pop_outer(void); | |
| + Value *compile_outer_stack(void); | |
| + Value *compile_set_current_outer(void); | |
| Value *compile_landing_pad_header(void); | |
| void compile_landing_pad_footer(bool pop_exception=true); | |
| diff --git a/dispatcher.cpp b/dispatcher.cpp | |
| index 2aaed32..e79a37c 100644 | |
| --- a/dispatcher.cpp | |
| +++ b/dispatcher.cpp | |
| @@ -1286,6 +1286,9 @@ rb_vm_yield_under(VALUE klass, VALUE self, int argc, const VALUE *argv) | |
| VALUE old_class = b->klass; | |
| b->klass = klass; | |
| + rb_vm_outer_t *o = vm->push_outer((Class)klass); | |
| + o->pushed_by_eval = true; | |
| + | |
| struct Finally { | |
| RoxorVM *vm; | |
| rb_vm_block_t *b; | |
| @@ -1299,6 +1302,7 @@ rb_vm_yield_under(VALUE klass, VALUE self, int argc, const VALUE *argv) | |
| old_self = _old_self; | |
| } | |
| ~Finally() { | |
| + vm->pop_outer(); | |
| b->self = old_self; | |
| b->klass = old_class; | |
| vm->add_current_block(b); | |
| diff --git a/eval.c b/eval.c | |
| index 28bcd79..9bb7b29 100644 | |
| --- a/eval.c | |
| +++ b/eval.c | |
| @@ -229,18 +229,9 @@ ruby_run_node(void *n) | |
| */ | |
| static VALUE | |
| -rb_mod_nesting(VALUE rcv, SEL sel, VALUE top, int argc, VALUE *argv) | |
| +rb_mod_nesting(VALUE self, SEL sel) | |
| { | |
| - rb_scan_args(argc, argv, "00"); | |
| - | |
| - switch (TYPE(top)) { | |
| - case T_CLASS: | |
| - case T_MODULE: | |
| - return rb_vm_module_nesting(top); | |
| - | |
| - default: | |
| - return rb_ary_new(); | |
| - } | |
| + return rb_vm_module_nesting(); | |
| } | |
| /* | |
| @@ -884,7 +875,7 @@ Init_eval(void) | |
| Init_vm_eval(); | |
| Init_eval_method(); | |
| - rb_objc_define_method(*(VALUE *)rb_cModule, "nesting", rb_mod_nesting, -3); | |
| + rb_objc_define_method(*(VALUE *)rb_cModule, "nesting", rb_mod_nesting, 0); | |
| rb_objc_define_method(*(VALUE *)rb_cModule, "constants", rb_mod_s_constants, -1); | |
| VALUE cTopLevel = *(VALUE *)rb_vm_top_self(); | |
| diff --git a/interpreter.cpp b/interpreter.cpp | |
| index 7fe4362..dda7c65 100644 | |
| --- a/interpreter.cpp | |
| +++ b/interpreter.cpp | |
| @@ -120,6 +120,12 @@ RoxorInterpreter::interpret_call(CallInst *call) | |
| return rb_singleton_class(klass); | |
| } | |
| + else if (called == RoxorCompiler::shared->setCurrentOuterFunc) { | |
| + rb_vm_outer_t *outer_stack = value_as(call_arg(call, 0), rb_vm_outer_t *); | |
| + | |
| + rb_vm_set_current_outer(outer_stack); | |
| + return Qnil; | |
| + } | |
| oops("unrecognized call instruction:", call); | |
| } | |
| diff --git a/kernel.c b/kernel.c | |
| index c0f8d14..cc22b25 100644 | |
| --- a/kernel.c | |
| +++ b/kernel.c | |
| @@ -140,11 +140,13 @@ vm_cvar_set(VALUE klass, ID id, VALUE val, unsigned char dynamic_class) | |
| PRIMITIVE VALUE | |
| vm_get_const(VALUE outer, uint64_t outer_mask, void *cache_p, ID path, | |
| - int flags) | |
| + int flags, void *outer_stack_p) | |
| { | |
| struct ccache *cache = (struct ccache *)cache_p; | |
| + rb_vm_outer_t *outer_stack = (rb_vm_outer_t *)outer_stack_p; | |
| const bool lexical_lookup = (flags & CONST_LOOKUP_LEXICAL); | |
| const bool dynamic_class = (flags & CONST_LOOKUP_DYNAMIC_CLASS); | |
| + const bool inside_eval = (flags & CONST_LOOKUP_INSIDE_EVAL); | |
| if (dynamic_class) { | |
| Class k = rb_vm_get_current_class(); | |
| @@ -153,16 +155,21 @@ vm_get_const(VALUE outer, uint64_t outer_mask, void *cache_p, ID path, | |
| } | |
| } | |
| + if (inside_eval) { | |
| + outer_stack = rb_vm_get_outer_stack(); | |
| + } | |
| + | |
| VALUE val; | |
| if (cache->outer == outer && cache->outer_mask == outer_mask | |
| - && cache->val != Qundef) { | |
| + && cache->outer_stack == outer_stack && cache->val != Qundef) { | |
| val = cache->val; | |
| } | |
| else { | |
| val = rb_vm_const_lookup_level(outer, outer_mask, path, | |
| - lexical_lookup, false); | |
| + lexical_lookup, false, outer_stack); | |
| cache->outer = outer; | |
| cache->outer_mask = outer_mask; | |
| + cache->outer_stack = outer_stack; | |
| cache->val = val; | |
| } | |
| return val; | |
| diff --git a/spec/frozen/tags/macruby/core/module/nesting_tags.txt b/spec/frozen/tags/macruby/core/module/nesting_tags.txt | |
| index 72dd88b..e69de29 100644 | |
| --- a/spec/frozen/tags/macruby/core/module/nesting_tags.txt | |
| +++ b/spec/frozen/tags/macruby/core/module/nesting_tags.txt | |
| @@ -1,2 +0,0 @@ | |
| -fails:Module::Nesting returns the list of Modules nested at the point of call | |
| -fails:Module::Nesting returns the nesting for module/class declaring the called method | |
| diff --git a/spec/frozen/tags/macruby/language/constants_tags.txt b/spec/frozen/tags/macruby/language/constants_tags.txt | |
| index a9127f3..e69de29 100644 | |
| --- a/spec/frozen/tags/macruby/language/constants_tags.txt | |
| +++ b/spec/frozen/tags/macruby/language/constants_tags.txt | |
| @@ -1,2 +0,0 @@ | |
| -fails:Constant resolution within methods with statically assigned constants searches Object as a lexical scope only if Object is explicitly opened | |
| -fails:Constant resolution within methods with dynamically assigned constants searches Object as a lexical scope only if Object is explicitly opened | |
| diff --git a/test_vm/constant.rb b/test_vm/constant.rb | |
| index b28efc8..8bbe959 100644 | |
| --- a/test_vm/constant.rb | |
| +++ b/test_vm/constant.rb | |
| @@ -80,7 +80,7 @@ assert '42', %q{ | |
| def baz; Baz; end | |
| end | |
| p o.baz.bar | |
| -}, :known_bug => true | |
| +} | |
| assert '42', %q{ | |
| module M | |
| @@ -130,7 +130,7 @@ assert '42', %{ | |
| end | |
| end | |
| Foo.new.hey | |
| -}, :known_bug => true | |
| +} | |
| assert '42', %{ | |
| module Foo; end | |
| diff --git a/test_vm/eval.rb b/test_vm/eval.rb | |
| index 2e759f8..351ee1f 100644 | |
| --- a/test_vm/eval.rb | |
| +++ b/test_vm/eval.rb | |
| @@ -85,7 +85,7 @@ assert ':ok', %{ | |
| rescue NameError | |
| p :ok | |
| end | |
| -}, :known_bug => true | |
| +} | |
| assert '42', %{ | |
| class A; end | |
| diff --git a/vm.cpp b/vm.cpp | |
| index 86c39e8..04ed4a4 100644 | |
| --- a/vm.cpp | |
| +++ b/vm.cpp | |
| @@ -8,6 +8,7 @@ | |
| #define ROXOR_VM_DEBUG 0 | |
| #define ROXOR_COMPILER_DEBUG 0 | |
| +#define ROXOR_VM_DEBUG_CONST 0 | |
| #if MACRUBY_STATIC | |
| # include <vector> | |
| @@ -396,6 +397,8 @@ RoxorVM::RoxorVM(void) | |
| { | |
| current_top_object = Qnil; | |
| current_class = NULL; | |
| + outer_stack = NULL; | |
| + current_outer = NULL; | |
| safe_level = 0; | |
| backref = Qnil; | |
| broken_with = Qundef; | |
| @@ -1240,33 +1243,89 @@ retry: | |
| return Qundef; | |
| } | |
| +#if ROXOR_VM_DEBUG_CONST | |
| +extern "C" const char *ruby_node_name(int node); | |
| + | |
| +static void | |
| +rb_vm_print_outer_stack(const char *fname, NODE *node, const char *function, int line, | |
| + rb_vm_outer_t *outer_stack, const char *prefix) | |
| +{ | |
| + if (fname != NULL) { | |
| + printf("%s:", fname); | |
| + } | |
| + if (node != NULL) { | |
| + printf("%ld:%s:", nd_line(node), ruby_node_name(nd_type(node))); | |
| + } | |
| + printf("%s:%d:", function, line); | |
| + if (prefix != NULL && prefix[0] != '\0') { | |
| + printf("%s ", prefix); | |
| + } | |
| + printf("outer_stack("); | |
| + | |
| + bool first = true; | |
| + for (rb_vm_outer_t *o = outer_stack; o != NULL; o = o->outer) { | |
| + if (first) { | |
| + first = false; | |
| + } | |
| + else { | |
| + printf(" > "); | |
| + } | |
| + printf("%s", class_getName(o->klass)); | |
| + if (o->pushed_by_eval) { | |
| + printf("[skip]"); | |
| + } | |
| + } | |
| + printf(")\n"); | |
| +} | |
| +#endif | |
| + | |
| extern "C" | |
| VALUE | |
| rb_vm_const_lookup_level(VALUE outer, uint64_t outer_mask, ID path, | |
| - bool lexical, bool defined) | |
| + bool lexical, bool defined, rb_vm_outer_t *outer_stack) | |
| { | |
| rb_vm_check_if_module(outer); | |
| - | |
| +#if ROXOR_VM_DEBUG_CONST | |
| + printf("%s:%d:%s:outer(%s) outer_mask(%llu) path(%s) lexical(%s) defined(%s) outer_stack(%p)\n", __FILE__, __LINE__, __FUNCTION__, | |
| + class_getName((Class)outer), outer_mask, rb_id2name(path), lexical ? "true" : "false", defined ? "true" : "false", outer_stack); | |
| if (lexical) { | |
| + GET_CORE()->lock(); | |
| + rb_vm_print_outer_stack(NULL, NULL, __FUNCTION__, __LINE__, | |
| + GET_VM()->get_outer_stack(), "vm->get_outer_stack"); | |
| + | |
| + rb_vm_print_outer_stack(NULL, NULL, __FUNCTION__, __LINE__, | |
| + GET_VM()->get_current_outer(), "vm->get_current_outer"); | |
| + | |
| + rb_vm_print_outer_stack(NULL, NULL, __FUNCTION__, __LINE__, | |
| + GET_CORE()->get_outer((Class)outer), "core->get_outer"); | |
| + GET_CORE()->unlock(); | |
| + } | |
| +#endif | |
| + | |
| + if (lexical && outer_stack != NULL) { | |
| // Let's do a lexical lookup before a hierarchical one, by looking for | |
| // the given constant in all modules under the given outer. | |
| GET_CORE()->lock(); | |
| - struct rb_vm_outer *o = GET_CORE()->get_outer((Class)outer); | |
| - unsigned int n = 0; | |
| - while (o != NULL && o->klass != (Class)rb_cNSObject) { | |
| - // If the current outer isn't in the mask, it means we can use it | |
| - // for const lookup. The outer mask is used when performing const | |
| - // lookups inside modules defined using the :: notation | |
| - // (ex: class A::B; class C; class D::E; ...) | |
| - if (!(outer_mask & (1 << n))) { | |
| - VALUE val = rb_const_get_direct((VALUE)o->klass, path); | |
| - if (val != Qundef) { | |
| - GET_CORE()->unlock(); | |
| - return defined ? Qtrue : val; | |
| - } | |
| +#if ROXOR_VM_DEBUG_CONST | |
| + rb_vm_print_outer_stack(NULL, NULL, __FUNCTION__, __LINE__, | |
| + outer_stack, "compile time"); | |
| +#endif | |
| + rb_vm_outer_t *root_outer = outer_stack; | |
| + while (root_outer != NULL && root_outer->pushed_by_eval) { | |
| + root_outer = root_outer->outer; | |
| + } | |
| + for (rb_vm_outer_t *o = root_outer; o != NULL; o = o->outer) { | |
| + if (o->pushed_by_eval) { | |
| + continue; | |
| } | |
| - o = o->outer; | |
| - n++; | |
| + VALUE val = rb_const_get_direct((VALUE)o->klass, path); | |
| + if (val != Qundef) { | |
| + GET_CORE()->unlock(); | |
| + return defined ? Qtrue : val; | |
| + } | |
| + } | |
| + if (root_outer && !NIL_P(root_outer->klass)) { | |
| + outer = (VALUE)root_outer->klass; | |
| } | |
| GET_CORE()->unlock(); | |
| } | |
| @@ -1329,24 +1388,24 @@ rb_vm_get_outer(VALUE klass) | |
| extern "C" | |
| VALUE | |
| -rb_vm_module_nesting(VALUE mod) | |
| +rb_vm_module_nesting(void) | |
| { | |
| VALUE ary = rb_ary_new(); | |
| - rb_vm_outer_t *o = GET_CORE()->get_outer((Class)mod); | |
| - while (o != NULL) { | |
| - rb_ary_push(ary, (VALUE)o->klass); | |
| - o = o->outer; | |
| + for (rb_vm_outer_t *o = GET_VM()->get_current_outer(); o != NULL; o = o->outer) { | |
| + if (!o->pushed_by_eval) { | |
| + rb_ary_push(ary, (VALUE)o->klass); | |
| + } | |
| } | |
| return ary; | |
| } | |
| static VALUE | |
| -get_klass_const(VALUE outer, ID path, bool lexical) | |
| +get_klass_const(VALUE outer, ID path, bool lexical, rb_vm_outer_t *outer_stack) | |
| { | |
| VALUE klass = Qundef; | |
| if (lexical) { | |
| - if (rb_vm_const_lookup(outer, path, true, true) == Qtrue) { | |
| - klass = rb_vm_const_lookup(outer, path, true, false); | |
| + if (rb_vm_const_lookup(outer, path, true, true, outer_stack) == Qtrue) { | |
| + klass = rb_vm_const_lookup(outer, path, true, false, outer_stack); | |
| } | |
| } | |
| else { | |
| @@ -1367,19 +1426,22 @@ get_klass_const(VALUE outer, ID path, bool lexical) | |
| extern "C" | |
| VALUE | |
| rb_vm_define_class(ID path, VALUE outer, VALUE super, int flags, | |
| - unsigned char dynamic_class) | |
| + unsigned char dynamic_class, rb_vm_outer_t *outer_stack) | |
| { | |
| assert(path > 0); | |
| rb_vm_check_if_module(outer); | |
| if (dynamic_class) { | |
| - Class k = GET_VM()->get_current_class(); | |
| - if (k != NULL) { | |
| - outer = (VALUE)k; | |
| + rb_vm_outer_t *root_outer = outer_stack; | |
| + while (root_outer != NULL && root_outer->pushed_by_eval) { | |
| + root_outer = root_outer->outer; | |
| + } | |
| + if (root_outer != NULL) { | |
| + outer = (VALUE)root_outer->klass; | |
| } | |
| } | |
| - VALUE klass = get_klass_const(outer, path, dynamic_class); | |
| + VALUE klass = get_klass_const(outer, path, dynamic_class, outer_stack); | |
| if (klass != Qundef) { | |
| // Constant is already defined. | |
| if (!(flags & DEFINE_MODULE) && super != 0) { | |
| @@ -1584,7 +1646,7 @@ rb_vm_undef2(VALUE klass, VALUE sym, unsigned char dynamic_class) | |
| extern "C" | |
| VALUE | |
| -rb_vm_defined(VALUE self, int type, VALUE what, VALUE what2) | |
| +rb_vm_defined(VALUE self, int type, VALUE what, VALUE what2, rb_vm_outer_t *outer_stack) | |
| { | |
| const char *str = NULL; | |
| @@ -1610,8 +1672,7 @@ rb_vm_defined(VALUE self, int type, VALUE what, VALUE what2) | |
| case DEFINED_CONST: | |
| case DEFINED_LCONST: | |
| { | |
| - if (rb_vm_const_lookup(what2, (ID)what, | |
| - type == DEFINED_LCONST, true)) { | |
| + if (rb_vm_const_lookup(what2, (ID)what, type == DEFINED_LCONST, true, outer_stack)) { | |
| str = "constant"; | |
| } | |
| } | |
| @@ -3032,6 +3093,72 @@ rb_vm_add_binding_lvar_use(rb_vm_binding_t *binding, rb_vm_block_t *block, | |
| rb_vm_add_lvar_use(parent_var_uses, binding, VM_LVAR_USE_TYPE_BINDING); | |
| } | |
| +rb_vm_outer_t * | |
| +RoxorVM::push_outer(Class klass) | |
| +{ | |
| + rb_vm_outer_t *o = (rb_vm_outer_t *)malloc(sizeof(rb_vm_outer_t)); | |
| + o->klass = klass; | |
| + o->outer = outer_stack; | |
| + o->pushed_by_eval = false; | |
| + outer_stack = o; | |
| + | |
| +#if ROXOR_VM_DEBUG_CONST | |
| + rb_vm_print_outer_stack(NULL, NULL, __FUNCTION__, __LINE__, | |
| + outer_stack, "push_outer"); | |
| +#endif | |
| + | |
| + return o; | |
| +} | |
| + | |
| +rb_vm_outer_t * | |
| +RoxorVM::pop_outer(void) | |
| +{ | |
| + assert(outer_stack != NULL); | |
| + // KOUJI_TODO: collect garbage. but not all, only unused. | |
| + rb_vm_outer_t *old = outer_stack; | |
| + outer_stack = outer_stack->outer; | |
| + | |
| +#if ROXOR_VM_DEBUG_CONST | |
| + rb_vm_print_outer_stack(NULL, NULL, __FUNCTION__, __LINE__, | |
| + outer_stack, "pop_outer"); | |
| +#endif | |
| + | |
| + return old; | |
| +} | |
| + | |
| +rb_vm_outer_t * | |
| +rb_vm_push_outer(Class klass) | |
| +{ | |
| + return GET_VM()->push_outer(klass); | |
| +} | |
| + | |
| +rb_vm_outer_t * | |
| +rb_vm_pop_outer(void) | |
| +{ | |
| + return GET_VM()->pop_outer(); | |
| +} | |
| + | |
| +rb_vm_outer_t * | |
| +rb_vm_get_outer_stack(void) | |
| +{ | |
| + return GET_VM()->get_outer_stack(); | |
| +} | |
| + | |
| +rb_vm_outer_t * | |
| +rb_vm_set_current_outer(rb_vm_outer_t *outer) | |
| +{ | |
| + RoxorVM *vm = GET_VM(); | |
| + rb_vm_outer_t *old = vm->get_current_outer(); | |
| + vm->set_current_outer(outer); | |
| + return old; | |
| +} | |
| + | |
| +rb_vm_outer_t * | |
| +rb_vm_get_current_outer(void) | |
| +{ | |
| + return GET_VM()->get_current_outer(); | |
| +} | |
| + | |
| struct rb_vm_kept_local { | |
| ID name; | |
| VALUE *stack_address; | |
| @@ -3995,7 +4122,16 @@ rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node, | |
| } | |
| Class old_class = GET_VM()->get_current_class(); | |
| bool old_dynamic_class = RoxorCompiler::shared->is_dynamic_class(); | |
| + rb_vm_outer_t *old_outer_stack = vm->get_outer_stack(); | |
| + rb_vm_outer_t *old_current_outer = vm->get_current_outer(); | |
| + | |
| vm->set_current_class((Class)klass); | |
| + vm->set_outer_stack(old_current_outer); | |
| + bool pushed = false; | |
| + if (klass != 0 && !NIL_P(klass)) { | |
| + vm->push_outer((Class)klass); | |
| + pushed = true; | |
| + } | |
| RoxorCompiler::shared->set_dynamic_class(true); | |
| vm->add_current_block(binding != NULL ? binding->block : NULL); | |
| @@ -4005,19 +4141,30 @@ rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node, | |
| bool old_dynamic_class; | |
| Class old_class; | |
| VALUE old_top_object; | |
| - Finally(RoxorVM *_vm, bool _dynamic_class, Class _class, VALUE _obj) { | |
| + rb_vm_outer_t *old_outer_stack; | |
| + rb_vm_outer_t *old_current_outer; | |
| + bool pushed; | |
| + Finally(RoxorVM *_vm, bool _dynamic_class, Class _class, VALUE _obj, rb_vm_outer_t *_outer_stack, rb_vm_outer_t *_current_outer, bool _pushed) { | |
| vm = _vm; | |
| old_dynamic_class = _dynamic_class; | |
| old_class = _class; | |
| old_top_object = _obj; | |
| + old_outer_stack = _outer_stack; | |
| + old_current_outer = _current_outer; | |
| + pushed = _pushed; | |
| } | |
| ~Finally() { | |
| RoxorCompiler::shared->set_dynamic_class(old_dynamic_class); | |
| vm->set_current_top_object(old_top_object); | |
| + if (pushed) { | |
| + vm->pop_outer(); | |
| + } | |
| + vm->set_current_outer(old_current_outer); | |
| + vm->set_outer_stack(old_outer_stack); | |
| vm->set_current_class(old_class); | |
| vm->pop_current_block(); | |
| } | |
| - } finalizer(vm, old_dynamic_class, old_class, old_top_object); | |
| + } finalizer(vm, old_dynamic_class, old_class, old_top_object, old_outer_stack, old_current_outer, pushed); | |
| return rb_vm_run(fname, node, binding, inside_eval); | |
| #endif | |
| diff --git a/vm.h b/vm.h | |
| index 93560cb..34ae17f 100644 | |
| --- a/vm.h | |
| +++ b/vm.h | |
| @@ -181,6 +181,7 @@ typedef struct rb_vm_thread { | |
| typedef struct rb_vm_outer { | |
| Class klass; | |
| + bool pushed_by_eval; | |
| struct rb_vm_outer *outer; | |
| } rb_vm_outer_t; | |
| @@ -321,11 +322,11 @@ void rb_vm_const_is_defined(ID path); | |
| VALUE rb_vm_resolve_const_value(VALUE val, VALUE klass, ID name); | |
| VALUE rb_vm_const_lookup_level(VALUE outer, uint64_t outer_mask, ID path, | |
| - bool lexical, bool defined); | |
| + bool lexical, bool defined, rb_vm_outer_t *outer_stack); | |
| static inline VALUE | |
| -rb_vm_const_lookup(VALUE outer, ID path, bool lexical, bool defined) | |
| +rb_vm_const_lookup(VALUE outer, ID path, bool lexical, bool defined, rb_vm_outer_t *outer_stack) | |
| { | |
| - return rb_vm_const_lookup_level(outer, 0, path, lexical, defined); | |
| + return rb_vm_const_lookup_level(outer, 0, path, lexical, defined, outer_stack); | |
| } | |
| bool rb_vm_lookup_method(Class klass, SEL sel, IMP *pimp, | |
| @@ -358,7 +359,7 @@ void rb_vm_push_methods(VALUE ary, VALUE mod, bool include_objc_methods, | |
| int (*filter) (VALUE, ID, VALUE)); | |
| void rb_vm_set_outer(VALUE klass, VALUE under); | |
| VALUE rb_vm_get_outer(VALUE klass); | |
| -VALUE rb_vm_module_nesting(VALUE mod); | |
| +VALUE rb_vm_module_nesting(void); | |
| VALUE rb_vm_catch(VALUE tag); | |
| VALUE rb_vm_throw(VALUE tag, VALUE value); | |
| @@ -498,6 +499,12 @@ void rb_vm_set_abort_on_exception(bool flag); | |
| Class rb_vm_set_current_class(Class klass); | |
| Class rb_vm_get_current_class(void); | |
| +rb_vm_outer_t *rb_vm_push_outer(Class klass); | |
| +rb_vm_outer_t *rb_vm_pop_outer(void); | |
| +rb_vm_outer_t *rb_vm_get_outer_stack(void); | |
| +rb_vm_outer_t *rb_vm_set_current_outer(rb_vm_outer_t *outer); | |
| +rb_vm_outer_t *rb_vm_get_current_outer(void); | |
| + | |
| bool rb_vm_aot_feature_load(const char *name); | |
| bool rb_vm_generate_objc_class_name(const char *name, char *buf, | |
| @@ -583,6 +590,7 @@ struct icache *rb_vm_ivar_slot_allocate(void); | |
| struct ccache { | |
| VALUE outer; | |
| uint64_t outer_mask; | |
| + rb_vm_outer_t *outer_stack; | |
| VALUE val; | |
| }; | |
| @@ -1063,6 +1071,8 @@ class RoxorVM { | |
| std::vector<rb_vm_binding_t *> bindings; | |
| std::map<VALUE, int *> catch_nesting; | |
| std::vector<VALUE> recursive_objects; | |
| + rb_vm_outer_t *outer_stack; | |
| + rb_vm_outer_t *current_outer; | |
| // Method cache. | |
| struct mcache *mcache; | |
| @@ -1114,6 +1124,8 @@ class RoxorVM { | |
| READER(mcache, struct mcache *); | |
| ACCESSOR(current_mri_method_self, VALUE); | |
| ACCESSOR(current_mri_method_sel, SEL); | |
| + ACCESSOR(outer_stack, rb_vm_outer_t *); | |
| + ACCESSOR(current_outer, rb_vm_outer_t *); | |
| void debug_blocks(void); | |
| @@ -1212,6 +1224,9 @@ class RoxorVM { | |
| VALUE exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, | |
| VALUE arg); | |
| + | |
| + rb_vm_outer_t *push_outer(Class klass); | |
| + rb_vm_outer_t *pop_outer(void); | |
| }; | |
| #define GET_VM() (RoxorVM::current()) |
I just read through the patch, the code looks very good, I have nothing to say against it. Now I need to try it to understand better how it works. I can see that you came up with a much smarter idea for lexical const lookup :-)
Just one thing, there seems to be changes in this patch that have been integrated already in master (test_vm.rb, builder.rake, ...), could you remove them for clarity? Thanks.
OK. I rebased to master and regenerated a patch. Thanks.
Okay, I finished reviewing and testing the change. Sorry for the delay, I was interrupted 3 times :) Please go ahead and commit! Thanks a lot for working on this, I hope that there won't be (many) const lookup bugs left in MacRuby now!
Thanks. I will commit it this weekend, because I found some bug that is related const lookup. I try to fix it.
Okay. There are a couple things we could improve in your patch but we can do it once it's committed: a) making sure the pop_outer() and set_current_outer() calls are entered even if an exception is raised from a class body b) rewriting the push/pop_set outer functions into kernel.c so that they will be properly optimized and inlined. I can take care of that once the patch is merged into master :)
updated. supported Module.module_eval(str) and Class.class_eval(str).