public static bool tableSet(ref Table_t table, ObjString key, Value_t value) { if (table.count + 1 > (table.capacity + 1) * TABLE_MAX_LOAD) { int capacity = Memory.GROW_CAPACITY(table.capacity + 1) - 1; adjustCapacity(ref table, capacity); } int _index = -1; // work around Entry entry = findEntry(ref table.entries, table.capacity, key, ref _index); bool isNewKey = entry.key == null; if (isNewKey && Value.IS_NIL(entry.value)) { table.count++; } entry.key = key; entry.value = value; table.entries[_index] = entry; // CS workaround return(isNewKey); }
public static ObjClass newClass(ObjString name) { ObjClass klass = (ObjClass)ALLOCATE_OBJ(ObjType.OBJ_CLASS); klass.name = name; Table.initTable(ref klass.methods); return(klass); }
private static void defineMethod(ObjString name) { Value_t method = peek(0); ObjClass klass = Object.AS_CLASS(peek(1)); Table.tableSet(ref klass.methods, name, method); pop(); }
// Generics, <T>, doesn't seem to work with classes? static Obj allocateObject(int size, ObjType type) { Obj object_ = null; switch (type) { case ObjType.OBJ_STRING: object_ = new ObjString(); break; case ObjType.OBJ_FUNCTION: object_ = new ObjFunction(); break; case ObjType.OBJ_INSTANCE: object_ = new ObjInstance(); break; case ObjType.OBJ_NATIVE: object_ = new ObjNative(); break; case ObjType.OBJ_CLOSURE: object_ = new ObjClosure(); break; case ObjType.OBJ_UPVALUE: object_ = new ObjUpvalue(); break; case ObjType.OBJ_CLASS: object_ = new ObjClass(); break; case ObjType.OBJ_BOUND_METHOD: object_ = new ObjBoundMethod(); break; default: object_ = null; // clox: (Obj*)reallocate(NULL, 0, size); break; } object_.type = type; object_.isMarked = false; object_.next = VM.vm.objects; VM.vm.objects = object_; #if DEBUG_LOG_GC System.Console.WriteLine("{0} allocate {1} for {2}", object_._mem_id.ToString(), size.ToString(), type.ToString()); #endif return(object_); }
private static bool invokeFromClass(ObjClass klass, ObjString name, int argCount) { Value_t method = new Value_t(); if (!Table.tableGet(ref klass.methods, name, ref method)) { runtimeError("Undefined property '{0}'.", new string(name.chars, 0, name.chars.Length - 1)); return(false); } return(call(Object.AS_CLOSURE(method), argCount)); }
public static ObjString takeString(char[] chars, int _start, int length) { uint hash = hashString(chars, _start, length); ObjString interned = Table.tableFindString(ref VM.vm.strings, chars, _start, length, hash); if (interned != null) { Memory.FREE_ARRAY <char>(typeof(char), ref chars, length + 1); return(interned); } return(allocateString(chars, length, hash)); }
public static ObjString copyString(char[] chars, int _start, int length) { uint hash = hashString(chars, _start, length); ObjString interned = Table.tableFindString(ref VM.vm.strings, chars, _start, length, hash); if (interned != null) { return(interned); } char[] heapChars = Memory.ALLOCATE <char>(length + 1); util.util._memcpy <char>(heapChars, chars, _start, length); heapChars[length] = '\0'; return(allocateString(heapChars, length, hash)); }
private static ObjString allocateString(char[] chars, int length, uint hash) { ObjString string_ = (ObjString)ALLOCATE_OBJ(ObjType.OBJ_STRING); string_.length = length; string_.chars = chars; string_.hash = hash; string_._start = 0; string_.chars[length] = '\0'; VM.push(Value.OBJ_VAL(string_)); Table.tableSet(ref VM.vm.strings, string_, Value.NIL_VAL()); VM.pop(); return(string_); }
private static bool bindMethod(ObjClass klass, ObjString name) { Value_t method = new Value_t(); if (!Table.tableGet(ref klass.methods, name, ref method)) { runtimeError("Undefined property '{0}'.", new string(name.chars, 0, name.chars.Length - 1)); return(false); } ObjBoundMethod bound = Object.newBoundMethod(peek(0), Object.AS_CLOSURE(method)); pop(); push(Value.OBJ_VAL(bound)); return(true); }
public static bool tableGet(ref Table_t table, ObjString key, ref Value_t value) { if (table.count == 0) { return(false); } int _index = -1; // work around Entry entry = findEntry(ref table.entries, table.capacity, key, ref _index); if (entry.key == null) { return(false); } value = entry.value; return(true); }
private static void concatenate() { ObjString b = Object.AS_STRING(peek(0)); ObjString a = Object.AS_STRING(peek(1)); int length = a.length + b.length; char[] chars = Memory.ALLOCATE <char>(length + 1); util.util._memcpy <char>(chars, a.chars, a._start, a.length); util.util._memcpy <char>(chars, a.length, b.chars, b._start, b.length); chars[length] = '\0'; ObjString result = Object.takeString(chars, 0, length); pop(); pop(); push(Value.OBJ_VAL(result)); }
private static bool invoke(ObjString name, int argCount) { Value_t receiver = peek(argCount); if (!Object.IS_INSTANCE(receiver)) { runtimeError("Only instances have methods."); return(false); } ObjInstance instance = Object.AS_INSTANCE(receiver); Value_t value = new Value_t(); if (Table.tableGet(ref instance.fields, name, ref value)) { vm.stack[vm.stackTop - argCount - 1] = value; return(callValue(value, argCount)); } return(invokeFromClass(instance.klass, name, argCount)); }
public static bool tableDelete(ref Table_t table, ObjString key) { if (table.count == 0) { return(false); } // Find the entry. int _index = -1; Entry entry = findEntry(ref table.entries, table.capacity, key, ref _index); if (entry.key == null) { return(false); } // Place a tombstone in the entry. entry.key = null; entry.value = Value.BOOL_VAL(true); table.entries[_index] = entry; // Csharp ref workaround return(true); }
private static Entry findEntry(ref Entry[] entries, int capacity, ObjString key, ref int _foundIndex) { _foundIndex = -1; uint index = key.hash & (uint)capacity; Entry tombstone = null; for (;;) { Entry entry = entries[index]; // clox &entries[index] if (entry.key == null) { if (Value.IS_NIL(entry.value)) { // Empty entry. _foundIndex = (int)index; return(tombstone != null ? tombstone : entry); } else { // We found a tombstone. if (tombstone == null) { tombstone = entry; } } } else if (entry.key == key) { // We found the key. _foundIndex = (int)index; return(entry); } index = (index + 1) & (uint)capacity; } }
public static InterpretResult run() { CallFrame frame = vm.frames[vm.frameCount - 1]; frame._slot_offset = 0; for (;;) { #if DEBUG_TRACE_EXECUTION System.Console.Write(" "); for (int slot = 0; slot < vm.stackTop; slot++) { System.Console.Write("[ "); Value.printValue(vm.stack[slot]); System.Console.Write(" ]"); } System.Console.WriteLine(); Debug.disassembleInstruction(ref frame.closure.function.chunk, frame._ip_index); #endif OpCode instruction; switch (instruction = (OpCode)READ_BYTE(ref frame)) { case OpCode.OP_CONSTANT: { Value_t constant = READ_CONSTANT(ref frame); push(constant); break; } case OpCode.OP_NIL: push(Value.NIL_VAL()); break; case OpCode.OP_TRUE: push(Value.BOOL_VAL(true)); break; case OpCode.OP_FALSE: push(Value.BOOL_VAL(false)); break; case OpCode.OP_POP: pop(); break; case OpCode.OP_GET_LOCAL: { byte slot = READ_BYTE(ref frame); push(frame.slots[frame._slot_offset + slot]); break; } case OpCode.OP_SET_LOCAL: { byte slot = READ_BYTE(ref frame); frame.slots[frame._slot_offset + slot] = peek(0); break; } case OpCode.OP_GET_GLOBAL: { ObjString name = READ_STRING(ref frame); Value_t value = new Value_t(); if (!Table.tableGet(ref vm.globals, name, ref value)) { runtimeError("Undefined variable '{0}'.", new string(name.chars, 0, name.chars.Length - 1)); return(InterpretResult.INTERPRET_RUNTIME_ERROR); } push(value); break; } case OpCode.OP_DEFINE_GLOBAL: { ObjString name = READ_STRING(ref frame); Table.tableSet(ref vm.globals, name, peek(0)); pop(); break; } case OpCode.OP_SET_GLOBAL: { ObjString name = READ_STRING(ref frame); if (Table.tableSet(ref vm.globals, name, peek(0))) { Table.tableDelete(ref vm.globals, name); runtimeError("Undefined variable '{0}'.", new string(name.chars, 0, name.chars.Length - 1)); return(InterpretResult.INTERPRET_RUNTIME_ERROR); } break; } case OpCode.OP_GET_UPVALUE: { byte slot = READ_BYTE(ref frame); push(frame.closure.upvalues[slot]._value_src[frame.closure.upvalues[slot].location]); break; } case OpCode.OP_SET_UPVALUE: { byte slot = READ_BYTE(ref frame); frame.closure.upvalues[slot]._value_src[frame.closure.upvalues[slot].location] = peek(0); break; } case OpCode.OP_GET_PROPERTY: { if (!Object.IS_INSTANCE(peek(0))) { runtimeError("Only instances have properties."); return(InterpretResult.INTERPRET_RUNTIME_ERROR); } ObjInstance instance = Object.AS_INSTANCE(peek(0)); ObjString name = READ_STRING(ref frame); Value_t value = new Value_t(); if (Table.tableGet(ref instance.fields, name, ref value)) { pop(); // Instance. push(value); break; } if (!bindMethod(instance.klass, name)) { return(InterpretResult.INTERPRET_RUNTIME_ERROR); } break; } case OpCode.OP_SET_PROPERTY: { if (!Object.IS_INSTANCE(peek(1))) { runtimeError("Only instances have fields."); return(InterpretResult.INTERPRET_RUNTIME_ERROR); } ObjInstance instance = Object.AS_INSTANCE(peek(1)); Table.tableSet(ref instance.fields, READ_STRING(ref frame), peek(0)); Value_t value = pop(); pop(); push(value); break; } case OpCode.OP_GET_SUPER: { ObjString name = READ_STRING(ref frame); ObjClass superclass = Object.AS_CLASS(pop()); if (!bindMethod(superclass, name)) { return(InterpretResult.INTERPRET_RUNTIME_ERROR); } break; } case OpCode.OP_EQUAL: { Value_t b = pop(); Value_t a = pop(); push(Value.BOOL_VAL(Value.valuesEqual(a, b))); break; } case OpCode.OP_ADD: { if (Object.IS_STRING(peek(0)) && Object.IS_STRING(peek(1))) { concatenate(); } else if (Value.IS_NUMBER(peek(0)) && Value.IS_NUMBER(peek(1))) { BINARY_OP(instruction); } else { runtimeError("Operands must be two numbers or two strings."); return(InterpretResult.INTERPRET_RUNTIME_ERROR); } } break; case OpCode.OP_GREATER: case OpCode.OP_LESS: case OpCode.OP_SUBTRACT: case OpCode.OP_MULTIPLY: case OpCode.OP_DIVIDE: if (!BINARY_OP(instruction)) { return(InterpretResult.INTERPRET_RUNTIME_ERROR); } break; case OpCode.OP_NOT: push(Value.BOOL_VAL(isFalsey(pop()))); break; case OpCode.OP_NEGATE: if (!Value.IS_NUMBER(peek(0))) { runtimeError("Operand must be a number."); return(InterpretResult.INTERPRET_RUNTIME_ERROR); } push(Value.NUMBER_VAL(-Value.AS_NUMBER(pop()))); break; case OpCode.OP_PRINT: { Value.printValue(pop()); System.Console.WriteLine(); break; } case OpCode.OP_JUMP: { ushort offset = READ_SHORT(ref frame); frame._ip_index += offset; break; } case OpCode.OP_JUMP_IF_FALSE: { ushort offset = READ_SHORT(ref frame); if (isFalsey(peek(0))) { frame._ip_index += offset; } break; } case OpCode.OP_LOOP: { ushort offset = READ_SHORT(ref frame); frame._ip_index -= offset; break; } case OpCode.OP_CALL: { int argCount = READ_BYTE(ref frame); _caller_ip_index = frame._ip_index; if (!callValue(peek(argCount), argCount)) { return(InterpretResult.INTERPRET_RUNTIME_ERROR); } frame = vm.frames[vm.frameCount - 1]; break; } case OpCode.OP_INVOKE: { ObjString method = READ_STRING(ref frame); int argCount = READ_BYTE(ref frame); _caller_ip_index = frame._ip_index; if (!invoke(method, argCount)) { return(InterpretResult.INTERPRET_RUNTIME_ERROR); } frame = vm.frames[vm.frameCount - 1]; break; } case OpCode.OP_SUPER_INVOKE: { ObjString method = READ_STRING(ref frame); int argCount = READ_BYTE(ref frame); ObjClass superclass = Object.AS_CLASS(pop()); _caller_ip_index = frame._ip_index; if (!invokeFromClass(superclass, method, argCount)) { return(InterpretResult.INTERPRET_RUNTIME_ERROR); } frame = vm.frames[vm.frameCount - 1]; break; } case OpCode.OP_CLOSURE: { ObjFunction function = Object.AS_FUNCTION(READ_CONSTANT(ref frame)); ObjClosure closure = Object.newClosure(function); for (int i = 0; i < closure.upvalueCount; i++) { byte isLocal = READ_BYTE(ref frame); byte index = READ_BYTE(ref frame); if (isLocal != 0) { closure.upvalues[i] = captureUpvalue(frame._slot_offset + index); } else { closure.upvalues[i] = frame.closure.upvalues[index]; } } push(Value.OBJ_VAL(closure)); // cs push after modify? break; } case OpCode.OP_CLOSE_UPVALUE: closeUpvalues(vm.stackTop - 1); pop(); break; case OpCode.OP_RETURN: { Value_t result = pop(); closeUpvalues(frame._slot_offset); vm.frameCount--; if (vm.frameCount == 0) { pop(); return(InterpretResult.INTERPRET_OK); } vm.stackTop = frame._slot_offset; push(result); frame = vm.frames[vm.frameCount - 1]; break; } case OpCode.OP_CLASS: push(Value.OBJ_VAL(Object.newClass(READ_STRING(ref frame)))); break; case OpCode.OP_INHERIT: { Value_t superclass = peek(1); if (!Object.IS_CLASS(superclass)) { runtimeError("Superclass must be a class."); return(InterpretResult.INTERPRET_RUNTIME_ERROR); } ObjClass subclass = Object.AS_CLASS(peek(0)); Table.tableAddAll(ref Object.AS_CLASS(superclass).methods, ref subclass.methods); pop(); // Subclass. break; } case OpCode.OP_METHOD: defineMethod(READ_STRING(ref frame)); break; } vm.frames[vm.frameCount - 1]._ip_index = frame._ip_index; //Csharp reference updating workaround } }
private static void freeObject(ref Obj object_) { #if DEBUG_LOG_GC System.Console.WriteLine("{0} free type {1}", object_._mem_id.ToString(), object_.type.ToString()); #endif switch (object_.type) { case ObjType.OBJ_BOUND_METHOD: object_._free(); object_ = null; break; case ObjType.OBJ_CLASS: { ObjClass klass = (ObjClass)object_; Table.freeTable(ref klass.methods); //FREE<ObjClass>(ref object_); object_._free(); object_ = null; break; } case ObjType.OBJ_CLOSURE: { ObjClosure closure = (ObjClosure)object_; FREE_ARRAY <ObjUpvalue>(typeof(ObjUpvalue), ref closure.upvalues, closure.upvalueCount); object_._free(); object_ = null; break; } case ObjType.OBJ_FUNCTION: { ObjFunction function = (ObjFunction)object_; Chunk.freeChunk(ref function.chunk); // the function's byte code object_._free(); object_ = null; break; } case ObjType.OBJ_INSTANCE: { ObjInstance instance = (ObjInstance)object_; Table.freeTable(ref instance.fields); object_._free(); object_ = null; break; } case ObjType.OBJ_NATIVE: { object_._free(); object_ = null; break; } case ObjType.OBJ_STRING: { ObjString string_ = (ObjString)object_; FREE_ARRAY <char>(typeof(char), ref string_.chars, string_.length + 1); object_._free(); object_ = null; break; } case ObjType.OBJ_UPVALUE: util.cHeap.values.remove(((ObjUpvalue)object_).location); object_._free(); object_ = null; break; } }