private void TryCatchSetTargetRegister(FinallyBlockState finallyBlock, bool needTargetRegister) { var localTargets = finallyBlock.Targets.Where(t => t.State == finallyBlock); if (needTargetRegister) { finallyBlock.TargetRegister = Frame.AllocateTemp(PrimitiveType.Int); var setToZero = new Instruction(RCode.Const, 0, new [] { finallyBlock.TargetRegister }); instructions.Insert(finallyBlock.FirstInstruction.Index, setToZero); foreach (var t in localTargets) { if (t.SetTarget.Code != RCode.Nop) { t.SetTarget.Registers.Add(finallyBlock.TargetRegister); } } } else { foreach (var t in localTargets) { t.SetTarget.ConvertToNop(); } } }
private RLRange BranchToFinally(FinallyTarget target, IEnumerable <RLRange> prefix, ISourceLocation seqp, ref int insIdx) { Debug.Assert(this.HasFinally); Targets.Add(target); var setTarget = new Instruction(RCode.Const) { SequencePoint = seqp }; // operand and register are set later _compiler.instructions.Insert(insIdx++, setTarget); var branch = new Instruction(RCode.Goto, NonException) { SequencePoint = seqp }; _compiler.instructions.Insert(insIdx++, branch); target.SetTarget = setTarget; target.GotoFinally = branch; target.State = this; return(new RLRange(prefix, setTarget, branch, null)); }
public RLRange BranchToFinally_Leave(Instruction insTarget, ref int insIdx) { var target = new FinallyTarget { IsLeave = true, Destination = insTarget }; return(BranchToFinally(target, null, AstNode.NoSource, ref insIdx)); }
public RLRange BranchToFinally_FallOut(ISourceLocation sourceLocation, Instruction insTarget, List <RLRange> args) { int insIdx = _compiler.instructions.Count; var target = new FinallyTarget { Destination = insTarget, IsFallOut = true }; return(BranchToFinally(target, args, sourceLocation, ref insIdx)); }
public FinallyBlockState(FinallyBlockState outerBlock, int depth, AstCompilerVisitor compiler, Instruction first, Instruction last) { _compiler = compiler; NonException = new Instruction(); AfterExceptionCheck = new Instruction(); FirstInstruction = first; LastInstruction = last; Depth = depth; OuterFinallyBlock = outerBlock; }
private Instruction TryCatchGotoEnd(FinallyBlockState state, Instruction last) { if (state.HasFinally) { var rlrange = state.BranchToFinally_FallOut(AstNode.NoSource, last, new List <RLRange>()); return(rlrange.Last); } // goto end return(this.Add(AstNode.NoSource, RCode.Goto, last)); }
/// <summary> /// Create the body of the equals method. /// </summary> private static MethodBody CreateEqualsBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition equalsMethod, Prototype equalsPrototype, FieldDefinition instanceField, ClassReference delegateClass) { MethodBody body = new MethodBody(null); // This pointer and method argument. Register rthis = body.AllocateRegister(RCategory.Argument, RType.Object); Register rother = body.AllocateRegister(RCategory.Argument, RType.Object); // Create code. var ins = body.Instructions; // Temporary parameter result. Register result = body.AllocateRegister(RCategory.Temp, RType.Value); // Prepare the return instruction. Instruction returnInstruction = new Instruction(RCode.Return, result); // Check if other object can be casted. ins.Add(new Instruction(RCode.Instance_of, delegateClass, new[] { result, rother })); ins.Add(new Instruction(RCode.If_eqz, returnInstruction, new[] { result })); // compare instance members // Cast of the other object. ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { rother })); // Get instance fields of this and other. var thisInstance = body.AllocateRegister(RCategory.Temp, RType.Object); var otherInstance = body.AllocateRegister(RCategory.Temp, RType.Object); // Load the instance fields. ins.Add(new Instruction(RCode.Iget_object, thisInstance, rthis) { Operand = instanceField }); ins.Add(new Instruction(RCode.Iget_object, otherInstance, rother) { Operand = instanceField }); // Compare the instance fields. ins.Add(new Instruction(RCode.If_eq, returnInstruction, new[] { thisInstance, otherInstance })); // compare instance members // Set result to false if not equal. ins.Add(new Instruction(RCode.Const, 0, new[] { result })); // Add return instructions ins.Add(returnInstruction); return(body); }
/// <summary> /// Gets the lowest size (in bitsX) that is available for the given register in the given instruction. /// </summary> private static RegisterFlags GetLowestSize(Instruction instruction, Register r) { var result = RegisterFlags.Bits16; var info = OpCodeInfo.Get(instruction.Code.ToDex()); var registers = instruction.Registers; for (var i = 0; i < registers.Count; i++) { if (registers[i] == r) { var size = info.GetUsage(i) & RegisterFlags.SizeMask; if (size < result) { result = size; } } } return(result); }
/// <summary> /// return the first emitted instruction /// </summary> private Instruction TryCatchEmitTargetInstruction(FinallyTarget target, ref int insIdx, FinallyBlockState outerFinallyBlock) { bool emitReturn = outerFinallyBlock == null && target.IsReturn; bool chainToOuterBlock = outerFinallyBlock != null && target.IsReturn; if (target.IsLeave && outerFinallyBlock != null) { // check if the leave leaves the outer finally block as well. if (target.Destination.Index < outerFinallyBlock.FirstInstruction.Index || target.Destination.Index > outerFinallyBlock.LastInstruction.Index) { chainToOuterBlock = true; } } if (emitReturn) { Instruction ret; if (currentMethod.ReturnsVoid) { ret = new Instruction(RCode.Return_void); } else { var retCode = currentMethod.ReturnsDexWide ? RCode.Return_wide : currentMethod.ReturnsDexValue ? RCode.Return : RCode.Return_object; ret = new Instruction(retCode, finallyState.ReturnValueRegister); } instructions.Insert(insIdx++, ret); return ret; } else if (chainToOuterBlock) { Debug.Assert(outerFinallyBlock != null); int id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand; RLRange range; if (target.IsReturn) range = outerFinallyBlock.BranchToFinally_Ret(ref insIdx); else // IsLeave range = outerFinallyBlock.BranchToFinally_Leave(target.Destination, ref insIdx); // This is a little bit hackish. We need to set the operand in the setTarget instruction. if (id == 0) range.First.ConvertToNop(); else range.First.Operand = id; return range.First; } else { // goto var insGoto = new Instruction(RCode.Goto, target.Destination); instructions.Insert(insIdx++, insGoto); return insGoto; } }
/// <summary> /// Create try/catch/finally/fault block /// </summary> public override RLRange Visit(AstTryCatchBlock node, AstNode parent) { var handler = new ExceptionHandler(); //if (node.FaultBlock != null) //{ // Debugger.Break(); //} // Setup instruction before/after my node. var first = new Instruction() { SequencePoint = node.SourceLocation}; var last = new Instruction(RCode.Nop); FinallyBlockState finState = null; FinallyBlockState outerFinState = tryCatchStack.FirstOrDefault(f => f.HasFinally); if (tryCatchStack.Count == 0) finallyState.FinallyStacks.Add(new List<FinallyBlockState>()); if (node.FinallyBlock != null) { // store finaly state finState = new FinallyBlockState(outerFinState, tryCatchStack.Count, this, first, last); finState.FinallyExceptionRegister = frame.AllocateTemp(new ClassReference("java.lang.Throwable")).Register; // clear the variable to make sure it isn't stale from a previous loop. // make sure this is outside the try block for edge case exceptions. this.Add(node.SourceLocation, RCode.Const, 0, finState.FinallyExceptionRegister); tryCatchStack.Push(finState); finallyState.FinallyStacks.Last().Add(finState); } else { finState = new FinallyBlockState(outerFinState, tryCatchStack.Count); tryCatchStack.Push(finState); } instructions.Add(first); // Emit try block handler.TryStart = first; node.TryBlock.AcceptOrDefault(this, node); handler.TryEnd = TryCatchGotoEnd(finState, last); var catchesStart = this.Add(AstNode.NoSource, RCode.Nop); // Emit "normal" catch blocks foreach (var catchBlock in node.CatchBlocks.Where(x => !x.IsCatchAll())) { var c = new Catch { Type = catchBlock.ExceptionType.GetReference(targetPackage) }; handler.Catches.Add(c); var catchStart = this.Add(catchBlock.SourceLocation, RCode.Nop); catchBlock.Accept(this, node); c.Instruction = catchStart; TryCatchGotoEnd(finState, last); } // Emit "catch all" (if any) var catchAllBlock = node.CatchBlocks.SingleOrDefault(x => x.IsCatchAll()); if (catchAllBlock != null) { var catchStart = this.Add(catchAllBlock.SourceLocation, RCode.Nop); catchAllBlock.Accept(this, node); handler.CatchAll = catchStart; TryCatchGotoEnd(finState, last); } var catchesEnd = this.Add(AstNode.NoSource, RCode.Nop); // clear try/catch/finally stack: we don't want to cover ourselves! tryCatchStack.Pop(); // Emit finally code if (node.FinallyBlock != null) { // preparation. var finallyStart = this.Add(node.FinallyBlock.SourceLocation, RCode.Move_exception, finState.FinallyExceptionRegister); instructions.Add(finState.NonException); // the original handler node.FinallyBlock.Accept(this, node); // prepare the routing this.Add(AstNode.NoSource, RCode.If_eqz, finState.AfterExceptionCheck, finState.FinallyExceptionRegister); this.Add(AstNode.NoSource, RCode.Throw, finState.FinallyExceptionRegister); instructions.Add(finState.AfterExceptionCheck); // Set up exception handlers. if (catchAllBlock == null) { // we need to cover the try block. handler.CatchAll = finallyStart; } if (node.CatchBlocks.Any()) { // we need to cover the catch blocks var finallyHandler = new ExceptionHandler { TryStart = catchesStart, TryEnd = catchesEnd, CatchAll = finallyStart }; body.Exceptions.Add(finallyHandler); } } // Add end instructions.Add(last); // Record catch/catch-all handler if ((handler.CatchAll != null) || handler.Catches.Any()) { body.Exceptions.Add(handler); } return new RLRange(first, last, null); }
/// <summary> /// return the first emitted instruction /// </summary> private Instruction TryCatchEmitTargetInstruction(FinallyTarget target, ref int insIdx, FinallyBlockState outerFinallyBlock) { bool emitReturn = outerFinallyBlock == null && target.IsReturn; bool chainToOuterBlock = outerFinallyBlock != null && target.IsReturn; if (target.IsLeave && outerFinallyBlock != null) { // check if the leave leaves the outer finally block as well. if (target.Destination.Index < outerFinallyBlock.FirstInstruction.Index || target.Destination.Index > outerFinallyBlock.LastInstruction.Index) { chainToOuterBlock = true; } } if (emitReturn) { Instruction ret; if (currentMethod.ReturnsVoid) { ret = new Instruction(RCode.Return_void); } else { var retCode = currentMethod.ReturnsDexWide ? RCode.Return_wide : currentMethod.ReturnsDexValue ? RCode.Return : RCode.Return_object; ret = new Instruction(retCode, finallyState.ReturnValueRegister); } instructions.Insert(insIdx++, ret); return(ret); } else if (chainToOuterBlock) { Debug.Assert(outerFinallyBlock != null); int id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand; RLRange range; if (target.IsReturn) { range = outerFinallyBlock.BranchToFinally_Ret(ref insIdx); } else // IsLeave { range = outerFinallyBlock.BranchToFinally_Leave(target.Destination, ref insIdx); } // This is a little bit hackish. We need to set the operand in the setTarget instruction. if (id == 0) { range.First.ConvertToNop(); } else { range.First.Operand = id; } return(range.First); } else { // goto var insGoto = new Instruction(RCode.Goto, target.Destination); instructions.Insert(insIdx++, insGoto); return(insGoto); } }
private void TryCatchSetTargetRegister(FinallyBlockState finallyBlock, bool needTargetRegister) { var localTargets = finallyBlock.Targets.Where(t => t.State == finallyBlock); if (needTargetRegister) { finallyBlock.TargetRegister = Frame.AllocateTemp(PrimitiveType.Int); var setToZero = new Instruction(RCode.Const, 0, new []{ finallyBlock.TargetRegister}); instructions.Insert(finallyBlock.FirstInstruction.Index, setToZero); foreach (var t in localTargets) { if (t.SetTarget.Code != RCode.Nop) t.SetTarget.Registers.Add(finallyBlock.TargetRegister); } } else { foreach (var t in localTargets) t.SetTarget.ConvertToNop(); } }
private void FixFinallyStack(List <FinallyBlockState> finallyStack) { // Find all identical targets in this stack. // To minimize assigments, order by number of targets. To simplify the routing // code when possible, prefer IsFallOut over IsLeave over IsReturn var targets = TryCatchGroupTargets(finallyStack.SelectMany(f => f.Targets)); if (targets.Count == 0) { return; } // assign target group ids. zero is the default, and does not need to be set. int id = -1; foreach (var targetGroup in targets) { ++id; foreach (var target in targetGroup) { if (id == 0) { target.SetTarget.ConvertToNop(); } else { target.SetTarget.Operand = id; } } } // now work from the innermost to the outermost. var finallyBlocks = finallyStack.Where(f => f.HasFinally) .OrderByDescending(p => p.Depth) .ToList(); foreach (var finallyBlock in finallyBlocks) { targets = TryCatchGroupTargets(finallyBlock.Targets); // reset 'leave' instruction that do not leave this block. foreach (var targetGroup in targets.Where(t => t.Key.IsLeave).ToList()) { bool staysInBlock = targetGroup.Key.Destination.Index >= finallyBlock.FirstInstruction.Index && targetGroup.Key.Destination.Index <= finallyBlock.LastInstruction.Index; if (staysInBlock) { foreach (var source in targetGroup) { source.GotoFinally.Operand = source.Destination; source.SetTarget.ConvertToNop(); } targets.Remove(targetGroup); } } if (targets.Count == 0) { continue; } bool needTargetRegister = targets.Count > 1; TryCatchSetTargetRegister(finallyBlock, needTargetRegister); var outerFinallyBlock = finallyBlock.OuterFinallyBlock; int insIdx = finallyBlock.AfterExceptionCheck.Index + 1; if (targets.Count == 1) { // primitive case var target = targets[0]; TryCatchEmitTargetInstruction(target.Key, ref insIdx, outerFinallyBlock); } else if (targets.Count == 2) { // use a single comparison var target = targets[0].Key; id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand; Instruction compare; if (id == 0) { compare = new Instruction(RCode.If_nez, finallyBlock.TargetRegister); } else { var comparisonRegister = frame.AllocateTemp(PrimitiveType.Int); var iConst = new Instruction(RCode.Const, id, comparisonRegister.Registers.ToArray()); instructions.Insert(insIdx++, iConst); compare = new Instruction(RCode.If_ne, finallyBlock.TargetRegister, comparisonRegister); } instructions.Insert(insIdx++, compare); TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock); target = targets[1].Key; var def = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock); compare.Operand = def; } else { // use a sparse-switch List <Tuple <int, Instruction> > sparseSwitchData = new List <Tuple <int, Instruction> >(); var ps = new Instruction(RCode.Sparse_switch, finallyBlock.TargetRegister); instructions.Insert(insIdx++, ps); // emit default. TryCatchEmitTargetInstruction(targets.Last().Key, ref insIdx, outerFinallyBlock); foreach (var targetGrouping in targets.Take(targets.Count - 1)) { var target = targetGrouping.Key; id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand; var first = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock); sparseSwitchData.Add(Tuple.Create(id, first)); } ps.Operand = sparseSwitchData.ToArray(); } } }
/// <summary> /// Create try/catch/finally/fault block /// </summary> public override RLRange Visit(AstTryCatchBlock node, AstNode parent) { var handler = new ExceptionHandler(); //if (node.FaultBlock != null) //{ // Debugger.Break(); //} // Setup instruction before/after my node. var first = new Instruction() { SequencePoint = node.SourceLocation }; var last = new Instruction(RCode.Nop); FinallyBlockState finState = null; FinallyBlockState outerFinState = tryCatchStack.FirstOrDefault(f => f.HasFinally); if (tryCatchStack.Count == 0) { finallyState.FinallyStacks.Add(new List <FinallyBlockState>()); } if (node.FinallyBlock != null) { // store finaly state finState = new FinallyBlockState(outerFinState, tryCatchStack.Count, this, first, last); finState.FinallyExceptionRegister = frame.AllocateTemp(new ClassReference("java.lang.Throwable")).Register; // clear the variable to make sure it isn't stale from a previous loop. // make sure this is outside the try block for edge case exceptions. this.Add(node.SourceLocation, RCode.Const, 0, finState.FinallyExceptionRegister); tryCatchStack.Push(finState); finallyState.FinallyStacks.Last().Add(finState); } else { finState = new FinallyBlockState(outerFinState, tryCatchStack.Count); tryCatchStack.Push(finState); } instructions.Add(first); // Emit try block handler.TryStart = first; node.TryBlock.AcceptOrDefault(this, node); handler.TryEnd = TryCatchGotoEnd(finState, last); var catchesStart = this.Add(AstNode.NoSource, RCode.Nop); // Emit "normal" catch blocks foreach (var catchBlock in node.CatchBlocks.Where(x => !x.IsCatchAll())) { var c = new Catch { Type = catchBlock.ExceptionType.GetReference(targetPackage) }; handler.Catches.Add(c); var catchStart = this.Add(catchBlock.SourceLocation, RCode.Nop); catchBlock.Accept(this, node); c.Instruction = catchStart; TryCatchGotoEnd(finState, last); } // Emit "catch all" (if any) var catchAllBlock = node.CatchBlocks.SingleOrDefault(x => x.IsCatchAll()); if (catchAllBlock != null) { var catchStart = this.Add(catchAllBlock.SourceLocation, RCode.Nop); catchAllBlock.Accept(this, node); handler.CatchAll = catchStart; TryCatchGotoEnd(finState, last); } var catchesEnd = this.Add(AstNode.NoSource, RCode.Nop); // clear try/catch/finally stack: we don't want to cover ourselves! tryCatchStack.Pop(); // Emit finally code if (node.FinallyBlock != null) { // preparation. var finallyStart = this.Add(node.FinallyBlock.SourceLocation, RCode.Move_exception, finState.FinallyExceptionRegister); instructions.Add(finState.NonException); // the original handler node.FinallyBlock.Accept(this, node); // prepare the routing this.Add(AstNode.NoSource, RCode.If_eqz, finState.AfterExceptionCheck, finState.FinallyExceptionRegister); this.Add(AstNode.NoSource, RCode.Throw, finState.FinallyExceptionRegister); instructions.Add(finState.AfterExceptionCheck); // Set up exception handlers. if (catchAllBlock == null) { // we need to cover the try block. handler.CatchAll = finallyStart; } if (node.CatchBlocks.Any()) { // we need to cover the catch blocks var finallyHandler = new ExceptionHandler { TryStart = catchesStart, TryEnd = catchesEnd, CatchAll = finallyStart }; body.Exceptions.Add(finallyHandler); } } // Add end instructions.Add(last); // Record catch/catch-all handler if ((handler.CatchAll != null) || handler.Catches.Any()) { body.Exceptions.Add(handler); } return(new RLRange(first, last, null)); }
/// <summary> /// Gets the lowest size (in bitsX) that is available for the given register in the given instruction. /// </summary> private static RegisterFlags GetLowestSize(Instruction instruction, Register r) { var result = RegisterFlags.Bits16; var info = OpCodeInfo.Get(instruction.Code.ToDex()); var registers = instruction.Registers; for (var i = 0; i < registers.Count; i++) { if (registers[i] == r) { var size = info.GetUsage(i) & RegisterFlags.SizeMask; if (size < result) result = size; } } return result; }
private void FixFinallyStack(List<FinallyBlockState> finallyStack) { // Find all identical targets in this stack. // To minimize assigments, order by number of targets. To simplify the routing // code when possible, prefer IsFallOut over IsLeave over IsReturn var targets = TryCatchGroupTargets(finallyStack.SelectMany(f => f.Targets)); if (targets.Count == 0) return; // assign target group ids. zero is the default, and does not need to be set. int id = -1; foreach (var targetGroup in targets) { ++id; foreach (var target in targetGroup) { if (id == 0) target.SetTarget.ConvertToNop(); else target.SetTarget.Operand = id; } } // now work from the innermost to the outermost. var finallyBlocks = finallyStack.Where(f=>f.HasFinally) .OrderByDescending(p=>p.Depth) .ToList(); foreach(var finallyBlock in finallyBlocks) { targets = TryCatchGroupTargets(finallyBlock.Targets); // reset 'leave' instruction that do not leave this block. foreach (var targetGroup in targets.Where(t => t.Key.IsLeave).ToList()) { bool staysInBlock = targetGroup.Key.Destination.Index >= finallyBlock.FirstInstruction.Index && targetGroup.Key.Destination.Index <= finallyBlock.LastInstruction.Index; if(staysInBlock) { foreach (var source in targetGroup) { source.GotoFinally.Operand = source.Destination; source.SetTarget.ConvertToNop(); } targets.Remove(targetGroup); } } if (targets.Count == 0) continue; bool needTargetRegister = targets.Count > 1; TryCatchSetTargetRegister(finallyBlock, needTargetRegister); var outerFinallyBlock = finallyBlock.OuterFinallyBlock; int insIdx = finallyBlock.AfterExceptionCheck.Index + 1; if (targets.Count == 1) { // primitive case var target = targets[0]; TryCatchEmitTargetInstruction(target.Key, ref insIdx, outerFinallyBlock); } else if (targets.Count == 2) { // use a single comparison var target = targets[0].Key; id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand; Instruction compare; if (id == 0) { compare = new Instruction(RCode.If_nez, finallyBlock.TargetRegister); } else { var comparisonRegister = frame.AllocateTemp(PrimitiveType.Int); var iConst = new Instruction(RCode.Const, id, comparisonRegister.Registers.ToArray()); instructions.Insert(insIdx++, iConst); compare = new Instruction(RCode.If_ne, finallyBlock.TargetRegister, comparisonRegister); } instructions.Insert(insIdx++, compare); TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock); target = targets[1].Key; var def = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock); compare.Operand = def; } else { // use a sparse-switch List<Tuple<int,Instruction>> sparseSwitchData = new List<Tuple<int, Instruction>>(); var ps = new Instruction(RCode.Sparse_switch, finallyBlock.TargetRegister); instructions.Insert(insIdx++, ps); // emit default. TryCatchEmitTargetInstruction(targets.Last().Key, ref insIdx, outerFinallyBlock); foreach (var targetGrouping in targets.Take(targets.Count - 1)) { var target = targetGrouping.Key; id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand; var first = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock); sparseSwitchData.Add(Tuple.Create(id, first)); } ps.Operand = sparseSwitchData.ToArray(); } } }
/// <summary> /// Create the body of the equals method. /// </summary> private static MethodBody CreateEqualsBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition equalsMethod, Prototype equalsPrototype, FieldDefinition instanceField, ClassReference delegateClass) { MethodBody body = new MethodBody(null); // This pointer and method argument. Register rthis = body.AllocateRegister(RCategory.Argument, RType.Object); Register rother = body.AllocateRegister(RCategory.Argument, RType.Object); // Create code. var ins = body.Instructions; // Temporary parameter result. Register result = body.AllocateRegister(RCategory.Temp, RType.Value); // Prepare the return instruction. Instruction returnInstruction = new Instruction(RCode.Return, result); // Check if other object can be casted. ins.Add(new Instruction(RCode.Instance_of, delegateClass, new[] { result, rother })); ins.Add(new Instruction(RCode.If_eqz, returnInstruction, new[] { result })); // compare instance members // Cast of the other object. ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { rother })); // Get instance fields of this and other. var thisInstance = body.AllocateRegister(RCategory.Temp, RType.Object); var otherInstance = body.AllocateRegister(RCategory.Temp, RType.Object); // Load the instance fields. ins.Add(new Instruction(RCode.Iget_object, thisInstance, rthis) { Operand = instanceField }); ins.Add(new Instruction(RCode.Iget_object, otherInstance, rother) { Operand = instanceField }); // Compare the instance fields. ins.Add(new Instruction(RCode.If_eq, returnInstruction, new[] { thisInstance, otherInstance })); // compare instance members // Set result to false if not equal. ins.Add(new Instruction(RCode.Const, 0, new[] { result })); // Add return instructions ins.Add(returnInstruction); return body; }
private RLRange BranchToFinally(FinallyTarget target, IEnumerable<RLRange> prefix, ISourceLocation seqp, ref int insIdx) { Debug.Assert(this.HasFinally); Targets.Add(target); var setTarget = new Instruction(RCode.Const) { SequencePoint = seqp }; // operand and register are set later _compiler.instructions.Insert(insIdx++, setTarget); var branch = new Instruction(RCode.Goto, NonException) {SequencePoint = seqp}; _compiler.instructions.Insert(insIdx++, branch); target.SetTarget = setTarget; target.GotoFinally = branch; target.State = this; return new RLRange(prefix, setTarget, branch, null); }
/// <summary> /// Create the body of the invoke method. /// </summary> private static MethodBody CreateInvokeBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition calledMethod, XMethodDefinition invokeMethod, Prototype invokePrototype, Prototype calledMethodPrototype, FieldDefinition instanceField, ClassReference delegateClass) { var body = new MethodBody(null); var rthis = body.AllocateRegister(RCategory.Argument, RType.Object); foreach (var p in invokePrototype.Parameters) { if (p.Type.IsWide()) { body.AllocateWideRegister(RCategory.Argument); } else { var type = (p.Type is PrimitiveType) ? RType.Value : RType.Object; body.AllocateRegister(RCategory.Argument, type); } } var incomingMethodArgs = body.Registers.ToArray(); // Create code var ins = body.Instructions; Register instance = null; if (!calledMethod.IsStatic) { // load instance instance = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Iget_object, instance, rthis) { Operand = instanceField }); } // Invoke var calledMethodRef = calledMethod.GetReference(targetPackage); var inputArgs = calledMethod.IsStatic ? incomingMethodArgs.Skip(1).ToArray() : incomingMethodArgs; // Cast arguments (if needed) var outputArgs = new List <Register>(); if (!calledMethod.IsStatic) { outputArgs.Add(instance); } var parameterIndex = 0; for (var i = calledMethod.IsStatic ? 0 : 1; i < inputArgs.Length;) { var invokeType = invokePrototype.Parameters[parameterIndex].Type; var inputIsWide = invokeType.IsWide(); var calledType = calledMethodPrototype.Parameters[parameterIndex].Type; if (!invokeType.Equals(calledType)) { // Add cast / unbox var source = inputIsWide ? new RegisterSpec(inputArgs[i], inputArgs[i + 1], invokeType) : new RegisterSpec(inputArgs[i], null, invokeType); var tmp = ins.Unbox(sequencePoint, source, calledMethod.Parameters[parameterIndex].ParameterType, compiler, targetPackage, body); outputArgs.Add(tmp.Result.Register); if (calledType.IsWide()) { outputArgs.Add(tmp.Result.Register2); } } else { outputArgs.Add(inputArgs[i]); if (calledType.IsWide()) { outputArgs.Add(inputArgs[i + 1]); } } i += inputIsWide ? 2 : 1; parameterIndex++; } // Actual call ins.Add(new Instruction(calledMethod.Invoke(calledMethod, null), calledMethodRef, outputArgs.ToArray())); // Collect return value var invokeReturnType = invokePrototype.ReturnType; var calledReturnType = calledMethodPrototype.ReturnType; var needsBoxing = !invokeReturnType.Equals(calledReturnType); Instruction returnInstruction; if (calledReturnType.IsWide()) { var r = body.AllocateWideRegister(RCategory.Temp); ins.Add(new Instruction(RCode.Move_result_wide, r.Item1)); if (needsBoxing) { // Box var source = new RegisterSpec(r.Item1, r.Item2, calledReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return wide returnInstruction = new Instruction(RCode.Return_wide, r.Item1); } } else if (calledMethod.ReturnType.IsVoid()) { // Void return returnInstruction = new Instruction(RCode.Return_void); } else if (calledReturnType is PrimitiveType) { // Single register return var r = body.AllocateRegister(RCategory.Temp, RType.Value); ins.Add(new Instruction(RCode.Move_result, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return, r); } } else { var r = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Move_result_object, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, invokeMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return_object, r); } } // Call next delegate (if any) var next = body.AllocateRegister(RCategory.Temp, RType.Object); var multicastDelegateType = new ClassReference(targetPackage.NameConverter.GetConvertedFullName("System.MulticastDelegate")); var nextReference = new FieldReference(multicastDelegateType, "next", multicastDelegateType); ins.Add(new Instruction(RCode.Iget_object, nextReference, new[] { next, rthis })); // load this.next var afterCallNext = new Instruction(RCode.Nop); ins.Add(new Instruction(RCode.If_eqz, afterCallNext, new[] { next })); // if next == null, continue ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { next })); var nextInvokeMethod = new MethodReference(delegateClass, "Invoke", invokePrototype); var nextInvokeArgs = new[] { next }.Concat(incomingMethodArgs.Skip(1)).ToArray(); ins.Add(new Instruction(RCode.Invoke_virtual, nextInvokeMethod, nextInvokeArgs)); ins.Add(afterCallNext); // Add return instructions ins.Add(returnInstruction); return(body); }
/// <summary> /// Generate code for the given expression. /// </summary> private RLRange VisitExpression(AstExpression node, List<RLRange> args, AstNode parent) { switch (node.Code) { case AstCode.Nop: case AstCode.Endfinally: case AstCode.Endfilter: return new RLRange(this.Add(node.SourceLocation, RCode.Nop), null); case AstCode.Ldexception: throw new InvalidOperationException("ldexception should not occur"); case AstCode.Dup: return new RLRange(this.Add(node.SourceLocation, RCode.Nop), args[0].Result); #region Constants case AstCode.Ldc_I4: case AstCode.Ldc_R4: { var type = node.GetResultType(); var converter = type.ConstValueConverter(true); var value = converter(node.Operand); var r = frame.AllocateTemp(type.IsFloat() ? PrimitiveType.Float : PrimitiveType.Int); var first = this.Add(node.SourceLocation, RCode.Const, value, r); return new RLRange(first, r); } case AstCode.Ldc_I8: case AstCode.Ldc_R8: { var type = node.GetResultType(); var converter = type.ConstValueConverter(false); var value = converter(node.Operand); var r = frame.AllocateTemp(type.IsDouble() ? PrimitiveType.Double : PrimitiveType.Long); return new RLRange(args, this.Add(node.SourceLocation, RCode.Const_wide, value, r), r); } case AstCode.Ldnull: { //Debugger.Launch(); var r = frame.AllocateTemp(node.GetResultType().GetReference(targetPackage)); return new RLRange(args, this.Add(node.SourceLocation, RCode.Const, 0, r), r); } case AstCode.Ldstr: { var str = (string) node.Operand; var r = frame.AllocateTemp(node.GetResultType().GetReference(targetPackage)); return new RLRange(args, this.Add(node.SourceLocation, RCode.Const_string, str, r), r); } case AstCode.DefaultValue: { var type = (XTypeReference) node.Operand; if (type.IsPrimitive) { var r = frame.AllocateTemp(type.GetReference(targetPackage)); return new RLRange(args, this.Add(node.SourceLocation, node.Arguments[0].Const(), 0, r), r); } if (type.IsEnum()) { var r = frame.AllocateTemp(type.GetReference(targetPackage)); var denumType = type.GetClassReference(targetPackage); var defaultField = new FieldReference(denumType, NameConstants.Enum.DefaultFieldName, denumType); return new RLRange(this.Add(node.SourceLocation, RCode.Sget_object, defaultField, r), r); } else { var r = frame.AllocateTemp(type.GetReference(targetPackage)); return new RLRange(args, this.Add(node.SourceLocation, RCode.Const, 0, r), r); } } case AstCode.TypeOf: { var type = (XTypeReference) node.Operand; var dtype = type.IsVoid() ? PrimitiveType.Void : type.GetReference(targetPackage); var typeReg = frame.AllocateTemp(FrameworkReferences.Class); var first = this.Add(node.SourceLocation, RCode.Const_class, dtype, typeReg); return new RLRange(first, typeReg); } case AstCode.BoxedTypeOf: { var type = (XTypeReference) node.Operand; var typeReg = frame.AllocateTemp(FrameworkReferences.Class); var first = this.Add(node.SourceLocation, RCode.Const_class, type.GetBoxedType(), typeReg); return new RLRange(first, typeReg); } #endregion #region Arithmetic case AstCode.Neg: { var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); return new RLRange(args, this.Add(node.SourceLocation, node.Neg(), tmp.Result, tmp.Result), tmp.Result); } case AstCode.Not: { var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); return new RLRange(args, this.Add(node.SourceLocation, node.Not(), tmp.Result, tmp.Result), tmp.Result); } case AstCode.Add: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.Add2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Add(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.Add_Ovf: { var isLong = node.Arguments[0].IsInt64(); var addMethods = compiler.GetDot42InternalType("Checked").Resolve().Methods.Where(x => x.Name == "Add"); var ilMethod = addMethods.First(x => isLong ? x.ReturnType.IsInt64() : x.ReturnType.IsInt32()); var method = ilMethod.GetReference(targetPackage); var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); var registers = tmp.Result.Registers.Concat(args[1].Result.Registers); this.Add(node.SourceLocation, RCode.Invoke_static, method, registers); return new RLRange(args, this.Add(node.SourceLocation, isLong ? RCode.Move_result_wide : RCode.Move_result, tmp.Result), tmp.Result); } case AstCode.CompoundAdd: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Add2Addr(), localReg, args[0].Result), localReg); } case AstCode.Sub: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.Sub2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Sub(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.Sub_Ovf: { var isLong = node.Arguments[0].IsInt64(); var addMethods = compiler.GetDot42InternalType("Checked").Resolve().Methods.Where(x => x.Name == "Sub"); var ilMethod = addMethods.First(x => isLong ? x.ReturnType.IsInt64() : x.ReturnType.IsInt32()); var method = ilMethod.GetReference(targetPackage); var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); var registers = tmp.Result.Registers.Concat(args[1].Result.Registers); this.Add(node.SourceLocation, RCode.Invoke_static, method, registers); return new RLRange(args, this.Add(node.SourceLocation, isLong ? RCode.Move_result_wide : RCode.Move_result, tmp.Result), tmp.Result); } case AstCode.CompoundSub: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Sub2Addr(), localReg, args[0].Result), localReg); } case AstCode.Mul: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.Mul2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Mul(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.Mul_Ovf: { var isLong = node.Arguments[0].IsInt64(); var addMethods = compiler.GetDot42InternalType("Checked").Resolve().Methods.Where(x => x.Name == "Mul"); var ilMethod = addMethods.First(x => isLong ? x.ReturnType.IsInt64() : x.ReturnType.IsInt32()); var method = ilMethod.GetReference(targetPackage); var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); var registers = tmp.Result.Registers.Concat(args[1].Result.Registers); this.Add(node.SourceLocation, RCode.Invoke_static, method, registers); return new RLRange(args, this.Add(node.SourceLocation, isLong ? RCode.Move_result_wide : RCode.Move_result, tmp.Result), tmp.Result); } case AstCode.CompoundMul: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Mul2Addr(), localReg, args[0].Result), localReg); } case AstCode.Div: case AstCode.Div_Un: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.Div2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Div(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.CompoundDiv: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Div2Addr(), localReg, args[0].Result), localReg); } case AstCode.Rem: case AstCode.Rem_Un: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.Rem2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Rem(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.CompoundRem: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Rem2Addr(), localReg, args[0].Result), localReg); } case AstCode.And: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.And2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.And(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.CompoundAnd: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.And2Addr(), localReg, args[0].Result), localReg); } case AstCode.Or: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.Or2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Or(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.CompoundOr: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Or2Addr(), localReg, args[0].Result), localReg); } case AstCode.Xor: { if (args[0].Result.Register.IsTemp) return new RLRange(args, this.Add(node.SourceLocation, node.Xor2Addr(), args[0].Result, args[1].Result), args[0].Result); var tmp = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Xor(), tmp, args[0].Result, args[1].Result), tmp); } case AstCode.CompoundXor: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Xor2Addr(), localReg, args[0].Result), localReg); } case AstCode.Shl: { var r = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Shl(), r, args[0].Result, args[1].Result), r); } case AstCode.CompoundShl: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Shl2Addr(), localReg, args[0].Result), localReg); } case AstCode.Shr: { var r = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.Shr(), r, args[0].Result, args[1].Result), r); } case AstCode.CompoundShr: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.Shr2Addr(), localReg, args[0].Result), localReg); } case AstCode.Shr_Un: { var r = frame.AllocateTemp(args[0].Result.Type); return new RLRange(args, this.Add(node.SourceLocation, node.UShr(), r, args[0].Result, args[1].Result), r); } case AstCode.CompoundShr_Un: { var localReg = frame.GetArgument((AstVariable) node.Operand); return new RLRange(args, this.Add(node.SourceLocation, node.UShr2Addr(), localReg, args[0].Result), localReg); } case AstCode.Conditional: // arg[0] ? arg[1] : arg[2] { var valueType = (XTypeReference) node.Operand; var result = frame.AllocateTemp(valueType.GetReference(targetPackage)); var move = node.Arguments[1].Move(); var move2 = node.Arguments[2].Move(); if (move2 == RCode.Move_object) move = move2; // condition var gotoArg2 = this.Add(node.SourceLocation, RCode.If_eqz, null, args[0].Result.Registers); // Generate code for arg[1] var arg1 = node.Arguments[1].Accept(this, node); this.Add(node.SourceLocation, move, result, arg1.Result); var gotoEnd = this.Add(node.SourceLocation, RCode.Goto, null); // Generate code for arg[2] var arg2Start = this.Add(node.SourceLocation, RCode.Nop); var arg2 = node.Arguments[2].Accept(this, node); this.Add(node.SourceLocation, move, result, arg2.Result); var end = this.Add(node.SourceLocation, RCode.Nop); // Set branch targets gotoArg2.Operand = arg2Start; gotoEnd.Operand = end; return new RLRange(gotoArg2, end, result); } #endregion #region Conversion /*case AstCode.Conv_U4: case AstCode.Conv_Ovf_U4: { return new RLRange(this.Add(node.SourceLocation, RCode.Nop), args[0].Result); }*/ case AstCode.Conv_I1: case AstCode.Conv_Ovf_I1: case AstCode.Conv_Ovf_I1_Un: return ConvX(node.SourceLocation, RCode.Int_to_byte, PrimitiveType.Byte, ConvToInt(node.SourceLocation, args[0])); case AstCode.Conv_U1: case AstCode.Conv_Ovf_U1: case AstCode.Conv_Ovf_U1_Un: { var result = ConvX(node.SourceLocation, RCode.Int_to_byte, PrimitiveType.Byte, ConvToInt(node.SourceLocation, args[0])); var last = this.Add(node.SourceLocation, RCode.And_int_lit, 0xFF, result.Result, result.Result); return new RLRange(result.First, last, result.Result); } case AstCode.Conv_I2: case AstCode.Conv_Ovf_I2: case AstCode.Conv_Ovf_I2_Un: return ConvX(node.SourceLocation, RCode.Int_to_short, PrimitiveType.Short, ConvToInt(node.SourceLocation, args[0])); case AstCode.Conv_U2: case AstCode.Conv_Ovf_U2: case AstCode.Conv_Ovf_U2_Un: { if (node.GetResultType().IsUInt16()) { var result = ConvX(node.SourceLocation, RCode.Int_to_short, PrimitiveType.Short, ConvToInt(node.SourceLocation, args[0])); var r2 = frame.AllocateTemp(PrimitiveType.Int); this.Add(node.SourceLocation, RCode.Const, 0xFFFF, r2); var last = this.Add(node.SourceLocation, RCode.And_int_2addr, result.Result, r2); return new RLRange(result.First, last, result.Result); } else { return ConvX(node.SourceLocation, RCode.Int_to_char, PrimitiveType.Char, ConvToInt(node.SourceLocation, args[0])); } } case AstCode.Conv_I4: case AstCode.Conv_Ovf_I4: case AstCode.Conv_Ovf_I4_Un: case AstCode.Conv_I: // Convert to native int case AstCode.Conv_Ovf_I: // Convert to native with overflow check case AstCode.Conv_Ovf_I_Un: // Convert to native without overflow check case AstCode.Conv_U: // Convert to native uint case AstCode.Conv_Ovf_U: // Convert to native uint with overflow check case AstCode.Conv_Ovf_U_Un: // Convert to native uint without overflow check case AstCode.Conv_U4: case AstCode.Conv_Ovf_U4: case AstCode.Conv_Ovf_U4_Un: return ConvX(node.SourceLocation, node.Arguments[0].ConvI4(), PrimitiveType.Int, args[0]); case AstCode.Conv_I8: case AstCode.Conv_Ovf_I8: case AstCode.Conv_Ovf_I8_Un: case AstCode.Conv_U8: case AstCode.Conv_Ovf_U8: case AstCode.Conv_Ovf_U8_Un: return ConvX(node.SourceLocation, node.Arguments[0].ConvI8(), PrimitiveType.Long, args[0]); case AstCode.Conv_R4: return ConvX(node.SourceLocation, node.Arguments[0].ConvR4(), PrimitiveType.Float, args[0]); case AstCode.Conv_R8: case AstCode.Conv_R_Un: return ConvX(node.SourceLocation, node.Arguments[0].ConvR8(), PrimitiveType.Double, args[0]); case AstCode.Int_to_ubyte: { var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); var last = this.Add(node.SourceLocation, RCode.And_int_lit, 0xFF, tmp.Result, tmp.Result); return new RLRange(args, tmp.First, last, tmp.Result); } case AstCode.Int_to_ushort: { var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); var r2 = frame.AllocateTemp(PrimitiveType.Int); this.Add(node.SourceLocation, RCode.Const, 0xFFFF, r2); var last = this.Add(node.SourceLocation, RCode.And_int_2addr, tmp.Result, r2); return new RLRange(args, tmp.First, last, tmp.Result); } case AstCode.Box: { var type = (XTypeReference) node.Operand; var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); return this.Box(node.SourceLocation, tmp.Result, type, targetPackage, frame); } case AstCode.Unbox: case AstCode.Unbox_Any: { var type = (XTypeReference) node.Operand; var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); return this.Unbox(node.SourceLocation, tmp.Result, type, compiler, targetPackage, frame); } case AstCode.AddressOf: { return args[0]; } case AstCode.Enum_to_int: case AstCode.Enum_to_long: { var enumType = node.Arguments[0].GetResultType().Resolve(); var isWide = enumType.GetEnumUnderlyingType().IsWide(); var internalEnumType = compiler.GetDot42InternalType("Enum").GetClassReference(targetPackage); var methodName = isWide ? "LongValue" : "IntValue"; var enumNumericType = isWide ? PrimitiveType.Long : PrimitiveType.Int; var rvalue = frame.AllocateTemp(enumNumericType); var convMethodDex = new MethodReference(internalEnumType, methodName, new Prototype(enumNumericType)); var call = this.Add(node.SourceLocation, RCode.Invoke_virtual, convMethodDex, args[0].Result); var last = this.Add(node.SourceLocation, isWide ? RCode.Move_result_wide : RCode.Move_result, rvalue); return new RLRange(call, last, rvalue); } case AstCode.Int_to_enum: case AstCode.Long_to_enum: { var enumType = node.GetResultType().Resolve(); var denumType = enumType.GetClassReference(targetPackage); var isWide = enumType.GetEnumUnderlyingType().IsWide(); var literalIsWide = node.Arguments[0].GetResultType().IsWide(); var internalEnumType = compiler.GetDot42InternalType("Enum").GetClassReference(targetPackage); var internalEnumInfoType = compiler.GetDot42InternalType("EnumInfo").GetClassReference(targetPackage); var enumNumericType = isWide ? PrimitiveType.Long : PrimitiveType.Int; var getValueMethod = new MethodReference(internalEnumInfoType, "GetValue", new Prototype(internalEnumType, new Parameter(enumNumericType, "value"))); var rinfo = frame.AllocateTemp(internalEnumInfoType); var infoField = new FieldReference(denumType, NameConstants.Enum.InfoFieldName, internalEnumInfoType); var getInfo = this.Add(node.SourceLocation, RCode.Sget_object, infoField, rinfo); var valueR = args[0].Result; if (isWide != literalIsWide) { var convValueR = frame.AllocateTemp(isWide ? PrimitiveType.Long : PrimitiveType.Int); var convCode = isWide ? RCode.Int_to_long : RCode.Long_to_int; this.Add(node.SourceLocation, convCode, convValueR, valueR); valueR = convValueR; } this.Add(node.SourceLocation, RCode.Invoke_virtual, getValueMethod, rinfo.Registers.Concat(valueR.Registers)); var renum = frame.AllocateTemp(denumType); this.Add(node.SourceLocation, RCode.Move_result_object, renum); var castClass = this.Add(node.SourceLocation, RCode.Check_cast, denumType, renum); return new RLRange(getInfo, castClass, renum); } #endregion #region Branching case AstCode.Cle: case AstCode.Cle_Un: case AstCode.Clt: case AstCode.Clt_Un: case AstCode.Ceq: case AstCode.Cne: case AstCode.Cgt: case AstCode.Cgt_Un: case AstCode.Cge: case AstCode.Cge_Un: { var r = frame.AllocateTemp(PrimitiveType.Int); var start = this.Add(node.SourceLocation, RCode.Const, 0, r); Instruction test; var narg0 = node.Arguments[0]; var narg1 = node.Arguments[1]; if (narg0.IsWide() || narg1.IsWide()) { var r2 = frame.AllocateTemp(PrimitiveType.Int); var code = narg0.IsDouble() ? RCode.Cmpg_double : RCode.Cmp_long; this.Add(node.SourceLocation, code, r2, args[0].Result, args[1].Result); test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTestZ(), r2); } else if (narg0.IsFloat() || narg1.IsFloat()) { var r2 = frame.AllocateTemp(PrimitiveType.Int); this.Add(node.SourceLocation, RCode.Cmpg_float, r2, args[0].Result, args[1].Result); test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTestZ(), r2); } else { test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTest(), args[0].Result, args[1].Result); } this.Add(node.SourceLocation, RCode.Const, 1, r); var end = this.Add(node.SourceLocation, RCode.Nop, null); test.Operand = end; return new RLRange(args, start, end, r); } case AstCode.CmpLFloat: case AstCode.CmpGFloat: case AstCode.CmpLong: { var r = frame.AllocateTemp(PrimitiveType.Int); var code = node.CmpFloatOrLong(); var test = this.Add(node.SourceLocation, code, r, args[0].Result, args[1].Result); return new RLRange(args, test, r); } case AstCode.CIsNotNull: case AstCode.CIsNull: { var r = frame.AllocateTemp(PrimitiveType.Int); var start = this.Add(node.SourceLocation, RCode.Const, 0, r); var test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTestZ(), args[0].Result); this.Add(node.SourceLocation, RCode.Const, 1, r); var end = this.Add(node.SourceLocation, RCode.Nop, null); test.Operand = end; return new RLRange(args, start, end, r); } case AstCode.Brtrue: case AstCode.Brfalse: case AstCode.BrIfEq: case AstCode.BrIfNe: case AstCode.BrIfGe: case AstCode.BrIfGt: case AstCode.BrIfLe: case AstCode.BrIfLt: { var label = (AstLabel) node.Operand; var opcode = node.Code.ToIfTestZ(); var branch = this.Add(node.SourceLocation, opcode, args[0].Result); labelManager.AddResolveAction(label, x => branch.Operand = x); return new RLRange(branch, null); } case AstCode.__Beq: case AstCode.__Bne_Un: case AstCode.__Ble: case AstCode.__Ble_Un: case AstCode.__Blt: case AstCode.__Blt_Un: case AstCode.__Bgt: case AstCode.__Bgt_Un: case AstCode.__Bge: case AstCode.__Bge_Un: { var label = (AstLabel) node.Operand; var opcode = node.Code.ToIfTest(); var branch = this.Add(node.SourceLocation, opcode, args[0].Result, args[1].Result); labelManager.AddResolveAction(label, x => branch.Operand = x); return new RLRange(branch, null); } case AstCode.Br: { var label = (AstLabel) node.Operand; var branch = this.Add(node.SourceLocation, RCode.Goto, null); labelManager.AddResolveAction(label, x => branch.Operand = x); return new RLRange(branch, null); } case AstCode.Leave: { var label = (AstLabel) node.Operand; var branch = this.Add(node.SourceLocation, RCode.Leave, null); labelManager.AddResolveAction(label, x => branch.Operand = x); return new RLRange(branch, null); } case AstCode.Switch: { var labels = (AstLabel[]) node.Operand; var targets = new Instruction[labels.Length]; for (var i = 0; i < labels.Length; i++) { var index = i; labelManager.AddResolveAction(labels[i], x => targets[index] = x); } var @switch = this.Add(node.SourceLocation, RCode.Packed_switch, targets, args[0].Result); return new RLRange(@switch, null); } case AstCode.LookupSwitch: { var labelKeyPairs = (AstLabelKeyPair[]) node.Operand; var targetPairs = new Tuple<int, Instruction>[labelKeyPairs.Length]; for (var i = 0; i < labelKeyPairs.Length; i++) { var index = i; var key = labelKeyPairs[i].Key; var label = labelKeyPairs[i].Label; labelManager.AddResolveAction(label, x => targetPairs[index] = Tuple.Create(key, x)); } var @switch = this.Add(node.SourceLocation, RCode.Sparse_switch, targetPairs, args[0].Result); return new RLRange(@switch, null); } case AstCode.NullCoalescing: { var r = frame.AllocateTemp(node.InferredType.GetReference(targetPackage)); var first = this.Add(node.SourceLocation, RCode.Move_object, r, args[0].Result); // r := leftExpr var if_nez = this.Add(node.SourceLocation, RCode.If_nez, r); // if r not null, skip this.Add(node.SourceLocation, RCode.Move_object, r, args[1].Result); // r := rightExpr var end = this.Add(node.SourceLocation, RCode.Nop); if_nez.Operand = end; return new RLRange(first, end, r); } #endregion #region Call case AstCode.Call: case AstCode.Callvirt: case AstCode.CallIntf: case AstCode.CallSpecial: { return VisitCallExpression(node, args, parent); } case AstCode.Ret: if (currentMethod.ReturnsVoid) return new RLRange(args, this.Add(node.SourceLocation, RCode.Return_void), null); return new RLRange(args, this.Add(node.SourceLocation, node.Return(currentMethod), args[0].Result), null); #endregion #region Array case AstCode.Newarr: { var type = (XTypeReference) node.Operand; var dType = new ArrayType(type.GetReference(targetPackage)); var r = frame.AllocateTemp(dType); var newArray = this.Add(node.SourceLocation, RCode.New_array, dType, r, args[0].Result); return new RLRange(newArray, r); } case AstCode.ArrayNewInstance: { // Resolve type to a Class<?> var newInstance = FrameworkReferences.ArrayNewInstance; var first = this.Add(node.SourceLocation, RCode.Invoke_static, newInstance, args[0].Result, args[1].Result); var r = frame.AllocateTemp(newInstance.Prototype.ReturnType); var last = this.Add(node.SourceLocation, RCode.Move_result_object, r); return new RLRange(first, last, r); } case AstCode.ArrayNewInstance2: { // Call java.lang.reflect.Array.newInstance(type, int[]) var newInstance = FrameworkReferences.ArrayNewInstance2; var first = this.Add(node.SourceLocation, RCode.Invoke_static, newInstance, args[0].Result, args[1].Result); var resultReg = frame.AllocateTemp(FrameworkReferences.Object); var last = this.Add(node.SourceLocation, RCode.Move_result_object, resultReg); return new RLRange(args, first, last, resultReg); } case AstCode.InitEnumArray: { var type = (XTypeReference) node.Operand; var typeDef = type.Resolve(); // Initialize array var fillMethod = FrameworkReferences.ArraysFillObject; var denumType = typeDef.GetClassReference(targetPackage); var defaultField = new FieldReference(denumType, NameConstants.Enum.DefaultFieldName, denumType); var rdefault = frame.AllocateTemp(denumType); var first = this.Add(node.SourceLocation, RCode.Sget_object, defaultField, rdefault); var arrayR = args[0].Result; var last = this.Add(node.SourceLocation, RCode.Invoke_static, fillMethod, arrayR, rdefault); return new RLRange(first, last, arrayR); } case AstCode.InitStructArray: { var defaultCtor = (XMethodReference) node.Operand; var dDefaultCtor = defaultCtor.GetReference(targetPackage); var arrayR = args[0].Result; var indexR = frame.AllocateTemp(PrimitiveType.Int); var oneR = frame.AllocateTemp(PrimitiveType.Int); var elementR = frame.AllocateTemp(dDefaultCtor.Owner); var first = this.Add(node.SourceLocation, RCode.Array_length, indexR, arrayR); this.Add(node.SourceLocation, RCode.Const, 1, oneR); var ifZero = this.Add(node.SourceLocation, RCode.If_eqz, indexR); // if (index == 0) goto end; (Operand set later) this.Add(node.SourceLocation, RCode.Sub_int_2addr, indexR, oneR); // index--; this.Add(node.SourceLocation, RCode.New_instance, dDefaultCtor.Owner, elementR); // element = new Struct; this.Add(node.SourceLocation, RCode.Invoke_direct, dDefaultCtor, elementR); // invoke element.ctor() this.Add(node.SourceLocation, RCode.Aput_object, elementR, arrayR, indexR); // Store element in array this.Add(node.SourceLocation, RCode.Goto, ifZero); // End of loop var end = this.Add(node.SourceLocation, RCode.Nop); ifZero.Operand = end; return new RLRange(first, end, arrayR); } case AstCode.MultiNewarr: { var arrType = (XTypeReference) node.Operand; var darrType = arrType.GetReference(targetPackage); var compType = arrType; // Unwind array type to component type for (var i = 0; i < node.Arguments.Count; i++) { compType = compType.ElementType; } var dcompType = new ArrayType(compType.GetReference(targetPackage)); var dimArrayR = frame.AllocateTemp(new ArrayType(PrimitiveType.Int)); var lengthR = frame.AllocateTemp(PrimitiveType.Int); var first = this.Add(node.SourceLocation, RCode.Nop); // Allocate dimensions array this.Add(node.SourceLocation, RCode.Const, node.Arguments.Count, lengthR); this.Add(node.SourceLocation, RCode.New_array, PrimitiveType.Int, dimArrayR, lengthR); var indexR = lengthR; // Initialize dimensions array for (var i = 0; i < node.Arguments.Count; i++) { this.Add(node.SourceLocation, RCode.Const, i, indexR); this.Add(node.SourceLocation, RCode.Aput, args[i], dimArrayR, indexR); } // Load component type var compTypeR = frame.AllocateTemp(new ClassReference("java/lang/Class")); this.Add(node.SourceLocation, RCode.Const_class, dcompType, compTypeR); // Call java/lang/reflect/Array/newInstance var reflectArrayType = new ClassReference("java/lang/reflect/Array"); var prototype = PrototypeBuilder.ParseMethodSignature("(Ljava/lang/Class;[I)Ljava/lang/Object;"); var methodRef = new MethodReference(reflectArrayType, "newInstance", prototype); var arrayR = frame.AllocateTemp(darrType); this.Add(node.SourceLocation, RCode.Invoke_static, methodRef, compTypeR, dimArrayR); var last = this.Add(node.SourceLocation, RCode.Move_result_object, arrayR); return new RLRange(first, last, arrayR); } case AstCode.ByRefArray: case AstCode.ByRefOutArray: { // Create array var type = (XTypeReference) node.Operand; var dType = new ArrayType(type.GetReference(targetPackage)); var arrayR = frame.AllocateTemp(dType); var lengthR = frame.AllocateTemp(PrimitiveType.Int); // length=1 var initLength = this.Add(node.SourceLocation, RCode.Const, 1, lengthR); // newarray this.Add(node.SourceLocation, RCode.New_array, dType, arrayR, lengthR); if (node.Code == AstCode.ByRefArray) { var valueR = args[0].Result; var arrayType = node.InferredType; // Perform type conversion if needed bool isConverted; var converted = this.ConvertTypeBeforeStore(node.SourceLocation, type, arrayType.ElementType, valueR, targetPackage, frame, compiler, out isConverted); if (isConverted) valueR = converted.Result; // array[0]=value var indexR = frame.AllocateTemp(PrimitiveType.Int); this.Add(node.SourceLocation, RCode.Const, 0, indexR); this.Add(node.SourceLocation, arrayType.APut(), valueR, arrayR, indexR); } var end = this.Add(node.SourceLocation, RCode.Nop); return new RLRange(initLength, end, arrayR); } case AstCode.Ldlen: { var r = frame.AllocateTemp(PrimitiveType.Int); return new RLRange(this.Add(node.SourceLocation, RCode.Array_length, r, args[0].Result), r); } case AstCode.Stelem_I: case AstCode.Stelem_I1: case AstCode.Stelem_I2: case AstCode.Stelem_I4: case AstCode.Stelem_I8: case AstCode.Stelem_R4: case AstCode.Stelem_R8: case AstCode.Stelem_Ref: case AstCode.Stelem_Any: { var first = this.Add(node.SourceLocation, RCode.Nop); var arrayR = args[0].Result; var indexR = args[1].Result; var valueR = args[2].Result; var valueType = node.Arguments[2].GetResultType(); var arrayType = node.Arguments[0].GetResultType(); // Perform type conversion if needed bool isConverted; var converted = this.ConvertTypeBeforeStore(node.SourceLocation, valueType, arrayType.ElementType, valueR, targetPackage, frame, compiler, out isConverted); if (isConverted) valueR = converted.Result; // Store in array var aput = this.Add(node.SourceLocation, node.APut(), valueR, arrayR, indexR); return new RLRange(first, aput, valueR); } case AstCode.Ldelem_I: case AstCode.Ldelem_I1: case AstCode.Ldelem_I2: case AstCode.Ldelem_I4: case AstCode.Ldelem_I8: case AstCode.Ldelem_R4: case AstCode.Ldelem_R8: case AstCode.Ldelem_U1: case AstCode.Ldelem_U2: case AstCode.Ldelem_U4: case AstCode.Ldelem_Ref: case AstCode.Ldelem_Any: { var arrayR = args[0].Result; var indexR = args[1].Result; var arrayType = node.Arguments[0].GetResultType(); var elementType = arrayType.ElementType; // Allocate registry for value var valueR = frame.AllocateTemp(elementType.GetReference(targetPackage)); // Get from array var first = this.Add(node.SourceLocation, node.AGet(), valueR, arrayR, indexR); return new RLRange(first, valueR); } #endregion #region Local variables / arguments case AstCode.Ldloc: { var variable = (AstVariable) node.Operand; var valueR = frame.GetArgument(variable); return new RLRange(args, valueR); } case AstCode.Ldthis: if (frame.ThisArgument == null) throw new ArgumentException("No this in current method"); return new RLRange(args, frame.ThisArgument); case AstCode.Stloc: { var variable = (AstVariable) node.Operand; var resultType = node.Arguments[0].GetResultType(); var valueR = args[0].Result; var first = this.Add(node.SourceLocation, RCode.Nop); // Convert value if needed bool isConverted; var converted = this.ConvertTypeBeforeStore(node.SourceLocation, resultType, variable.Type, valueR, targetPackage, frame, compiler, out isConverted); if (isConverted) valueR = converted.Result; // Store in variable var variableR = frame.GetArgument(variable); return new RLRange(args, first, this.Add(node.SourceLocation, node.Move(), variableR, valueR), variableR); } #endregion #region Fields case AstCode.Ldfld: { var fieldRef = (XFieldReference) node.Operand; var field = fieldRef.Resolve(); var dField = field.GetReference(targetPackage); var fieldType = field.FieldType; // Allocate register var valueR = frame.AllocateTemp(fieldType.GetReference(targetPackage)); // Get from field var iget = this.Add(node.SourceLocation, field.IGet(), dField, valueR, args[0].Result); return new RLRange(iget, valueR); } case AstCode.Stfld: { var fieldRef = (XFieldReference) node.Operand; var field = fieldRef.Resolve(); var dField = field.GetReference(targetPackage); var type = node.InferredType; var first = this.Add(node.SourceLocation, RCode.Nop); var valueR = args[1].Result; // Perform type conversion if needed bool isConverted; var converted = this.ConvertTypeBeforeStore(node.SourceLocation, type, field.FieldType, valueR, targetPackage, frame, compiler, out isConverted); if (isConverted) valueR = converted.Result; // Store in field var iput = this.Add(node.SourceLocation, field.IPut(), dField, valueR, args[0].Result); return new RLRange(first, iput, valueR); } case AstCode.Ldsfld: { var field = (XFieldReference) node.Operand; XFieldDefinition fieldDef; field.TryResolve(out fieldDef); var fieldType = field.FieldType; // Allocate register var valueR = frame.AllocateTemp(fieldType.GetReference(targetPackage)); string resourceName; if ((fieldDef != null) && fieldDef.TryGetResourceIdAttribute(out resourceName)) { // Replace by resource id. var id = FindResourceId(compiler.ResourceTable, resourceName); return new RLRange(args, this.Add(node.SourceLocation, RCode.Const, id, valueR), valueR); } else { // Normal get from field var dField = field.GetReference(targetPackage); var iget = this.Add(node.SourceLocation, node.SGet(), dField, valueR); return new RLRange(iget, valueR); } } case AstCode.Stsfld: { var field = (XFieldReference) node.Operand; var dField = field.GetReference(targetPackage); var type = node.InferredType; var first = this.Add(node.SourceLocation, RCode.Nop); var valueR = args[0].Result; // Perform type conversion if needed bool isConverted; var converted = this.ConvertTypeBeforeStore(node.SourceLocation, type, field.FieldType, valueR, targetPackage, frame, compiler, out isConverted); if (isConverted) valueR = converted.Result; // Store in field var sput = this.Add(node.SourceLocation, node.SPut(), dField, valueR); return new RLRange(first, sput, valueR); } #endregion #region Object model case AstCode.Newobj: // IL new { var ilCtorRef = (XMethodReference) node.Operand; var ilType = ilCtorRef.DeclaringType; // New normal object var ilCtor = ilCtorRef.Resolve(); var dCtor = ilCtor.GetReference(targetPackage); var dType = ilType.GetReference(targetPackage); // Create instance var r = frame.AllocateTemp(dType); var first = this.Add(node.SourceLocation, RCode.New_instance, dType, r); // Prepare arguments int argsOffset; List<RLRange> originalArgs; ConvertParametersBeforeCall(node, args, ilCtor, out argsOffset, out originalArgs); // Collect arguments var arguments = args.SelectMany(x => x.Result.Registers).ToList(); // Insert this argument arguments.Insert(0, r); // Invoke ctor this.Add(node.SourceLocation, RCode.Invoke_direct, dCtor, arguments); // Post process arguments ConvertParametersAfterCall(node, args, ilCtor, argsOffset, originalArgs); var last = this.Add(node.SourceLocation, RCode.Nop); return new RLRange(args, first, last, r); } case AstCode.New: // Java new { var ilType = (XTypeReference) node.Operand; // New normal object var dType = ilType.GetReference(targetPackage); // Create instance var r = frame.AllocateTemp(dType); var first = this.Add(node.SourceLocation, RCode.New_instance, dType, r); return new RLRange(args, first, r); } case AstCode.CallBaseCtor: { var dtype = currentDexMethod.Owner; var dBaseType = dtype.SuperClass as ClassDefinition; if (dBaseType == null) throw new CompilerException(string.Format("Type {0} base no superclass as definition", dtype.Fullname)); var paramCount = node.Arguments.Count - 1; var baseCtor = dBaseType.Methods.Single(x => x.IsConstructor && (x.Prototype.Parameters.Count == paramCount)); var call = this.Add(node.SourceLocation, RCode.Invoke_direct, baseCtor, args.SelectMany(x => x.Result.Registers)); return new RLRange(call, args[0].Result); } case AstCode.Castclass: { throw new NotSupportedException("castclass is not supported"); } case AstCode.SimpleCastclass: { var type = (XTypeReference) node.Operand; var dType = type.GetReference(targetPackage); if (type.IsPrimitive) { // Convert type to boxed type dType = BoxInfo.GetBoxedType(type); } // Normal cast var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame); var checkCast = this.Add(node.SourceLocation, RCode.Check_cast, dType, tmp.Result); return new RLRange(tmp, checkCast, tmp.Result); } case AstCode.Isinst: // "as" operator { throw new NotSupportedException("isinst is not supported"); } case AstCode.InstanceOf: // "is" operator { throw new NotSupportedException("instanceof is not supported"); } case AstCode.SimpleInstanceOf: // "is" operator { var type = (XTypeReference) node.Operand; var dType = type.GetReference(targetPackage); if (type.IsPrimitive) { // Convert type to boxed type dType = BoxInfo.GetBoxedType(type); } // Normal "is" var rResult = frame.AllocateTemp(PrimitiveType.Boolean); var instanceOf = this.Add(node.SourceLocation, RCode.Instance_of, dType, rResult, args[0].Result); return new RLRange(instanceOf, rResult); } #endregion #region Generics case AstCode.LdGenericInstanceField: { var giField = GetGenericInstanceField(); var r = frame.AllocateTemp(FrameworkReferences.ClassArray); var iget = this.Add(node.SourceLocation, RCode.Iget_object, giField, r, frame.ThisArgument); return new RLRange(iget, r); } case AstCode.StGenericInstanceField: { var giField = GetGenericInstanceField(); var r = args[0].Result; var iput = this.Add(node.SourceLocation, RCode.Iput_object, giField, r, frame.ThisArgument); return new RLRange(iput, r); } case AstCode.LdGenericInstanceTypeArgument: { if (frame.GenericInstanceTypeArgument == null) { throw new CompilerException(string.Format("Method {0} has no generic instance type argument", currentMethod.FullName)); } return new RLRange(frame.GenericInstanceTypeArgument); } case AstCode.LdGenericInstanceMethodArgument: { if (frame.GenericInstanceMethodArgument == null) { throw new CompilerException(string.Format("Method {0} has no generic instance method argument", currentMethod.FullName)); } return new RLRange(frame.GenericInstanceMethodArgument); } case AstCode.UnboxFromGeneric: { // Get result var elementType = (XTypeReference) node.Operand; var r = args[0].Result; var start = this.Add(node.SourceLocation, RCode.Nop); var last = start; // Convert result when needed if (elementType.IsGenericParameter) { var returnType = node.GetResultType(); var tmp = this.Unbox(node.SourceLocation, r, returnType, compiler, targetPackage, frame); last = tmp.Last; r = tmp.Result; } else if (elementType.IsGenericParameterArray()) { var returnType = node.GetResultType(); if (returnType.IsPrimitiveArray()) { var tmp = this.UnboxGenericArrayResult(node.SourceLocation, r, returnType, targetPackage, frame, compiler); last = tmp.Last; r = tmp.Result; } else { var tmp = this.Unbox(node.SourceLocation, r, returnType, compiler, targetPackage, frame); last = tmp.Last; r = tmp.Result; } } return new RLRange(start, last, r); } #endregion case AstCode.Throw: { var @throw = this.Add(node.SourceLocation, RCode.Throw, args[0].Result); return new RLRange(@throw, null); } case AstCode.Rethrow: { if (currentExceptionRegister == null) throw new CompilerException("retrow outside catch block"); var @throw = this.Add(node.SourceLocation, RCode.Throw, currentExceptionRegister); return new RLRange(@throw, null); } case AstCode.Delegate: { //Debugger.Launch(); var delegateInfo = (Tuple<XTypeDefinition, XMethodDefinition>)node.Operand; var delegateType = compiler.GetDelegateType(delegateInfo.Item1); var delegateInstanceType = delegateType.GetOrCreateInstance(node.SourceLocation, targetPackage, delegateInfo.Item2); // Create instance var r = frame.AllocateTemp(delegateInstanceType.InstanceDefinition); var newobj = this.Add(node.SourceLocation, RCode.New_instance, delegateInstanceType.InstanceDefinition, r); // Call ctor if (delegateInstanceType.CalledMethodIsStatic) { // Call without instance argument var invokeCtor = this.Add(node.SourceLocation, RCode.Invoke_direct, delegateInstanceType.InstanceCtor, r); return new RLRange(newobj, invokeCtor, r); } else { // Call with instance argument var invokeCtor = this.Add(node.SourceLocation, RCode.Invoke_direct, delegateInstanceType.InstanceCtor, r, args[0].Result); return new RLRange(newobj, invokeCtor, r); } } case AstCode.InitArray: { var arrayData = (InitArrayData) node.Operand; var size = arrayData.Length; var type = arrayData.ArrayType; var dType = type.GetReference(targetPackage); // Allocate new array var rArray = frame.AllocateTemp(dType); var rSize = frame.AllocateTemp(PrimitiveType.Int); var start = this.Add(node.SourceLocation, RCode.Const, size, rSize); this.Add(node.SourceLocation, RCode.New_array, dType, rArray, rSize); // Initialize array if (arrayData.IsSupportedByFillArrayData()) { // Use fill-array-data this.Add(node.SourceLocation, RCode.Fill_array_data, arrayData.Values, rArray); } else { // Use const/aput sequence var rValue = frame.AllocateTemp(arrayData.ArrayType.ElementType.GetReference(targetPackage)); var rIndex = frame.AllocateTemp(PrimitiveType.Int); var isWide = arrayData.ArrayType.ElementType.IsWide(); var constCode = isWide ? RCode.Const_wide : RCode.Const; var aputCode = arrayData.ArrayType.APut(); var convertCode = arrayData.ArrayType.AConstConvertBeforePut(); var valueConverter = arrayData.ArrayType.ElementType.ConstValueConverter(false); // Initialize index this.Add(node.SourceLocation, RCode.Const, 0, rIndex); // aput for each for (var i = 0; i < arrayData.Length; i++) { var value = valueConverter(arrayData.Values.GetValue(i)); this.Add(node.SourceLocation, constCode, value, rValue); if (convertCode != RCode.Nop) { this.Add(node.SourceLocation, convertCode, rValue, rValue); } this.Add(node.SourceLocation, aputCode, rValue, rArray, rIndex); if (i + 1 < arrayData.Length) { // Increment index this.Add(node.SourceLocation, RCode.Add_int_lit, 1, rIndex, rIndex); } } } //throw new NotImplementedException(); return new RLRange(this.Add(node.SourceLocation, RCode.Nop), rArray); } case AstCode.InitArrayFromArguments: { var size = args.Count; var type = (XArrayType) node.Operand; var dType = type.GetReference(targetPackage); // Allocate new array var rArray = frame.AllocateTemp(dType); var rSize = frame.AllocateTemp(PrimitiveType.Int); var start = this.Add(node.SourceLocation, RCode.Nop); this.Add(node.SourceLocation, RCode.Const, size, rSize); this.Add(node.SourceLocation, RCode.New_array, dType, rArray, rSize); // Use const/aput sequence var rIndex = frame.AllocateTemp(PrimitiveType.Int); var aputCode = type.APut(); // Initialize index this.Add(node.SourceLocation, RCode.Const, 0, rIndex); // aput for each for (var i = 0; i < size; i++) { this.Add(node.SourceLocation, aputCode, args[i].Result, rArray, rIndex); if (i + 1 < size) { // Increment index this.Add(node.SourceLocation, RCode.Add_int_lit, 1, rIndex, rIndex); } } return new RLRange(start, this.Add(node.SourceLocation, RCode.Nop), rArray); } case AstCode.LdClass: { var ilType = (XTypeReference)node.Operand; var dType = ilType.GetReference(targetPackage); // Load type var r = frame.AllocateTemp(FrameworkReferences.Class); var first = this.Add(node.SourceLocation, RCode.Const_class, dType, r); return new RLRange(args, first, r); } case AstCode.Ldtoken: { throw new NotSupportedException("ldtoken is not supported"); } default: string opcodeName; #if DEBUG //Debugger.Launch(); opcodeName = node.Code.ToString(); #else opcodeName = ((int)node.Code).ToString(); #endif var source = FormatSource(node); var msg = string.Format("Unexpected opcode {0} in {1}.", opcodeName, source); throw new ArgumentException(msg); } }
public RLRange BranchToFinally_Leave(Instruction insTarget, ref int insIdx) { var target = new FinallyTarget { IsLeave = true, Destination = insTarget}; return BranchToFinally(target, null, AstNode.NoSource, ref insIdx); }
public RLRange BranchToFinally_FallOut(ISourceLocation sourceLocation, Instruction insTarget, List<RLRange> args) { int insIdx = _compiler.instructions.Count; var target = new FinallyTarget {Destination = insTarget, IsFallOut = true}; return BranchToFinally(target, args, sourceLocation, ref insIdx); }
/// <summary> /// Create the body of the invoke method. /// </summary> private static MethodBody CreateInvokeBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition calledMethod, XMethodDefinition invokeMethod, Prototype invokePrototype, Prototype calledMethodPrototype, FieldDefinition instanceField, ClassReference delegateClass) { var body = new MethodBody(null); var rthis = body.AllocateRegister(RCategory.Argument, RType.Object); foreach (var p in invokePrototype.Parameters) { if (p.Type.IsWide()) { body.AllocateWideRegister(RCategory.Argument); } else { var type = (p.Type is PrimitiveType) ? RType.Value : RType.Object; body.AllocateRegister(RCategory.Argument, type); } } var incomingMethodArgs = body.Registers.ToArray(); // Create code var ins = body.Instructions; Register instance = null; if (!calledMethod.IsStatic) { // load instance instance = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Iget_object, instance, rthis) { Operand = instanceField }); } // Invoke var calledMethodRef = calledMethod.GetReference(targetPackage); var inputArgs = calledMethod.IsStatic ? incomingMethodArgs.Skip(1).ToArray() : incomingMethodArgs; // Cast arguments (if needed) var outputArgs = new List<Register>(); if (!calledMethod.IsStatic) { outputArgs.Add(instance); } var parameterIndex = 0; for (var i = calledMethod.IsStatic ? 0 : 1; i < inputArgs.Length; ) { var invokeType = invokePrototype.Parameters[parameterIndex].Type; var inputIsWide = invokeType.IsWide(); var calledType = calledMethodPrototype.Parameters[parameterIndex].Type; if (!invokeType.Equals(calledType)) { // Add cast / unbox var source = inputIsWide ? new RegisterSpec(inputArgs[i], inputArgs[i + 1], invokeType) : new RegisterSpec(inputArgs[i], null, invokeType); var tmp = ins.Unbox(sequencePoint, source, calledMethod.Parameters[parameterIndex].ParameterType, compiler, targetPackage, body); outputArgs.Add(tmp.Result.Register); if (calledType.IsWide()) { outputArgs.Add(tmp.Result.Register2); } } else { outputArgs.Add(inputArgs[i]); if (calledType.IsWide()) { outputArgs.Add(inputArgs[i + 1]); } } i += inputIsWide ? 2 : 1; parameterIndex++; } // Actual call ins.Add(new Instruction(calledMethod.Invoke(calledMethod, null), calledMethodRef, outputArgs.ToArray())); // Collect return value var invokeReturnType = invokePrototype.ReturnType; var calledReturnType = calledMethodPrototype.ReturnType; var needsBoxing = !invokeReturnType.Equals(calledReturnType); Instruction returnInstruction; if (calledReturnType.IsWide()) { var r = body.AllocateWideRegister(RCategory.Temp); ins.Add(new Instruction(RCode.Move_result_wide, r.Item1)); if (needsBoxing) { // Box var source = new RegisterSpec(r.Item1, r.Item2, calledReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return wide returnInstruction = new Instruction(RCode.Return_wide, r.Item1); } } else if (calledMethod.ReturnType.IsVoid()) { // Void return returnInstruction = new Instruction(RCode.Return_void); } else if (calledReturnType is PrimitiveType) { // Single register return var r = body.AllocateRegister(RCategory.Temp, RType.Value); ins.Add(new Instruction(RCode.Move_result, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return, r); } } else { var r = body.AllocateRegister(RCategory.Temp, RType.Object); ins.Add(new Instruction(RCode.Move_result_object, r)); if (needsBoxing) { // Box var source = new RegisterSpec(r, null, invokeReturnType); var tmp = ins.Box(sequencePoint, source, invokeMethod.ReturnType, targetPackage, body); returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register); } else { // Return returnInstruction = new Instruction(RCode.Return_object, r); } } // Call next delegate (if any) var next = body.AllocateRegister(RCategory.Temp, RType.Object); var multicastDelegateType = new ClassReference(targetPackage.NameConverter.GetConvertedFullName("System.MulticastDelegate")); var nextReference = new FieldReference(multicastDelegateType, "next", multicastDelegateType); ins.Add(new Instruction(RCode.Iget_object, nextReference, new[] { next, rthis })); // load this.next var afterCallNext = new Instruction(RCode.Nop); ins.Add(new Instruction(RCode.If_eqz, afterCallNext, new[] { next })); // if next == null, continue ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { next })); var nextInvokeMethod = new MethodReference(delegateClass, "Invoke", invokePrototype); var nextInvokeArgs = new[] { next }.Concat(incomingMethodArgs.Skip(1)).ToArray(); ins.Add(new Instruction(RCode.Invoke_virtual, nextInvokeMethod, nextInvokeArgs)); ins.Add(afterCallNext); // Add return instructions ins.Add(returnInstruction); return body; }
private Instruction TryCatchGotoEnd(FinallyBlockState state, Instruction last) { if (state.HasFinally) { var rlrange = state.BranchToFinally_FallOut(AstNode.NoSource, last, new List<RLRange>()); return rlrange.Last; } // goto end return this.Add(AstNode.NoSource, RCode.Goto, last); }