コード例 #1
0
ファイル: CodeGenerator.cs プロジェクト: dw4dev/Phalanger
		/// <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);
		}
コード例 #2
0
ファイル: CodeGenerator.cs プロジェクト: dw4dev/Phalanger
		/// <summary>
		/// Emits stubs for one overridden or implemented PHP method.
		/// </summary>
		/// <param name="stubs">Already generated stubs.</param>
		/// <param name="target">The overriding/implementing method.</param>
		/// <param name="targetType">The type (perhaps constructed) that declared <paramref name="target"/>.</param>
		/// <param name="declaringType">The type where the stubs should be emitted.</param>
		/// <param name="template">The method being overridden/implemented (surely PHP).</param>
		/// <param name="newSlot"><B>True</B> if the stub should be assigned a new vtable slot,
		/// <B>false</B> otherwise.</param>
		/// <remarks>
		/// This method handles situations where method overriding/implementing does not work by itself because of
		/// the fact that method names in PHP are case insensitive.
		/// </remarks>
		private void EmitOverrideStubsForPhpTemplate(IDictionary<string, MethodBuilder>/*!*/ stubs,
			PhpMethod/*!*/ target, DType/*!*/ targetType, PhpType/*!*/ declaringType, DMemberRef/*!*/ template,
			bool newSlot)
		{
            PhpMethod php_template = (PhpMethod)template.Member;

            // Emit method stub if needed here ... (resolve .NET incompatibility of base method and overriding method)
            // 
            // Until now, several possible cases or their combination are known:
            // - base and overriding methods match, but their name letter-casing don't (need to define override explicitly to properly Bake the type)
            // - base and overriding methods name match exactly, but overriding methods has additional arguments (with default values) (in PHP it is allowed) (stub needed)
            // - ghost stub, where B extends A implements I, where A contains definition of method in I and casing does not match
            //
            // if signatures don't match, virtual sealed stub must be created, it only calls the target method
            // if signatures match, only explicit override must be stated

            if (target.Name.ToString() != php_template.Name.ToString() ||           // the names differ (perhaps only in casing)
                target.Signature.ParamCount != php_template.Signature.ParamCount || // signature was extended (additional arguments added, with implicit value only)
                target.Signature.AliasReturn != php_template.Signature.AliasReturn  // returns PhpReference instead of Object
                )
			{
				MethodInfo target_argfull = DType.MakeConstructed(target.ArgFullInfo, targetType as ConstructedType);
				TypeBuilder type_builder = declaringType.RealTypeBuilder;

				// we have to generate a pass-thru override stub that overrides the template based on
				// name since it is impossible to install an explicit override of a method declared by
				// a generic type in v2.0 SRE (feedback ID=97425)
				bool sre_bug_workaround = (template.Type is ConstructedType);

                if (target.DeclaringType == declaringType && !sre_bug_workaround &&
                    target.Signature.ParamCount == php_template.Signature.ParamCount &&
                    target.Signature.AliasReturn == php_template.Signature.AliasReturn)
				{
                    // signatures match, just install an explicit override if possible
					type_builder.DefineMethodOverride(target_argfull,
						DType.MakeConstructed(php_template.ArgFullInfo, template.Type as ConstructedType));
				}
				else
				{
					string stubs_key = null;
					MethodAttributes attrs;

                    if (sre_bug_workaround)
                    {
                        // check whether we have generated a stub having the template name before
                        if (stubs.ContainsKey(stubs_key = "," + php_template.ArgFullInfo.Name)) return;

                        attrs = php_template.ArgFullInfo.Attributes & ~MethodAttributes.Abstract;
                    }
                    else
                    {
                        attrs = MethodAttributes.PrivateScope | MethodAttributes.Virtual;
                    }

                    if (newSlot) attrs |= MethodAttributes.NewSlot; 
					else attrs &= ~MethodAttributes.NewSlot;

					// determine stub return and parameters type
					Type return_type;
					Type[] param_types = php_template.Signature.ToArgfullSignature(1, out return_type);
                    param_types[0] = Types.ScriptContext[0];

					MethodBuilder override_stub = type_builder.DefineMethod(
                        (sre_bug_workaround ? php_template.ArgFullInfo.Name : "<Override>"),
						attrs, return_type, param_types);

					ILEmitter il = new ILEmitter(override_stub);

                    //
                    // return target( [arg1, ...[, default, ...]] );
                    //

					// pass-thru all arguments, including this (arg0)
                    int pass_args = Math.Min(param_types.Length, target.Signature.ParamCount + 1);
					for (int i = 0; i <= pass_args; ++i) il.Ldarg(i);  // this, param1, ....
                    for (int i = pass_args; i <= target.Signature.ParamCount; ++i)
                    {
                        // ... // PhpException.MissingArgument(i, target.FullName); // but in some override it can be optional argument 
                        il.Emit(OpCodes.Ldsfld, PHP.Core.Emit.Fields.Arg_Default);  // paramN
                    }
                    il.Emit(OpCodes.Callvirt, target_argfull);

                    // return
                    if (target.Signature.AliasReturn != php_template.Signature.AliasReturn)
                        il.Emit(OpCodes.Call, target.Signature.AliasReturn
                            ? Methods.PhpVariable.Dereference       // PhpVariable.Deference(obj)                   
                            : Methods.PhpVariable.MakeReference);   // PhpVariable.MakeReference(obj)
                    il.Emit(OpCodes.Ret);

					if (sre_bug_workaround)
					{
						stubs.Add(stubs_key, override_stub);
					}
					else
					{
                        if (!php_template.ArgFullInfo.IsVirtual)
                            throw new InvalidOperationException(string.Format("Cannot override non-virtual method '{0}'!", php_template.ArgFullInfo.Name));

						type_builder.DefineMethodOverride(override_stub,
							DType.MakeConstructed(php_template.ArgFullInfo, template.Type as ConstructedType));
					}
				}
			}
		}
