-
-
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).