/// <summary> /// Emits code that loads current <see cref="PHP.Core.ScriptContext"/> by calling /// <see cref="PHP.Core.ScriptContext.CurrentContext"/> and remembers it in a local. /// </summary> /// <param name="il"></param> public void EmitLoad(ILEmitter il) { if (localBuilder == null) { localBuilder = il.DeclareLocal(typeof(ScriptContext)); il.EmitCall(OpCodes.Call, Methods.ScriptContext.GetCurrentContext, null); il.Stloc(localBuilder); } il.Ldloc(localBuilder); }
public static void EmitArgFullPreCall(ILEmitter/*!*/ il, IPlace/*!*/ stack, bool argsAware, int formalParamCount, int formalTypeParamCount, out LocalBuilder locArgsCount) { if (argsAware) { locArgsCount = il.DeclareLocal(typeof(int)); // locArgsCount = stack.MakeArgsAware(<formal tpye param count | formal param count>); stack.EmitLoad(il); il.LdcI4((formalTypeParamCount << 16) | formalParamCount); il.Emit(OpCodes.Call, Methods.PhpStack.MakeArgsAware); il.Stloc(locArgsCount); } else { locArgsCount = null; // CALL stack.RemoveFrame(); stack.EmitLoad(il); il.Emit(OpCodes.Call, Methods.PhpStack.RemoveFrame); } }
/// <summary> /// Converts object to CLR type /// </summary> private static bool EmitConvertObjectToClr(ILEmitter il, PhpTypeCode typeCode, Type formalType, LocalBuilder strictnessLocal) { MethodInfo convert_method = null; switch (Type.GetTypeCode(formalType)) { case TypeCode.Boolean: if (typeCode != PhpTypeCode.Boolean) convert_method = Methods.ConvertToClr.TryObjectToBoolean; break; case TypeCode.Int32: if (typeCode != PhpTypeCode.Integer) convert_method = Methods.ConvertToClr.TryObjectToInt32; break; case TypeCode.Int64: if (typeCode != PhpTypeCode.LongInteger) convert_method = Methods.ConvertToClr.TryObjectToInt64; break; case TypeCode.Double: if (typeCode != PhpTypeCode.Double) convert_method = Methods.ConvertToClr.TryObjectToDouble; break; case TypeCode.String: if (typeCode != PhpTypeCode.String) convert_method = Methods.ConvertToClr.TryObjectToString; break; case TypeCode.SByte: convert_method = Methods.ConvertToClr.TryObjectToInt8; break; case TypeCode.Int16: convert_method = Methods.ConvertToClr.TryObjectToInt16; break; case TypeCode.Byte: convert_method = Methods.ConvertToClr.TryObjectToUInt8; break; case TypeCode.UInt16: convert_method = Methods.ConvertToClr.TryObjectToUInt16; break; case TypeCode.UInt32: convert_method = Methods.ConvertToClr.TryObjectToUInt32; break; case TypeCode.UInt64: convert_method = Methods.ConvertToClr.TryObjectToUInt64; break; case TypeCode.Single: convert_method = Methods.ConvertToClr.TryObjectToSingle; break; case TypeCode.Decimal: convert_method = Methods.ConvertToClr.TryObjectToDecimal; break; case TypeCode.Char: convert_method = Methods.ConvertToClr.TryObjectToChar; break; case TypeCode.DateTime: convert_method = Methods.ConvertToClr.TryObjectToDateTime; break; case TypeCode.DBNull: convert_method = Methods.ConvertToClr.TryObjectToDBNull; break; case TypeCode.Object: { if (formalType.IsValueType) { if (formalType.IsGenericType && NullableType == formalType.GetGenericTypeDefinition()) { // This is an ugly corner case (using generic TryObjectToStruct wouldn't work, because // for nullables .IsValueType returns true, but it doesn't match "T : struct" constraint)! // We have to try converting object to Nullable<T> first and then to T // (which requires a new call to 'EmitConvertObjectToClr') Type nullableArg = formalType.GetGenericArguments()[0]; Type nullableType = NullableType.MakeGenericType(nullableArg); LocalBuilder tmpVar = il.DeclareLocal(typeof(object)); // This succeeds only for exact match il.Emit(OpCodes.Call, Methods.ConvertToClr.UnwrapNullable); il.Emit(OpCodes.Dup); il.Stloc(tmpVar); // <stack_0> = tmpVar = UnwrapNullable(...) // if (<stack_0> != null) Label lblNull = il.DefineLabel(), lblDone = il.DefineLabel(); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Beq, lblNull); // { // Convert tmpVar to T and wrap it into Nullable<T> il.Ldloc(tmpVar); bool ret = EmitConvertObjectToClr(il, typeCode, nullableArg, strictnessLocal); // TODO: use reflection cache? il.Emit(OpCodes.Newobj, nullableType.GetConstructors()[0]); il.Emit(OpCodes.Br, lblDone); // } else /* == null */ { il.MarkLabel(lblNull); // return (T?)null; LocalBuilder tmpNull = il.DeclareLocal(nullableType); il.Ldloca(tmpNull); il.Emit(OpCodes.Initobj, nullableType); il.Ldloc(tmpNull); // } il.MarkLabel(lblDone); return ret; } else convert_method = Methods.ConvertToClr.TryObjectToStruct.MakeGenericMethod(formalType); } else { if (formalType.IsArray) convert_method = Methods.ConvertToClr.TryObjectToArray.MakeGenericMethod(formalType.GetElementType()); else if (typeof(Delegate).IsAssignableFrom(formalType)) convert_method = Methods.ConvertToClr.TryObjectToDelegate.MakeGenericMethod(formalType); else convert_method = Methods.ConvertToClr.TryObjectToClass.MakeGenericMethod(formalType); } break; } default: Debug.Fail(); return true; } if (convert_method != null) { il.Ldloca(strictnessLocal); il.Emit(OpCodes.Call, convert_method); return false; } else return true; }
protected virtual GetterDelegate/*!*/ GenerateGetterStub() { #if SILVERLIGHT DynamicMethod stub = new DynamicMethod("<^GetterStub>", Types.Object[0], Types.Object); #else DynamicMethod stub = new DynamicMethod("<^GetterStub>", PhpFunctionUtils.DynamicStubAttributes, CallingConventions.Standard, Types.Object[0], Types.Object, this.declaringType.RealType, true); #endif ILEmitter il = new ILEmitter(stub); ClrEvent clr_event; ClrProperty clr_property; Type result_type; if ((clr_event = Member as ClrEvent) != null) { Debug.Assert(!declaringType.RealType.IsValueType, "Value type with ClrEvent not handled! TODO: arg(0) is ClrValue<T>."); LocalBuilder temp = il.DeclareLocal(declaringType.RealType); il.Ldarg(0); il.Emit(OpCodes.Castclass, declaringType.RealType); il.Stloc(temp); clr_event.EmitGetEventObject(il, new Place(null, Properties.ScriptContext_CurrentContext), new Place(temp), true); } else { if ((clr_property = Member as ClrProperty) != null) { // return error-throwing getter if the property is write-only if (!clr_property.HasGetter) return new GetterDelegate(MissingGetter); if (!clr_property.Getter.IsStatic) { ClrOverloadBuilder.EmitLoadInstance(il, IndexedPlace.ThisArg, declaringType.RealType); // il.Emit(OpCodes.Ldarg_0); // if (declaringType.RealType.IsValueType) // il.Emit(OpCodes.Unbox, declaringType.RealType); //#if EMIT_VERIFIABLE_STUBS // else // il.Emit(OpCodes.Castclass, this.declaringType.RealType); //#endif } il.Emit(OpCodes.Call, clr_property.Getter); result_type = clr_property.Getter.ReturnType; } else { ClrField clr_field = ClrField; if (!clr_field.FieldInfo.IsStatic) { ClrOverloadBuilder.EmitLoadInstance(il, IndexedPlace.ThisArg, declaringType.RealType); //il.Emit(OpCodes.Ldarg_0); ////il.Emit(OpCodes.Castclass, this.declaringType.RealType); //if (declaringType.RealType.IsValueType) il.Emit(OpCodes.Unbox, declaringType.RealType); il.Emit(OpCodes.Ldfld, clr_field.FieldInfo); } else { il.Emit(OpCodes.Ldsfld, clr_field.FieldInfo); } result_type = clr_field.FieldInfo.FieldType; } il.EmitBoxing(ClrOverloadBuilder.EmitConvertToPhp(il, result_type/*, null*/)); } il.Emit(OpCodes.Ret); return (GetterDelegate)stub.CreateDelegate(typeof(GetterDelegate)); }
/// <summary> /// Called when a <see cref="PHP.Core.AST.MethodDecl"/> AST node is entered during the emit phase. /// </summary> public void EnterMethodDeclaration(PhpMethod/*!*/ method) { bool is_optimized = (method.Properties & RoutineProperties.HasUnoptimizedLocals) == 0; bool rt_variables_table = (method.Properties & RoutineProperties.HasRTVariablesTable) != 0; CompilerLocationStack.TypeDeclContext class_context = locationStack.PeekTypeDecl(); CompilerLocationStack.MethodDeclContext md_context = new CompilerLocationStack.MethodDeclContext(); md_context.Type = class_context.Type; md_context.Method = method; // Set whether access to variables should be generated via locals or table md_context.OptimizedLocals = this.OptimizedLocals; OptimizedLocals = is_optimized; // set compile-time variables table: md_context.CurrentVariablesTable = this.currentVariablesTable; currentVariablesTable = method.Builder.LocalVariables; // set compile-time variables table: md_context.CurrentLabels = this.currentLabels; currentLabels = method.Builder.Labels; // Set the valid method to emit the "return" statement md_context.ReturnsPhpReference = this.ReturnsPhpReference; this.ReturnsPhpReference = method.Signature.AliasReturn; // CallSites (same as in TypeDecl, not changed): //md_context.CallSites = callSites; //this.callSites = new Compiler.CodeGenerator.CallSites(/*class_context = TypeContextPlace*/); // create new IL emitter for the method: md_context.IL = il; il = new ILEmitter(method.ArgFullInfo); // set RT variables table place: md_context.RTVariablesTablePlace = RTVariablesTablePlace; if (rt_variables_table) { LocalBuilder var_table_local = il.DeclareLocal(PhpVariable.RTVariablesTableType); if (sourceUnit.SymbolDocumentWriter != null) var_table_local.SetLocalSymInfo("<locals>"); RTVariablesTablePlace = new Place(var_table_local); } else RTVariablesTablePlace = LiteralPlace.Null; // sets ScriptContext and Self places appropriately: md_context.ClassContextPlace = TypeContextPlace; md_context.ScriptContextPlace = ScriptContextPlace; md_context.SelfPlace = SelfPlace; md_context.LateStaticBindTypePlace = LateStaticBindTypePlace; if (method.IsStatic) { ScriptContextPlace = new IndexedPlace(PlaceHolder.Argument, FunctionBuilder.ArgContextStatic); SelfPlace = LiteralPlace.Null; } else { ScriptContextPlace = new IndexedPlace(PlaceHolder.Argument, FunctionBuilder.ArgContextInstance); if (method.DeclaringPhpType.ProxyFieldInfo != null) { // the real this is not a DObject SelfPlace = new Place(IndexedPlace.ThisArg, method.DeclaringPhpType.ProxyFieldInfo); } else { // the real this is a DObject SelfPlace = IndexedPlace.ThisArg; } } // set Result place and return label: md_context.ResultPlace = ResultPlace; md_context.ReturnLabel = ReturnLabel; ResultPlace = null; LateStaticBindTypePlace = null; // set exception block nesting: md_context.ExceptionBlockNestingLevel = ExceptionBlockNestingLevel; ExceptionBlockNestingLevel = 0; // set current PhpRoutine md_context.PhpRoutine = method; // locationStack.PushMethodDecl(md_context); }
private bool EnterFunctionDeclarationInternal(PhpRoutine/*!*/ function, QualifiedName qualifiedName) { Debug.Assert(function.IsFunction); bool is_optimized = (function.Properties & RoutineProperties.HasUnoptimizedLocals) == 0; bool indirect_local_access = (function.Properties & RoutineProperties.IndirectLocalAccess) != 0; CompilerLocationStack.FunctionDeclContext fd_context = new CompilerLocationStack.FunctionDeclContext(); fd_context.Name = qualifiedName; // Set whether access to variables should be generated via locals or table fd_context.OptimizedLocals = this.OptimizedLocals; this.OptimizedLocals = is_optimized; // Set the valid method to emit the "return" statement fd_context.ReturnsPhpReference = this.ReturnsPhpReference; this.ReturnsPhpReference = function.Signature.AliasReturn; // CallSites fd_context.CallSites = null;//fd_context.CallSites = callSites; //this.callSites = new Compiler.CodeGenerator.CallSitesBuilder( // sourceUnit.CompilationUnit.Module.GlobalType.RealModuleBuilder, // fd_context.Name.ToString(), // LiteralPlace.Null); // keep current site container, just change the class context (to avoid of creating and baking so many types) this.callSites.PushClassContext(LiteralPlace.Null, null); // Set ILEmitter to function's body fd_context.IL = this.il; this.il = new ILEmitter(function.ArgFullInfo); // Set current variables table (at codeGenerator) fd_context.CurrentVariablesTable = this.currentVariablesTable; this.currentVariablesTable = function.Builder.LocalVariables; // Set current variables table (at codeGenerator) fd_context.CurrentLabels = this.currentLabels; this.currentLabels = function.Builder.Labels; // Set place for loading hashtable with variables at runtime fd_context.RTVariablesTablePlace = this.RTVariablesTablePlace; if (indirect_local_access || !is_optimized) { LocalBuilder var_table_local = il.DeclareLocal(PhpVariable.RTVariablesTableType); if (sourceUnit.SymbolDocumentWriter != null) var_table_local.SetLocalSymInfo("<locals>"); this.RTVariablesTablePlace = new Place(var_table_local); } else this.RTVariablesTablePlace = LiteralPlace.Null; // Set ScriptContext fd_context.ScriptContextPlace = this.ScriptContextPlace; this.ScriptContextPlace = new IndexedPlace(PlaceHolder.Argument, FunctionBuilder.ArgContext); // Set Class context fd_context.ClassContextPlace = this.TypeContextPlace; this.TypeContextPlace = LiteralPlace.Null; // Set Self fd_context.SelfPlace = this.SelfPlace; this.SelfPlace = LiteralPlace.Null; // Set Static fd_context.LateStaticBindTypePlace = this.LateStaticBindTypePlace; this.LateStaticBindTypePlace = null; // set Result place fd_context.ResultPlace = this.ResultPlace; fd_context.ReturnLabel = this.ReturnLabel; this.ResultPlace = null; // set exception block nesting: fd_context.ExceptionBlockNestingLevel = this.ExceptionBlockNestingLevel; this.ExceptionBlockNestingLevel = 0; // set current PhpRoutine fd_context.PhpRoutine = function; // locationStack.PushFunctionDecl(fd_context); return true; }
/// <summary> /// Emit static method that creates an instance of given value type using default ctor. /// </summary> /// <param name="valueType">Value type to be instantiated by resulting method.</param> /// <returns>The method that returns value boxed into new instance of <see cref="ClrValue<T>"/>.</returns> private static MethodBase BuildDefaultValueCtor(Type/*!*/valueType) { Debug.Assert(valueType != null && valueType.IsValueType, "Argument 'valueType' must not be null and must represent a value type!"); DynamicMethod method = new DynamicMethod(valueType.Name + "..ctor", Types.Object[0], Type.EmptyTypes); Emit.ILEmitter il = new PHP.Core.Emit.ILEmitter(method); // local 0 // ldloca.s 0 // initobj <valueType> var loc = il.DeclareLocal(valueType); il.Emit(OpCodes.Ldloca_S, loc); il.Emit(OpCodes.Initobj, valueType); // LOAD <loc> // box ConvertToPhp <valueType> il.Emit(OpCodes.Ldloc, loc); il.EmitBoxing(ClrOverloadBuilder.EmitConvertToPhp(il, valueType)); // return il.Emit(OpCodes.Ret); // done return method; }