Example #1
0
        public bool Set(ulong key, GearsValue value)
        {
            bool exists = _Table.ContainsKey(key);

            _Table[key] = value;
            return(!exists);
        }
Example #2
0
        /// <summary>
        /// Reset the VM. Heap, globals, and stack are cleared.
        /// Then the VM is run once, unless firstRun == false
        /// </summary>
        internal Gears Reset(GearsChunk chunk, bool firstRun)
        {
            Chunk = chunk;
            _SP   = 0;
            for (int i = 0; i < _Stack.Length; i++)
            {
                _Stack[i] = 0;
            }
            _FrameCount = 0;
            for (int i = 0; i < _Frames.Length; i++)
            {
                _Frames[i] = null;
            }
            for (int i = 0; i < _Heap.Length; i++)
            {
                _Heap[i] = null;
            }
            Globals.Reset();
            _GrayList.Clear();
            GearsObjFunction closure = new GearsObjFunction(Chunk, 0, 0, 0);

            PushFrame(new GearsCallFrame(closure));
            Push(GearsValue.CreateObjPtr(HeapAddObject(closure)));
            AddNativeFunctionToGlobals("clock", 0, NativeFnClock);
            AddNativeFunctionToGlobals("print", 1, NativeFnPrint);
            if (firstRun)
            {
                Run();
            }
            return(this);
        }
Example #3
0
        private int DisassembleConstant(string name, GearsChunk chunk, int offset, EGearsOpCode constantType, Action <string> writeLine)
        {
            int constantIndex = (chunk.ReadCode(ref offset) << 8) + chunk.ReadCode(ref offset);

            switch (constantType)
            {
            case OP_LOAD_CONSTANT: {
                GearsValue value = chunk.ReadConstantValue(constantIndex);
                writeLine($"{name} const[{constantIndex}] ({value})");
            }
            break;

            case OP_LOAD_STRING: {
                string value = chunk.Strings.ReadStringConstant(constantIndex);
                writeLine($"{name} string[{constantIndex}] ({value})");
            }
            break;

            case OP_LOAD_FUNCTION: {
                string value = chunk.ReadConstantValueAsBitStr(constantIndex);
                writeLine($"{name} bitstr[{constantIndex}] ({value})");
            }
            break;
            }
            return(offset);
        }
Example #4
0
 internal void StackSet(int index, GearsValue value)
 {
     if (index < 0 || index >= _SP)
     {
         throw new GearsRuntimeException(Chunk.LineAt(_IP), "Stack exception");
     }
     _Stack[index] = value;
 }
Example #5
0
 public bool TryGet(ulong key, out GearsValue value)
 {
     if (_Table.TryGetValue(key, out value))
     {
         return(true);
     }
     return(false);
 }
Example #6
0
 internal void Push(GearsValue value)
 {
     if (_SP >= STACK_MAX)
     {
         throw new GearsRuntimeException(Chunk.LineAt(_IP), "Stack exception");
     }
     _Stack[_SP++] = value;
 }
Example #7
0
 public void MarkValue(GearsValue value)
 {
     // we don't need to collect value types, as they require no heap allocation.
     if (!value.IsObjPtr)
     {
         return;
     }
     MarkObject(value.AsObject(this));
 }
Example #8
0
        // === Functions =============================================================================================
        // ===========================================================================================================

        private void DefineMethod()
        {
            ulong            methodName = (ulong)ReadConstant();
            GearsValue       methodPtr  = Peek();
            GearsObjFunction method     = GetObjectFromPtr <GearsObjFunction>(methodPtr);
            GearsObjClass    objClass   = GetObjectFromPtr <GearsObjClass>(Peek(1));

            objClass.Methods.Set(methodName, methodPtr);
            Pop();
        }
Example #9
0
        private bool BindLoxMethod(GearsObjClass classObj, ulong name)
        {
            if (!classObj.Methods.TryGet(name, out GearsValue method))
            {
                return(false);
            }
            int objPtr = HeapAddObject(new GearsObjBoundMethod(Peek(), HeapGetObject(method.AsObjPtr) as GearsObjFunction));

            Pop();
            Push(GearsValue.CreateObjPtr(objPtr));
            return(true);
        }
Example #10
0
        private T GetObjectFromPtr <T>(GearsValue ptr) where T : GearsObj
        {
            if (!ptr.IsObjPtr)
            {
                throw new Exception($"GetObjectFromPtr: Value is not a pointer and cannot reference a {typeof(T).Name}.");
            }
            GearsObj obj = HeapGetObject(ptr.AsObjPtr);

            if (obj is T)
            {
                return(obj as T);
            }
            throw new Exception($"GetObjectFromPtr: Object is not {typeof(T).Name}.");
        }
