public static void BindMethodCode(ObjClass classObj, ObjFn fn) { int ip = 0; for (; ; ) { Instruction instruction = (Instruction)fn.Bytecode[ip++]; switch (instruction) { case Instruction.LoadField: case Instruction.StoreField: case Instruction.LoadFieldThis: case Instruction.StoreFieldThis: // 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.Super0: case Instruction.Super1: case Instruction.Super2: case Instruction.Super3: case Instruction.Super4: case Instruction.Super5: case Instruction.Super6: case Instruction.Super7: case Instruction.Super8: case Instruction.Super9: case Instruction.Super10: case Instruction.Super11: case Instruction.Super12: case Instruction.Super13: case Instruction.Super14: case Instruction.Super15: case Instruction.Super16: { // 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; } } }
public static string DumpByteCode(SophieVM vm, ObjFn fn) { string s = ""; int ip = 0; byte[] bytecode = fn.Bytecode; while (ip < bytecode.Length) { s += ip + ": "; Instruction instruction = (Instruction)bytecode[ip++]; s += (instruction + " "); switch (instruction) { case Instruction.Null: case Instruction.False: case Instruction.True: case Instruction.Zero: case Instruction.One: case Instruction.Pop: case Instruction.Dup: case Instruction.CloseUpvalue: case Instruction.Return: case Instruction.End: case Instruction.LoadLocal0: case Instruction.LoadLocal1: case Instruction.LoadLocal2: case Instruction.LoadLocal3: case Instruction.LoadLocal4: case Instruction.LoadLocal5: case Instruction.LoadLocal6: case Instruction.LoadLocal7: case Instruction.LoadLocal8: s += ("\n"); break; case Instruction.LoadLocal: case Instruction.StoreLocal: case Instruction.LoadUpvalue: case Instruction.StoreUpvalue: case Instruction.LoadFieldThis: case Instruction.StoreFieldThis: case Instruction.LoadField: case Instruction.StoreField: case Instruction.Class: case Instruction.SmallConstant: s += (bytecode[ip++] + "\n"); break; case Instruction.Call0: case Instruction.Call1: case Instruction.Call2: case Instruction.Call3: case Instruction.Call4: case Instruction.Call5: case Instruction.Call6: case Instruction.Call7: case Instruction.Call8: case Instruction.Call9: case Instruction.Call10: case Instruction.Call11: case Instruction.Call12: case Instruction.Call13: case Instruction.Call14: case Instruction.Call15: case Instruction.Call16: int method = (bytecode[ip] << 8) + bytecode[ip + 1]; s += vm.MethodNames[method] + "\n"; ip += 2; break; case Instruction.Constant: case Instruction.LoadModuleVar: case Instruction.StoreModuleVar: case Instruction.Jump: case Instruction.Loop: case Instruction.JumpIf: case Instruction.And: case Instruction.Or: case Instruction.MethodInstance: case Instruction.MethodStatic: case Instruction.LoadModule: int method1 = (bytecode[ip] << 8) + bytecode[ip + 1]; s += method1 + "\n"; ip += 2; break; case Instruction.Super0: case Instruction.Super1: case Instruction.Super2: case Instruction.Super3: case Instruction.Super4: case Instruction.Super5: case Instruction.Super6: case Instruction.Super7: case Instruction.Super8: case Instruction.Super9: case Instruction.Super10: case Instruction.Super11: case Instruction.Super12: case Instruction.Super13: case Instruction.Super14: case Instruction.Super15: case Instruction.Super16: case Instruction.ImportVariable: s += (bytecode[ip++]); s += (" "); s += (bytecode[ip++]); s += (" "); s += (bytecode[ip++]); s += (" "); s += (bytecode[ip++] + "\n"); break; case Instruction.Closure: { int constant = (bytecode[ip + 1] << 8) | bytecode[ip + 2]; 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; }
// 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() { // 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()); // 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.EmitConstant(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; }