コード例 #3
0
ファイル: CodeGenerator.cs プロジェクト: dw4dev/Phalanger
		/// <summary>
		/// Emits stubs for all overloads of one exported method.
		/// </summary>
		/// <param name="stubs">Already generated stubs.</param>
		/// <param name="target">The exported method.</param>
		private void EmitExportStubs(IDictionary<string, MethodBuilder>/*!*/ stubs, PhpMethod/*!*/ target)
		{
			Debug.Assert(target.IsExported);

			string clr_sig = null;
			MethodAttributes attributes = Reflection.Enums.ToMethodAttributes(target.MemberDesc.MemberAttributes);
			attributes |= MethodAttributes.HideBySig;

			foreach (StubInfo stub in ClrStubBuilder.DefineMethodExportStubs(
				target, target.DeclaringPhpType,
				attributes,
				false,
				delegate(string[] genericParamNames, object[] parameterTypes, object returnType)
				{
					// check whether we have not generated this signature before
					clr_sig = ClrMethod.Overload.ClrSignatureToString(genericParamNames.Length, parameterTypes, returnType);
					return !stubs.ContainsKey(clr_sig);
				}))
			{
				// set parameter names and attributes
				ClrStubBuilder.DefineStubParameters(stub.MethodBuilder, null, stub.Parameters);

				if (!stub.MethodBuilder.IsAbstract)
				{
					EmissionContext emission_context = SetupStubPlaces(target.DeclaringPhpType, stub.MethodBuilder.IsStatic);
					try
					{
						// stub body
						ClrStubBuilder.EmitMethodStubBody(
							new ILEmitter(stub.MethodBuilder),
							ScriptContextPlace,
							stub.Parameters,
							stub.TypeParameters,
							stub.ReturnType,
							target,
							target.DeclaringType);
					}
					finally
					{
						RestorePlaces(emission_context);
					}
				}

				stubs.Add(clr_sig, stub.MethodBuilder);
			}
		}