Example #11
0
 private GearsValue AreValuesEqual(GearsValue a, GearsValue b)
 {
     if (a.IsBool && b.IsBool)
     {
         return(a.AsBool == b.AsBool);
     }
     else if (a.IsNil && b.IsNil)
     {
         return(true);
     }
     else if (a.IsNumber && b.IsNumber)
     {
         return(a.Equals(b));
     }
     return(false);
 }
Example #12
0
 public bool TryGetField(Gears vm, object wrappedObject, ulong name, out GearsValue value)
 {
     if (_Fields.TryGetValue(name, out FieldInfo fieldInfo))
     {
         if (IsNumeric(fieldInfo.FieldType))
         {
             double fieldValue = Convert.ToDouble(fieldInfo.GetValue(wrappedObject));
             value = new GearsValue(fieldValue);
             return(true);
         }
         else if (fieldInfo.FieldType == typeof(bool))
         {
             bool fieldValue = Convert.ToBoolean(fieldInfo.GetValue(wrappedObject));
             value = fieldValue ? GearsValue.TrueValue : GearsValue.FalseValue;
             return(true);
         }
         else if (fieldInfo.FieldType == typeof(string))
         {
             if (!(fieldInfo.GetValue(wrappedObject) is string fieldValue))
             {
                 value = GearsValue.NilValue;
             }
             else
             {
                 value = GearsValue.CreateObjPtr(vm.HeapAddObject(new GearsObjString(fieldValue)));
             }
             return(true);
         }
         else if (fieldInfo.FieldType.IsSubclassOf(typeof(object)))
         {
             if (!(fieldInfo.GetValue(wrappedObject) is object wrappedFieldObject))
             {
                 value = GearsValue.NilValue;
             }
             else
             {
                 value = GearsValue.CreateObjPtr(vm.HeapAddObject(new GearsObjInstanceNative(vm, wrappedFieldObject)));
             }
             return(true);
         }
Example #13
0
        private void InvokeFromClass(int argCount, ulong methodName, GearsValue receiverPtr, GearsObjClass objClass)
        {
            if (!objClass.Methods.TryGet(methodName, out GearsValue methodPtr))
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{objClass} has no method with name '{BitString.GetBitStr(methodName)}'.");
            }
            if ((!methodPtr.IsObjPtr) || !(HeapGetObject(methodPtr.AsObjPtr) is GearsObjFunction method))
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Could not resolve method '{BitString.GetBitStr(methodName)}' in class {objClass}.");
            }
            if (method.Arity != argCount)
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{method} expects {method.Arity} arguments but was passed {argCount}.");
            }
            int ip = method.IP;
            int bp = _SP - (method.Arity + 1);

            if (!receiverPtr.IsNil)
            {
                StackSet(bp, receiverPtr); // todo: this wipes out the method object. Is this bad?
            }
            PushFrame(new GearsCallFrame(method, ip, bp));
        }
Example #14
0
 /// <summary>
 /// Defines a function that can be called by scripts.
 /// Arity is the number of arguments expected.
 /// </summary>
 internal void AddNativeFunctionToGlobals(string name, int arity, GearsFunctionNativeDelegate onInvoke)
 {
     Globals.Set(BitString.GetBitStr(name), GearsValue.CreateObjPtr(HeapAddObject(new GearsObjFunctionNative(name, arity, onInvoke))));
 }
Example #15
0
 private bool IsFalsey(GearsValue value)
 {
     return(value.IsNil || (value.IsBool && !value.AsBool));
 }
Example #16
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}");
                }
            }
        }
