/// <summary> /// Gets the type of this variable. /// </summary> protected override TTypeRef GetType <TTypeRef>(ITypeResolver <TTypeRef> typeResolver) { if ((originalVariable == null) || (originalVariable.VariableType == null)) { return(default(TTypeRef)); } var xVarType = XBuilder.AsTypeReference(Type.Module, originalVariable.VariableType); return(typeResolver.GetTypeReference(xVarType)); }
/// <summary> /// Gets a class reference for the given type reference. /// </summary> internal static TypeReference GetReference(this JvmClassLib.TypeReference type, XTypeUsageFlags usageFlags, DexTargetPackage targetPackage, XModule module) { if (type == null) { throw new ArgumentNullException("type"); } var xType = XBuilder.AsTypeReference(module, type, usageFlags); return(xType.GetReference(targetPackage)); }
/// <summary> /// Gets a class reference for the given type reference. /// </summary> internal static TypeReference GetReference(this Mono.Cecil.TypeReference type, DexTargetPackage targetPackage, XModule module) { if (type == null) { throw new ArgumentNullException("type"); } var xType = XBuilder.AsTypeReference(module, type); return(xType.GetReference(targetPackage)); }
protected override XTypeDefinition CreateXType(XTypeDefinition parentXType) { var typeDef = (XBuilder.ILTypeDefinition)XBuilder.AsTypeReference(Compiler.Module, Type) .Resolve(); string name = NameConverter.GetNullableClassName(typeDef.Name); XSyntheticTypeFlags xflags = default(XSyntheticTypeFlags); return(XSyntheticTypeDefinition.Create(Compiler.Module, parentXType, xflags, typeDef.Namespace, name, Compiler.Module.TypeSystem.Object, string.Join(":", Type.Scope.Name, Type.MetadataToken.ToScopeId(), "Nullable"))); }
void ConvertParameters(List <ByteCode> body) { AstVariable thisParameter = null; if (methodDef.HasThis) { TypeReference type = methodDef.DeclaringType; thisParameter = new AstILVariable("this", XBuilder.AsTypeReference(module, type.IsValueType ? new ByReferenceType(type) : type), methodDef.Body.ThisParameter); } foreach (var p in methodDef.Parameters) { Parameters.Add(new AstILVariable(p.Name, XBuilder.AsTypeReference(module, p.ParameterType), p)); } if (Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) { // last parameter must be 'value', so rename it Parameters.Last().Name = "value"; } foreach (ByteCode byteCode in body) { ParameterDefinition p; switch (byteCode.Code) { case AstCode.__Ldarg: p = (ParameterDefinition)byteCode.Operand; byteCode.Code = AstCode.Ldloc; byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; break; case AstCode.__Starg: p = (ParameterDefinition)byteCode.Operand; byteCode.Code = AstCode.Stloc; byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; break; case AstCode.__Ldarga: p = (ParameterDefinition)byteCode.Operand; byteCode.Code = AstCode.Ldloca; byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; break; } } if (thisParameter != null) { this.Parameters.Add(thisParameter); } }
/// <summary> /// Implement the class now that all classes have been created /// </summary> protected override void CreateMembers(DexTargetPackage targetPackage) { // Build ctors foreach (var baseCtor in GetBaseClassCtors()) { // Build ctor var prototype = PrototypeBuilder.BuildPrototype(Compiler, targetPackage, null, baseCtor); var ctor = new MethodDefinition(Class, "<init>", prototype); ctor.AccessFlags = AccessFlags.Public | AccessFlags.Constructor; Class.Methods.Add(ctor); // Create ctor body var ctorBody = CreateCtorBody(prototype); targetPackage.Record(new CompiledMethod { DexMethod = ctor, RLBody = ctorBody }); } // build original type field // Create field definition var dfield = new Dot42.DexLib.FieldDefinition(); dfield.Owner = Class; dfield.Name = "underlying$"; dfield.IsSynthetic = true; dfield.IsFinal = true; dfield.IsStatic = true; dfield.IsPublic = true; dfield.Type = Compiler.Module.TypeSystem.Type.GetClassReference(targetPackage); // not sure if GetClassReference is the best way to go forward here. // might depend on the sort order of the class builders. var underlyingType = XBuilder.AsTypeReference(Compiler.Module, Type); dfield.Value = underlyingType.GetClassReference(targetPackage); Class.Fields.Add(dfield); }
private static MethodDefinition CreateFactoryMethod(AssemblyCompiler compiler, DexTargetPackage targetPackage, CustomAttribute attribute, AttributeAnnotationMapping mapping) { var targetClass = mapping.AttributeClass; // is this really the right place for the factory methods? ISourceLocation seqp = null; var attributeTypeDef = attribute.AttributeType.Resolve(); // create method string methodName = CreateAttributeFactoryMethodName(targetClass); MethodDefinition method = new MethodDefinition(targetClass, methodName, new Prototype(mapping.AttributeClass)); method.AccessFlags = AccessFlags.Public | AccessFlags.Static | AccessFlags.Synthetic; targetClass.Methods.Add(method); // create method body MethodBody body = new MethodBody(null); // Allocate attribute Register attributeReg = body.AllocateRegister(RCategory.Temp, RType.Object); body.Instructions.Add(seqp, RCode.New_instance, mapping.AttributeClass, attributeReg); // collect ctor arguments List <Register> ctorArgRegs = new List <Register>() { attributeReg }; foreach (var p in attribute.ConstructorArguments) { XTypeReference xType = XBuilder.AsTypeReference(compiler.Module, p.Type); Register[] valueRegs = CreateInitializeValueInstructions(seqp, body, xType, p, compiler, targetPackage); ctorArgRegs.AddRange(valueRegs); } // Invoke ctor DexLib.MethodReference dctor = attribute.Constructor.GetReference(targetPackage, compiler.Module); body.Instructions.Add(seqp, RCode.Invoke_direct, dctor, ctorArgRegs.ToArray()); // set field values foreach (var p in attribute.Fields) { var field = GetField(attributeTypeDef, p.Name); var xField = XBuilder.AsFieldReference(compiler.Module, field); Register[] valueRegs = CreateInitializeValueInstructions(seqp, body, xField.FieldType, p.Argument, compiler, targetPackage); body.Instructions.Add(seqp, xField.FieldType.IPut(), xField.GetReference(targetPackage), valueRegs[0], attributeReg); } // set property values foreach (var p in attribute.Properties) { PropertyDefinition property = GetSettableProperty(attributeTypeDef, p.Name); XTypeReference xType = XBuilder.AsTypeReference(compiler.Module, property.PropertyType); Register[] valueRegs = CreateInitializeValueInstructions(seqp, body, xType, p.Argument, compiler, targetPackage); XMethodDefinition xSetMethod = XBuilder.AsMethodDefinition(compiler.Module, property.SetMethod); body.Instructions.Add(seqp, xSetMethod.Invoke(xSetMethod, null), xSetMethod.GetReference(targetPackage), new[] { attributeReg }.Concat(valueRegs).ToArray()); } // Return attribute body.Instructions.Add(seqp, RCode.Return_object, attributeReg); // Register method body targetPackage.Record(new CompiledMethod() { DexMethod = method, RLBody = body }); // Return method return(method); }
/// <summary> /// Create all annotations for this method /// </summary> internal virtual void CreateAnnotations(DexTargetPackage targetPackage) { // Build method annotations AttributeAnnotationInstanceBuilder.CreateAttributeAnnotations(compiler, method, dmethod, targetPackage); // only add generics annotation for getters or setters or constructors if (method.IsGetter) { // Note that the return type might has been // changed above, to compensate for interface // inheritance and generic specialization. // We need to use the original declaration. // TODO: why not get rid of "OriginalReturnType" // and use the IL's return type?? var returnType = xMethod.OriginalReturnType; var xType = XBuilder.AsTypeReference(compiler.Module, returnType); dmethod.AddGenericDefinitionAnnotationIfGeneric(xType, compiler, targetPackage); } else if (method.IsSetter) { for (int i = 0; i < xMethod.Parameters.Count; ++i) { var dp = dmethod.Prototype.Parameters[i]; dp.AddGenericDefinitionAnnotationIfGeneric(xMethod.Parameters[i].ParameterType, compiler, targetPackage); } } else if (method.IsConstructor && !method.IsStatic) { // Add parameter names and original access flags, as these might be important // in serialization and/or dependency injection. var reflectionInfo = compiler.GetDot42InternalType(InternalConstants.ReflectionInfoAnnotation) .GetClassReference(targetPackage); var annotation = new Annotation { Type = reflectionInfo, Visibility = AnnotationVisibility.Runtime }; bool isPublic = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; // Not sure if it makes any sense to remap the access flags. bool isProtected = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; bool isInternal = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem || (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; bool isPrivate = (method.OriginalAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; // only create accessFlags if they differs from java's if (isPublic != dmethod.IsPublic || isProtected != dmethod.IsProtected || isPrivate != dmethod.IsPrivate || isInternal) { int accessFlags = 0; if (isPublic) { accessFlags |= 0x01; } if (isProtected) { accessFlags |= 0x02; } if (isPrivate) { accessFlags |= 0x04; } if (isInternal) { accessFlags |= 0x08; } annotation.Arguments.Add(new AnnotationArgument(InternalConstants.ReflectionInfoAccessFlagsField, accessFlags)); } if (method.Parameters.Count > 0) { annotation.Arguments.Add(new AnnotationArgument(InternalConstants.ReflectionInfoParameterNamesField, method.Parameters.Select(p => p.Name).ToArray())); } if (annotation.Arguments.Count > 0) { dmethod.Annotations.Add(annotation); } //for (int i = 0; i < xMethod.Parameters.Count; ++i) //{ // var dp = dmethod.Prototype.Parameters[i]; // dp.AddGenericDefinitionAnnotationIfGeneric(xMethod.Parameters[i].ParameterType, compiler, // targetPackage); //} } }
/// <summary> /// Make a xtype reference for a java class name. /// </summary> private XTypeReference AsTypeReference(string className, XTypeUsageFlags usageFlags) { return(XBuilder.AsTypeReference(module, className, usageFlags)); }
/// <summary> /// Make a .NET type reference for a java type reference. /// </summary> private XTypeReference AsTypeReference(JvmClassLib.TypeReference javaRef, XTypeUsageFlags usageFlags) { return(XBuilder.AsTypeReference(module, javaRef, usageFlags)); }
/// <summary> /// Make a .NET type reference for a java class file. /// </summary> private XTypeReference AsTypeReference(ClassFile classFile, XTypeUsageFlags usageFlags) { return(XBuilder.AsTypeReference(module, classFile, usageFlags)); }
/// <summary> /// Convert the given interface method if it has explicit implementations. /// </summary> private void ConvertInterfaceMethod(TypeDefinition iType, MethodDefinition iMethod) { var implementations = GetImplementations(iMethod); var iMethodIsJavaWithGenericParams = iMethod.IsJavaMethodWithGenericParams(); var iMethodContainsGenericParams = iMethod.ContainsGenericParameter; if (!iMethodIsJavaWithGenericParams && !iMethodContainsGenericParams && (!implementations.Any(x => x.IsExplicitImplementation()))) { // There are no explicit implementation. // No need to convert return; } // Rename method string newName; bool createExplicitStubs = true; var oldName = iMethod.Name; var attr = iMethod.GetDexOrJavaImportAttribute(); if (attr != null) { string className; string memberName; string descriptor; attr.GetDexOrJavaImportNames(iMethod, out memberName, out descriptor, out className); newName = memberName; } else if ((attr = iMethod.GetDexNameAttribute()) != null) { newName = (string)(attr.ConstructorArguments[0].Value); createExplicitStubs = false; } else { var module = reachableContext.Compiler.Module; var xiType = XBuilder.AsTypeReference(module, iType); newName = methodNames.GetUniqueName(NameConverter.GetConvertedName(xiType) + "_" + iMethod.Name); oldName = newName; } Rename(iMethod, newName); // Update implementations foreach (var impl in implementations) { if (impl.IsExplicitImplementation()) { // Convert to implicit impl.IsPublic = true; // Rename Rename(impl, newName); // Update names of overrides foreach (var @override in impl.Overrides) { @override.Name = newName; } } else if (!(impl.HasDexImportAttribute() || impl.HasJavaImportAttribute())) { // Add stub redirecting explicit implementation to implicit implementation if (createExplicitStubs) { CreateExplicitStub(impl, newName, oldName, iMethod, iMethodIsJavaWithGenericParams /*|| iMethodContainsGenericParams*/); } } } }
/// <summary> /// Create the XType for this builder. /// </summary> protected virtual XTypeDefinition CreateXType(XTypeDefinition parentXType) { return(XBuilder.AsTypeReference(compiler.Module, typeDef).Resolve()); }
/// <summary> /// Create a method definition for the builder method that builds a custom attribute from an annotation. /// </summary> private static MethodDefinition CreateBuildMethod( ISourceLocation seqp, Mono.Cecil.MethodDefinition ctor, List <MethodDefinition> paramGetMethods, AssemblyCompiler compiler, DexTargetPackage targetPackage, ClassDefinition attributeClass, AttributeAnnotationInterface mapping) { // Create method definition string name = CreateBuildMethodName(attributeClass); TypeReference attributeTypeRef = ctor.DeclaringType.GetReference(targetPackage, compiler.Module); MethodDefinition method = new MethodDefinition(attributeClass, name, new Prototype(attributeTypeRef, new Parameter(mapping.AnnotationInterfaceClass, "ann"))); method.AccessFlags = AccessFlags.Public | AccessFlags.Static | AccessFlags.Synthetic; attributeClass.Methods.Add(method); // Create method body MethodBody body = new MethodBody(null); Register annotationReg = body.AllocateRegister(RCategory.Argument, RType.Object); //body.Instructions.Add(seqp, RCode.Check_cast, mapping.AnnotationInterfaceClass, annotationReg); // Allocate attribute Register attributeReg = body.AllocateRegister(RCategory.Temp, RType.Object); body.Instructions.Add(seqp, RCode.New_instance, attributeClass, attributeReg); // Get ctor arguments List <Register> ctorArgRegs = new List <Register>(); foreach (MethodDefinition p in paramGetMethods) { TypeReference paramType = p.Prototype.ReturnType; Register[] valueRegs = CreateLoadValueSequence(seqp, body, paramType, annotationReg, p); ctorArgRegs.AddRange(valueRegs); } // Invoke ctor DexLib.MethodReference dctor = ctor.GetReference(targetPackage, compiler.Module); body.Instructions.Add(seqp, RCode.Invoke_direct, dctor, new[] { attributeReg }.Concat(ctorArgRegs).ToArray()); // Get field values foreach (var fieldMap in mapping.FieldToGetMethodMap) { Mono.Cecil.FieldDefinition field = fieldMap.Key; MethodDefinition getter = fieldMap.Value; Register[] valueRegs = CreateLoadValueSequence(seqp, body, getter.Prototype.ReturnType, annotationReg, getter); DexLib.FieldReference dfield = field.GetReference(targetPackage, compiler.Module); XModel.XTypeReference xFieldType = XBuilder.AsTypeReference(compiler.Module, field.FieldType); body.Instructions.Add(seqp, xFieldType.IPut(), dfield, valueRegs[0], attributeReg); } // Get property values foreach (var propertyMap in mapping.PropertyToGetMethodMap) { PropertyDefinition property = propertyMap.Key; MethodDefinition getter = propertyMap.Value; Register[] valueRegs = CreateLoadValueSequence(seqp, body, getter.Prototype.ReturnType, annotationReg, getter); DexLib.MethodReference dmethod = property.SetMethod.GetReference(targetPackage, compiler.Module); XModel.XMethodDefinition xSetMethod = XBuilder.AsMethodDefinition(compiler.Module, property.SetMethod); body.Instructions.Add(seqp, xSetMethod.Invoke(xSetMethod, null), dmethod, new[] { attributeReg }.Concat(valueRegs).ToArray()); } // Return attribute body.Instructions.Add(seqp, RCode.Return_object, attributeReg); // Register method body targetPackage.Record(new CompiledMethod() { DexMethod = method, RLBody = body }); // Return method return(method); }
private List <AstNode> ConvertToAst(IEnumerable <ByteCode> body) { var ast = new List <AstNode>(); // Convert stack-based IL code to ILAst tree foreach (var byteCode in body) { var ilRange = new InstructionRange(byteCode.Offset, byteCode.EndOffset); if (byteCode.StackBefore == null) { // Unreachable code continue; } var expr = new AstExpression(byteCode.SequencePoint, byteCode.Code, byteCode.Operand); expr.ILRanges.Add(ilRange); if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0) { var prefixes = new AstExpressionPrefix[byteCode.Prefixes.Length]; for (var i = 0; i < prefixes.Length; i++) { var operand = byteCode.Prefixes[i].Operand; if (operand is FieldReference) { operand = XBuilder.AsFieldReference(module, (FieldReference)operand); } else if (operand is MethodReference) { operand = XBuilder.AsMethodReference(module, (MethodReference)operand); } else if (operand is TypeReference) { operand = XBuilder.AsTypeReference(module, (TypeReference)operand); } prefixes[i] = new AstExpressionPrefix((AstCode)byteCode.Prefixes[i].OpCode.Code, operand); } expr.Prefixes = prefixes; } // Label for this instruction if (byteCode.Label != null) { ast.Add(byteCode.Label); } // Reference arguments using temporary variables var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { var slot = byteCode.StackBefore[i]; expr.Arguments.Add(new AstExpression(byteCode.SequencePoint, AstCode.Ldloc, slot.LoadFrom)); } // Store the result to temporary variable(s) if needed if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) { ast.Add(expr); } else if (byteCode.StoreTo.Count == 1) { ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, byteCode.StoreTo[0], expr)); } else { var tmpVar = new AstGeneratedVariable("expr_" + byteCode.Offset.ToString("X2"), byteCode.StoreTo.Select(x => x.OriginalName).FirstOrDefault()); ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, tmpVar, expr)); foreach (var storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) { ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, storeTo, new AstExpression(byteCode.SequencePoint, AstCode.Ldloc, tmpVar))); } } } return(ast); }
List <AstNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs) { var ast = new List <AstNode>(); while (ehs.Any()) { var tryCatchBlock = new AstTryCatchBlock(null); // Find the first and widest scope var tryStart = ehs.Min(eh => eh.TryStart.Offset); var tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset); var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList(); // Remember that any part of the body migt have been removed due to unreachability // Cut all instructions up to the try block { var tryStartIdx = 0; while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) { tryStartIdx++; } ast.AddRange(ConvertToAst(CollectionExtensions.CutRange(body, 0, tryStartIdx))); } // Cut the try block { var nestedEHs = new HashSet <ExceptionHandler>( ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd))); ehs.ExceptWith(nestedEHs); var tryEndIdx = 0; while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) { tryEndIdx++; } var converted = ConvertToAst(CollectionExtensions.CutRange(body, 0, tryEndIdx), nestedEHs); tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted); } // Cut all handlers tryCatchBlock.CatchBlocks.Clear(); foreach (var eh in handlers) { var handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset; var startIdx = 0; while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) { startIdx++; } var endIdx = 0; while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) { endIdx++; } var nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset))); ehs.ExceptWith(nestedEHs); var handlerAst = ConvertToAst(CollectionExtensions.CutRange(body, startIdx, endIdx - startIdx), nestedEHs); if (eh.HandlerType == ExceptionHandlerType.Catch) { var catchType = eh.CatchType.IsSystemObject() ? module.TypeSystem.Exception : XBuilder.AsTypeReference(module, eh.CatchType); var catchBlock = new AstTryCatchBlock.CatchBlock(handlerAst.Select(x => x.SourceLocation).FirstOrDefault(), tryCatchBlock) { ExceptionType = catchType, Body = handlerAst }; // Handle the automatically pushed exception on the stack var ldexception = ldexceptions[eh]; if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0) { // Exception is not used catchBlock.ExceptionVariable = null; } else if (ldexception.StoreTo.Count == 1) { var first = catchBlock.Body[0] as AstExpression; if (first != null && first.Code == AstCode.Pop && first.Arguments[0].Code == AstCode.Ldloc && first.Arguments[0].Operand == ldexception.StoreTo[0]) { // The exception is just poped - optimize it all away; if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks) { catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null); } else { catchBlock.ExceptionVariable = null; } catchBlock.Body.RemoveAt(0); } else { catchBlock.ExceptionVariable = ldexception.StoreTo[0]; } } else { var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null); catchBlock.ExceptionVariable = exTemp; foreach (var storeTo in ldexception.StoreTo) { catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp))); } } tryCatchBlock.CatchBlocks.Add(catchBlock); } else if (eh.HandlerType == ExceptionHandlerType.Finally) { tryCatchBlock.FinallyBlock = new AstBlock(handlerAst); } else if (eh.HandlerType == ExceptionHandlerType.Fault) { tryCatchBlock.FaultBlock = new AstBlock(handlerAst); } else { // TODO: ExceptionHandlerType.Filter } } ehs.ExceptWith(handlers); ast.Add(tryCatchBlock); } // Add whatever is left ast.AddRange(ConvertToAst(body)); return(ast); }
/// <summary> /// Analyse the instructions in the method code and convert them to a ByteCode list. /// </summary> private List <ByteCode> StackAnalysis() { var instrToByteCode = new Dictionary <Instruction, ByteCode>(); // Create temporary structure for the stack analysis var body = new List <ByteCode>(methodDef.Body.Instructions.Count); List <Instruction> prefixes = null; foreach (var inst in methodDef.Body.Instructions) { if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { if (prefixes == null) { prefixes = new List <Instruction>(1); } prefixes.Add(inst); continue; } var code = (AstCode)inst.OpCode.Code; var operand = inst.Operand; AstCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body); var byteCode = new ByteCode { Offset = inst.Offset, EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize, Code = code, Operand = operand, PopCount = inst.GetPopDelta(methodDef), PushCount = inst.GetPushDelta(), SequencePoint = SequencePointWrapper.Wrap(inst.SequencePoint) }; if (prefixes != null) { instrToByteCode[prefixes[0]] = byteCode; byteCode.Offset = prefixes[0].Offset; byteCode.Prefixes = prefixes.ToArray(); prefixes = null; } else { instrToByteCode[inst] = byteCode; } body.Add(byteCode); } for (int i = 0; i < body.Count - 1; i++) { body[i].Next = body[i + 1]; } var agenda = new Stack <ByteCode>(); var varCount = methodDef.Body.Variables.Count; var exceptionHandlerStarts = new HashSet <ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart])); // Add known states if (methodDef.Body.HasExceptionHandlers) { foreach (var ex in methodDef.Body.ExceptionHandlers) { var handlerStart = instrToByteCode[ex.HandlerStart]; handlerStart.StackBefore = new StackSlot[0]; handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { // Catch and Filter handlers start with the exeption on the stack var ldexception = new ByteCode() { Code = AstCode.Ldexception, Operand = ex.CatchType, PopCount = 0, PushCount = 1 }; ldexceptions[ex] = ldexception; handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) }; } agenda.Push(handlerStart); if (ex.HandlerType == ExceptionHandlerType.Filter) { var filterStart = instrToByteCode[ex.FilterStart]; var ldexception = new ByteCode { Code = AstCode.Ldexception, Operand = ex.CatchType, PopCount = 0, PushCount = 1 }; // TODO: ldexceptions[ex] = ldexception; filterStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) }; filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); agenda.Push(filterStart); } } } body[0].StackBefore = new StackSlot[0]; body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount); agenda.Push(body[0]); // Process agenda while (agenda.Count > 0) { var byteCode = agenda.Pop(); // Calculate new stack var newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode); // Calculate new variable state var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); if (byteCode.IsVariableDefinition) { newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false); } // After the leave, finally block might have touched the variables if (byteCode.Code == AstCode.Leave) { newVariableState = VariableSlot.MakeUknownState(varCount); } // Find all successors var branchTargets = new List <ByteCode>(); if (!byteCode.Code.IsUnconditionalControlFlow()) { if (exceptionHandlerStarts.Contains(byteCode.Next)) { // Do not fall though down to exception handler // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it } else { branchTargets.Add(byteCode.Next); } } if (byteCode.Operand is Instruction[]) { foreach (var inst in (Instruction[])byteCode.Operand) { var target = instrToByteCode[inst]; branchTargets.Add(target); // The target of a branch must have label if (target.Label == null) { target.Label = new AstLabel(target.SequencePoint, target.Name); } } } else if (byteCode.Operand is Instruction) { var target = instrToByteCode[(Instruction)byteCode.Operand]; branchTargets.Add(target); // The target of a branch must have label if (target.Label == null) { target.Label = new AstLabel(target.SequencePoint, target.Name); } } // Apply the state to successors foreach (var branchTarget in branchTargets) { if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) { if (branchTargets.Count == 1) { branchTarget.StackBefore = newStack; branchTarget.VariablesBefore = newVariableState; } else { // Do not share data for several bytecodes branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null); branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); } agenda.Push(branchTarget); } else { if (branchTarget.StackBefore.Length != newStack.Length) { throw new Exception("Inconsistent stack size at " + byteCode.Name); } // Be careful not to change our new data - it might be reused for several branch targets. // In general, be careful that two bytecodes never share data structures. bool modified = false; // Merge stacks - modify the target for (int i = 0; i < newStack.Length; i++) { var oldDefs = branchTarget.StackBefore[i].Definitions; var newDefs = oldDefs.Union(newStack[i].Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.StackBefore[i] = new StackSlot(newDefs, null); modified = true; } } // Merge variables - modify the target for (int i = 0; i < newVariableState.Length; i++) { var oldSlot = branchTarget.VariablesBefore[i]; var newSlot = newVariableState[i]; if (!oldSlot.UnknownDefinition) { if (newSlot.UnknownDefinition) { branchTarget.VariablesBefore[i] = newSlot; modified = true; } else { ByteCode[] oldDefs = oldSlot.Definitions; ByteCode[] newDefs = CollectionExtensions.Union(oldDefs, newSlot.Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false); modified = true; } } } } if (modified) { agenda.Push(branchTarget); } } } } // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid) // I belive it is safe to just remove it body.RemoveAll(b => b.StackBefore == null); // Genertate temporary variables to replace stack foreach (var byteCode in body) { int argIdx = 0; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null); byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar); foreach (ByteCode pushedBy in byteCode.StackBefore[i].Definitions) { if (pushedBy.StoreTo == null) { pushedBy.StoreTo = new List <AstVariable>(1); } pushedBy.StoreTo.Add(tmpVar); } argIdx++; } } // Try to use single temporary variable insted of several if possilbe (especially useful for dup) // This has to be done after all temporary variables are assigned so we know about all loads foreach (var byteCode in body) { if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { var locVars = byteCode.StoreTo; // For each of the variables, find the location where it is loaded - there should be preciesly one var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); // We now know that all the variables have a single load, // Let's make sure that they have also a single store - us if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode)) { // Great - we can reduce everything into single variable var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault()); byteCode.StoreTo = new List <AstVariable>() { tmpVar }; foreach (var bc in body) { for (int i = 0; i < bc.StackBefore.Length; i++) { // Is it one of the variable to be merged? if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { // Replace with the new temp variable bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar); } } } } } } // Split and convert the normal local variables ConvertLocalVariables(body); // Convert branch targets to labels and references to xreferences foreach (var byteCode in body) { if (byteCode.Operand is Instruction[]) { byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label).ToArray(); } else if (byteCode.Operand is Instruction) { byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label; } else if (byteCode.Operand is FieldReference) { byteCode.Operand = XBuilder.AsFieldReference(module, (FieldReference)byteCode.Operand); } else if (byteCode.Operand is MethodReference) { byteCode.Operand = XBuilder.AsMethodReference(module, (MethodReference)byteCode.Operand); } else if (byteCode.Operand is TypeReference) { byteCode.Operand = XBuilder.AsTypeReference(module, (TypeReference)byteCode.Operand); } } // Convert parameters to ILVariables ConvertParameters(body); return(body); }
/// <summary> /// Create code to initialize a value from an attribute. /// </summary> /// <returns>The register(s) holding the value</returns> private static Register[] CreateInitializeValueInstructions(ISourceLocation seqp, MethodBody body, XTypeReference targetType, CustomAttributeArgument value, AssemblyCompiler compiler, DexTargetPackage targetPackage) { List <Register> result = new List <Register>(); // allocate result, initialize to default value. if (targetType.IsWide()) { Tuple <Register, Register> regs = body.AllocateWideRegister(RCategory.Temp); //body.Instructions.Add(seqp, RCode.Const_wide, 0, regs.Item1); result.Add(regs.Item1); result.Add(regs.Item2); } else if (targetType.IsPrimitive) { Register reg = body.AllocateRegister(RCategory.Temp, RType.Value); //body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } else // object { Register reg = body.AllocateRegister(RCategory.Temp, RType.Object); //body.Instructions.Add(seqp, RCode.Const, 0, reg); result.Add(reg); } // load data if (value.Value == null) // must be a reference type { body.Instructions.Add(seqp, RCode.Const, 0, result[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); return(result.ToArray()); } var valueType = XBuilder.AsTypeReference(compiler.Module, value.Type); if (value.Value is CustomAttributeArgument) { // this happens if a type conversion is neccessary var nestedValue = (CustomAttributeArgument)value.Value; valueType = XBuilder.AsTypeReference(compiler.Module, nestedValue.Type); var rOrigValue = CreateInitializeValueInstructions(seqp, body, valueType, nestedValue, compiler, targetPackage); if (!nestedValue.Type.IsPrimitive) { body.Instructions.Add(seqp, RCode.Move_object, result[0], rOrigValue[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else if (!targetType.IsPrimitive) { body.Instructions.Add(seqp, RCode.Invoke_static, valueType.GetBoxValueOfMethod(), rOrigValue); body.Instructions.Add(seqp, RCode.Move_result_object, result[0]); body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else { throw new Exception(string.Format("type converstion in attribute {0}=>{1} not yet supported", valueType.FullName, targetType.FullName)); } } else if (valueType.IsArray) { var array = (CustomAttributeArgument[])value.Value; var elementType = valueType.ElementType; Register rIndex = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Const, array.Length, rIndex); body.Instructions.Add(seqp, RCode.New_array, valueType.GetReference(targetPackage), result[0], rIndex); // iterate through each value for (int i = 0; i < array.Length; i++) { Register rLoaded = CreateInitializeValueInstructions(seqp, body, elementType, array[i], compiler, targetPackage)[0]; body.Instructions.Add(seqp, RCode.Const, i, rIndex); body.Instructions.Add(seqp, valueType.APut(), rLoaded, result[0], rIndex); } } else if (targetType.IsEnum()) { var enumClass = (targetType.IsEnum()? targetType:valueType).GetReference(targetPackage); Register rEnumClass = body.AllocateRegister(RCategory.Temp, RType.Object); body.Instructions.Add(seqp, RCode.Const_class, enumClass, rEnumClass); long lVal = Convert.ToInt64(value.Value); if (lVal <= int.MaxValue && lVal >= int.MinValue) { Register regTmp = body.AllocateRegister(RCategory.Temp, RType.Value); body.Instructions.Add(seqp, RCode.Const, (int)lVal, regTmp); var get = compiler.GetDot42InternalType("Enum").Resolve() .Methods.Single(p => p.Name == "Get" && p.Parameters.Count == 2 && !p.Parameters[1].ParameterType.IsWide()) .GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, get, rEnumClass, regTmp); body.Instructions.Add(seqp, targetType.MoveResult(), result[0]); } else { var regTmp = body.AllocateWideRegister(RCategory.Temp); body.Instructions.Add(seqp, RCode.Const, (long)lVal, regTmp.Item1); var get = compiler.GetDot42InternalType("Enum").Resolve() .Methods.Single(p => p.Name == "Get" && p.Parameters.Count == 2 && p.Parameters[1].ParameterType.IsWide()) .GetReference(targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, get, rEnumClass, regTmp.Item1); body.Instructions.Add(seqp, targetType.MoveResult(), result[0]); } body.Instructions.Add(seqp, RCode.Check_cast, targetType.GetReference(targetPackage), result[0]); } else if (valueType.IsSystemString()) { body.Instructions.Add(seqp, RCode.Const_string, (string)value.Value, result[0]); } else if (valueType.IsSystemType()) { var type = XBuilder.AsTypeReference(compiler.Module, (TypeReference)value.Value); // TODO: this might not work with typeof(void) on ART runtime. body.Instructions.Add(seqp, RCode.Const_class, type.GetReference(targetPackage), result[0]); } else if (!valueType.IsPrimitive) { // can this happen? throw new Exception("invalid value type in attribute: " + targetType.FullName); } else { if (targetType.IsSystemObject()) { // can this happen? or is this always handled above? // boxing required. var rUnboxed = CreateInitializeValueInstructions(seqp, body, valueType, value, compiler, targetPackage); body.Instructions.Add(seqp, RCode.Invoke_static, valueType.GetBoxValueOfMethod(), rUnboxed); body.Instructions.Add(seqp, RCode.Move_result_object, result[0]); } else if (targetType.IsDouble()) { body.Instructions.Add(seqp, RCode.Const_wide, Convert.ToDouble(value.Value), result[0]); } else if (targetType.IsWide() && valueType.IsUInt64()) { body.Instructions.Add(seqp, RCode.Const_wide, (long)Convert.ToUInt64(value.Value), result[0]); } else if (targetType.IsWide()) { body.Instructions.Add(seqp, RCode.Const_wide, Convert.ToInt64(value.Value), result[0]); } else if (targetType.IsFloat()) { body.Instructions.Add(seqp, RCode.Const, Convert.ToSingle(value.Value), result[0]); } else { body.Instructions.Add(seqp, RCode.Const, (int)Convert.ToInt64(value.Value), result[0]); } } return(result.ToArray()); }
/// <summary> /// If possible, separates local variables into several independent variables. /// It should undo any compilers merging. /// </summary> void ConvertLocalVariables(List <ByteCode> body) { foreach (var varDef in methodDef.Body.Variables) { // Find all definitions and uses of this variable var defs = body.Where(b => b.Operand == varDef && b.IsVariableDefinition).ToList(); var uses = body.Where(b => b.Operand == varDef && !b.IsVariableDefinition).ToList(); List <VariableInfo> newVars; // If the variable is pinned, use single variable. // If any of the uses is from unknown definition, use single variable // If any of the uses is ldloca with a nondeterministic usage pattern, use single variable if (!optimize || varDef.IsPinned || uses.Any(b => b.VariablesBefore[varDef.Index].UnknownDefinition || (b.Code == AstCode.Ldloca && !IsDeterministicLdloca(b)))) { newVars = new List <VariableInfo>(1) { new VariableInfo() { Variable = new AstILVariable( string.IsNullOrEmpty(varDef.Name) ? "var_" + varDef.Index : varDef.Name, XBuilder.AsTypeReference(module, varDef.IsPinned ? ((PinnedType)varDef.VariableType).ElementType : varDef.VariableType), varDef), Defs = defs, Uses = uses } }; } else { // Create a new variable for each definition newVars = defs.Select(def => new VariableInfo() { Variable = new AstILVariable( (string.IsNullOrEmpty(varDef.Name) ? "var_" + varDef.Index : varDef.Name) + "_" + def.Offset.ToString("X2"), XBuilder.AsTypeReference(module, varDef.VariableType), varDef), Defs = new List <ByteCode> { def }, Uses = new List <ByteCode>() }).ToList(); // VB.NET uses the 'init' to allow use of uninitialized variables. // We do not really care about them too much - if the original variable // was uninitialized at that point it means that no store was called and // thus all our new variables must be uninitialized as well. // So it does not matter which one we load. // TODO: We should add explicit initialization so that C# code compiles. // Remember to handle cases where one path inits the variable, but other does not. // Add loads to the data structure; merge variables if necessary foreach (ByteCode use in uses) { ByteCode[] useDefs = use.VariablesBefore[varDef.Index].Definitions; if (useDefs.Length == 1) { VariableInfo newVar = newVars.Single(v => v.Defs.Contains(useDefs[0])); newVar.Uses.Add(use); } else { List <VariableInfo> mergeVars = newVars.Where(v => v.Defs.Intersect(useDefs).Any()).ToList(); VariableInfo mergedVar = new VariableInfo() { Variable = mergeVars[0].Variable, Defs = mergeVars.SelectMany(v => v.Defs).ToList(), Uses = mergeVars.SelectMany(v => v.Uses).ToList() }; mergedVar.Uses.Add(use); newVars = newVars.Except(mergeVars).ToList(); newVars.Add(mergedVar); } } } // Set bytecode operands foreach (VariableInfo newVar in newVars) { foreach (ByteCode def in newVar.Defs) { def.Operand = newVar.Variable; } foreach (ByteCode use in newVar.Uses) { use.Operand = newVar.Variable; } } } }