Example #1
0
        // --- end merge candidates ---

        // === Closures ==============================================================================================
        // ===========================================================================================================

        private GearsObjUpvalue CaptureUpvalue(int sp)
        {
            GearsObjUpvalue previousUpvalue = null;
            GearsObjUpvalue currentUpValue  = _OpenUpvalues;

            while (currentUpValue != null && currentUpValue.OriginalSP > sp)
            {
                previousUpvalue = currentUpValue;
                currentUpValue  = currentUpValue.Next;
            }
            if (currentUpValue != null && currentUpValue.OriginalSP == sp)
            {
                return(currentUpValue);
            }
            GearsObjUpvalue createdUpvalue = new GearsObjUpvalue(sp);

            createdUpvalue.Next = currentUpValue;
            if (previousUpvalue == null)
            {
                _OpenUpvalues = createdUpvalue;
            }
            else
            {
                previousUpvalue.Next = currentUpValue;
            }
            return(createdUpvalue);
        }
Example #2
0
 /// <summary>
 /// Starting with the passed stack slot, closes every open upvalue it can find that points to that slot or any
 /// slot above it on the stack. Close these upvalues, moving the values from the stack to the heap.
 /// </summary>
 private void CloseUpvalues(int sp)
 {
     // this function takes as a parameter an index of a stack slot. It closes every open upvalue it can find
     // pointing to that slot or any slot above it on the stack.
     // To do this, it walks the list of open upvalues, from top to bottom. If an upvalue's location points
     // into the range of slots we are closing, we close the upvalue. Otherwise, once we reach an upvalue
     // outside of that range, we know the rest will be too so we stop iterating.
     while (_OpenUpvalues != null && _OpenUpvalues.OriginalSP >= sp)
     {
         // To close an upvalue, we copy the variable' value into the closed field.
         GearsObjUpvalue upvalue = _OpenUpvalues;
         upvalue.Value    = StackGet(upvalue.OriginalSP);
         upvalue.IsClosed = true;
         _OpenUpvalues    = upvalue.Next;
     }
 }
Example #3
0
        private void CallInvokeSuper()
        {
            int argCount = ReadByte();

            // next instruction will always be OP_GET_UPVALUE (for the super class). we include it here:
            if (!(ReadByte() == (int)OP_GET_UPVALUE))
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), "OP_SUPER_INVOKE must be followed by OP_GET_UPVALUE.");
            }
            int             slot       = ReadShort();
            GearsObjUpvalue upvalue    = _OpenFrame.Function.Upvalues[slot];
            GearsObjClass   superclass = GetObjectFromPtr <GearsObjClass>(upvalue.IsClosed ? upvalue.Value : StackGet(upvalue.OriginalSP));
            ulong           methodName = (ulong)ReadConstant();

            InvokeFromClass(argCount, methodName, GearsValue.NilValue, superclass);
        }
Example #4
0
        private void MarkRoots()
        {
            for (int i = 0; i < _SP; i++)
            {
                MarkValue(_Stack[i]);
            }
            MarkTable(Globals);
            for (int i = 0; i < _FrameCount; i++)
            {
                MarkObject((_Frames[i] as GearsCallFrame)?.Function);
            }
            GearsObjUpvalue upvalue = _OpenUpvalues;

            while (upvalue != null)
            {
                MarkObject(upvalue);
                upvalue = upvalue.Next;
            }
        }
