M zlox/src/chunk.h => zlox/src/chunk.h +1 -0
@@ 32,6 32,7 @@ typedef enum {
OP_LOOP,
OP_CALL,
OP_CLOSURE,
+ OP_CLOSE_UPVALUE,
OP_RETURN,
} OpCode;
M zlox/src/compiler.c => zlox/src/compiler.c +9 -1
@@ 44,6 44,7 @@ typedef struct {
typedef struct {
Token name;
int depth;
+ bool isCaptured;
} Local;
typedef struct {
@@ 201,6 202,7 @@ static void initCompiler(Compiler *compiler, FunctionType type) {
Local *local = ¤t->locals[current->localCount++];
local->depth = 0;
+ local->isCaptured = false;
local->name.start = "";
local->name.length = 0;
}
@@ 229,7 231,11 @@ static void endScope() {
while (current->localCount > 0 &&
current->locals[current->localCount - 1].depth > current->scopeDepth) {
- emitByte(OP_POP);
+ if (current->locals[current->localCount - 1].isCaptured) {
+ emitByte(OP_CLOSE_UPVALUE);
+ } else {
+ emitByte(OP_POP);
+ }
current->localCount--;
}
}
@@ 372,6 378,7 @@ static int resolveUpvalue(Compiler *compiler, Token *name) {
int local = resolveLocal(compiler->enclosing, name);
if (local != -1) {
+ compiler->enclosing->locals[local].isCaptured = true;
return addUpvalue(compiler, (uint8_t)local, true);
}
@@ 392,6 399,7 @@ static void addLocal(Token name) {
Local *local = ¤t->locals[current->localCount++];
local->name = name;
local->depth = -1;
+ local->isCaptured = false;
}
static void declareVariable() {
M zlox/src/debug.c => zlox/src/debug.c +2 -0
@@ 123,6 123,8 @@ int disassembleInstruction(Chunk *chunk, int offset) {
}
return offset;
}
+ case OP_CLOSE_UPVALUE:
+ return simpleInstruction("OP_CLOSE_UPVALUE", offset);
case OP_RETURN:
return simpleInstruction("OP_RETURN", offset);
default:
M zlox/src/object.c => zlox/src/object.c +2 -0
@@ 93,6 93,8 @@ ObjString* copyString(const char *chars, int length) {
ObjUpvalue* newUpvalue(Value *slot) {
ObjUpvalue *upvalue = ALLOCATE_OBJ(ObjUpvalue, OBJ_UPVALUE);
upvalue->location = slot;
+ upvalue->closed = NIL_VAL;
+ upvalue->next = NULL;
return upvalue;
}
M zlox/src/object.h => zlox/src/object.h +2 -0
@@ 57,6 57,8 @@ struct ObjString {
typedef struct ObjUpvalue {
Obj obj;
Value *location;
+ Value closed;
+ struct ObjUpvalue *next;
} ObjUpvalue;
typedef struct {
M zlox/src/vm.c => zlox/src/vm.c +35 -0
@@ 21,6 21,7 @@ static Value clockNative(int argCount, Value *args) {
static void resetStack() {
vm.stackTop = vm.stack;
vm.frameCount = 0;
+ vm.openUpvalues = NULL;
}
static void runtimeError(const char *format, ...) {
@@ 114,10 115,39 @@ static bool callValue(Value callee, int argCount) {
}
static ObjUpvalue* captureUpvalue(Value *local) {
+ ObjUpvalue *prevUpvalue = NULL;
+ ObjUpvalue *upvalue = vm.openUpvalues;
+ while (upvalue != NULL && upvalue->location > local) {
+ prevUpvalue = upvalue;
+ upvalue = upvalue->next;
+ }
+
+ if (upvalue != NULL && upvalue->location == local) {
+ return upvalue;
+ }
+
ObjUpvalue *createdUpvalue = newUpvalue(local);
+ createdUpvalue->next = upvalue;
+
+ if (prevUpvalue == NULL) {
+ vm.openUpvalues = createdUpvalue;
+ } else {
+ prevUpvalue->next = createdUpvalue;
+ }
+
return createdUpvalue;
}
+static void closeUpvalues(Value *last) {
+ while (vm.openUpvalues != NULL &&
+ vm.openUpvalues->location >= last) {
+ ObjUpvalue *upvalue = vm.openUpvalues;
+ upvalue->closed = *upvalue->location;
+ upvalue->location = &upvalue->closed;
+ vm.openUpvalues = upvalue->next;
+ }
+}
+
static bool isFalsey(Value value) {
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
}
@@ 318,9 348,14 @@ static InterpretResult run() {
}
break;
}
+ case OP_CLOSE_UPVALUE:
+ closeUpvalues(vm.stackTop - 1);
+ pop();
+ break;
case OP_RETURN:
{
Value result = pop();
+ closeUpvalues(frame->slots);
vm.frameCount--;
if (vm.frameCount == 0) {
pop();
M zlox/src/vm.h => zlox/src/vm.h +1 -0
@@ 22,6 22,7 @@ typedef struct {
Value *stackTop;
Table globals;
Table strings;
+ ObjUpvalue *openUpvalues;
Obj *objects;
} VM;