private void TraceReferences() { while (_GrayList.Count > 0) { GearsObj obj = _GrayList.Dequeue(); #if DEBUG_LOG_GC Console.WriteLine($"Blacken {obj}"); #endif obj.Blacken(this); } }
public void MarkObject(GearsObj obj) { if (obj == null || obj.IsMarked) { return; } #if DEBUG_LOG_GC Console.WriteLine($"Mark {obj}"); #endif _GrayList.Enqueue(obj); obj.IsMarked = true; }
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}."); }
// === Heap ================================================================================================== // =========================================================================================================== internal int HeapAddObject(GearsObj obj, bool allowGC = true) { for (int i = 0; i < _Heap.Length; i++) { if (_Heap[i] == null) { _Heap[i] = obj; #if DEBUG_LOG_GC Console.WriteLine($"Allocate {obj.Type} at {i}"); #endif return(i); } } if (allowGC) { CollectGarbage(); int newIndex = HeapAddObject(obj, false); if (newIndex != -1) { return(newIndex); } } throw new GearsRuntimeException(Chunk.LineAt(_IP), "Out of heap space."); }
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}."); }
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); }
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}"); } }
// --- 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."); }