Example #17
0
 public void SetField(Gears context, object wrappedObject, ulong name, GearsValue value)
 {
     if (_Fields.TryGetValue(name, out FieldInfo fieldInfo))
     {
         if (value.IsNumber)
         {
             if (!IsNumeric(fieldInfo.FieldType))
             {
                 throw new GearsRuntimeException($"Attempted to set {WrappedType.Name}.{fieldInfo.Name} to numeric value.");
             }
             try {
                 fieldInfo.SetValue(wrappedObject, Convert.ChangeType((double)value, fieldInfo.FieldType));
                 return;
             }
             catch (Exception e) {
                 throw new GearsRuntimeException($"Error setting {WrappedType.Name}.{fieldInfo.Name} to {(double)value}: {e.Message}");
             }
         }
         else if (value.IsNil && fieldInfo.FieldType == typeof(string))
         {
             fieldInfo.SetValue(wrappedObject, null);
             return;
         }
         else if (fieldInfo.FieldType == typeof(bool) && value.IsBool)
         {
             fieldInfo.SetValue(wrappedObject, value.IsTrue);
             return;
         }
         else if (value.IsObjPtr)
         {
             GearsObj obj = value.AsObject(context);
             if (fieldInfo.FieldType == typeof(string) && obj is GearsObjString objString)
             {
                 fieldInfo.SetValue(wrappedObject, objString.Value);
                 return;
             }
         }
     }
     else if (_Properties.TryGetValue(name, out PropertyInfo propertyInfo))
     {
         if (!propertyInfo.GetSetMethod().IsPublic)
         {
             throw new GearsRuntimeException($"Unsupported reference: Native class {WrappedType.Name} does not have a public set method for '{BitString.GetBitStr(name)}'.");
         }
         if (value.IsNumber)
         {
             if (!IsNumeric(propertyInfo.PropertyType))
             {
                 throw new GearsRuntimeException($"Attempted to set {WrappedType.Name}.{propertyInfo.Name} to numeric value.");
             }
             try {
                 propertyInfo.SetValue(wrappedObject, Convert.ChangeType((double)value, propertyInfo.PropertyType), null);
                 return;
             }
             catch (Exception e) {
                 throw new GearsRuntimeException($"Error setting {WrappedType.Name}.{propertyInfo.Name} to {(double)value}: {e.Message}");
             }
         }
         else if (value.IsNil && propertyInfo.PropertyType == typeof(string))
         {
             propertyInfo.SetValue(wrappedObject, null, null);
             return;
         }
         else if (propertyInfo.PropertyType == typeof(bool) && value.IsBool)
         {
             propertyInfo.SetValue(wrappedObject, value.IsTrue, null);
             return;
         }
         else if (value.IsObjPtr)
         {
             GearsObj obj = value.AsObject(context);
             if (propertyInfo.PropertyType == typeof(string) && obj is GearsObjString objString)
             {
                 propertyInfo.SetValue(wrappedObject, objString.Value, null);
                 return;
             }
         }
     }
     throw new GearsRuntimeException($"Unsupported native conversion: Error setting {WrappedType.Name}.{BitString.GetBitStr(name)} to {value}.");
 }
Example #18
0
 internal void AddNativeObjectToGlobals(string name, object obj)
 {
     Globals.Set(BitString.GetBitStr(name), GearsValue.CreateObjPtr(HeapAddObject(new GearsObjInstanceNative(this, obj))));
 }
Example #19
0
        internal bool CallGearsFunction(ulong name, out object returned, params object[] args)
        {
            if (!Globals.TryGet(name, out GearsValue fnValue) || !fnValue.IsObjPtr)
            {
                // error: no function with that name.
                returned = $"Error: no function with name '{BitString.GetBitStr(name)}'.";
                return(false);
            }
            GearsObj fnObject = fnValue.AsObject(this);

            if (fnObject is GearsObjFunction fnFunction)
            {
                if (fnFunction.Arity != args.Length)
                {
                    // error: wrong arity.
                    returned = $"Error: called '{BitString.GetBitStr(name)}' with wrong arity (passed arity is '{args?.Length ?? 0}').";
                    return(false);
                }
            }
            Push(fnValue);
            for (int i = 0; i < (args?.Length ?? 0); i++)
            {
                object arg     = args[i];
                Type   argType = arg?.GetType() ?? null;
                if (arg == null)
                {
                    Push(GearsValue.NilValue);
                }
                else if (GearsNativeWrapper.IsNumeric(argType))
                {
                    double fieldValue = Convert.ToDouble(arg);
                    Push(new GearsValue(fieldValue));
                }
                else if (argType == typeof(bool))
                {
                    bool fieldValue = Convert.ToBoolean(arg);
                    Push(fieldValue ? GearsValue.TrueValue : GearsValue.FalseValue);
                }
                else if (argType == typeof(string))
                {
                    string fieldValue = Convert.ToString(arg);
                    if (fieldValue == null)
                    {
                        Push(GearsValue.NilValue);
                    }
                    else
                    {
                        Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjString(fieldValue))));
                    }
                }
                else if (argType.IsSubclassOf(typeof(object)))
                {
                    if (arg == null)
                    {
                        Push(GearsValue.NilValue);
                    }
                    else
                    {
                        Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjInstanceNative(this, arg))));
                    }
                }
                else
                {
                    // error: could not pass arg of this type
                    returned = $"Error: called '{BitString.GetBitStr(name)}' with argument of type '{argType.Name}' as parameter {i}. Gears could not interpret this argument.";
                    return(false);
                }
            }
            Call(args?.Length ?? 0);
            Run();
            returned = LastReturnValue; // the return value
            // todo: process return value?
            return(true);
        }
