forked from Mirror/wren
Closing over "this".
This commit is contained in:
@ -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]`.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
12
test/this/closure.wren
Normal 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
|
||||
22
test/this/nested_class.wren
Normal file
22
test/this/nested_class.wren
Normal 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
|
||||
16
test/this/nested_closure.wren
Normal file
16
test/this/nested_closure.wren
Normal 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
|
||||
Reference in New Issue
Block a user