diff options
-rw-r--r-- | clox/src/chunk.h | 1 | ||||
-rw-r--r-- | clox/src/compiler.c | 10 | ||||
-rw-r--r-- | clox/src/debug.c | 2 | ||||
-rw-r--r-- | clox/src/memory.c | 5 | ||||
-rw-r--r-- | clox/src/object.c | 18 | ||||
-rw-r--r-- | clox/src/object.h | 11 | ||||
-rw-r--r-- | clox/src/vm.c | 58 | ||||
-rw-r--r-- | clox/src/vm.h | 1 |
8 files changed, 105 insertions, 1 deletions
diff --git a/clox/src/chunk.h b/clox/src/chunk.h index fe585de..e992c04 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -32,6 +32,7 @@ typedef enum { OP_LOOP, OP_CALL, OP_CLOSURE, + OP_CLOSE_UPVALUE, OP_RETURN, } OpCode; diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 6e98b5e..75ed697 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -42,6 +42,7 @@ typedef struct { typedef struct { Token name; int depth; + bool isCaptured; } Local; typedef struct { @@ -197,6 +198,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; } @@ -224,7 +226,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--; } } @@ -740,6 +746,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); } @@ -760,6 +767,7 @@ static void addLocal(Token name) { Local *local = ¤t->locals[current->localCount++]; local->name = name; local->depth = -1; + local->isCaptured = false; } static void declareVariable() { diff --git a/clox/src/debug.c b/clox/src/debug.c index f175030..750fdb9 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -118,6 +118,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: diff --git a/clox/src/memory.c b/clox/src/memory.c index 3124bb6..1db1c61 100644 --- a/clox/src/memory.c +++ b/clox/src/memory.c @@ -18,6 +18,8 @@ void *reallocate(void *pointer, size_t oldSize, size_t newSize) { static void freeObject(Obj *object) { switch (object->type) { case OBJ_CLOSURE: { + ObjClosure *closure = (ObjClosure *)object; + FREE_ARRAY(ObjUpvalue *, closure->upvalues, closure->upvalueCount); FREE(ObjClosure, object); break; } @@ -36,6 +38,9 @@ static void freeObject(Obj *object) { FREE(ObjString, object); break; } + case OBJ_UPVALUE: + FREE(ObjUpvalue, object); + break; } } diff --git a/clox/src/object.c b/clox/src/object.c index 8d26d8b..31550ae 100644 --- a/clox/src/object.c +++ b/clox/src/object.c @@ -20,8 +20,15 @@ static Obj *allocateObject(size_t size, ObjType type) { } ObjClosure *newClosure(ObjFunction *function) { + ObjUpvalue **upvalues = ALLOCATE(ObjUpvalue *, function->upvalueCount); + for (int i = 0; i < function->upvalueCount; i++) { + upvalues[i] = NULL; + } + ObjClosure *closure = ALLOCATE_OBJ(ObjClosure, OBJ_CLOSURE); closure->function = function; + closure->upvalues = upvalues; + closure->upvalueCount = function->upvalueCount; return closure; } @@ -81,6 +88,14 @@ ObjString *copyString(const char *chars, int length) { return allocateString(heapChars, length, hash); } +ObjUpvalue *newUpvalue(Value *slot) { + ObjUpvalue *upvalue = ALLOCATE_OBJ(ObjUpvalue, OBJ_UPVALUE); + upvalue->closed = NIL_VAL; + upvalue->location = slot; + upvalue->next = NULL; + return upvalue; +} + static void printFunction(ObjFunction *function) { if (function->name == NULL) { printf("<script>"); @@ -103,5 +118,8 @@ void printObject(Value value) { case OBJ_STRING: printf("%s", AS_CSTRING(value)); break; + case OBJ_UPVALUE: + printf("upvalue"); + break; } } diff --git a/clox/src/object.h b/clox/src/object.h index f650b30..e6b842d 100644 --- a/clox/src/object.h +++ b/clox/src/object.h @@ -23,6 +23,7 @@ typedef enum { OBJ_FUNCTION, OBJ_NATIVE, OBJ_STRING, + OBJ_UPVALUE, } ObjType; struct Obj { @@ -52,9 +53,18 @@ struct ObjString { uint32_t hash; }; +typedef struct ObjUpvalue { + Obj obj; + Value *location; + Value closed; + struct ObjUpvalue *next; +} ObjUpvalue; + typedef struct { Obj obj; ObjFunction *function; + ObjUpvalue **upvalues; + int upvalueCount; } ObjClosure; ObjClosure *newClosure(ObjFunction *function); @@ -62,6 +72,7 @@ ObjFunction *newFunction(); ObjNative *newNative(NativeFn function); ObjString *takeString(char *chars, int length); ObjString *copyString(const char *chars, int length); +ObjUpvalue *newUpvalue(Value *slot); void printObject(Value value); static inline bool isObjType(Value value, ObjType type) { diff --git a/clox/src/vm.c b/clox/src/vm.c index b728ed5..b11a52a 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -19,6 +19,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, ...) { @@ -118,6 +119,39 @@ static bool callValue(Value callee, int argCount) { return false; } +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)); } @@ -225,6 +259,16 @@ static InterpretResult run() { } break; } + case OP_GET_UPVALUE: { + uint8_t slot = READ_BYTE(); + push(*frame->closure->upvalues[slot]->location); + break; + } + case OP_SET_UPVALUE: { + uint8_t slot = READ_BYTE(); + *frame->closure->upvalues[slot]->location = peek(0); + break; + } case OP_EQUAL: { Value b = pop(); Value a = pop(); @@ -303,10 +347,24 @@ static InterpretResult run() { ObjFunction *function = AS_FUNCTION(READ_CONSTANT()); ObjClosure *closure = newClosure(function); push(OBJ_VAL(closure)); + for (int i = 0; i < closure->upvalueCount; i++) { + uint8_t isLocal = READ_BYTE(); + uint8_t index = READ_BYTE(); + if (isLocal) { + closure->upvalues[i] = captureUpvalue(frame->slots + index); + } else { + closure->upvalues[i] = frame->closure->upvalues[index]; + } + } 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(); diff --git a/clox/src/vm.h b/clox/src/vm.h index ce14d0c..08706a3 100644 --- a/clox/src/vm.h +++ b/clox/src/vm.h @@ -23,6 +23,7 @@ typedef struct { Value *stackTop; Table globals; Table strings; + ObjUpvalue *openUpvalues; Obj *objects; } VM; |