private static Func <IExtractContext, string[]> Apply( ILocalVariableInformation target, ITypeInformation targetType, DecodeContext decodeContext) { var si = decodeContext.PopStack(); var codeInformation = decodeContext.CurrentCode; return(extractContext => { var rightExpression = extractContext.GetRightExpression(targetType, si); if (rightExpression == null) { throw new InvalidProgramSequenceException( "Invalid store operation: Location={0}, StackType={1}, LocalType={2}, SymbolName={3}", codeInformation.RawLocation, si.TargetType.FriendlyName, targetType.FriendlyName, target); } return new[] { string.Format( "{0} = {1}", extractContext.GetSymbolName(target), rightExpression) }; }); }
private static ExpressionEmitter Prepare( ILocalVariableInformation target, ITypeInformation targetType, DecodeContext decodeContext, bool isReference) { targetType = isReference ? targetType.MakeByReference() : targetType; var symbol = decodeContext.PushStack(targetType); return((extractContext, _) => new[] { string.Format( "{0} = {1}{2}", extractContext.GetSymbolName(symbol), // NOTE: Don't check "targetType.IsByReference" instead "isReference." // Because it's maybe double encoded byref type. isReference ? "&" : string.Empty, extractContext.GetSymbolName(target)) }); }
private static (ITypeInformation type, ILocalVariableInformation variable, string format) GetArg0ParameterInformation( DecodeContext decodeContext, ref IMethodInformation method, IParameterInformation parameter0, ILocalVariableInformation arg0, ref ITypeInformation arg0ValueType, ref ILocalVariableInformation requiredBoxingAtArg0PointerVariable, ref ILocalVariableInformation requiredCastingAtArg0PointerVariable, ref bool isVirtualCall) { Debug.Assert(method.IsStatic || parameter0.TargetType.Equals(method.DeclaringType) || (parameter0.TargetType.IsByReference && parameter0.TargetType.ElementType.Equals(method.DeclaringType))); // Required boxing at the arg0 if (arg0ValueType != null) { if (!arg0.TargetType.IsByReference) { throw new InvalidProgramSequenceException( "Cannot apply constrained prefix, arg0 isn't byref: Location={0}, Arg0Type={1}", decodeContext.CurrentCode.RawLocation, arg0.TargetType.FriendlyName); } // VERY DIRTY: The arg0 (managed pointer) uses below. requiredBoxingAtArg0PointerVariable = arg0; // VERY DIRTY: Make the boxing expression below, // it's reserving for the boxed value symbol at the evaluation stack. var arg0BoxedSymbol = decodeContext.PushStack( new BoxedValueTypeInformation(arg0ValueType)); decodeContext.PopStack(); return(parameter0.TargetType, arg0BoxedSymbol, "{0}"); } // If it's virtual method else if (isVirtualCall) { // Invoke interface method with the inline boxed-type instance. // ex: ((IFoo)foo).Bar(...) // If not inlined (ex: bound to local variable), arg0 doesn't box type. Next to the else block. if (method.DeclaringType.IsInterface && arg0.TargetType.IsBoxedType) { // VERY DIRTY: The arg0 (boxed value) uses below. requiredCastingAtArg0PointerVariable = arg0; arg0ValueType = arg0.TargetType; // VERY DIRTY: Make the implicitly cast expression below, // it's reserving for the casted value symbol at the evaluation stack. var arg0CastedSymbol = decodeContext.PushStack(method.DeclaringType); decodeContext.PopStack(); return(parameter0.TargetType, arg0CastedSymbol, "il2c_adjusted_reference({0})"); } // Invoke interface method with the class-type instance and // IL2C detected here for can cast statically. else if (method.DeclaringType.IsInterface && arg0.TargetType.IsClass && method.DeclaringType.IsAssignableFrom(arg0.TargetType)) { // All declared methods from derived to base types. var allDeclaredMethods = arg0.TargetType.AllInheritedDeclaredMethods; var m = method; var implementationMethod = allDeclaredMethods.First( dm => MetadataUtilities.VirtualMethodSignatureComparer.Equals(dm, m)); Debug.Assert(implementationMethod.DeclaringType.IsAssignableFrom(arg0.TargetType)); // Drop virtual call and turn to the direct call (devirtualize) isVirtualCall = false; method = implementationMethod; return(arg0.TargetType, arg0, "il2c_adjusted_reference({0})"); } else if (!(parameter0.TargetType.IsClass && arg0.TargetType.IsClass && parameter0.TargetType.IsAssignableFrom(arg0.TargetType))) { // Include adjust offset expression return(parameter0.TargetType, arg0, "il2c_adjusted_reference({0})"); } } return(parameter0.TargetType, arg0, "{0}"); }
public static Func <IExtractContext, string[]> Apply( IMethodInformation method, DecodeContext decodeContext, bool isVirtualCall) { // ECMA-335 I.12.4.1.4: Virtual calling convention // This method is too complex because it will try devirtualizing. ////////////////////////////////////////////////////////// // Step 1: // Constrained prefix applied, drop virtual call and hack the boxing for arg0 if required. // (See ConstrainedConverter.) ITypeInformation arg0ValueType = null; var prefixCode = decodeContext.PrefixCode; if (prefixCode?.OpCode == OpCodes.Constrained) { // 'The constrained. prefix is permitted only on a callvirt instruction.' if (!isVirtualCall) { throw new InvalidProgramSequenceException( "Cannot apply constrained prefix: Location={0}", decodeContext.CurrentCode.RawLocation); } // ECMA-335 III.2.1 constrained. - (prefix) invoke a member on a value of a variable type var constrainedType = (ITypeInformation)(prefixCode.Operand); // 'If thisType is a value type...' if (constrainedType.IsValueType) { // All declared methods from derived to base types. var allDeclaredMethods = constrainedType.AllInheritedDeclaredMethods; // '...and thisType implements method then' var implementationMethod = allDeclaredMethods.First( dm => MetadataUtilities.VirtualMethodSignatureComparer.Equals(dm, method)); // Drop virtual call and turn to the direct call (devirtualize) isVirtualCall = false; method = implementationMethod; // '...and thisType does not implement method then' if (!implementationMethod.DeclaringType.Equals(constrainedType)) { // 'ptr is dereferenced, boxed, and passed as the ‘this’ pointer to the callvirt of method' // VERY DIRTY: The constrained type uses below. arg0ValueType = constrainedType; } } } ////////////////////////////////////////////////////////// // Step 2: ILocalVariableInformation requiredBoxingAtArg0PointerVariable = null; ILocalVariableInformation requiredCastingAtArg0PointerVariable = null; // Construct parameters with expressions. var pairParameters = method.Parameters. Reverse(). // arg(n - 1) ... arg0 Select(parameter => { var arg = decodeContext.PopStack(); return((parameter.Index == 0) ? GetArg0ParameterInformation( decodeContext, ref method, parameter, arg, ref arg0ValueType, ref requiredBoxingAtArg0PointerVariable, ref requiredCastingAtArg0PointerVariable, ref isVirtualCall) : (type: parameter.TargetType, variable: arg, format: "{0}")); }). Reverse(). ToArray(); ////////////////////////////////////////////////////////// // Step 3: Fixing special cases var codeInformation = decodeContext.CurrentCode; ILocalVariableInformation result; // TODO: HACK: IL2C can't handle the generic types/methods in this version. // Roslyn will generate implementation for the event member with using for // "System.Threading.Interlocked.CompareExchange<T>(...)" method. // It's special resolver for event member. if ((method.UniqueName == "T System.Threading.Interlocked::CompareExchange(T&,T,T)") && pairParameters[1].variable.TargetType.IsReferenceType) { result = decodeContext.PushStack(pairParameters[1].variable.TargetType); return(extractContext => { var parameters = pairParameters.Select(parameter => new Utilities.RightExpressionGivenParameter( parameter.variable.TargetType, parameter.variable, string.Format(parameter.format, extractContext.GetSymbolName(parameter.variable)))). ToArray(); var parameterString = Utilities.GetGivenParameterDeclaration( parameters, extractContext, codeInformation); return new[] { string.Format( "{0} = ({1})System_Threading_Interlocked_CompareExchange_6({2})", extractContext.GetSymbolName(result), pairParameters[1].variable.TargetType.CLanguageTypeName, parameterString) }; }); } if (method.ReturnType.IsVoidType) { // HACK: If we will call the RuntimeHelpers.InitializeArray, we can memoize array type hint. // (See LdtokenConverter.) if (method.FriendlyName.StartsWith( "System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray")) { Debug.Assert(pairParameters.Length == 2); Debug.Assert(pairParameters[1].variable.HintInformation is string); decodeContext.PrepareContext.RegisterDeclaredValuesHintType( (string)pairParameters[1].variable.HintInformation, pairParameters[0].variable.TargetType); } result = null; } else { result = decodeContext.PushStack(method.ReturnType); } // Register callee method declaring type (at the file scope). decodeContext.PrepareContext.RegisterType(method.DeclaringType, decodeContext.Method); return(extractContext => { var receiveResultExpression = (result != null) ? string.Format("{0} = ", extractContext.GetSymbolName(result)) : string.Empty; var parameters = pairParameters.Select(parameter => new Utilities.RightExpressionGivenParameter( parameter.type, parameter.variable, string.Format(parameter.format, extractContext.GetSymbolName(parameter.variable)))). ToArray(); var parameterString = Utilities.GetGivenParameterDeclaration( parameters, extractContext, codeInformation); // Construct call expression between virtual and non-virtual. string callExpression; if (isVirtualCall && method.IsVirtual && !method.IsSealed) { var overloadIndex = method.DeclaringType.CalculatedVirtualMethods. First(entry => entry.method.Equals(method)). overloadIndex; callExpression = string.Format( "{0}{1}->vptr0__->{2}({3})", receiveResultExpression, extractContext.GetSymbolName(pairParameters[0].variable), method.GetCLanguageDeclarationName(overloadIndex), parameterString); } else { callExpression = string.Format( "{0}{1}({2})", receiveResultExpression, method.CLanguageFunctionName, parameterString); } // If requires boxing expression if (requiredBoxingAtArg0PointerVariable != null) { Debug.Assert(arg0ValueType != null); // Emit call expression with boxing expression. return new[] { string.Format( "{0} = il2c_box({1}, {2})", extractContext.GetSymbolName(pairParameters[0].variable), extractContext.GetSymbolName(requiredBoxingAtArg0PointerVariable), arg0ValueType.MangledUniqueName), callExpression }; } // If requires casting expression else if (requiredCastingAtArg0PointerVariable != null) { // Emit call expression with casting expression. return new[] { string.Format( "{0} = il2c_cast_from_boxed_to_interface({1}, {2}, {3}, {4})", extractContext.GetSymbolName(pairParameters[0].variable), method.DeclaringType.MangledUniqueName, arg0ValueType.ElementType.CLanguageStaticSizeOfExpression, arg0ValueType.CalculateInterfaceIndex(method.DeclaringType), extractContext.GetSymbolName(requiredCastingAtArg0PointerVariable)), callExpression }; } else { // Emit only call expression return new[] { callExpression }; } }); }
private static (ITypeInformation type, ILocalVariableInformation variable, string format) GetArg0ParameterInformation( DecodeContext decodeContext, ref IMethodInformation method, IParameterInformation parameter0, ILocalVariableInformation arg0, ref ITypeInformation arg0ValueType, ref ILocalVariableInformation requiredBoxingAtArg0PointerVariable, ref ILocalVariableInformation requiredCastingAtArg0PointerVariable, ref bool isVirtualCall) { // Required boxing at the arg0 if (arg0ValueType != null) { if (!arg0.TargetType.IsByReference) { throw new InvalidProgramSequenceException( "Cannot apply constrained prefix, arg0 isn't byref: Location={0}, Arg0Type={1}", decodeContext.CurrentCode.RawLocation, arg0.TargetType.FriendlyName); } // VERY DIRTY: The arg0 (managed pointer) uses below. requiredBoxingAtArg0PointerVariable = arg0; // VERY DIRTY: Make the boxing expression below, // it's reserving for the boxed value symbol at the evaluation stack. var arg0BoxedSymbol = decodeContext.PushStack( new BoxedValueTypeInformation(arg0ValueType)); decodeContext.PopStack(); return(parameter0.TargetType, arg0BoxedSymbol, "{0}"); } // If it's virtual method else if (isVirtualCall) { // Invoke interface method with the inline boxed-type instance. // ex: ((IFoo)foo).Bar(...) // If not inlined (ex: bound to local variable), arg0 doesn't box type. Next to the else block. if (method.DeclaringType.IsInterface && arg0.TargetType.IsBoxedType) { // VERY DIRTY: The arg0 (boxed value) uses below. requiredCastingAtArg0PointerVariable = arg0; arg0ValueType = arg0.TargetType; // VERY DIRTY: Make the implicitly cast expression below, // it's reserving for the casted value symbol at the evaluation stack. var arg0CastedSymbol = decodeContext.PushStack(method.DeclaringType); decodeContext.PopStack(); return(parameter0.TargetType, arg0CastedSymbol, "il2c_adjusted_reference({0})"); } else { // Include adjust offset expression return(parameter0.TargetType, arg0, "il2c_adjusted_reference({0})"); } } else { return(parameter0.TargetType, arg0, "{0}"); } }
public override Func <IExtractContext, string[]> Apply( IMethodInformation ctor, DecodeContext decodeContext) { if (!ctor.IsConstructor) { throw new InvalidProgramSequenceException( "Invalid new object constructor: Location={0}, Method={1}", decodeContext.CurrentCode.RawLocation, ctor.FriendlyName); } // Generate the constructor's argument expressions. var type = ctor.DeclaringType; ILocalVariableInformation thisSymbol = null; var pairParameters = new LinkedList <Utilities.RightExpressionGivenParameter>(); foreach (var parameter in ctor.Parameters.Reverse()) { // Except this parameter if (pairParameters.Count < (ctor.Parameters.Length) - 1) { pairParameters.AddFirst(new Utilities.RightExpressionGivenParameter( parameter.TargetType, decodeContext.PopStack())); } // "this" parameter else { Debug.Assert(thisSymbol == null); // Instance from get_uninitialized_object thisSymbol = decodeContext.PushStack(type, ctor); pairParameters.AddFirst(new Utilities.RightExpressionGivenParameter( type, thisSymbol)); } } Debug.Assert(thisSymbol != null); var codeInformation = decodeContext.CurrentCode; // Specialized the delegate type: // We can use for instantiate the delegate instance with "il2c_new_delegate()." if (type.IsDelegate) { if (type.IsAbstract || type.IsDelegateType || type.IsMulticastDelegateType) { throw new InvalidProgramSequenceException( "Invalid delegate type: Location={0}, Method={1}", codeInformation.RawLocation, type.FriendlyName); } if (!(ctor.Parameters.Length == 2) && (ctor.Parameters[0].TargetType.IsObjectType) && (ctor.Parameters[0].TargetType.IsIntPtrType)) { throw new InvalidProgramSequenceException( "Invalid delegate constructor: Location={0}, Method={1}", codeInformation.RawLocation, ctor.FriendlyName); } return(extractContext => { var parameterString = Utilities.GetGivenParameterDeclaration( pairParameters.Skip(1).ToArray(), extractContext, codeInformation); return new[] { string.Format( "{0} = il2c_new_delegate({1}, {2})", extractContext.GetSymbolName(thisSymbol), type.MangledName, parameterString) }; }); } var overloadIndex = ctor.OverloadIndex; return(extractContext => { var parameterString = Utilities.GetGivenParameterDeclaration( pairParameters.ToArray(), extractContext, codeInformation); // newobj opcode can handle value type with parameter applied constructor. if (type.IsValueType) { // If constructor's arguments greater than or equal 2 (this and others) if (pairParameters.Count >= 2) { var typeName = type.CLanguageTypeName; return new[] { string.Format( "memset(&{0}, 0x00, {1})", extractContext.GetSymbolName(thisSymbol), type.CLanguageStaticSizeOfExpression), (overloadIndex >= 1) ? string.Format( "{0}__ctor_{1}(&{2})", typeName, overloadIndex, parameterString) : string.Format( "{0}__ctor(&{1})", typeName, parameterString) }; } else { // ValueType's default constructor not declared. return new[] { string.Format( "memset(&{0}, 0x00, {1})", extractContext.GetSymbolName(thisSymbol), type.CLanguageStaticSizeOfExpression) }; } } // Object reference types. else { var get = new[] { string.Format( "{0} = il2c_get_uninitialized_object({1})", extractContext.GetSymbolName(thisSymbol), type.MangledName) }; var callCtor = new[] { (overloadIndex >= 1) ? string.Format( "{0}__ctor_{1}({2})", type.MangledName, overloadIndex, parameterString) : string.Format( "{0}__ctor({1})", type.MangledName, parameterString) }; return get. Concat(callCtor). ToArray(); } }); }
public override ExpressionEmitter Prepare( IMethodInformation ctor, DecodeContext decodeContext) { if (!ctor.IsConstructor) { throw new InvalidProgramSequenceException( "Invalid new object constructor: Location={0}, Method={1}", decodeContext.CurrentCode.RawLocation, ctor.FriendlyName); } // Generate the constructor's argument expressions. var type = ctor.DeclaringType; ILocalVariableInformation thisSymbol = null; var pairParameters = new LinkedList <Utilities.RightExpressionGivenParameter>(); foreach (var parameter in ctor.Parameters.Reverse()) { // Except this parameter if (pairParameters.Count < (ctor.Parameters.Length) - 1) { pairParameters.AddFirst(new Utilities.RightExpressionGivenParameter( parameter.TargetType, decodeContext.PopStack())); } // "this" parameter else { Debug.Assert(thisSymbol == null); // Instance from get_uninitialized_object thisSymbol = decodeContext.PushStack(type, ctor); pairParameters.AddFirst(new Utilities.RightExpressionGivenParameter( type, thisSymbol)); } } Debug.Assert(thisSymbol != null); var codeInformation = decodeContext.CurrentCode; // Register target constructor declaring type (at the file scope). decodeContext.PrepareContext.RegisterType(type, decodeContext.Method); // Specialized the delegate type: // We can use for instantiate the delegate instance with "il2c_new_delegate()." if (type.IsDelegate) { if (type.IsAbstract || type.IsDelegateType || type.IsMulticastDelegateType) { throw new InvalidProgramSequenceException( "Invalid delegate type: Location={0}, Method={1}", codeInformation.RawLocation, type.FriendlyName); } if ((ctor.Parameters.Length != 3) || !ctor.Parameters[1].TargetType.IsObjectType || !ctor.Parameters[2].TargetType.IsIntPtrType) { throw new InvalidProgramSequenceException( "Invalid delegate constructor: Location={0}, Method={1}", codeInformation.RawLocation, ctor.FriendlyName); } return((extractContext, _) => { var parameterString = Utilities.GetGivenParameterDeclaration( pairParameters.Skip(1).ToArray(), extractContext, codeInformation); return new[] { string.Format( "{0} = il2c_new_delegate({1}, {2})", extractContext.GetSymbolName(thisSymbol), type.MangledUniqueName, parameterString) }; }); } // Specialized the thread type: if (type.UniqueName == "System.Threading.Thread") { if (!type.IsClass || !type.IsSealed) { throw new InvalidProgramSequenceException( "Invalid thread type: Location={0}, Method={1}", codeInformation.RawLocation, type.FriendlyName); } if ((ctor.Parameters.Length != 2) || ((ctor.Parameters[1].TargetType.UniqueName != "System.Threading.ThreadStart") && (ctor.Parameters[1].TargetType.UniqueName != "System.Threading.ParameterizedThreadStart"))) { throw new InvalidProgramSequenceException( "Invalid thread constructor: Location={0}, Method={1}", codeInformation.RawLocation, ctor.FriendlyName); } return((extractContext, _) => { var parameterString = Utilities.GetGivenParameterDeclaration( pairParameters.Skip(1).ToArray(), extractContext, codeInformation); return new[] { string.Format( "{0} = il2c_new_thread({1})", extractContext.GetSymbolName(thisSymbol), parameterString) }; }); } // TODO: overloadIndex var overloadIndex = ctor.OverloadIndex; return((extractContext, _) => { var parameterString = Utilities.GetGivenParameterDeclaration( pairParameters.ToArray(), extractContext, codeInformation); // newobj opcode can handle value type with parameter applied constructor. if (type.IsValueType) { // IL2C can't understand the native type size. // So, the expression will make calculation at the C compiler. var memsetExpression = (type.NativeType != null) ? "memset(&{0}, 0x00, sizeof {0})" : "memset(&{0}, 0x00, {1})"; // If constructor's arguments greater than or equal 2 (this and others) if (pairParameters.Count >= 2) { var typeName = type.CLanguageTypeName; return new[] { string.Format( memsetExpression, extractContext.GetSymbolName(thisSymbol), type.CLanguageStaticSizeOfExpression), string.Format( "{0}(&{1})", ctor.CLanguageFunctionFullName, parameterString) }; } else { // ValueType's default constructor not declared. return new[] { string.Format( memsetExpression, extractContext.GetSymbolName(thisSymbol), type.MangledUniqueName) }; } } // Object reference types. else { var get = new[] { string.Format( "{0} = il2c_get_uninitialized_object({1})", extractContext.GetSymbolName(thisSymbol), type.MangledUniqueName) }; var callCtor = new[] { string.Format( "{0}({1})", ctor.CLanguageFunctionFullName, parameterString) }; return get. Concat(callCtor). ToArray(); } }); }