diff --git a/src/wren_compiler.c b/src/wren_compiler.c index 295f7a31..044377bf 100644 --- a/src/wren_compiler.c +++ b/src/wren_compiler.c @@ -243,11 +243,23 @@ static int initCompiler(Compiler* compiler, Parser* parser, Compiler* parent, } else { - // Declare a fake local variable for "this" so that it's slot in the stack - // is taken. + // Declare a fake local variable for the receiver so that it's slot in the + // stack is taken. For methods, we call this "this", so that we can resolve + // references to that like a normal variable. For functions, they have no + // explicit "this". So we pick a bogus name. That way references to "this" + // inside a function will try to walk up the parent chain to find a method + // enclosing the function whose "this" we can close over. compiler->numLocals = 1; - compiler->locals[0].name = NULL; - compiler->locals[0].length = 0; + if (methodName != NULL) + { + compiler->locals[0].name = "this"; + compiler->locals[0].length = 4; + } + else + { + compiler->locals[0].name = NULL; + compiler->locals[0].length = 0; + } compiler->locals[0].depth = -1; compiler->locals[0].isUpvalue = false; @@ -1391,8 +1403,6 @@ static void super_(Compiler* compiler, bool allowAssignment) emit(compiler, 0); // TODO: Super operator calls. - // TODO: A bare "super" should call the superclass method of the same name - // with no arguments. // See if it's a named super call, or an unnamed one. if (match(compiler, TOKEN_DOT)) @@ -1429,13 +1439,11 @@ static void this_(Compiler* compiler, bool allowAssignment) if (!isInsideMethod(compiler)) { error(compiler, "Cannot use 'this' outside of a method."); + return; } - // The receiver is always stored in the first local slot. - // TODO: Will need to do something different to handle functions enclosed in - // methods. - emit(compiler, CODE_LOAD_LOCAL); - emit(compiler, 0); + // "this" works just like any other lexically scoped variable. + name(compiler, false); } // Subscript or "array indexing" operator like `foo[bar]`. diff --git a/src/wren_vm.c b/src/wren_vm.c index ae3767aa..26175e3d 100644 --- a/src/wren_vm.c +++ b/src/wren_vm.c @@ -546,7 +546,7 @@ static void closeUpvalue(Fiber* fiber) Upvalue* upvalue = fiber->openUpvalues; // Move the value into the upvalue itself and point the upvalue to it. - upvalue->closed = fiber->stack[fiber->stackSize - 1]; + upvalue->closed = *upvalue->value; upvalue->value = &upvalue->closed; // Remove it from the open upvalue list. @@ -1042,10 +1042,6 @@ Value interpret(WrenVM* vm, Value function) // If we are returning from the top-level block, just return the value. if (fiber->numFrames == 0) return result; - // Store the result of the block in the first slot, which is where the - // caller expects it. - fiber->stack[frame->stackStart] = result; - // Close any upvalues still in scope. Value* firstValue = &fiber->stack[frame->stackStart]; while (fiber->openUpvalues != NULL && @@ -1054,6 +1050,10 @@ Value interpret(WrenVM* vm, Value function) closeUpvalue(fiber); } + // Store the result of the block in the first slot, which is where the + // caller expects it. + fiber->stack[frame->stackStart] = result; + // Discard the stack slots for the call frame (leaving one slot for the // result). fiber->stackSize = frame->stackStart + 1; diff --git a/test/closure/reuse_closure_slot.wren b/test/closure/reuse_closure_slot.wren index 11dcd55d..e0458af8 100644 --- a/test/closure/reuse_closure_slot.wren +++ b/test/closure/reuse_closure_slot.wren @@ -14,6 +14,5 @@ } } -// TODO: Closing over this. -// TODO: Close over fn/method parameter. // TODO: Maximum number of closed-over variables (directly and/or indirect). +// TODO: Shadow variable used in closure. \ No newline at end of file diff --git a/test/this/closure.wren b/test/this/closure.wren new file mode 100644 index 00000000..45e53407 --- /dev/null +++ b/test/this/closure.wren @@ -0,0 +1,12 @@ +class Foo { + getClosure { + return fn { + return this.toString + } + } + + toString { return "Foo" } +} + +var closure = (new Foo).getClosure +io.write(closure.call) // expect: Foo diff --git a/test/this/nested_class.wren b/test/this/nested_class.wren new file mode 100644 index 00000000..b269ddd2 --- /dev/null +++ b/test/this/nested_class.wren @@ -0,0 +1,22 @@ +class Outer { + method { + io.write(this.toString) // expect: Outer + + fn { + io.write(this.toString) // expect: Outer + + class Inner { + method { + io.write(this.toString) // expect: Inner + } + toString { return "Inner" } + } + + (new Inner).method + }.call + } + + toString { return "Outer" } +} + +(new Outer).method diff --git a/test/this/nested_closure.wren b/test/this/nested_closure.wren new file mode 100644 index 00000000..341823f0 --- /dev/null +++ b/test/this/nested_closure.wren @@ -0,0 +1,16 @@ +class Foo { + getClosure { + return fn { + return fn { + return fn { + return this.toString + } + } + } + } + + toString { return "Foo" } +} + +var closure = (new Foo).getClosure +io.write(closure.call.call.call) // expect: Foo