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);
        }
Beispiel #2
0
        public static ObjClass newClass(ObjString name)
        {
            ObjClass klass = (ObjClass)ALLOCATE_OBJ(ObjType.OBJ_CLASS);

            klass.name = name;
            Table.initTable(ref klass.methods);
            return(klass);
        }
Beispiel #3
0
        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();
        }
Beispiel #4
0
        // 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_);
        }
Beispiel #5
0
        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));
        }
Beispiel #6
0
        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));
        }
Beispiel #7
0
        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));
        }
Beispiel #8
0
        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_);
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        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);
        }
Beispiel #11
0
        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));
        }
Beispiel #12
0
        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));
        }
Beispiel #13
0
        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);
        }
Beispiel #14
0
        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;
            }
        }
Beispiel #15
0
        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;
            }
        }