コード例 #4
0
ファイル: CodeGenerator.cs プロジェクト: dw4dev/Phalanger
		/// <summary>
		/// Emits stubs for overridden/implemented methods and explicit export stubs.
		/// </summary>
		/// <param name="method">The overriding/implementing/exported method.</param>
		/// <remarks>
		/// If the <paramref name="method"/> implements or overrides a CLR method (or methods),
		/// appropriate stub(s) are generated and emitted to its declaring type. In addition,
		/// if the method is exported using the <c>Export</c> pseudo-custom attribute, stub(s)
		/// created according to its signature (type hints, default params, etc.) are also
		/// generated.
		/// </remarks>
		public void EmitOverrideAndExportStubs(PhpMethod/*!*/ method)
		{
			// keep track of the signatures that have already been generated
			Dictionary<string, MethodBuilder> stubs = null;

			// emit stub(s) for overridden method(s)

			if (method.Overrides != null)
			{
				stubs = new Dictionary<string, MethodBuilder>();
				EmitOverrideStubs(stubs, method, method.DeclaringPhpType, method.DeclaringPhpType,
					method.Overrides, false);
			}

			if (method.Implements != null)
			{
				// emit stub(s) for implemented method(s)

				for (int i = 0; i < method.Implements.Count; i++)
				{
					if (stubs == null) stubs = new Dictionary<string, MethodBuilder>();
					EmitOverrideStubs(stubs, method, method.DeclaringPhpType, method.DeclaringPhpType,
						method.Implements[i], true);
				}
			}

			// emit explicit export stubs

			if (method.IsExported)
			{
				if (stubs == null) stubs = new Dictionary<string, MethodBuilder>();
				EmitExportStubs(stubs, method);
			}
		}
コード例 #5
0
ファイル: CodeGenerator.cs プロジェクト: dw4dev/Phalanger
		/// <summary>
		/// Emits stubs for all overloads of one overridden or implemented method.
		/// </summary>
		/// <param name="stubs">Already generated stubs.</param>
		/// <param name="target">The overriding/implementing method.</param>
		/// <param name="targetType">The type (perhaps constructed) that declared <paramref name="target"/>.</param>
		/// <param name="declaringType">The type where the stubs should be emitted.</param>
		/// <param name="template">The method being overridden/implemented.</param>
		/// <param name="newSlot"><B>True</B> if the stub should be assigned a new vtable slot,
		/// <B>false</B> otherwise.</param>
		private void EmitOverrideStubs(IDictionary<string, MethodBuilder>/*!*/ stubs, PhpMethod/*!*/ target,
			DType/*!*/ targetType, PhpType/*!*/ declaringType, DMemberRef/*!*/ template, bool newSlot)
		{
			ClrMethod clr_template = template.Member as ClrMethod;
			if (clr_template == null)
			{
                if (!target.IsStatic)
				    EmitOverrideStubsForPhpTemplate(stubs, target, targetType, declaringType, template, newSlot);

				return;
			}

            //
            // following code emits stubs in case of CLR base method
            //

			ConstructedType constructed_type = template.Type as ConstructedType;
			TypeBuilder type_builder = declaringType.RealTypeBuilder;

			// override all virtual non-final overloads
			foreach (ClrMethod.Overload overload in clr_template.Overloads)
			{
				if (overload.Method.IsVirtual && !overload.Method.IsFinal)
				{
					// map generic type parameters according to the constructed type
					Type constructed_return_type;
					ParameterInfo[] constructed_params = overload.MakeConstructed(constructed_type, out constructed_return_type);

					// check whether we have not generated this signature before
					string clr_sig = ClrMethod.Overload.ClrSignatureToString(
						overload.GenericParamCount,
						constructed_params,
						constructed_return_type);

					if (stubs.ContainsKey(clr_sig)) continue;

					Type[] param_types = new Type[constructed_params.Length];

					for (int j = 0; j < param_types.Length; j++)
					{
						param_types[j] = constructed_params[j].ParameterType;
					}

					// determine the stub attributes
					MethodAttributes attr;
					string name;

					name = overload.Method.Name;
					attr = Reflection.Enums.ToMethodAttributes(target.MemberDesc.MemberAttributes);
					attr |= (MethodAttributes.Virtual | MethodAttributes.HideBySig);

					if (newSlot) attr |= MethodAttributes.NewSlot;

					MethodBuilder overload_builder = type_builder.DefineMethod(name, attr);

					if (overload.MandatoryGenericParamCount > 0)
					{
						// define the same generic parameters that are defined for the overridden method
						// (the same constraints but possibly having different names)
						ClrStubBuilder.DefineStubGenericParameters(
							overload_builder,
							overload.GenericParameters,
							target.Signature,
							param_types);
					}

					overload_builder.SetReturnType(constructed_return_type);
					overload_builder.SetParameters(param_types);

					// set parameter names and attributes
					ClrStubBuilder.DefineStubParameters(overload_builder,
						target.Builder.Signature.FormalParams, constructed_params);

					if (!overload_builder.IsAbstract)
					{
						EmissionContext emission_context = SetupStubPlaces(target.DeclaringPhpType, false);

						try
						{
							// convert parameters and invoke the target
							ClrStubBuilder.EmitMethodStubBody(
								new ILEmitter(overload_builder),
								ScriptContextPlace,
								constructed_params,
								overload.GenericParameters,
								constructed_return_type,
								target,
								targetType);
						}
						finally
						{
							RestorePlaces(emission_context);
						}
					}

					stubs.Add(clr_sig, overload_builder);
				}
			}
		}
