1
0
forked from Mirror/wren

Closing over "this".

This commit is contained in:
Bob Nystrom
2013-12-21 15:55:08 -08:00
parent 76ac818eaf
commit 24a6f4cd8c
6 changed files with 75 additions and 18 deletions

View File

@ -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]`.

View File

@ -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;

View File

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

12
test/this/closure.wren Normal file
View File

@ -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

View File

@ -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

View File

@ -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