Example #20
0
        private void Call(int argCount)
        {
            GearsValue ptr = Peek(argCount);

            if (!ptr.IsObjPtr)
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), "Attempted call to non-pointer.");
            }
            GearsObj obj = HeapGetObject(ptr.AsObjPtr);

            if (obj is GearsObjFunction function)
            {
                if (function.Arity != argCount)
                {
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{function} expects {function.Arity} arguments but was passed {argCount}.");
                }
                int ip = function.IP;
                int bp = _SP - (function.Arity + 1);
                PushFrame(new GearsCallFrame(function, ip, bp));
            }
            else if (obj is GearsObjFunctionNative native)
            {
                if (native.Arity != argCount)
                {
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{native} expects {native.Arity} arguments but was passed {argCount}.");
                }
                GearsValue[] args = new GearsValue[argCount];
                for (int i = argCount - 1; i >= 0; i--)
                {
                    args[i] = Pop();
                }
                Pop(); // pop the function signature
                Push(native.Invoke(args));
            }
            else if (obj is GearsObjBoundMethod method)
            {
                if (method.Method.Arity != argCount)
                {
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{method} expects {method.Method.Arity} arguments but was passed {argCount}.");
                }
                int ip = method.Method.IP;
                int bp = _SP - (method.Method.Arity + 1);
                StackSet(bp, method.Receiver); // todo: this wipes out the method object. Is this bad?
                PushFrame(new GearsCallFrame(method.Method, ip, bp));
            }
            else if (obj is GearsObjClass classObj)
            {
                StackSet(_SP - argCount - 1, GearsValue.CreateObjPtr(HeapAddObject(new GearsObjInstanceLox(classObj))));
                if (classObj.Methods.TryGet(InitString, out GearsValue initPtr))
                {
                    if (!initPtr.IsObjPtr)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Attempted call to non-pointer.");
                    }
                    GearsObjFunction initFn = HeapGetObject(initPtr.AsObjPtr) as GearsObjFunction;
                    PushFrame(new GearsCallFrame(initFn, initFn.IP, _SP - argCount - 1));
                }
            }
            else
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Unhandled call to object {obj}");
            }
        }
Example #21
0
        // --- Can probably merge a ton of code from the three call methods ---

        private void CallInvoke()
        {
            int        argCount    = ReadByte();
            ulong      methodName  = (ulong)ReadConstant();
            GearsValue receiverPtr = Peek(argCount);

            if (!receiverPtr.IsObjPtr)
            {
                throw new GearsRuntimeException(Chunk.LineAt(_IP), "Attempted invoke to non-pointer.");
            }
            GearsObj obj = receiverPtr.AsObject(this);

            if (obj is GearsObjInstance instance)
            {
                if (instance.TryGetField(methodName, out GearsValue value))
                {
                    if (!value.IsObjPtr)
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), "Attempted call to non-pointer.");
                    }
                    GearsObj objFn = HeapGetObject(value.AsObjPtr);
                    if (objFn is GearsObjFunction function)
                    {
                        if (function.Arity != argCount)
                        {
                            throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{function} expects {function.Arity} arguments but was passed {argCount}.");
                        }
                        int ip = function.IP;
                        int bp = _SP - (function.Arity + 1);
                        PushFrame(new GearsCallFrame(function, ip, bp));
                    }
                    else if (objFn is GearsObjFunctionNative native)
                    {
                        if (native.Arity != argCount)
                        {
                            throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{native} expects {native.Arity} arguments but was passed {argCount}.");
                        }
                        GearsValue[] args = new GearsValue[argCount];
                        for (int i = argCount - 1; i >= 0; i--)
                        {
                            args[i] = Pop();
                        }
                        Pop(); // pop the function signature
                        Push(native.Invoke(args));
                    }
                    else
                    {
                        throw new GearsRuntimeException(Chunk.LineAt(_IP), $"Could not resolve method {methodName} in {instance}.");
                    }
                }
                else if (instance is GearsObjInstanceLox instanceLox)
                {
                    InvokeFromClass(argCount, methodName, receiverPtr, instanceLox.Class);
                }
                else
                {
                    throw new GearsRuntimeException(Chunk.LineAt(_IP), $"{instance} does not have a public method named '{BitString.GetBitStr(methodName)}'.");
                }
                return;
            }
            throw new GearsRuntimeException(Chunk.LineAt(_IP), "Attempted invoke to non-instance.");
        }