コード例 #6
0
ファイル: ClrStubBuilder.cs プロジェクト: dw4dev/Phalanger
		/// <summary>
		/// Emits stub for one overridden/implemented/exported CLR overload.
		/// </summary>
		/// <param name="il"></param>
		/// <param name="scriptContextPlace"></param>
		/// <param name="stubParameters">The overload parameters.</param>
		/// <param name="stubTypeParameters">The overload type parameters.</param>
		/// <param name="stubReturnType">The overload return type.</param>
		/// <param name="target">The overriding/implementing/exporting method.</param>
		/// <param name="targetType">The type (perhaps constructed) that declared <paramref name="target"/>.</param>
		public static void EmitMethodStubBody(ILEmitter/*!*/ il, IPlace/*!*/ scriptContextPlace,
			ParameterInfo[]/*!*/ stubParameters, Type[]/*!*/ stubTypeParameters,
			Type/*!*/ stubReturnType, PhpMethod/*!*/ target, DType/*!*/ targetType)
		{
			bool stub_is_static = il.MethodBase.IsStatic;

			ClrStubBuilder stub_builder =
				new ClrStubBuilder(il, scriptContextPlace, stubParameters.Length, (stub_is_static ? 0 : 1));

			if (stubParameters.Length >= target.Signature.MandatoryParamCount &&
				stubTypeParameters.Length >= target.Signature.MandatoryGenericParamCount &&
				(target.Properties & RoutineProperties.IsArgsAware) == 0)
			{
				// we can directly call the target argful

				if (!stub_is_static) il.Ldarg(FunctionBuilder.ArgThis);
				scriptContextPlace.EmitLoad(il);

				stub_builder.EmitLoadArgfullParameters(stubParameters, stubTypeParameters, target);

				// invoke the target (virtually if it's not static)
				il.Emit(stub_is_static ? OpCodes.Call : OpCodes.Callvirt,
					DType.MakeConstructed(target.ArgFullInfo, targetType as ConstructedType));
			}
			else
			{
				// we have to take the argless way

				stub_builder.EmitLoadArglessParameters(stubParameters, stubTypeParameters, target);

				// invoke the target's argless
				// TODO: this is not behaving 100% correct, because we're losing virtual dispatch here
				if (stub_is_static) il.Emit(OpCodes.Ldnull);
				else il.Ldarg(FunctionBuilder.ArgThis);

				scriptContextPlace.EmitLoad(il);
				il.Emit(OpCodes.Ldfld, Fields.ScriptContext_Stack);

				il.Emit(OpCodes.Call, DType.MakeConstructed(target.ArgLessInfo, targetType as ConstructedType));
			}

			// do not keep it on stack needlessly
			if (stubReturnType == Types.Void) il.Emit(OpCodes.Pop);

			// convert ref/out parameters back to CLR type
			for (int i = 0; i < stubParameters.Length; i++)
			{
				stub_builder.EmitStoreClrParameter(stubParameters[i]);
			}

			if (stubReturnType != Types.Void)
			{
				// convert the return parameter back to CLR type
				stub_builder.EmitConvertReturnValue(
					stubReturnType,
					target.Signature.AliasReturn ? PhpTypeCode.PhpReference : PhpTypeCode.Object);
			}

			il.Emit(OpCodes.Ret);
		}
