public static CompiledMethod compile(string str) { CompiledMethod compiled = null; Evaluator.Compile(str, out compiled); return(compiled); }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> static public string Compile(string input, out CompiledMethod compiled) { if (input == null || input.Length == 0) { compiled = null; return(null); } lock (evaluator_lock) { if (!inited) { Init(); } else { ctx.Report.Printer.Reset(); } // RootContext.ToplevelTypes = new ModuleContainer (ctx); bool partial_input; CSharpParser parser = ParseString(ParseMode.Silent, input, out partial_input); if (parser == null) { compiled = null; if (partial_input) { return(input); } ParseString(ParseMode.ReportErrors, input, out partial_input); return(null); } object parser_result = parser.InteractiveResult; if (!(parser_result is Class)) { int errors = ctx.Report.Errors; NamespaceEntry.VerifyAllUsing(); if (errors == ctx.Report.Errors) { parser.CurrentNamespace.Extract(using_alias_list, using_list); } else { NamespaceEntry.Reset(); } } #if STATIC throw new NotSupportedException(); #else compiled = CompileBlock(parser_result as Class, parser.undo, ctx.Report); return(null); #endif } }
/// <summary> /// Look-up a method implementation starting with the given class. /// </summary> /// <param name="cls">Class where to start searching for the method (unless superLookupScope) is set.</param> /// <param name="superLookupScope">If set, start the lookup from the superclass of this class.</param> /// <param name="lookupFunction">Function to perform the method lookup.</param> /// <returns>Returns the compiled method for the given selector or null if none was found.</returns> public static CompiledMethod LookupMethod(ref SmalltalkClass cls, Symbol superLookupScope, Func <SmalltalkClass, CompiledMethod> lookupFunction) { if (lookupFunction == null) { throw new ArgumentNullException("lookupFunction"); } while (cls != null) { if (superLookupScope == null) { CompiledMethod method = lookupFunction(cls); if (method != null) { return(method); } } else { if (cls.Name == superLookupScope) { superLookupScope = null; } } cls = cls.Superclass; } // No method ... no luck; return(null); }
/// <summary> /// Creates a new entity that was embedded next in the stream /// </summary> /// <param name="stream">The System.IO.BinaryReader to use.</param> public Entity(BinaryReader stream) { X = stream.ReadInt16(); Y = stream.ReadInt16(); Layer = stream.ReadInt16(); Scripts = new List<string>(); Type = (EntityType)stream.ReadInt16(); stream.ReadBytes(8); short len; if (Type == EntityType.Person) { len = stream.ReadInt16(); Name = new string(stream.ReadChars(len)); len = stream.ReadInt16(); Spriteset = new string(stream.ReadChars(len)); int scripts = stream.ReadInt16(); // read the person script data for (int i = 0; i < scripts; ++i) { len = stream.ReadInt16(); Scripts.Add(new string(stream.ReadChars(len))); } stream.ReadBytes(16); // reserved } else { len = stream.ReadInt16(); Function = new string(stream.ReadChars(len)); Trigger = new CompiledMethod(Program._engine, Function); } }
/// <summary> /// If <paramref name="method"/> matches the pattern, the method will be disassembled /// to the output writer. /// </summary> /// <param name="method">The method IR.</param> public void DumpMethod(CompiledMethod method) { if (!ShouldLog(method.FullName)) { return; } Debug.Assert(Writer is object); // Write the full name as a comment Writer.Write("; "); Writer.WriteLine(method.FullName); // Disassemble the method if (method.Body is null) { Writer.WriteLine("; (Method has no body)"); Writer.WriteLine(); } else { var builder = new StringBuilder(); MethodDisassembler.Disassemble(method, builder); Writer.Write(builder); } Writer.WriteLine(); }
public void Bool_non_constant_unary_expression_compiled_successfully(string expressionString, Opcode expectedOp) { var expressionSyntax = ParseExpression(expressionString); var method = new CompiledMethod("Test::Method"); var builder = new BasicBlockGraphBuilder().GetInitialBlockBuilder(); var diagnostics = new TestingDiagnosticSink(); var variableMap = new ScopedVariableMap(); variableMap.PushScope(); variableMap.TryAddVariable("a", method.AddLocal(SimpleType.Bool, LocalFlags.None)); var localIndex = ExpressionCompiler.TryCompileExpression(expressionSyntax, SimpleType.Bool, method, builder, new TestingResolver(variableMap), diagnostics); Assert.That(diagnostics.Diagnostics, Is.Empty); Assert.That(localIndex, Is.EqualTo(1)); Assert.That(method.Values[localIndex].Type, Is.EqualTo(SimpleType.Bool)); Assert.That(builder.Instructions, Has.Exactly(1).Items); var instruction = builder.Instructions[0]; Assert.That(instruction.Operation, Is.EqualTo(expectedOp)); Assert.That(instruction.Left, Is.EqualTo(0)); Assert.That(instruction.Destination, Is.EqualTo(1)); }
public static LowMethod <X64Register> Lower(CompiledMethod highMethod) { Debug.Assert(highMethod.Body != null); Debug.Assert(highMethod.Body.BasicBlocks.Count > 0); var lowMethod = new LowMethod <X64Register>(); // Create locals for SSA values // Additional locals may be created by instructions var paramCount = 0; foreach (var value in highMethod.Values) { if (value.Flags.HasFlag(LocalFlags.Parameter)) { paramCount++; } lowMethod.Locals.Add(new LowLocal <X64Register>(value.Type)); } // Convert each basic block var methodHasCalls = false; for (var i = 0; i < highMethod.Body.BasicBlocks.Count; i++) { var highBlock = highMethod.Body.BasicBlocks[i]; lowMethod.Blocks.Add(ConvertBlock(highBlock, highMethod, lowMethod, i == 0, paramCount, out var blockHasCalls)); methodHasCalls |= blockHasCalls; } lowMethod.IsLeafMethod = !methodHasCalls; return(lowMethod); }
public void DisassembleBody_three_basic_blocks_with_branch_2() { var graphBuilder = new BasicBlockGraphBuilder(); var firstBuilder = graphBuilder.GetInitialBlockBuilder(); // In this test case, the return succeeds the initial block... var returnBuilder = firstBuilder.CreateBranch(1); returnBuilder.AppendInstruction(Opcode.Return, 0, 0, 0); var loopBuilder = firstBuilder.CreateSuccessorBlock(); loopBuilder.AppendInstruction(Opcode.Nop, 0, 0, 0); loopBuilder.SetSuccessor(0); var method = new CompiledMethod("Test::Method") { Body = graphBuilder.Build() }; // ...and both successors are displayed. const string expected = "BB_0:\n BranchIf #1 ==> BB_1\n ==> BB_2\n\n" + "BB_1:\n Return #0\n\n" + "BB_2:\n Nop\n ==> BB_0\n\n"; var builder = new StringBuilder(); MethodDisassembler.DisassembleBody(method, builder); Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected)); }
public void DisassembleBody_single_basic_block_with_several_instructions() { // TODO: Add new instructions to this test var graphBuilder = new BasicBlockGraphBuilder(); var blockBuilder = graphBuilder.GetInitialBlockBuilder(); blockBuilder.AppendInstruction(Opcode.Load, unchecked ((ulong)-17), 0, 0); blockBuilder.AppendInstruction(Opcode.Load, 1, 0, 1); blockBuilder.AppendInstruction(Opcode.CopyValue, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Add, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Subtract, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Multiply, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Divide, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Modulo, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.ArithmeticNegate, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.ShiftLeft, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.ShiftRight, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.BitwiseAnd, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.BitwiseNot, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.BitwiseOr, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.BitwiseXor, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Less, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.LessOrEqual, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Equal, 1, 0, 2); blockBuilder.AppendInstruction(Opcode.Return, 2, 0, 0); var method = new CompiledMethod("Test::Method") { Body = graphBuilder.Build() }; method.AddLocal(SimpleType.Int32, LocalFlags.None); method.AddLocal(SimpleType.Bool, LocalFlags.None); const string expected = "BB_0:\n" + " Load -17 -> #0\n" + " Load true -> #1\n" + " CopyValue #1 -> #2\n" + " Add #1 + #0 -> #2\n" + " Subtract #1 - #0 -> #2\n" + " Multiply #1 * #0 -> #2\n" + " Divide #1 / #0 -> #2\n" + " Modulo #1 % #0 -> #2\n" + " ArithmeticNegate #1 -> #2\n" + " ShiftLeft #1 << #0 -> #2\n" + " ShiftRight #1 >> #0 -> #2\n" + " BitwiseAnd #1 & #0 -> #2\n" + " BitwiseNot #1 -> #2\n" + " BitwiseOr #1 | #0 -> #2\n" + " BitwiseXor #1 ^ #0 -> #2\n" + " Less #1 < #0 -> #2\n" + " LessOrEqual #1 <= #0 -> #2\n" + " Equal #1 == #0 -> #2\n" + " Return #2\n\n"; var builder = new StringBuilder(); MethodDisassembler.DisassembleBody(method, builder); Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected)); }
/// <summary> /// Loads the script from the specified text. /// </summary> /// <param name="text">The code text.</param> /// <param name="scriptName">The script name.</param> public bool LoadScript(string text, string scriptName) { evaluator = new Evaluator(new CompilerContext(new CompilerSettings(), CompilationResult)); foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { { evaluator.ReferenceAssembly(assembly); } } var resAssembly = GetCompiledAssembly(scriptName); if (resAssembly == null) { CompiledMethod cm = evaluator.Compile(text + GetConnectionPointClassDeclaration(scriptName)); resAssembly = GetCompiledAssembly(scriptName); } if (resAssembly == null) { if (CompilationResult.HasErrors) { Debug.ULogErrorChannel( "CSharp", string.Format("[{0}] CSharp compile errors ({1}): {2}", scriptName, CompilationResult.Errors.Count, CompilationResult.GetErrorsLog())); } return(false); } CreateDelegates(resAssembly); return(true); }
private void PrintMethod(CompiledMethod cmethod, ITextOutput output, DecompilationOptions options) { if ((cmethod != null) && (cmethod.RLBody != null)) { var body = cmethod.RLBody; var basicBlocks = BasicBlock.Find(body); foreach (var block in basicBlocks) { output.Write(string.Format("D_{0:X4}:", block.Entry.Index)); output.WriteLine(); output.Indent(); foreach (var ins in block.Instructions) { if (ShowHasSeqPoint) { if (ins.SequencePoint != null) { output.Write(ins.SequencePoint.IsSpecial ? "!" : "~"); } } output.Write(ins.ToString()); output.WriteLine(); } output.Unindent(); } if (body.Exceptions.Any()) { output.WriteLine(); output.Write("Exception handlers:"); output.WriteLine(); output.Indent(); foreach (var handler in body.Exceptions) { output.Write(string.Format("{0:x4}-{1:x4}", handler.TryStart.Index, handler.TryEnd.Index)); output.WriteLine(); output.Indent(); foreach (var c in handler.Catches) { output.Write(string.Format("{0} => {1:x4}", c.Type, c.Instruction.Index)); output.WriteLine(); } if (handler.CatchAll != null) { output.Write(string.Format("{0} => {1:x4}", "<any>", handler.CatchAll.Index)); output.WriteLine(); } output.Unindent(); } output.Unindent(); } } else { output.Write("Method not found in dex"); output.WriteLine(); } }
/// <summary> /// Safe version of /// <see cref="Evaluator.Compile(string)"/> /// </summary> /// <param name="input"></param> /// <param name="result"></param> /// <param name="timeout"></param> /// <returns></returns> public bool TryCompile(string input, out CompiledMethod result, int timeout) { return(SafeEvaluate( () => Evaluator.Compile(input), timeout, out result)); }
/// <summary> /// Verifies that the method has disassembly equal to <paramref name="expected"/>. /// Both the actual and expected strings are trimmed and linefeed normalized. /// </summary> protected void AssertDisassembly(CompiledMethod compiledMethod, string expected) { var builder = new StringBuilder(); MethodDisassembler.Disassemble(compiledMethod, builder); Assert.That(builder.ToString().Trim().Replace("\r\n", "\n"), Is.EqualTo(expected.Trim().Replace("\r\n", "\n"))); }
public void TestCompiled(int milliseconds) { CompilerSettings cts = new CompilerSettings(); cts.Unsafe = true; CompilerContext ctx = new CompilerContext(cts, new ConsoleReportPrinter()); Evaluator ev = new Evaluator(ctx); // ------------------------------------------------------------------------------------------------------------ // A compiled expression will be simple sealed as a function. // THis method is parameterless. // ------------------------------------------------------------------------------------------------------------ Stopwatch st = new Stopwatch(); st.Reset(); st.Start(); const string evClassDef = @" public class EVTest { public int Add(int a, int b) { return a + b; } } "; const string evExpression = @"new EVTest();"; // Notice this is not C# itself, it is an REPL style C#. // This command will "import" this class to ev's execution context. ev.Run(evClassDef); // Looks like holding the returning object... CompiledMethod res = ev.Compile(evExpression); // This will obviously not cause memory leak... while (st.ElapsedMilliseconds <= milliseconds) { object evtest = null; res.Invoke(ref evtest); int ans = (int)evtest.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, evtest, new object[] { 1, 2 }); Debug.Assert(ans == 1 + 2); } // 64MB memory limitation. const int bytesLimit = 1 << 26; using (var proc = Process.GetCurrentProcess()) { Debug.Assert(Process.GetCurrentProcess().PrivateMemorySize64 <= bytesLimit); } Console.WriteLine("Test.Compile Finished."); }
public void DumpMethod_does_not_dump_non_matching_method() { var writer = new StringWriter(); var logger = new DebugLogger(writer, "Namespace::"); var method = new CompiledMethod("other::method"); logger.DumpMethod(method); Assert.That(writer.ToString(), Is.Empty); }
private static bool TryCompileCall(FunctionCallSyntax call, CompiledMethod method, BasicBlockBuilder builder, IDiagnosticSink diagnostics, INameResolver nameResolver, out Temporary value) { value = default; // Get the callee var matchingMethods = nameResolver.ResolveMethod(call.Function.Name); if (matchingMethods.Count == 0) { diagnostics.Add(DiagnosticCode.MethodNotFound, call.Function.Position, call.Function.Name); return(false); } else if (matchingMethods.Count > 1) { // TODO: Test this case throw new NotImplementedException("Multiple matching methods"); } var declaration = matchingMethods[0]; // Assert that there is the right number of parameters if (call.Parameters.Count != declaration.ParameterTypes.Count) { diagnostics.Add(DiagnosticCode.ParameterCountMismatch, call.Position, actual: call.Parameters.Count.ToString(), expected: declaration.ParameterTypes.Count.ToString()); return(false); } // Evaluate the parameters, verifying their types var parameterIndices = new int[declaration.ParameterTypes.Count]; for (var i = 0; i < parameterIndices.Length; i++) { var paramIndex = TryCompileExpression(call.Parameters[i], declaration.ParameterTypes[i], method, builder, nameResolver, diagnostics); // If the compilation of the expression failed for some reason, the diagnostic is already logged if (paramIndex == -1) { return(false); } parameterIndices[i] = paramIndex; } // Emit a call operation var callType = declaration is ImportedMethodDeclaration ? MethodCallType.Imported : MethodCallType.Native; var callInfoIndex = method.AddCallInfo(declaration.BodyIndex, parameterIndices, declaration.FullName, callType); var resultIndex = method.AddLocal(declaration.ReturnType, LocalFlags.None); builder.AppendInstruction(Opcode.Call, callInfoIndex, 0, resultIndex); value = Temporary.FromLocal(declaration.ReturnType, resultIndex); return(true); }
public void DumpMethod_dumps_matching_method_without_body() { var writer = new StringWriter(); var logger = new DebugLogger(writer, "*"); var method = new CompiledMethod("Namespace::Method"); logger.DumpMethod(method); Assert.That(writer.ToString(), Does.Contain("; Namespace::Method")); Assert.That(writer.ToString(), Does.Contain("; (Method has no body)")); }
/// <summary> /// Get a compiled method info for the given method. /// Create if needed. /// </summary> internal CompiledMethod GetOrCreateCompileMethod(XMethodDefinition method) { CompiledMethod result; if (!xMethodMap.TryGetValue(method, out result)) { result = new CompiledMethod(method); compiledMethods.Add(result); xMethodMap.Add(method, result); } return(result); }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> public string Compile(string input, out CompiledMethod compiled) { if (input == null || input.Length == 0) { compiled = null; return(null); } lock (evaluator_lock){ if (!inited) { Init(); ParseStartupFiles(); } else { ctx.Report.Printer.Reset(); } bool partial_input; CSharpParser parser = ParseString(ParseMode.Silent, input, out partial_input); // Terse mode, try to provide the trailing semicolon automatically. if (parser == null && Terse && partial_input) { bool ignore; // check if the source would compile with a block, if so, we should not // add the semicolon. var needs_block = ParseString(ParseMode.Silent, input + "{}", out ignore) != null; if (!needs_block) { parser = ParseString(ParseMode.Silent, input + ";", out ignore); } } if (parser == null) { compiled = null; if (partial_input) { return(input); } ParseString(ParseMode.ReportErrors, input, out partial_input); return(null); } Class parser_result = parser.InteractiveResult; compiled = CompileBlock(parser_result, parser.undo, ctx.Report); return(null); } }
/// <summary> /// Writes the type information and disassembled basic blocks of the method to the output string builder. /// </summary> public static void Disassemble(CompiledMethod method, StringBuilder outputBuilder) { // Write the types and initial values of locals for (var i = 0; i < method.Values.Count; i++) { var local = method.Values[i]; var flags = local.Flags.HasFlag(LocalFlags.Parameter) ? " param" : string.Empty; outputBuilder.AppendLine($"; #{i,-3} {local.Type.TypeName}{flags}"); } // Then write the basic block graph DisassembleBody(method, outputBuilder); }
public void CreateLocal_succeeds_for_int32() { var method = new CompiledMethod("Test::Method"); Assert.That(method.AddLocal(SimpleType.Int32, LocalFlags.None), Is.EqualTo(0)); Assert.That(method.AddLocal(SimpleType.Int32, LocalFlags.Parameter), Is.EqualTo(1)); Assert.That(method.Values, Has.Exactly(2).Items); Assert.That(method.Values[0].Type, Is.EqualTo(SimpleType.Int32)); Assert.That(method.Values[0].Flags, Is.EqualTo(LocalFlags.None)); Assert.That(method.Values[1].Type, Is.EqualTo(SimpleType.Int32)); Assert.That(method.Values[1].Flags, Is.EqualTo(LocalFlags.Parameter)); }
protected override bool InternalAddMethod(IDefinitionInstallerContext installer, SmalltalkClass cls) { CompiledMethod method = this.Factory.CreateMethod(this, installer, cls, CompiledMethod.MethodType.Instance); if (method == null) { return(false); } System.Diagnostics.Debug.Assert(this.Selector.Value == method.Selector.Value); cls.InstanceBehavior[method.Selector] = method; this.CompiledCode = method; return(true); }
private static LowBlock ConvertBlock(BasicBlock highBlock, CompiledMethod highMethod, LowMethod <X64Register> methodInProgress, bool isFirstBlock, int paramCount, out bool containsCalls) { var lowBlock = new LowBlock { Phis = highBlock.Phis, Predecessors = highBlock.Predecessors }; // Initialize the list of successors if (highBlock.AlternativeSuccessor >= 0) { lowBlock.Successors = new[] { highBlock.AlternativeSuccessor, highBlock.DefaultSuccessor }; } else if (highBlock.DefaultSuccessor >= 0) { lowBlock.Successors = new[] { highBlock.DefaultSuccessor }; } else { lowBlock.Successors = Array.Empty <int>(); } // At the start of the first block, we must copy parameters from fixed-location temps to freely assigned locals if (isFirstBlock) { // This assumes that the first paramCount locals are the parameters for (var i = 0; i < paramCount; i++) { methodInProgress.Locals.Add( new LowLocal <X64Register>(highMethod.Values[i].Type, GetLocationForParameter(i))); var tempIndex = methodInProgress.Locals.Count - 1; lowBlock.Instructions.Add(new LowInstruction(LowOp.Move, i, tempIndex, 0, 0)); } } // Convert the instructions containsCalls = false; var returns = false; ConvertInstructions(highBlock, highMethod, lowBlock, methodInProgress, ref containsCalls, ref returns); if (!returns) { lowBlock.Instructions.Add(new LowInstruction(LowOp.Jump, highBlock.DefaultSuccessor, 0, 0, 0)); } return(lowBlock); }
private static void FallbackFormatting(ITextOutput output, CompiledMethod cmethod) { var body = cmethod.DexMethod.Body; body.UpdateInstructionOffsets(); var targetInstructions = body.Instructions.Select(x => x.Operand).OfType <Instruction>().ToList(); targetInstructions.AddRange(body.Exceptions.Select(x => x.TryStart)); targetInstructions.AddRange(body.Exceptions.Select(x => x.TryEnd)); targetInstructions.AddRange(body.Exceptions.SelectMany(x => x.Catches, (h, y) => y.Instruction)); targetInstructions.AddRange(body.Exceptions.Select(x => x.CatchAll)); foreach (var ins in body.Instructions) { if (targetInstructions.Contains(ins) || (ins.Offset == 0)) { output.Write(string.Format("D_{0:X4}:", ins.Offset)); output.WriteLine(); } output.Indent(); output.Write(ins.ToString()); output.WriteLine(); output.Unindent(); } if (body.Exceptions.Any()) { output.WriteLine(); output.Write("Exception handlers:"); output.WriteLine(); output.Indent(); foreach (var handler in body.Exceptions) { output.Write(string.Format("{0:x4}-{1:x4}", handler.TryStart.Offset, handler.TryEnd.Offset)); output.WriteLine(); output.Indent(); foreach (var c in handler.Catches) { output.Write(string.Format("{0} => {1:x4}", c.Type, c.Instruction.Offset)); output.WriteLine(); } if (handler.CatchAll != null) { output.Write(string.Format("{0} => {1:x4}", "<any>", handler.CatchAll.Offset)); output.WriteLine(); } output.Unindent(); } output.Unindent(); } }
/// <summary> /// Emits native code for the given method. /// </summary> /// <param name="method">A compiled method in SSA form.</param> /// <param name="methodIndex">The compiler internal index for the method.</param> /// <param name="isEntryPoint">If true, this method is marked as the executable entry point.</param> /// <param name="dumpWriter">An optional text writer for debug dumping of the current method.</param> public void EmitMethod(CompiledMethod method, int methodIndex, bool isEntryPoint, TextWriter?dumpWriter) { _fixupsForMethod.Clear(); _blockPositions.Clear(); _savedRegisters.Clear(); _peWriter.StartNewMethod(methodIndex, method.FullName); if (isEntryPoint) { // TODO: Emit a compiler-generated entry point that calls the user-defined entry point _peWriter.MarkEntryPoint(); } // Lower the IR to a low-level form var loweredMethod = LoweringX64.Lower(method); // TODO: Does the LIR need some other optimization (block merging, etc.) before peephole? // Perform peephole optimization PeepholeOptimizer <X64Register> .Optimize(loweredMethod); // Debug log the lowering if (dumpWriter is object) { DebugLogBeforeAllocation(loweredMethod, method.FullName, dumpWriter); } // Allocate registers for locals (with special casing for parameters) var(allocatedMethod, allocationInfo) = X64RegisterAllocator.Allocate(loweredMethod); DetermineRegistersToSave(allocationInfo); if (dumpWriter is object) { DebugLogAfterAllocation(allocatedMethod, allocationInfo, dumpWriter); } // Emit the lowered IR for (var i = 0; i < allocatedMethod.Blocks.Count; i++) { _peWriter.Emitter.StartBlock(i, out var position); _blockPositions.Add(position); EmitBlock(i, allocatedMethod, allocationInfo, method); } // Apply fixups foreach (var fixup in _fixupsForMethod) { _peWriter.Emitter.ApplyFixup(fixup, _blockPositions[fixup.Tag]); } }
private static void ParseLocal(string line, CompiledMethod method) { var lineParts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); // Get the local index by removing the preceding #, and validate it var localIndex = int.Parse(lineParts[1].AsSpan(1)); Assert.That(localIndex, Is.EqualTo(method.Values.Count), "Local indices must be specified in order."); // Get the type and initial value var type = ResolveType(lineParts[2]); var isParam = lineParts.Length >= 4 && lineParts[3] == "param"; method.AddLocal(type, isParam ? LocalFlags.Parameter : LocalFlags.None); }
public void Constant_comparison_compiled_successfully(string expressionString, bool expectedValue) { var expressionSyntax = ParseExpression(expressionString); var method = new CompiledMethod("Test::Method"); var builder = new BasicBlockGraphBuilder().GetInitialBlockBuilder(); var diagnostics = new TestingDiagnosticSink(); var nameResolver = new TestingResolver(new ScopedVariableMap()); var localIndex = ExpressionCompiler.TryCompileExpression(expressionSyntax, SimpleType.Bool, method, builder, nameResolver, diagnostics); Assert.That(diagnostics.Diagnostics, Is.Empty); Assert.That(localIndex, Is.EqualTo(0)); Assert.That(method.Values[localIndex].Type, Is.EqualTo(SimpleType.Bool)); AssertSingleLoad(builder, localIndex, expectedValue); }
public void Integer_literal_that_is_too_large_fails() { var position = new TextPosition(10, 3, 4); var syntax = new IntegerLiteralSyntax((ulong)int.MaxValue + 1, position); var method = new CompiledMethod("Test::Method"); var builder = new BasicBlockGraphBuilder().GetInitialBlockBuilder(); var nameResolver = new TestingResolver(new ScopedVariableMap()); var diagnostics = new TestingDiagnosticSink(); var localIndex = ExpressionCompiler.TryCompileExpression( syntax, SimpleType.Int32, method, builder, nameResolver, diagnostics); Assert.That(localIndex, Is.EqualTo(-1)); diagnostics.AssertDiagnosticAt(DiagnosticCode.TypeMismatch, position) .WithActual("uint32").WithExpected("int32"); }
public HybInstance Invoke(HybInstance _this, HybInstance[] args, bool hasRefOrOut = false) { if (IsCompiled) { Console.WriteLine($"Invoke {CompiledMethod.Name}"); } else { Console.WriteLine($"Invoke "); } if (Type == InvokeType.ReflectionInvoke) { if (CompiledMethod.GetParameters().Length > args.Length) { args = ExpandArgs(args, CompiledMethod); } var unwrappedArgs = args.Unwrap(); var ret = CompiledMethod.Invoke(_this?.InnerObject, unwrappedArgs); if (hasRefOrOut) { for (int i = 0; i < args.Length; i++) { args[i] = HybInstance.Object(unwrappedArgs[i]); } } return(HybInstance.Object(ret)); } else if (Type == InvokeType.FuncInvoke) { return(Runner.RunWrappedFunc(_this, FuncMethod, args)); } else if (Type == InvokeType.Interpret) { var ps = InterpretMethod.ParameterList.Parameters; //if (args.Length != ps.Count) //throw new SemanticViolationException($"Parameters.Count does not match"); return(Runner.RunMethod( _this as HybInstance, MethodInfo, args)); } throw new NotImplementedException($"Unknown type: {Type}"); }
public void DisassembleBody_single_basic_block_with_only_return() { var graphBuilder = new BasicBlockGraphBuilder(); graphBuilder.GetInitialBlockBuilder().AppendInstruction(Opcode.Return, 2, 0, 0); var method = new CompiledMethod("Test::Method") { Body = graphBuilder.Build() }; const string expected = "BB_0:\n Return #2\n\n"; var builder = new StringBuilder(); MethodDisassembler.DisassembleBody(method, builder); Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected)); }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> static public string Compile(string input, out CompiledMethod compiled) { if (input == null || input.Length == 0) { compiled = null; return(null); } lock (evaluator_lock){ if (!inited) { Init(); } bool partial_input; CSharpParser parser = ParseString(true, input, out partial_input); if (parser == null) { compiled = null; if (partial_input) { return(input); } ParseString(false, input, out partial_input); return(null); } object parser_result = parser.InteractiveResult; if (!(parser_result is Class)) { int errors = Report.Errors; NamespaceEntry.VerifyAllUsing(); if (errors == Report.Errors) { parser.CurrentNamespace.Extract(using_alias_list, using_list); } } compiled = CompileBlock(parser_result as Class, parser.undo); } return(null); }
/// <summary> /// Create a method body for the given method. /// </summary> internal static MethodBody TranslateToRL(AssemblyCompiler compiler, DexTargetPackage targetPackage, MethodSource source, MethodDefinition dmethod, bool generateSetNextInstructionCode, out CompiledMethod compiledMethod) { try { #if DEBUG //Debugger.Launch(); if ((source.Method != null) && (source.Method.Name == "test6")) { //Debugger.Launch(); } #endif // Create Ast var optimizedAst = CreateOptimizedAst(compiler, source, generateSetNextInstructionCode); // Generate RL code var rlBody = new MethodBody(source); var rlGenerator = new AstCompilerVisitor(compiler, source, targetPackage, dmethod, rlBody); optimizedAst.Accept(rlGenerator, null); rlGenerator.Complete(); // Should we add return_void? if (source.ReturnsVoid) { var instructions = rlBody.Instructions; if ((instructions.Count == 0) || (instructions.Last().Code != RCode.Return_void && instructions.Last().Code != RCode.Throw)) { instructions.Add(new RL.Instruction(RCode.Return_void) { SequencePoint = source.GetLastSourceLine() }); } } // Record results compiledMethod = targetPackage.Record(source, rlBody, rlGenerator.Frame); return rlBody; } catch (Exception ex) { // Forward exception with more information var msg = string.Format("Error while compiling {0} in {1}: {2}", source.FullName, source.DeclaringTypeFullName, ex.Message); throw new CompilerException(msg, ex); } }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> public string Compile (string input, out CompiledMethod compiled) { if (input == null || input.Length == 0){ compiled = null; return null; } lock (evaluator_lock){ if (!inited) { Init (); ParseStartupFiles (); } else { ctx.Report.Printer.Reset (); } bool partial_input; CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); if (parser == null && Terse && partial_input){ bool ignore; parser = ParseString (ParseMode.Silent, input + ";", out ignore); } if (parser == null){ compiled = null; if (partial_input) return input; ParseString (ParseMode.ReportErrors, input, out partial_input); return null; } Class parser_result = parser.InteractiveResult; compiled = CompileBlock (parser_result, parser.undo, ctx.Report); return null; } }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> public string Compile (string input, out CompiledMethod compiled) { if (input == null || input.Length == 0){ compiled = null; return null; } lock (evaluator_lock){ if (!inited) { Init (); ParseStartupFiles (); } else { ctx.Report.Printer.Reset (); } bool partial_input; CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); // Terse mode, try to provide the trailing semicolon automatically. if (parser == null && Terse && partial_input){ bool ignore; // check if the source would compile with a block, if so, we should not // add the semicolon. var needs_block = ParseString (ParseMode.Silent, input + "{}", out ignore) != null; if (!needs_block) parser = ParseString (ParseMode.Silent, input + ";", out ignore); } if (parser == null){ compiled = null; if (partial_input) return input; ParseString (ParseMode.ReportErrors, input, out partial_input); return null; } Class parser_result = parser.InteractiveResult; compiled = CompileBlock (parser_result, parser.undo, ctx.Report); return null; } }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> static public string Compile (string input, out CompiledMethod compiled) { if (input == null || input.Length == 0){ compiled = null; return null; } lock (evaluator_lock){ if (!inited) Init (); bool partial_input; CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); if (parser == null){ compiled = null; if (partial_input) return input; ParseString (ParseMode.ReportErrors, input, out partial_input); return null; } object parser_result = parser.InteractiveResult; if (!(parser_result is Class)){ int errors = ctx.Report.Errors; NamespaceEntry.VerifyAllUsing (); if (errors == ctx.Report.Errors) parser.CurrentNamespace.Extract (using_alias_list, using_list); } compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report); } return null; }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> static public string Compile (string input, out CompiledMethod compiled) { if (input == null || input.Length == 0){ compiled = null; return null; } lock (evaluator_lock){ if (!inited) Init (); else ctx.Report.Printer.Reset (); // RootContext.ToplevelTypes = new ModuleContainer (ctx); bool partial_input; CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); if (parser == null){ compiled = null; if (partial_input) return input; ParseString (ParseMode.ReportErrors, input, out partial_input); return null; } #if STATIC throw new NotSupportedException (); #else Class parser_result = parser.InteractiveResult; compiled = CompileBlock (parser_result, parser.undo, ctx.Report); return null; #endif } }
/// <summary> /// Compiles the input string and returns a delegate that represents the compiled code. /// </summary> /// <remarks> /// /// Compiles the input string as a C# expression or /// statement, unlike the Evaluate method, the /// resulting delegate can be invoked multiple times /// without incurring in the compilation overhead. /// /// If the return value of this function is null, /// this indicates that the parsing was complete. /// If the return value is a string it indicates /// that the input string was partial and that the /// invoking code should provide more code before /// the code can be successfully compiled. /// /// If you know that you will always get full expressions or /// statements and do not care about partial input, you can use /// the other Compile overload. /// /// On success, in addition to returning null, the /// compiled parameter will be set to the delegate /// that can be invoked to execute the code. /// /// </remarks> static public string Compile (string input, out CompiledMethod compiled) { if (input == null || input.Length == 0){ compiled = null; return null; } lock (evaluator_lock){ if (!inited) Init (); else ctx.Report.Printer.Reset (); // RootContext.ToplevelTypes = new ModuleContainer (ctx); bool partial_input; CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); if (parser == null){ compiled = null; if (partial_input) return input; ParseString (ParseMode.ReportErrors, input, out partial_input); return null; } object parser_result = parser.InteractiveResult; if (!(parser_result is Class)){ int errors = ctx.Report.Errors; NamespaceEntry.VerifyAllUsing (); if (errors == ctx.Report.Errors) parser.CurrentNamespace.Extract (using_alias_list, using_list); else NamespaceEntry.Reset (); } #if STATIC throw new NotSupportedException (); #else compiled = CompileBlock (parser_result as Class, parser.undo, ctx.Report); return null; #endif } }
public static void BindKey(int code, string js_down, string js_up) { CompiledMethod down = new CompiledMethod(Program._engine, js_down); CompiledMethod up = new CompiledMethod(Program._engine, js_up); _boundKeys[code] = new Tuple<CompiledMethod, CompiledMethod>(down, up); }
/// <summary> /// Default ctor /// </summary> public DebugInfoBuilder(CompiledMethod compiledMethod) { this.compiledMethod = compiledMethod; }
public CompExe(CompiledMethod method) { _method = method; }
public static MethodEntry RecordMapping(TypeEntry typeEntry, XMethodDefinition xMethod, MethodDefinition method, DexLib.MethodDefinition dmethod, CompiledMethod compiledMethod) { var scopeId = xMethod.ScopeId; StringBuilder netSignature = new StringBuilder(); method.MethodSignatureFullName(netSignature); var entry = new MethodEntry(method.OriginalName, netSignature.ToString(), dmethod.Name, dmethod.Prototype.ToSignature(), dmethod.MapFileId, scopeId); typeEntry.Methods.Add(entry); if (compiledMethod != null) { compiledMethod.RecordMapping(entry); } return entry; }
public static string Compile(string input, out CompiledMethod compiled) { return Evaluator.fetch.Compile(input, out compiled); }