private void ProcessLines(string[] lines, LuaChunk chunk) { string line = ""; Opcode code = Opcode.NONE; Opcode peek = Opcode.NONE; Opcode prevOp = Opcode.NONE; LuaTable currentTable = null; LuaChunk newOne = null; LuaChunk parentChunk = chunk; LuaChunk currentChunk = new LuaChunk(); parentChunk.AddChunk(currentChunk); for (int i = 0; i < lines.Length; i++) { line = lines[i].Trim(); if (line.StartsWith("--") || line.Length < 2) { continue; } code = Operation.GetOpcode(line); peek = i + 1 < lines.Length ? Operation.GetOpcode(lines[i + 1]) : Opcode.NONE; switch (code) { case Opcode.LOADBOOL: break; case Opcode.GETGLOBAL: if (prevOp == Opcode.NEWTABLE) { currentTable.AddValue(new LuaChunk() { ConstantValue = Operation.GetName(line) }); } else if (currentChunk.GlobalName == null) { currentChunk.GlobalName = Operation.GetName(line); } else { newOne = new LuaChunk(); newOne.GlobalName = Operation.GetName(line); currentChunk.ParentChunk.AddChunk(newOne); currentChunk = newOne; } break; case Opcode.LOADK: currentChunk.AddConstant(new LuaChunk() { ConstantValue = Operation.GetArgument(line) }); break; case Opcode.CALL: currentChunk.SetCall(); newOne = new LuaChunk(); currentChunk.ParentChunk.AddChunk(newOne); currentChunk = newOne; break; case Opcode.SETGLOBAL: if (prevOp == Opcode.CALL) { LuaChunk bro = currentChunk.GetOlderSipling(); if (bro != null) { bro.AddAssignmentLvalue(Operation.GetName(line)); } else { throw new Exception("No older sibling to assign to!!! Logic needs to change!!!"); } } else if (prevOp == Opcode.LOADK || prevOp == Opcode.SETTABLE || prevOp == Opcode.LOADBOOL) { currentChunk.AddAssignmentLvalue(Operation.GetName(line)); newOne = new LuaChunk(); currentChunk.ParentChunk.AddChunk(newOne); currentChunk = newOne; } break; case Opcode.LT: case Opcode.LE: case Opcode.EQ: currentChunk = currentChunk.ParentChunk.ApplyExpression(code); if (peek == Opcode.JMP) { IfStatement stmt = new IfStatement(); stmt.Expression = currentChunk; stmt.ThenChunk = GatherThenChunk(lines, i + 2); stmt.ElseChunk = GatherElseChunk(lines, i + 1); currentChunk.ParentChunk.ReplaceChunk(currentChunk, stmt); //currentChunk = stmt; newOne = new LuaChunk(); currentChunk.ParentChunk.AddChunk(newOne); currentChunk = newOne; i = stmt.ElseChunk.LastLine + 1; } break; case Opcode.ADD: case Opcode.SUB: case Opcode.MUL: case Opcode.DIV: case Opcode.POW: List <string> mathArgs = Operation.GetArguments(line); if (mathArgs.Count == 2) { LuaExpression expr = new LuaExpression() { RValue = new LuaChunk() { ConstantValue = mathArgs[1] }, LValue = new LuaChunk() { ConstantValue = mathArgs[0] }, Operation = code }; if (currentChunk.GlobalName != null) { currentChunk.AddChunk(expr); } } else if (mathArgs.Count == 1) { LuaExpression le = new LuaExpression() { LValue = new LuaChunk() { ConstantValue = mathArgs[0] }, Operation = code }; //throw new Exception("Fix the code!!!"); } else { throw new Exception("Fix the MATH operations with no args!!!"); // previous 2 globals or locals? } break; case Opcode.JMP: // line example : 14 [-] JMP 0 4 ; to 19 if (" LE LT EQ ".IndexOf(prevOp.ToString()) > -1) { } break; case Opcode.SELF: break; case Opcode.SETLIST: if (currentTable != null) { currentTable.SetList(); } else { throw new Exception("FIX THE TABLE PARSING CODE!! SETLIST with no current table???"); } break; case Opcode.NEWTABLE: currentTable = new LuaTable(true); if (currentChunk.LuaType == LuaType.TABLE) { ((LuaTable)currentChunk).AddValue(currentTable); } else { currentChunk.AddChunk(currentTable); // I think this works, not sure though... } currentChunk = currentTable; break; case Opcode.SETTABLE: // When to transition 'currentChunk' to its parent? List <string> args = Operation.GetArguments(line); if (args.Count == 2) { currentTable.AddEntry(args[0], new LuaChunk() { ConstantValue = args[1] }); } else if (args.Count == 1 && currentTable.ParentChunk.LuaType == LuaType.TABLE) { //currentChunk = currentTable = (LuaTable)currentTable.ParentChunk; currentTable.AddKey(args[0]); } else if (args.Count == 1) { } else { throw new Exception("Fix the Table Parsing Code!!!"); } break; case Opcode.FUNCTION_DEF: LuaChunk func = GatherFunctionChunk(lines, i); i = func.LastLine + 1; currentChunk.ParentChunk.AddChunk(func); currentChunk = func; break; case Opcode.PARAMS: break; case Opcode.CLOSURE: string closureNumber = Operation.GetClosureNumber(line); string functionName = Operation.GetName(lines[i + 1]); sClosureMapping.Add(closureNumber, functionName); break; case Opcode.RETURN: break; } prevOp = code; } }
private LuaChunk[] mLocalVarBackup = new LuaChunk[250]; // the listing info does not give us enough to determine all the // info about 'locals' that we need so we create a backup for local variables. private void ProcessLines(string[] lines, LuaChunk chunk, List <LuaChunk> upValues) { string line = ""; Opcode code = Opcode.NONE; Opcode peek = Opcode.NONE; Opcode prevOp = Opcode.NONE; int j = 0; // looping var; int currentRegister = -1; LuaFunction func = null; LuaChunk tmp = null; LuaChunk currentRegisterValue = null; LuaTable currentTable = null; LuaTable tmpTable = null; LuaChunk newOne = null; LuaChunk scopeChunk = chunk; LuaExpression tmpExpr = null; List <int> vmArgs = null; for (int i = 0; i < lines.Length; i++) { if (i < 0) { Program.ReportError("Line number: " + i, "Something got messed up", null); break; } line = lines[i].Trim(); if (line.StartsWith("--") || line.Length < 2) { continue; } code = Operation.GetOpcode(line); currentRegister = Operation.GetRegister(line); vmArgs = Operation.GetVMArgs(line); currentRegisterValue = currentRegister > -1 ? mRegisters[currentRegister] : null; peek = i + 1 < lines.Length ? Operation.GetOpcode(lines[i + 1]) : Opcode.NONE; //try { switch (code) { case Opcode.LOADBOOL: // LOADBOOL A B C R(A) := (Bool)B; if (C) PC++ // Loads a boolean value (true or false) into register R(A). // true is usually encoded as an integer 1, false is always 0. If C is non-zero, // then the next instruction is skipped (this is used when you have an assignment // statement where the expression uses relational operators, e.g. M = K>5.) string bVal = (vmArgs[0] > 0).ToString().ToLower(); newOne = new LuaChunk() { ConstantValue = bVal }; mRegisters[currentRegister] = newOne; // 'C' part should be handled by compare & jump handling. break; case Opcode.GETGLOBAL: newOne = new LuaChunk(); newOne.GlobalName = Operation.GetName(line); mRegisters[currentRegister] = newOne; break; case Opcode.MOVE: // MOVE A B R(A) := R(B) // Copies the value of register R(B) into register R(A). // If R(B) holds a table, function or userdata, then the reference to that object // is copied. MOVE is often used for moving values into place for the next operation. /*if (mRegisters[vmArgs[0]] != null && mRegisters[vmArgs[0]].LuaType == LuaType.CONSTANT) * mRegisters[currentRegister] = mRegisters[vmArgs[0]].Clone(); * else * mRegisters[currentRegister] = mRegisters[vmArgs[0]];*/ mRegisters[currentRegister] = mRegisters[vmArgs[0]]; if (mRegisters[currentRegister] == null) { mRegisters[currentRegister] = GetLocalBackup(vmArgs[0]); // already does a clone } else if (mRegisters[currentRegister].LuaType != LuaType.TABLE) // make a better clone for table? - probably a good idea { mRegisters[currentRegister] = mRegisters[currentRegister].Clone(); } break; case Opcode.LOADK: // LOADK A Bx R(A) := Kst(Bx) // Loads constant number Bx into register R(A). Constants are usually // numbers or strings. Each function has its own constant list, or pool. tmp = new LuaChunk() { ConstantValue = Operation.GetArgument(line) }; mRegisters[currentRegister] = tmp; break; case Opcode.LOADNIL: // LOADNIL A B R(A) := ... := R(B) := nil // Sets a range of registers from R(A) to R(B) to nil. // If a single register is to be assigned to, then R(A) = R(B). // When two or more consecutive locals need to be assigned nil values, // only a single LOADNIL is needed for (j = currentRegister; j <= vmArgs[0]; j++) { mRegisters[j] = new LuaChunk() { ConstantValue = "nil" } } ; break; case Opcode.TAILCALL: case Opcode.CALL: // CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) // Performs a function call, with register R(A) holding the reference to the // function object to be called. Parameters to the function are placed in the // registers following R(A). If B is 1, the function has no parameters. // If B is 2 or more, there are (B-1) parameters. If C is 1, no return results are saved. // If C is 2 or more, (C-1) return values are saved. If C is 0, then multiple return results are saved, // depending on the called function. int numArgs = vmArgs[0] - 1; int reg = currentRegister; if (currentRegisterValue.Self != null) // For object-oriented programming using tables. Retrieves a function { // reference from a table element and places it in register R(A), then a reg++; // reference to the table itself is placed in the next register, R(A+1). numArgs--; } for (j = 0; j < numArgs; j++) { currentRegisterValue.AddChunk(PluckRegister(reg + 1 + j)); } // when 'B' argument == 0, that means multiple parameters; the function takes C-1 parameters if (vmArgs[0] == 0) { j = currentRegister + 1; while (mRegisters[j] != null) //need to check register before calling 'PluckRegister' because of locals backup. { currentRegisterValue.AddChunk(PluckRegister(j)); j++; } } currentRegisterValue.SetCall(); if (currentRegisterValue.ParentChunk == null) { scopeChunk.AddChunk(currentRegisterValue); } break; case Opcode.SETGLOBAL: // SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A) // Copies the value from register R(A) into the global variable whose name is given in constant number Bx. currentRegisterValue.AddAssignmentLvalue(Operation.GetName(line)); if (currentRegisterValue.ParentChunk == null) { scopeChunk.AddChunk(currentRegisterValue); } break; // GETUPVAL - Still buggy. debug with 'bes2g_bf1' case Opcode.GETUPVAL: // GETUPVAL A B R(A) := UpValue[B] // Copies the value in upvalue number B into register R(A). Each function may have its own upvalue list. // The opcode for GETUPVAL has a second purpose – it is also used in creating closures, always appearing // after the CLOSURE instruction; see CLOSURE for more information. // upvalue is a local variable defined in a closure parent's scope mRegisters[currentRegister] = upValues[vmArgs[0]].Clone(); break; case Opcode.LT: // Compares RK(B) and RK(C), which may be registers or constants. If the case Opcode.LE: // boolean result is not A, then skip the next instruction. Conversely, if the case Opcode.EQ: // boolean result equals A, continue with the next instruction. IfStatement if_stmt = new IfStatement(); if_stmt.Expression = new LuaExpression() { Operation = code, LValue = GetExpressionArgument(line, 1), RValue = GetExpressionArgument(line, 2) }; int currentLineNumber = GetLineNumber(lines[i]); // get the current line number int difference = i - currentLineNumber; int startLineNumber = JumpToLineNumber(lines[i + 1]); // get the jump to line number int startLinesIndex = startLineNumber + difference; if (Operation.GetOpcode(lines[startLinesIndex - 1]) == Opcode.JMP) // if / else case { if_stmt.ThenChunk = GatherThenChunk(lines, i + 2, upValues); if_stmt.ElseChunk = GatherElseChunk(lines, i + 1, upValues); i = if_stmt.ElseChunk.LastLine - 1; // is this always correct? } else { if_stmt.ThenChunk = GatherLinesChunk(lines, i + 2, startLinesIndex + difference - 1, upValues); i = if_stmt.ThenChunk.LastLine - 1; // is this always correct? } scopeChunk.AddChunk(if_stmt); break; case Opcode.ADD: // Binary operators (arithmetic operators with two inputs.) case Opcode.SUB: // The result of the operation between RK(B) and RK(C) is placed into R(A). case Opcode.MUL: // These instructions are in the classic 3-register style. RK(B) and RK(C) case Opcode.DIV: // may be either registers or constants in the constant pool. case Opcode.POW: // R(A) := RK(B) + RK(C) List <string> mathArgs = Operation.GetArguments(line); if (mathArgs.Count == 2) { tmpExpr = new LuaExpression() { Operation = code, LValue = GetExpressionArgument(line, 1), RValue = GetExpressionArgument(line, 2) }; mRegisters[currentRegister] = tmpExpr; } else if (mathArgs.Count == 1) { LuaExpression newExpr = new LuaExpression() { Operation = code }; newExpr.LValue = new LuaChunk() { ConstantValue = mathArgs[0] }; newExpr.RValue = mRegisters[vmArgs[1]]; mRegisters[currentRegister] = newExpr; } else // operate on registers { tmpExpr = new LuaExpression() { Operation = code }; tmpExpr.LValue = mRegisters[vmArgs[0]]; tmpExpr.RValue = mRegisters[vmArgs[1]]; mRegisters[currentRegister] = tmpExpr; } break; //case Opcode.TEST: // // TEST A B C if (R(B) != C) then R(A) := R(B) else PC++ // // Used to implement and and or logical operators, or for testing a single // // register in a conditional statement. // // For TEST, register R(B) is coerced into a boolean and compared to // // the boolean field C. If R(B) matches C, the next instruction is skipped, // // otherwise R(B) is assigned to R(A) and the VM continues with the next // // instruction. The 'and' operator uses a C of 0 (false) while 'or' uses a C value of 1 (true). // tmpExpr = new LuaExpression(); // tmpExpr.Operation = vmArgs[1] == 0 ? Opcode.AND : Opcode.OR; // tmpExpr.LValue = PluckRegister(vmArgs[0]); // // for RValue, skip the jmp // tmpExpr.RValue = GetTestRValue(lines[i + 2]); // mRegisters[currentRegister] = tmpExpr; // i += 2; // break; //case Opcode.TEST: // if_stmt = new IfStatement(); // if_stmt.Expression = // tmpExpr = new LuaExpression(); // tmpExpr.Operation = vmArgs[1] == 0 ? Opcode.AND : Opcode.OR; // tmpExpr.LValue = PluckRegister(vmArgs[0]); // // for RValue, skip the jmp // tmpExpr.RValue = GetTestRValue(lines[i + 2]); // mRegisters[currentRegister] = tmpExpr; // i += 2; // break; case Opcode.CONCAT: // CONCAT A B C R(A) := R(B).. ... ..R(C) // Performs concatenation of two or more strings. In a Lua source, this is equivalent to one or more concatenation // operators (‘..’) between two or more expressions. The source registers must be consecutive, and C must always be // greater than B. The result is placed in R(A). LuaConcat lc = new LuaConcat(); for (j = vmArgs[0]; j <= vmArgs[1]; j++) { lc.AddChunk(PluckRegister(j).Clone()); } mRegisters[currentRegister] = lc; break; case Opcode.SELF: // For object-oriented programming using tables. Retrieves a function reference from a // table element and places it in register R(A), then a reference to the table itself // is placed in the next register, R(A+1). This instruction saves some messy manipulation // when setting up a method call currentRegisterValue.Self = Operation.GetArgument(line).Replace("\"", ""); break; case Opcode.GETTABLE: tmp = currentRegisterValue != null ? currentRegisterValue : mLocalVarBackup[currentRegister]; tmp.GetTable = Operation.GetArgument(line).Replace("\"", ""); break; case Opcode.SETLIST: // 46 [-] SETLIST 0 7 ==> table in '0' gets the next 7 register values currentTable = currentRegisterValue as LuaTable; currentTable.ListMode = true; for (j = currentRegister + 1; j <= currentRegister + 1 + vmArgs[0]; j++) // this math correct??? { currentTable.AddValue(PluckRegister(j)); } break; case Opcode.NEWTABLE: // 6 [-] NEWTABLE 0 3 0 ;-- Creates a table (register 0) that will be used as a list to hold 3 items. // 7 [-] NEWTABLE 1 0 3 ;-- Creates a table (register 1) that will be used to hold 3 table entries. currentTable = new LuaTable(vmArgs[0] > 0); // 'vmArgs[0] > 0' will be true for lists mRegisters[currentRegister] = currentTable; break; case Opcode.SETTABLE: // SETTABLE A B C R(A)[RK(B)] := RK(C) // Copies the value from register R(C) or a constant into a table element. The // table is referenced by register R(A), while the index to the table is given by // RK(B), which may be the value of register R(B) or a constant. // // 12 [-] SETTABLE 2 136 3 ; "soldier" ==> "soldier" is the key, content of register '3' is the value List <string> args = Operation.GetArguments(line); tmpTable = currentRegisterValue as LuaTable; if (currentRegisterValue != null && tmpTable == null) { tmpTable = new LuaTable(false) { GetTable = currentRegisterValue.GetTable, GlobalName = currentRegisterValue.GlobalName, Self = currentRegisterValue.Self }; if (currentRegisterValue.ParentChunk != null) { currentRegisterValue.ParentChunk.ReplaceChunk(currentRegisterValue, tmpTable); } mRegisters[currentRegister] = tmpTable; } if (args.Count == 2) { tmpTable.AddEntry(args[0], new LuaChunk() { ConstantValue = args[1] }); } else if (args.Count == 1) { tmpTable.AddEntry(args[0], PluckRegister(vmArgs[1])); } break; case Opcode.FUNCTION_DEF: func = this.GetFunction(line); if (func == null) { Program.ReportError("Function Lookup failed for:", "lookup failed", line); } GatherFunctionChunk(func, lines, i); i = func.LastLine; if (func.ParentChunk == null) // could be a table value { scopeChunk.AddChunk(func); } else if (func.ParentChunk.LuaType == LuaType.TABLE) { func.AddAssignmentLvalue(func.ParentChunk.GlobalName + "." + func.Name); scopeChunk.AddChunk(func); } break; case Opcode.MAIN_DEF: case Opcode.PARAMS: break; case Opcode.CLOSURE: LuaFunction func2 = new LuaFunction(); mRegisters[currentRegister] = func2; func2.ClosureNumber = Operation.GetClosureNumber(line); i++; Opcode cCode = Opcode.NONE; while ((cCode = Operation.GetOpcode(lines[i])) == Opcode.MOVE || cCode == Opcode.GETUPVAL) { j = Operation.GetVMArgs(lines[i])[0]; // register to use for passing upvalue if (cCode == Opcode.MOVE) { tmp = mRegisters[j]; if (tmp.LocalName == null) { tmp.LocalName = "local_" + j; tmp.AddAssignmentLvalue("local " + tmp.LocalName); scopeChunk.AddChunk(tmp); } } else if (cCode == Opcode.GETUPVAL) { tmp = upValues[j]; } func2.UpValues.Add(tmp.Clone()); i++; } // current line should be 'SETGLOBAL' or 'SETTABLE' func2.Name = Operation.GetName(lines[i]); if (func2.Name != null) { func2.Name = func2.Name.Replace("\"", ""); } Closures.Add(func2); if (cCode != Opcode.MOVE && cCode != Opcode.GETUPVAL) { i--; } break; case Opcode.RETURN: if (vmArgs[0] > 1) { LuaReturn retStmt = new LuaReturn() { ReturnValue = currentRegisterValue }; scopeChunk.ReplaceChunk(currentRegisterValue, retStmt); //scopeChunk.AddChunk(retStmt); } break; case Opcode.NONE: break; default: Program.ReportError("", String.Format("OpCode {0} NOT IMPLEMENTED ", code), line); break; } prevOp = code; } }