Example #5
0
        /// <summary>
        /// Runs the script from top to bottom.
        /// </summary>
        private bool RunOne()
        {
            LastReturnValue = GearsValue.NilValue;
            while (true)
            {
                EGearsOpCode instruction = (EGearsOpCode)ReadByte();
                switch (instruction)
                {
                case OP_LOAD_CONSTANT:
                    Push(ReadConstant());
                    break;

                case OP_LOAD_STRING:
                    Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjString(ReadConstantString()))));
                    break;

                case OP_LOAD_FUNCTION: {
                    int arity                = ReadByte();
                    int address              = ReadShort();
                    int upvalueCount         = ReadByte();
                    GearsObjFunction closure = new GearsObjFunction(Chunk, arity, upvalueCount, address);
                    for (int i = 0; i < upvalueCount; i++)
                    {
                        bool isLocal = ReadByte() == 1;
                        int  index   = ReadByte();
                        if (isLocal)
                        {
                            int location = _OpenFrame.BP + index;
                            closure.Upvalues[i] = CaptureUpvalue(location);
                        }
                        else
                        {
                            closure.Upvalues[i] = _OpenFrame.Function.Upvalues[index];
                        }
                    }
                    Push(GearsValue.CreateObjPtr(HeapAddObject(closure)));
                }
                break;

                case OP_NIL:
                    Push(GearsValue.NilValue);
                    break;

                case OP_TRUE:
                    Push(GearsValue.TrueValue);
                    break;

                case OP_FALSE:
                    Push(GearsValue.FalseValue);
                    break;

                case OP_POP:
                    Pop();
                    break;

                case OP_GET_LOCAL: {
                    int slot = ReadShort();
                    Push(StackGet(slot + _BP));
                }
                break;

                case OP_SET_LOCAL: {
                    int slot = ReadShort();
                    StackSet(slot + _BP, Peek());
                }
                break;

                case OP_GET_GLOBAL: {
                    ulong name = (ulong)ReadConstant();
                    if (!Globals.TryGet(name, out GearsValue value))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Undefined variable '{BitString.GetBitStr(name)}'.");
                    }
                    Push(value);
                }
                break;

                case OP_DEFINE_GLOBAL: {
                    ulong name = (ulong)ReadConstant();
                    Globals.Set(name, Peek());
                    Pop();
                }
                break;

                case OP_SET_GLOBAL: {
                    ulong name = (ulong)ReadConstant();
                    if (!Globals.ContainsKey(name))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Undefined variable '{BitString.GetBitStr(name)}'.");
                    }
                    Globals.Set(name, Peek());
                    break;
                }

                case OP_GET_UPVALUE: {
                    int             slot    = ReadShort();
                    GearsObjUpvalue upvalue = _OpenFrame.Function.Upvalues[slot];
                    if (upvalue.IsClosed)
                    {
                        Push(upvalue.Value);
                    }
                    else
                    {
                        Push(StackGet(upvalue.OriginalSP));
                    }
                }
                break;

                case OP_SET_UPVALUE: {
                    int             slot    = ReadShort();
                    GearsObjUpvalue upvalue = _OpenFrame.Function.Upvalues[slot];
                    if (upvalue.IsClosed)
                    {
                        upvalue.Value = Peek();
                    }
                    else
                    {
                        StackSet(upvalue.OriginalSP, Peek());
                    }
                }
                break;

                case OP_GET_PROPERTY: {
                    GearsObjInstance instance = GetObjectFromPtr <GearsObjInstance>(Peek());
                    ulong            name     = (ulong)ReadConstant(); // property name
                    if (instance.TryGetField(name, out GearsValue value))
                    {
                        Pop();         // instance
                        Push(value);   // property value
                        break;
                    }
                    if (instance is GearsObjInstanceLox loxInstance && BindLoxMethod(loxInstance.Class, name))
                    {
                        break;
                    }
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Undefined property or method '{name}'.");
                }

                case OP_SET_PROPERTY: {
                    GearsObjInstance instance = GetObjectFromPtr <GearsObjInstance>(Peek(1));
                    ulong            name     = (ulong)ReadConstant(); // property name
                    GearsValue       value    = Pop();                 // value
                    instance.SetField(name, value);
                    Pop();                                             // ptr
                    Push(value);                                       // value
                }
                break;

                case OP_GET_SUPER: {
                    ulong         name       = (ulong)ReadConstant(); // method/property name
                    GearsObjClass superclass = GetObjectFromPtr <GearsObjClass>(Pop());
                    if (!BindLoxMethod(superclass, name))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Could not get {name} in superclass {superclass}");
                    }
                }
                break;

                case OP_EQUAL:
                    Push(AreValuesEqual(Pop(), Pop()));
                    break;

                case OP_GREATER: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a > b);
                }
                break;

                case OP_LESS: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a < b);
                }
                break;

                case OP_BITWISE_AND: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of bitwise operators must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push((long)((double)a) & (long)((double)b));
                }
                break;

                case OP_BITWISE_OR: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of bitwise operators must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push((long)((double)a) | (long)((double)b));
                }
                break;

                case OP_ADD: {
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    if (a.IsNumber && b.IsNumber)
                    {
                        Push(a + b);
                    }
                    else if (a.IsObjType <GearsObjString>(this) && b.IsObjType <GearsObjString>(this))
                    {
                        string sa = GetObjectFromPtr <GearsObjString>(a).Value;
                        string sb = GetObjectFromPtr <GearsObjString>(b).Value;
                        Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjString(sa + sb))));
                    }
                    else
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of add must be numbers or strings.");
                    }
                }
                break;

                case OP_SUBTRACT: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of subtract must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a - b);
                }
                break;

                case OP_MULTIPLY: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of multiply must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a * b);
                }
                break;

                case OP_DIVIDE: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operands of divide must be numbers.");
                    }
                    GearsValue b = Pop();
                    GearsValue a = Pop();
                    Push(a / b);
                }
                break;

                case OP_NOT:
                    Push(IsFalsey(Pop()));
                    break;

                case OP_NEGATE: {
                    if (!Peek(0).IsNumber)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Operand of negate must be a number.");
                    }
                    Push(-Pop());
                }
                break;

                case OP_JUMP: {
                    int offset = ReadShort();
                    ModIP(offset);
                }
                break;

                case OP_JUMP_IF_FALSE: {
                    int offset = ReadShort();
                    if (IsFalsey(Peek()))
                    {
                        ModIP(offset);
                    }
                }
                break;

                case OP_LOOP: {
                    int offset = ReadShort();
                    ModIP(-offset);
                }
                break;

                case OP_CALL: {
                    Call(argCount: ReadByte());
                }
                break;

                case OP_INVOKE: {
                    CallInvoke();
                }
                break;

                case OP_SUPER_INVOKE: {
                    CallInvokeSuper();
                }
                break;

                case OP_CLOSE_UPVALUE:
                    CloseUpvalues(_SP - 1);
                    Pop();
                    break;

                case OP_RETURN: {
                    GearsValue result = Pop();
                    CloseUpvalues(_OpenFrame.BP);
                    if (PopFrame())
                    {
                        if (_SP != 0)
                        {
                            throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Error after final return: SP is '{_SP}', not '0'.");
                        }
                        LastReturnValue = result;
                        _IP             = Chunk.SizeCode; // code is complete and no longer running.
                        return(true);
                    }
                    Push(result);
                }
                break;

                case OP_CLASS: {
                    Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjClass(ReadConstantString()))));
                }
                break;

                case OP_INHERIT: {
                    if (!Peek(0).IsObjType <GearsObjClass>(this))
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Superclass is not a class.");
                    }
                    GearsObjClass super = GetObjectFromPtr <GearsObjClass>(Peek(1));
                    GearsObjClass sub   = GetObjectFromPtr <GearsObjClass>(Peek(0));
                    foreach (ulong key in super.Methods.AllKeys)
                    {
                        if (!super.Methods.TryGet(key, out GearsValue methodPtr))
                        {
                            throw new GearsRuntimeException(Chunk.LineAt(_IP), "Could not copy superclass method table.");
                        }
                        sub.Methods.Set(key, methodPtr);
                    }
                    Pop();         // pop subclass
                }
                break;

                case OP_METHOD: {
                    DefineMethod();
                }
                break;

                default:
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Unknown opcode {instruction}");
                }
            }
        }