Skip to content

Instantly share code, notes, and snippets.

@takaokouji
Created April 18, 2011 06:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takaokouji/924911 to your computer and use it in GitHub Desktop.
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)
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())
@takaokouji
Copy link
Author

updated. supported Module.module_eval(str) and Class.class_eval(str).

@lrz
Copy link

lrz commented Apr 20, 2011

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.

@takaokouji
Copy link
Author

OK. I rebased to master and regenerated a patch. Thanks.

@lrz
Copy link

lrz commented Apr 21, 2011

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!

@takaokouji
Copy link
Author

Thanks. I will commit it this weekend, because I found some bug that is related const lookup. I try to fix it.

@lrz
Copy link

lrz commented Apr 21, 2011

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