コード例 #7
0
ファイル: ClrStubBuilder.cs プロジェクト: dw4dev/Phalanger
		private void EmitLoadArglessParameters(ParameterInfo[]/*!*/ stubParameters,
			Type[]/*!*/ stubTypeParameters, PhpMethod/*!*/ target)
		{
			PhpStackBuilder.EmitAddFrame(il, scriptContextPlace, stubTypeParameters.Length, stubParameters.Length,
				delegate(ILEmitter eil, int i)
				{
					il.Emit(OpCodes.Ldtoken, stubTypeParameters[i]);
					il.Emit(OpCodes.Call, Methods.DTypeDesc_Create);
				},
				delegate(ILEmitter eil, int i)
				{
					EmitLoadClrParameter(stubParameters[i], PhpTypeCode.Unknown);
				});
		}
コード例 #8
0
ファイル: ClrStubBuilder.cs プロジェクト: dw4dev/Phalanger
		private void EmitLoadArgfullParameters(ParameterInfo[]/*!*/ stubParameters,
			Type[]/*!*/ stubTypeParameters, PhpMethod/*!*/ target)
		{
			for (int i = 0; i < target.Signature.GenericParamCount; i++)
			{
				if (i < stubTypeParameters.Length)
				{
					il.Emit(OpCodes.Ldtoken, stubTypeParameters[i]);
					il.Emit(OpCodes.Call, Methods.DTypeDesc_Create);
				}
				else
				{
					// optional type parameter, whose value is not supplied
					il.Emit(OpCodes.Ldsfld, Fields.Arg_DefaultType);
				}
			}

			for (int i = 0; i < target.Signature.ParamCount; i++)
			{
				if (i < stubParameters.Length)
				{
					EmitLoadClrParameter(
						stubParameters[i],
						target.Signature.IsAlias(i) ? PhpTypeCode.PhpReference : PhpTypeCode.Object);
				}
				else
				{
					// optional parameter, whose value is not supplied
					il.Emit(OpCodes.Ldsfld, Fields.Arg_Default);
				}
			}
		}
コード例 #9
0
ファイル: Analyzer.cs プロジェクト: Ashod/Phalanger
		/// <summary>
		/// Notices the analyzer that method declaration is entered.
		/// </summary>
		internal void EnterMethodDeclaration(PhpMethod/*!*/ method)
		{
			//function declared within a method is global function 
			//=> method is only declared direct within a class declaration
			Debug.Assert(locationStack.Peek().Kind == Locations.TypeDecl);

			RoutineDeclLoc m = new RoutineDeclLoc(method, locationStack.Count);
			routineDeclStack.Push(m);
			locationStack.Push(m);

			EnterConditionalCode();
		}
コード例 #10
0
ファイル: TypeDecl.cs プロジェクト: Ashod/Phalanger
		internal override void AnalyzeMembers(Analyzer/*!*/ analyzer, PhpType/*!*/ declaringType)
		{
			method = declaringType.AddMethod(name, modifiers, body != null, signature, typeSignature, position,
				analyzer.SourceUnit, analyzer.ErrorSink);

			// method redeclared:
			if (method == null) return;

			method.WriteUp(typeSignature.ToPhpRoutineSignature(method));

			typeSignature.PreAnalyze(analyzer, method);

			base.AnalyzeMembers(analyzer, declaringType);

			typeSignature.AnalyzeMembers(analyzer, declaringType.Declaration.Scope);
			signature.AnalyzeMembers(analyzer, method);
            method.IsDllImport = this.IsDllImport;
            if(method.IsDllImport && Body.Count != 0)
                analyzer.ErrorSink.Add(Warnings.BodyOfDllImportedFunctionIgnored, analyzer.SourceUnit, position);
		}