Created
April 18, 2011 06:46
-
-
Save takaokouji/924911 to your computer and use it in GitHub Desktop.
a patch for MacRuby's issue #1192 (http://www.macruby.org/trac/ticket/1192)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
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 :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks. I will commit it this weekend, because I found some bug that is related const lookup. I try to fix it.