// The function that this closure is an instance of. // The upvalues this function has closed over. // Creates a new closure object that invokes [fn]. Allocates room for its // upvalues, but assumes outside code will populate it. public ObjClosure(ObjFn fn) { Function = fn; Upvalues = new ObjUpvalue[fn.NumUpvalues]; Type = ObjType.Closure; ClassObj = WrenVM.FnClass; }
public static string DumpByteCode(WrenVM vm, ObjFn fn) { string s = ""; int ip = 0; byte[] bytecode = fn.Bytecode; while (ip < bytecode.Length) { Instruction instruction = (Instruction)bytecode[ip++]; s += instruction + " "; switch (instruction) { case Instruction.NULL: case Instruction.FALSE: case Instruction.TRUE: case Instruction.POP: case Instruction.DUP: case Instruction.CLOSE_UPVALUE: case Instruction.RETURN: case Instruction.END: case Instruction.LOAD_LOCAL_0: case Instruction.LOAD_LOCAL_1: case Instruction.LOAD_LOCAL_2: case Instruction.LOAD_LOCAL_3: case Instruction.LOAD_LOCAL_4: case Instruction.LOAD_LOCAL_5: case Instruction.LOAD_LOCAL_6: case Instruction.LOAD_LOCAL_7: case Instruction.LOAD_LOCAL_8: s += ("\n"); break; case Instruction.LOAD_LOCAL: case Instruction.STORE_LOCAL: case Instruction.LOAD_UPVALUE: case Instruction.STORE_UPVALUE: case Instruction.LOAD_FIELD_THIS: case Instruction.STORE_FIELD_THIS: case Instruction.LOAD_FIELD: case Instruction.STORE_FIELD: case Instruction.CLASS: s += (bytecode[ip++] + "\n"); break; case Instruction.CALL_0: case Instruction.CALL_1: case Instruction.CALL_2: case Instruction.CALL_3: case Instruction.CALL_4: case Instruction.CALL_5: case Instruction.CALL_6: case Instruction.CALL_7: case Instruction.CALL_8: case Instruction.CALL_9: case Instruction.CALL_10: case Instruction.CALL_11: case Instruction.CALL_12: case Instruction.CALL_13: case Instruction.CALL_14: case Instruction.CALL_15: case Instruction.CALL_16: int method = (bytecode[ip] << 8) + bytecode[ip + 1]; s += vm.MethodNames[method] + "\n"; ip += 2; break; case Instruction.CONSTANT: case Instruction.LOAD_MODULE_VAR: case Instruction.STORE_MODULE_VAR: case Instruction.JUMP: case Instruction.LOOP: case Instruction.JUMP_IF: case Instruction.AND: case Instruction.OR: case Instruction.METHOD_INSTANCE: case Instruction.METHOD_STATIC: case Instruction.LOAD_MODULE: int method1 = (bytecode[ip] << 8) + bytecode[ip + 1]; s += method1 + "\n"; ip += 2; break; case Instruction.SUPER_0: case Instruction.SUPER_1: case Instruction.SUPER_2: case Instruction.SUPER_3: case Instruction.SUPER_4: case Instruction.SUPER_5: case Instruction.SUPER_6: case Instruction.SUPER_7: case Instruction.SUPER_8: case Instruction.SUPER_9: case Instruction.SUPER_10: case Instruction.SUPER_11: case Instruction.SUPER_12: case Instruction.SUPER_13: case Instruction.SUPER_14: case Instruction.SUPER_15: case Instruction.SUPER_16: case Instruction.IMPORT_VARIABLE: s += (bytecode[ip++]); s += (" "); s += (bytecode[ip++]); s += (" "); s += (bytecode[ip++]); s += (" "); s += (bytecode[ip++] + "\n"); break; case Instruction.CLOSURE: { int constant = (bytecode[ip] << 8) | bytecode[ip + 1]; ObjFn loadedFn = (ObjFn)fn.Constants[constant]; // There are two bytes for the constant, then two for each upvalue. int j = 2 + (loadedFn.NumUpvalues * 2); while (j > 0) { s += (bytecode[ip++]); s += (" "); j--; } s += "\n"; } break; } } return s; }
public static void BindMethodCode(ObjClass classObj, ObjFn fn) { int ip = 0; for (; ; ) { Instruction instruction = (Instruction)fn.Bytecode[ip++]; switch (instruction) { case Instruction.LOAD_FIELD: case Instruction.STORE_FIELD: case Instruction.LOAD_FIELD_THIS: case Instruction.STORE_FIELD_THIS: // Shift this class's fields down past the inherited ones. We don't // check for overflow here because we'll see if the number of fields // overflows when the subclass is created. fn.Bytecode[ip++] += (byte)classObj.Superclass.NumFields; break; case Instruction.SUPER_0: case Instruction.SUPER_1: case Instruction.SUPER_2: case Instruction.SUPER_3: case Instruction.SUPER_4: case Instruction.SUPER_5: case Instruction.SUPER_6: case Instruction.SUPER_7: case Instruction.SUPER_8: case Instruction.SUPER_9: case Instruction.SUPER_10: case Instruction.SUPER_11: case Instruction.SUPER_12: case Instruction.SUPER_13: case Instruction.SUPER_14: case Instruction.SUPER_15: case Instruction.SUPER_16: { // Skip over the symbol. ip += 2; // Fill in the constant slot with a reference to the superclass. int constant = (fn.Bytecode[ip] << 8) | fn.Bytecode[ip + 1]; fn.Constants[constant] = classObj.Superclass; break; } case Instruction.CLOSURE: { // Bind the nested closure too. int constant = (fn.Bytecode[ip] << 8) | fn.Bytecode[ip + 1]; BindMethodCode(classObj, fn.Constants[constant] as ObjFn); ip += GetNumArguments(fn.Bytecode, new List<Obj>(fn.Constants), ip - 1); break; } case Instruction.END: return; default: // Other instructions are unaffected, so just skip over them. ip += GetNumArguments(fn.Bytecode, new List<Obj>(fn.Constants), ip - 1); break; } } }
// Finishes [compiler], which is compiling a function, method, or chunk of top // level code. If there is a parent compiler, then this emits code in the // parent compiler to load the resulting function. private ObjFn EndCompiler(string debugName) { // If we hit an error, don't bother creating the function since it's borked // anyway. if (_parser.HasError) { _parser.vm.Compiler = _parent; return null; } // Mark the end of the bytecode. Since it may contain multiple early returns, // we can't rely on Instruction.RETURN to tell us we're at the end. Emit(Instruction.END); // Create a function object for the code we just compiled. ObjFn fn = new ObjFn(_parser.Module, _constants.ToArray(), _numUpValues, _numParams, _bytecode.ToArray(), new ObjString(_parser.SourcePath), debugName, _debugSourceLines); // In the function that contains this one, load the resulting function object. if (_parent != null) { int constant = _parent.AddConstant(fn); // If the function has no upvalues, we don't need to create a closure. // We can just load and run the function directly. if (_numUpValues == 0) { _parent.EmitShortArg(Instruction.CONSTANT, constant); } else { // Capture the upvalues in the new closure object. _parent.EmitShortArg(Instruction.CLOSURE, constant); // Emit arguments for each upvalue to know whether to capture a local or // an upvalue. // TODO: Do something more efficient here? for (int i = 0; i < _numUpValues; i++) { _parent.Emit(_upvalues[i].IsLocal ? 1 : 0); _parent.Emit(_upvalues[i].Index); } } } // Pop this compiler off the stack. _parser.vm.Compiler = _parent; return fn; }
// Creates a new closure object that invokes [fn]. Allocates room for its // upvalues, but assumes outside code will populate it. public ObjClosure(ObjFn fn) { Function = fn; Upvalues = new ObjUpvalue[fn.NumUpvalues]; ClassObj = WrenVM.FnClass; }