static bool Eval(WrenVM vm, Obj[] args, int stackStart) { if (args[stackStart + 1] is ObjString) { // Eval the code in the module where the calling function was defined. Obj callingFn = vm.Fiber.GetFrame().Fn; ObjModule module = (callingFn is ObjFn) ? ((ObjFn)callingFn).Module : ((ObjClosure)callingFn).Function.Module; // Compile it. ObjFn fn = Compiler.Compile(vm, module, "", args[stackStart + 1].ToString(), false); if (fn == null) { vm.Fiber.Error = Obj.MakeString("Could not compile source code."); return false; } // TODO: Include the compile errors in the runtime error message. // Create a fiber to run the code in. ObjFiber evalFiber = new ObjFiber(fn) { Caller = vm.Fiber }; // Switch to the fiber. args[stackStart] = evalFiber; return false; } vm.Fiber.Error = Obj.MakeString("Source code must be a string."); return false; }
public Obj GetKey(int index) { if (index < 0 || index >= _entries.Count) return Undefined; Obj[] v = new Obj[_entries.Count]; _entries.Keys.CopyTo(v, 0); return v[index]; }
// Removes [key] from [map], if present. Returns the value for the key if found // or `NULL_VAL` otherwise. public Obj Remove(Obj key) { Obj v; if (!_entries.TryGetValue(key, out v)) return Null; _entries.Remove(key); return v; }
// Resets [fiber] back to an initial state where it is ready to invoke [fn]. private void ResetFiber(Obj fn) { Stack = new Obj[InitialStackSize]; Capacity = InitialStackSize; Frames = new List<CallFrame>(); // Push the stack frame for the function. StackTop = 0; NumFrames = 1; OpenUpvalues = null; Caller = null; Error = null; CallerIsTrying = false; CallFrame frame = new CallFrame { Fn = fn, StackStart = 0, Ip = 0 }; Frames.Add(frame); }
// TODO: The argument list here is getting a bit gratuitous. // Creates a new function object with the given code and constants. The new // function will take over ownership of [bytecode] and [sourceLines]. It will // copy [constants] into its own array. public ObjFn(ObjModule module, Obj[] constants, int numUpvalues, int arity, byte[] bytecode, ObjString debugSourcePath, string debugName, int[] sourceLines) { Bytecode = bytecode; Constants = constants; Module = module; NumUpvalues = numUpvalues; NumConstants = constants.Length; Arity = arity; /* Debug Information */ SourcePath = debugSourcePath; Name = debugName; SourceLines = sourceLines; ClassObj = WrenVM.FnClass; }
static bool prim_map_subscriptSetter(WrenVM vm, Obj[] args, int stackStart) { if (ValidateKey(args[stackStart + 1])) { ObjMap map = args[stackStart] as ObjMap; if (map != null) { map.Set(args[stackStart + 1], args[stackStart + 2]); } args[stackStart] = args[stackStart + 2]; return true; } vm.Fiber.Error = Obj.MakeString("Key must be a value type or fiber."); return false; }
static bool prim_map_clear(WrenVM vm, Obj[] args, int stackStart) { ObjMap m = args[stackStart] as ObjMap; if (m != null) m.Clear(); args[stackStart] = Obj.Null; return true; }
// Looks up [key] in [map]. If found, returns the value. Otherwise, returns UNDEFINED. public Obj Get(Obj key) { Obj v; return(_entries.TryGetValue(key, out v) ? v : Undefined); }
static bool prim_map_subscript(WrenVM vm, Obj[] args, int stackStart) { Obj a = args[stackStart + 1]; if (ValidateKey(a)) { ObjMap map = args[stackStart] as ObjMap; if (map != null) { args[stackStart] = map.Get(a); if (args[stackStart] == Obj.Undefined) { args[stackStart] = Obj.Null; } } else { args[stackStart] = Obj.Null; } return true; } vm.Fiber.Error = Obj.MakeString("Key must be a value type or fiber."); return false; }
static bool prim_fn_toString(WrenVM vm, Obj[] args, int stackStart) { args[stackStart] = Obj.MakeString("<fn>"); return true; }
// Defines [methodValue] as a method on [classObj]. private static bool BindMethod(bool isStatic, int symbol, ObjClass classObj, Obj methodContainer) { // If we are binding a foreign method, just return, as this will be handled later if (methodContainer is ObjString) return true; ObjFn methodFn = methodContainer as ObjFn ?? ((ObjClosure)methodContainer).Function; Method method = new Method { MType = MethodType.Block, Obj = methodContainer }; if (isStatic) classObj = classObj.ClassObj; // Methods are always bound against the class, and not the metaclass, even // for static methods, because static methods don't have instance fields // anyway. Compiler.BindMethodCode(classObj, methodFn); classObj.BindMethod(symbol, method); return true; }
bool CheckArity(Obj[] args, int numArgs, int stackStart) { ObjFn fn = args[stackStart] as ObjFn; ObjClosure c = args[stackStart] as ObjClosure; if (c != null) { fn = c.Function; } if (fn == null) { Fiber.Error = Obj.MakeString("Receiver must be a function or closure."); return false; } if (numArgs - 1 < fn.Arity) { Fiber.Error = Obj.MakeString("Function expects more arguments."); return false; } return true; }
private static bool prim_map_iterate(WrenVM vm, Obj[] args, int stackStart) { ObjMap map = (ObjMap)args[stackStart]; if (map.Count() == 0) { args[stackStart] = Obj.False; return true; } // Start one past the last entry we stopped at. if (args[stackStart + 1].Type == ObjType.Num) { if (args[stackStart + 1].Num < 0) { args[stackStart] = Obj.False; return true; } int index = (int)args[stackStart + 1].Num; if (index == args[stackStart + 1].Num) { args[stackStart] = index > map.Count() || map.Get(index) == Obj.Undefined ? Obj.False : new Obj(index + 1); return true; } vm.Fiber.Error = Obj.MakeString("Iterator must be an integer."); return false; } // If we're starting the iteration, start at the first used entry. if (args[stackStart + 1] == Obj.Null) { args[stackStart] = new Obj(1); return true; } vm.Fiber.Error = Obj.MakeString("Iterator must be a number."); return false; }
static bool prim_list_count(WrenVM vm, Obj[] args, int stackStart) { ObjList list = args[stackStart] as ObjList; if (list != null) { args[stackStart] = new Obj(list.Count()); return true; } vm.Fiber.Error = Obj.MakeString("Trying to clear a non-list"); return false; }
static bool prim_list_insert(WrenVM vm, Obj[] args, int stackStart) { ObjList list = (ObjList)args[stackStart]; if (args[stackStart + 1].Type == ObjType.Num) { int index = (int)args[stackStart + 1].Num; if (args[stackStart + 1].Num == index) { if (index < 0) index += list.Count() + 1; if (index >= 0 && index <= list.Count()) { list.Insert(args[stackStart + 2], index); args[stackStart] = args[stackStart + 2]; return true; } vm.Fiber.Error = Obj.MakeString("Index out of bounds."); return false; } // count + 1 here so you can "insert" at the very end. vm.Fiber.Error = Obj.MakeString("Index must be an integer."); return false; } vm.Fiber.Error = Obj.MakeString("Index must be a number."); return false; }
static bool prim_list_clear(WrenVM vm, Obj[] args, int stackStart) { ObjList list = args[stackStart] as ObjList; if (list == null) { vm.Fiber.Error = Obj.MakeString("Trying to clear a non-list"); return false; } list.Clear(); args[stackStart] = Obj.Null; return true; }
static bool prim_list_add(WrenVM vm, Obj[] args, int stackStart) { ObjList list = args[stackStart] as ObjList; if (list == null) { vm.Fiber.Error = Obj.MakeString("Trying to add to a non-list"); return false; } list.Add(args[stackStart + 1]); args[stackStart] = args[stackStart + 1]; return true; }
static bool prim_list_instantiate(WrenVM vm, Obj[] args, int stackStart) { args[stackStart] = new ObjList(16); return true; }
static bool prim_map_containsKey(WrenVM vm, Obj[] args, int stackStart) { ObjMap map = (ObjMap)args[stackStart]; if (ValidateKey(args[stackStart + 1])) { Obj v = map.Get(args[stackStart + 1]); args[stackStart] = Obj.Bool(v != Obj.Undefined); return true; } vm.Fiber.Error = Obj.MakeString("Key must be a value type or fiber."); return false; }
private ObjFiber LoadModule(Obj name, string source) { ObjModule module = GetModule(name); // See if the module has already been loaded. if (module == null) { module = new ObjModule(name as ObjString); // Store it in the VM's module registry so we don't load the same module // multiple times. _modules.Set(name, module); // Implicitly import the core module. ObjModule coreModule = GetCoreModule(); foreach (ModuleVariable t in coreModule.Variables) { DefineVariable(module, t.Name, t.Container); } } ObjFn fn = Compiler.Compile(this, module, name.ToString(), source, true); if (fn == null) { // TODO: Should we still store the module even if it didn't compile? return null; } ObjFiber moduleFiber = new ObjFiber(fn); // Return the fiber that executes the module. return moduleFiber; }
static bool prim_map_count(WrenVM vm, Obj[] args, int stackStart) { ObjMap m = (ObjMap)args[stackStart]; args[stackStart] = new Obj(m.Count()); return true; }
private bool ImportVariable(Obj moduleName, Obj variableName, out Obj result) { ObjModule module = GetModule(moduleName); if (module == null) { result = Obj.MakeString("Could not load module"); return false; // Should only look up loaded modules } ObjString variable = variableName as ObjString; if (variable == null) { result = Obj.MakeString("Variable name must be a string"); return false; } int variableEntry = module.Variables.FindIndex(v => v.Name == variable.ToString()); // It's a runtime error if the imported variable does not exist. if (variableEntry == -1) { result = Obj.MakeString(string.Format("Could not find a variable named '{0}' in module '{1}'.", variableName, moduleName)); return false; } result = module.Variables[variableEntry].Container; return true; }
static bool prim_list_iterate(WrenVM vm, Obj[] args, int stackStart) { ObjList list = (ObjList)args[stackStart]; // If we're starting the iteration, return the first index. if (args[stackStart + 1] == Obj.Null) { if (list.Count() != 0) { args[stackStart] = new Obj(0.0); return true; } args[stackStart] = Obj.False; return true; } if (args[stackStart + 1].Type == ObjType.Num) { int index = (int)args[stackStart + 1].Num; if (args[stackStart + 1].Num == index) { if (!(index < 0) && !(index >= list.Count() - 1)) { // Move to the next index. args[stackStart] = new Obj(index + 1); return true; } // Stop if we're out of bounds. args[stackStart] = Obj.False; return true; } vm.Fiber.Error = Obj.MakeString("Iterator must be an integer."); return false; } vm.Fiber.Error = Obj.MakeString("Iterator must be a number."); return false; }
// Associates [key] with [value] in [map]. public void Set(Obj key, Obj c) { _entries[key] = c; }
// Looks up the previously loaded module with [name]. // Returns null if no module with that name has been loaded. private ObjModule GetModule(Obj name) { Obj moduleContainer = _modules.Get(name); return moduleContainer == Obj.Undefined ? null : moduleContainer as ObjModule; }
static bool prim_list_removeAt(WrenVM vm, Obj[] args, int stackStart) { ObjList list = (ObjList)args[stackStart]; if (args[stackStart + 1].Type == ObjType.Num) { int index = (int)args[stackStart + 1].Num; if (args[stackStart + 1].Num == index) { if (index < 0) index += list.Count(); if (index >= 0 && index < list.Count()) { args[stackStart] = list.RemoveAt(index); return true; } vm.Fiber.Error = Obj.MakeString("Index out of bounds."); return false; } vm.Fiber.Error = Obj.MakeString("Index must be an integer."); return false; } vm.Fiber.Error = Obj.MakeString("Index must be a number."); return false; }
private Obj ImportModule(Obj name) { // If the module is already loaded, we don't need to do anything. if (_modules.Get(name) != Obj.Undefined) return Obj.Null; // Load the module's source code from the embedder. string source = LoadModuleFn(name.ToString()); if (source == null) { // Couldn't load the module. return Obj.MakeString(string.Format("Could not find module '{0}'.", name)); } ObjFiber moduleFiber = LoadModule(name, source); // Return the fiber that executes the module. return moduleFiber; }
static bool prim_list_subscript(WrenVM vm, Obj[] args, int stackStart) { ObjList list = (ObjList)args[stackStart]; if (args[stackStart + 1].Type == ObjType.Num) { int index = (int)args[stackStart + 1].Num; if (index == args[stackStart + 1].Num) { if (index < 0) { index += list.Count(); } if (index >= 0 && index < list.Count()) { args[stackStart] = list.Get(index); return true; } vm.Fiber.Error = Obj.MakeString("Subscript out of bounds."); return false; } vm.Fiber.Error = Obj.MakeString("Subscript must be an integer."); return false; } ObjRange r = args[stackStart + 1] as ObjRange; if (r == null) { vm.Fiber.Error = Obj.MakeString("Subscript must be a number or a range."); return false; } // TODO: This is seriously broken and needs a rewrite int from = (int)r.From; if (from != r.From) { vm.Fiber.Error = Obj.MakeString("Range start must be an integer."); return false; } int to = (int)r.To; if (to != r.To) { vm.Fiber.Error = Obj.MakeString("Range end must be an integer."); return false; } if (from < 0) from += list.Count(); if (to < 0) to += list.Count(); int step = to < from ? -1 : 1; if (step > 0 && r.IsInclusive) to += 1; if (step < 0 && !r.IsInclusive) to += 1; // Handle copying an empty list if (list.Count() == 0 && to == (r.IsInclusive ? -1 : 0)) { to = 0; step = 1; } int count = (to - from) * step + (step < 0 ? 1 : 0); if (to < 0 || from + (count * step) > list.Count()) { vm.Fiber.Error = Obj.MakeString("Range end out of bounds."); return false; } if (from < 0 || (from >= list.Count() && from > 0)) { vm.Fiber.Error = Obj.MakeString("Range start out of bounds."); return false; } ObjList result = new ObjList(count); for (int i = 0; i < count; i++) { result.Add(list.Get(from + (i * step))); } args[stackStart] = result; return true; }
// Verifies that [superclass] is a valid object to inherit from. That means it // must be a class and cannot be the class of any built-in type. // // If successful, returns null. Otherwise, returns a string for the runtime // error message. private static Obj ValidateSuperclass(Obj name, Obj superclassContainer) { // Make sure the superclass is a class. ObjClass superClass = superclassContainer as ObjClass; if (superClass != null) { // Make sure it doesn't inherit from a sealed built-in type. Primitive methods // on these classes assume the instance is one of the other Obj___ types and // will fail horribly if it's actually an ObjInstance. return superClass.IsSealed ? Obj.MakeString(string.Format("Class '{0}' cannot inherit from built-in class '{1}'.", name as ObjString, (superClass.Name))) : null; } return Obj.MakeString(string.Format("Class '{0}' cannot inherit from a non-class object.", name)); }
static bool prim_list_subscriptSetter(WrenVM vm, Obj[] args, int stackStart) { ObjList list = (ObjList)args[stackStart]; if (args[stackStart + 1].Type == ObjType.Num) { int index = (int)args[stackStart + 1].Num; if (index == args[stackStart + 1].Num) { if (index < 0) { index += list.Count(); } if (list != null && index >= 0 && index < list.Count()) { list.Set(args[stackStart + 2], index); args[stackStart] = args[stackStart + 2]; return true; } vm.Fiber.Error = Obj.MakeString("Subscript out of bounds."); return false; } vm.Fiber.Error = Obj.MakeString("Subscript must be an integer."); return false; } vm.Fiber.Error = Obj.MakeString("Subscript must be a number."); return false; }
internal int DefineVariable(ObjModule module, string name, Obj c) { if (module == null) module = GetCoreModule(); if (module.Variables.Count == ObjModule.MaxModuleVars) return -2; // See if the variable is already explicitly or implicitly declared. int symbol = module.Variables.FindIndex(m => m.Name == name); if (symbol == -1) { // Brand new variable. module.Variables.Add(new ModuleVariable { Name = name, Container = c }); symbol = module.Variables.Count - 1; } else if (module.Variables[symbol].Container == Obj.Undefined) { // Explicitly declaring an implicitly declared one. Mark it as defined. module.Variables[symbol].Container = c; } else { // Already explicitly declared. symbol = -1; } return symbol; }
static bool prim_map_instantiate(WrenVM vm, Obj[] args, int stackStart) { args[stackStart] = new ObjMap(); return true; }