private static Expression/*!*/ GeneratePeekPseudoGenericArgument(PhpRoutine routine, DynamicMetaObject scriptContext, DynamicMetaObject arg, int index) { bool optional = index >= routine.Signature.MandatoryGenericParamCount; int indexTransformed = index + 1; // in PHP indexes of arguments starts from index 1 if (optional) return PeekTypeOptional(routine, scriptContext, arg, indexTransformed); else return PeekType(routine, scriptContext, arg, indexTransformed); }
/// <summary> /// This method binds rules for PhpMethod /// </summary> private void InvokePhpMethod(DynamicMetaObject/*!*/ target, DynamicMetaObject[]/*!!*/ args, /*object targetObj,*/ PhpRoutine/*!*/ routine, out BindingRestrictions restrictions, out Expression invokeMethodExpr) { Debug.Assert(target != null && target.Value != null); Debug.Assert(!(target.Value is IClrValue), "PhpRoutine should not be declared on CLR value type!"); /*if (target.Value is PhpObject) { // Restriction: typeof(target) == |target.TypeDesc.RealType| var targetPhpObj = (PhpObject)target.Value; Debug.Assert(targetPhpObj.TypeDesc.RealType == target.LimitType); Debug.Assert(target.Value.GetType() == target.LimitType); restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, targetPhpObj.TypeDesc.RealType); } else*/ Debug.Assert(typeof(ClrObject).IsSealed); // just to ensure following condition is correct if (target.Value.GetType() == typeof(ClrObject)) { target = new ClrDynamicMetaObject(target); // unwrap the real object, get restrictions restrictions = target.Restrictions; } else { Debug.Assert(target.Value.GetType() == target.LimitType); // just for sure Debug.Assert(!(target.Value is PhpObject) || ((PhpObject)target.Value).TypeDesc.RealType == target.LimitType); restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType); } BindingRestrictions argumentsRestrictions; Expression[] arguments; if (routine.Name != Name.SpecialMethodNames.Call) { args = GetArgumentsRange(args, 0, RealMethodArgumentCount);// This can't be done when _call method is invoked //Check if method has ArgAware attribute if ((routine.Properties & RoutineProperties.IsArgsAware) != 0 || routine.IsStatic)// this is because of hack in PHP.Library.XML library static methods that can be also called like instance methods { DynamicMetaObject scriptContext = args[0]; //Select arguments without scriptContext DynamicMetaObject[] realArgs = GetArgumentsRange(args, 1, RealMethodArgumentCount - 1); InvokeArgLess(target, scriptContext, routine.RoutineDesc, realArgs, out argumentsRestrictions, out invokeMethodExpr); restrictions = restrictions.Merge(argumentsRestrictions); return; } arguments = routine.PrepareArguments(args, _genericParamsCount, _paramsCount, out argumentsRestrictions); restrictions = restrictions.Merge(argumentsRestrictions); } else { arguments = BinderHelper.PackToExpressions(args); } //((PhpObject)target)) var realObjEx = Expression.Convert(target.Expression, routine.ArgFullInfo.DeclaringType);//targetObj.TypeDesc.RealType); //ArgFull( ((PhpObject)target), ScriptContext, args, ... ) invokeMethodExpr = Expression.Call(BinderHelper.WrapInstanceMethodCall(routine.ArgFullInfo), BinderHelper.CombineArguments(realObjEx, arguments)); invokeMethodExpr = ReturnArgumentHelpers.ReturnValueConversion(routine.ArgFullInfo, invokeMethodExpr); invokeMethodExpr = HandleResult(invokeMethodExpr, routine.ArgFullInfo.ReturnType, false); }
/// <summary> /// Stores late static binding type information if necessary. /// </summary> private void EmitArgfullLateStaticBindTypeInitialization(PhpRoutine/*!*/routine) { if (routine == null || !routine.UsesLateStaticBinding) return; if (routine.IsMethod) { if (routine.IsStatic) { // static method, // reads <context>.Stack.LateStaticBindType, // saves it into a local variable: // <context>.Stack.LateStaticBindType this.EmitLoadScriptContext(); this.il.Emit(OpCodes.Ldfld, Fields.ScriptContext_Stack); this.il.Emit(OpCodes.Ldfld, Fields.PhpStack_LateStaticBindType); // DTypeDesc <loc_lsb> = this.LateStaticBindTypePlace = new IndexedPlace(il.DeclareLocal(Types.DTypeDesc[0])); this.LateStaticBindTypePlace.EmitStore(il); } else { // instance method, // uses ((DObject)this).TypeDesc Debug.Assert(this.SelfPlace != null && this.SelfPlace != LiteralPlace.Null, "SelfPlace expected to be non-NULL"); this.LateStaticBindTypePlace = new MethodCallPlace(Properties.DObject_TypeDesc.GetGetMethod(), false, this.SelfPlace); } } else { this.LateStaticBindTypePlace = LiteralPlace.Null; } }
/// <summary> /// Declares all locals used in a function. /// </summary> private void EmitArgfullLocalsInitialization(PhpRoutine/*!*/ routine) { bool optimized = (routine.Properties & RoutineProperties.HasUnoptimizedLocals) == 0; bool rt_var_table = (routine.Properties & RoutineProperties.HasRTVariablesTable) != 0; // TODO: MarkSequencePoint(0xFeeFee, 0xFeeFee, 0xFeeFee, 0xFeeFee); // emits creation of a new table of variables if it will be used in a function: if (rt_var_table) { il.LdcI4(routine.Builder.LocalVariables.Count); il.Emit(OpCodes.Newobj, PhpVariable.RTVariablesTableCtor); RTVariablesTablePlace.EmitStore(il); } if (optimized) { // declares and initializes real locals (skips arguments): foreach (VariablesTable.Entry entry in routine.Builder.LocalVariables) { if (!entry.IsParameter) { LocalBuilder local; if (entry.IsPhpReference) { local = il.DeclareLocal(Types.PhpReference[0]); // local = new PhpReference(); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); il.Stloc(local); } else { local = il.DeclareLocal(Types.Object[0]); } // stores local to table: entry.Variable = new Place(local); // gives locals names (if they are not parameters): if (sourceUnit.SymbolDocumentWriter != null) local.SetLocalSymInfo(entry.VariableName.Value); } } } }
/// <summary> /// Emits a body of an arg-full function or method overload. /// </summary> public void EmitArgfullOverloadBody(PhpRoutine/*!*/ routine, IEnumerable<Statement>/*!*/ body, Text.Span entirePosition, int declarationBodyPosition) { Debug.Assert(!routine.IsAbstract); if (context.Config.Compiler.Debug) { if (!routine.IsLambda) { MarkSequencePoint(declarationBodyPosition); } il.Emit(OpCodes.Nop); EmitArgsAwareCheck(routine); } // declares and initializes real locals (should be before args init): EmitArgfullLocalsInitialization(routine); // initializes locals (from arguments or by empty value): EmitArgfullArgsInitialization(routine); // remember late static bind type from <stack> EmitArgfullLateStaticBindTypeInitialization(routine); // define user labels: DefineLabels(routine.Builder.Labels); // emits function's body: body.Emit(this); // marks ending "}" as the last sequence point of the routine: // (do not mark it in lambda functions as they are created from source code without braces); if (!routine.IsLambda) { MarkSequencePoint(entirePosition.End); } else if (context.Config.Compiler.Debug) { il.Emit(OpCodes.Nop); } EmitRoutineEpilogue(null, false); }
internal void EmitLoadArgsOnEvalStack(CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { ILEmitter il = codeGenerator.IL; int mandatory_count = (routine.Signature != null) ? routine.Signature.MandatoryParamCount : 0; int formal_count = (routine.Signature != null) ? routine.Signature.ParamCount : 0; int actual_count = node.Parameters.Count; PhpTypeCode param_type; // loads all actual parameters which are not superfluous: for (int i = 0; i < Math.Min(actual_count, formal_count); i++) { var p = node.Parameters[i]; codeGenerator.EmitBoxing(param_type = p.NodeCompiler<ActualParamCompiler>().Emit(p, codeGenerator)); // Actual param emitter should emit "boxing" to a reference if its access type is ReadRef. // That's why no operation is needed here and references should match. Debug.Assert((routine.Signature == null || routine.Signature.IsAlias(i)) == (param_type == PhpTypeCode.PhpReference)); } // loads missing mandatory arguments: for (int i = actual_count; i < mandatory_count; i++) { // CALL PhpException.MissingArgument(<i+1>,<name>); il.LdcI4(i + 1); il.Emit(OpCodes.Ldstr, routine.FullName); codeGenerator.EmitPhpException(Methods.PhpException.MissingArgument); // LOAD null; if (routine.Signature.IsAlias(i)) il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); else il.Emit(OpCodes.Ldnull); } // loads missing optional arguments: for (int i = Math.Max(mandatory_count, actual_count); i < formal_count; i++) { // LOAD Arg.Default; il.Emit(OpCodes.Ldsfld, Fields.Arg_Default); } }
/// <summary> /// Emits IL instructions that load actual parameters on the evaluation stack. /// </summary> /// <param name="node">Instance.</param> /// <param name="codeGenerator">Code generator.</param> /// <param name="routine">PHP method being called.</param> /// <remarks> /// <para> /// The function has mandatory and optional formal arguments. /// Mandatory arguments are those formal arguments which are not preceded by /// any formal argument with default value. The others are optional. /// If a formal argument without default value is declared beyond the last mandatory argument /// it is treated as optional one by the caller. The callee checks this and throws warning. /// </para> /// Missing arguments handling: /// <list type="bullet"> /// <item>missing mandatory argument - WARNING; LOAD(null);</item> /// <item>missing optional argument - LOAD(Arg.Default);</item> /// <item>superfluous arguments are ignored</item> /// </list> /// </remarks> public void EmitLoadOnEvalStack(CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { EmitLoadTypeArgsOnEvalStack(node, codeGenerator, routine); EmitLoadArgsOnEvalStack(node, codeGenerator, routine); }
/// <summary> /// Enumerates all export overloads for the given target PHP method. /// </summary> public static IEnumerable<StubInfo> DefineMethodExportStubs( PhpRoutine/*!*/ target, PhpType/*!*/ declaringType, MethodAttributes attributes, bool defineConstructors, StubSignatureFilter/*!*/ signatureFilter) { Debug.Assert(target.Builder != null); Type return_type = Types.Object[0]; PhpRoutineSignature signature = target.Signature; AST.FormalParam[] formal_params = target.Builder.Signature.FormalParams; AST.FormalTypeParam[] formal_type_params = target.Builder.TypeSignature.TypeParams; int gen_sig_count = signature.GenericParamCount - signature.MandatoryGenericParamCount + 1; int arg_sig_count = signature.ParamCount - signature.MandatoryParamCount + 1; // TODO: return type hints // HACK: change return type to void for methods that are apparently event handlers if (signature.GenericParamCount == 0 && arg_sig_count == 1 && signature.ParamCount == 2 && (signature.TypeHints[0] == null || signature.TypeHints[0].RealType == Types.Object[0]) && (signature.TypeHints[1] != null && typeof(EventArgs).IsAssignableFrom(signature.TypeHints[1].RealType))) { return_type = Types.Void; } for (int gen_sig = 0; gen_sig < gen_sig_count; gen_sig++) { for (int arg_sig = 0; arg_sig < arg_sig_count; arg_sig++) { // determine parameter types (except for method mandatory generic parameters) object[] parameter_types = GetStubParameterTypes( arg_sig + signature.MandatoryParamCount, gen_sig + signature.MandatoryGenericParamCount, signature, formal_type_params); // determine generic parameter names string[] generic_param_names = new string[target.Signature.MandatoryGenericParamCount + gen_sig]; for (int i = 0; i < generic_param_names.Length; i++) { generic_param_names[i] = formal_type_params[i].Name.ToString(); } // are we allowed to generate this signature? if (!signatureFilter(generic_param_names, parameter_types, return_type)) continue; GenericTypeParameterBuilder[] generic_params = StubInfo.EmptyGenericParameters; MethodBase method_base = null; MethodBuilder method = null; if (!defineConstructors) { method = declaringType.RealTypeBuilder.DefineMethod(target.FullName, attributes); // determine generic parameters if (generic_param_names.Length > 0) generic_params = method.DefineGenericParameters(generic_param_names); method_base = method; } ParameterInfo[] parameters = new ParameterInfo[parameter_types.Length]; // fill in parameter infos Type[] real_parameter_types = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type type = parameter_types[i] as Type; // generic method parameter fixup if (type == null) { int index = (int)parameter_types[i]; if (index < 0) type = generic_params[-(index + 1)].MakeByRefType(); else type = generic_params[index]; } string param_name; ParameterAttributes param_attrs; if (i < formal_params.Length) { param_name = formal_params[i].Name.ToString(); param_attrs = (formal_params[i].IsOut ? ParameterAttributes.Out : ParameterAttributes.None); } else { param_name = "args" + (i + 1); param_attrs = ParameterAttributes.None; } parameters[i] = new StubParameterInfo(i, type, param_attrs, param_name); real_parameter_types[i] = type; } if (method != null) { method.SetParameters(real_parameter_types); method.SetReturnType(return_type); method.SetCustomAttribute(AttributeBuilders.DebuggerHidden); } else { // constructor is never a generic method attributes |= MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; attributes &= ~MethodAttributes.Virtual; ConstructorBuilder constructor = declaringType.RealTypeBuilder.DefineConstructor( attributes, CallingConventions.Standard, real_parameter_types); constructor.SetCustomAttribute(AttributeBuilders.DebuggerHidden); method_base = constructor; } yield return new StubInfo(method_base, parameters, generic_params, return_type); } } }
/// <summary> /// Resolves a method of given <see cref="DType"/> by its name. /// </summary> /// <param name="type">The type of routine being resolved.</param> /// <param name="methodName">The name of routine to be resolved.</param> /// <param name="position">Position of method call used for error reporting.</param> /// <param name="referringType">The type where the seached routine is being called. Can be <c>null</c>.</param> /// <param name="referringRoutine">The routine where the searched routine is being called. Can be <c>null</c>.</param> /// <param name="calledStatically">True if the searched routine is called statically - if it uses static method call syntax. /// This affects the __call or __callStatic method lookup. /// It affects also the error reporting, where for instance method calls, the bad visibility error is /// ignored and falls back to return <see cref="UnknownMethod"/>.</param> /// <param name="checkVisibilityAtRuntime">Will determine if the routine call must be checked for visibility at runtime.</param> /// <param name="isCallMethod">Will determine if __call or __callStatic magic methods were found instead.</param> /// <returns>The resolved routine. Cannot return <c>null</c>.</returns> public DRoutine/*!*/ ResolveMethod(DType/*!*/ type, Name methodName, Position position, PhpType referringType, PhpRoutine referringRoutine, bool calledStatically, out bool checkVisibilityAtRuntime, out bool isCallMethod) { checkVisibilityAtRuntime = false; isCallMethod = false; // we cannot resolve a method unless we know the inherited members: if (type.IsDefinite) { KnownType known; // the method is a constructor: if (methodName.IsConstructName || (known = type as KnownType) != null && methodName.Equals(known.QualifiedName.Name)) return ResolveConstructor(type, position, referringType, referringRoutine, out checkVisibilityAtRuntime); DRoutine routine; GetMemberResult member_result; member_result = type.GetMethod(methodName, referringType, out routine); // Look for __call or __callStatic magic methods if no method was found: // Note: __call when looking for instance method is disabled, since there can be the searched method in some future override. if (member_result == GetMemberResult.NotFound && calledStatically) { // in PHP, it is possible to call instance methods statically if we are in instance method context. // In such case we have to look for __call instead of __callStatic: // determine the proper call method: // use __call for instance method invocation, including static method invocation within the current type (e.g. A::foo(), parent::foo(), ...) // use __callStatic for static method invocation Name callMethodName = (!calledStatically || // just to have complete condition here, always false (referringRoutine != null && referringType != null && !referringRoutine.IsStatic && // in non-static method type.TypeDesc.IsAssignableFrom(referringType.TypeDesc)) // {CurrentType} is inherited from or equal {type} ) ? DObject.SpecialMethodNames.Call : DObject.SpecialMethodNames.CallStatic; member_result = type.GetMethod(callMethodName, referringType, out routine); if (member_result != GetMemberResult.NotFound) isCallMethod = true; } switch (member_result) { case GetMemberResult.OK: return routine; case GetMemberResult.NotFound: if (calledStatically) // throw an error only in we are looking for static method, instance method can be defined in some future inherited class ErrorSink.Add(Errors.UnknownMethodCalled, SourceUnit, position, type.FullName, methodName); return new UnknownMethod(type, methodName.Value); case GetMemberResult.BadVisibility: { if (!calledStatically) // instance method will check the routine dynamically, there can be some override later return new UnknownMethod(type, methodName.Value); if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return routine; } else { // definitive error: if (routine.IsPrivate) { ErrorSink.Add(Errors.PrivateMethodCalled, SourceUnit, position, type.FullName, methodName.Value, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedMethodCalled, SourceUnit, position, type.FullName, methodName.Value, referringType.FullName); } return new UnknownMethod(type, methodName.Value); } } default: Debug.Fail(); return null; } } else { // warning (if any) reported by the type resolver: return new UnknownMethod(type, methodName.Value); } }
public DType/*!*/ ResolveTypeName(GenericQualifiedName genericName, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { DType type = ResolveTypeName(genericName.QualifiedName, referringType, referringRoutine, position, mustResolve); DTypeDesc[] arguments = (genericName.GenericParams.Length > 0) ? new DTypeDesc[genericName.GenericParams.Length] : DTypeDesc.EmptyArray; for (int i = 0; i < arguments.Length; i++) { arguments[i] = ResolveType(genericName.GenericParams[i], referringType, referringRoutine, position, mustResolve).TypeDesc; } return type.MakeConstructedType(this, arguments, position); }
private GenericParameter ResolveTypeParameterName(Name name, PhpType referringType, PhpRoutine referringRoutine) { GenericParameter result = null; if (referringRoutine != null) { result = referringRoutine.Signature.GetGenericParameter(name); if (result != null) return result; } if (referringType != null) { result = referringType.GetGenericParameter(name); if (result != null) return result; } return result; }
public DType/*!*/ ResolveTypeName(QualifiedName qualifiedName, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { DType result; if (qualifiedName.IsSelfClassName) { if (referringType != null) { result = referringType; } else { // we are sure the self is used incorrectly in function: if (referringRoutine != null) ErrorSink.Add(Errors.SelfUsedOutOfClass, SourceUnit, position); // global code can be included to the method: result = UnknownType.UnknownSelf; } } else if (qualifiedName.IsStaticClassName) { if (referringType != null) { if (referringType.IsFinal) { // we are sure the 'static' == 'self' result = referringType; } else { if (referringRoutine != null) referringRoutine.Properties |= RoutineProperties.LateStaticBinding; result = StaticType.Singleton; } } else { // we are sure the static is used incorrectly in function: //if (referringRoutine != null) // do not allow 'static' in global code: ErrorSink.Add(Errors.StaticUsedOutOfClass, SourceUnit, position); // global code can be included to the method: result = UnknownType.UnknownStatic; } } else if (qualifiedName.IsParentClassName) { if (referringType != null) { if (referringType.IsInterface) { ErrorSink.Add(Errors.ParentUsedOutOfClass, SourceUnit, position); result = UnknownType.UnknownParent; } else { DType base_type = referringType.Base; if (base_type == null) { ErrorSink.Add(Errors.ClassHasNoParent, SourceUnit, position, referringType.FullName); result = UnknownType.UnknownParent; } else { result = base_type; } } } else { // we are sure the self is used incorrectly when we are in a function: if (referringRoutine != null) ErrorSink.Add(Errors.ParentUsedOutOfClass, SourceUnit, position); // global code can be included to the method: result = UnknownType.UnknownParent; } } else { // try resolve the name as a type parameter name: if (qualifiedName.IsSimpleName) { result = ResolveTypeParameterName(qualifiedName.Name, referringType, referringRoutine); if (result != null) return result; } Scope referring_scope = GetReferringScope(referringType, referringRoutine); QualifiedName? alias; result = sourceUnit.ResolveTypeName(qualifiedName, referring_scope, out alias, ErrorSink, position, mustResolve); ReportUnknownType(result, alias, position); } return result; }
public DType ResolveType(object typeNameOrPrimitiveType, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { Debug.Assert(typeNameOrPrimitiveType == null || typeNameOrPrimitiveType is PrimitiveType || typeNameOrPrimitiveType is GenericQualifiedName); DType result = typeNameOrPrimitiveType as PrimitiveType; if (result != null) return result; if (typeNameOrPrimitiveType != null) { return ResolveTypeName((GenericQualifiedName)typeNameOrPrimitiveType, referringType, referringRoutine, position, mustResolve); } return null; }
private Scope GetReferringScope(PhpType referringType, PhpRoutine referringRoutine) { if (referringType != null) return referringType.Declaration.Scope; if (referringRoutine is PhpFunction) return ((PhpFunction)referringRoutine).Declaration.Scope; //if (referringRoutine is PhpLambdaFunction) ... // used for global statements during full analysis: Debug.Assert(currentScope.IsValid, "Scope is available only during full analysis."); return currentScope; }
/// <summary> /// Resolves type based on provided <paramref name="typeName"/>. /// </summary> /// <param name="typeName">Either <see cref="GenericQualifiedName"/> or <see cref="PrimitiveTypeName"/> or <c>null</c> reference.</param> /// <param name="referringType"></param> /// <param name="referringRoutine"></param> /// <param name="position"></param> /// <param name="mustResolve"></param> /// <returns></returns> internal DType ResolveType(object typeName, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { DType result = null; if (typeName != null) { if (typeName.GetType() == typeof(GenericQualifiedName)) { result = ResolveTypeName((GenericQualifiedName)typeName, referringType, referringRoutine, position, mustResolve); } else if (typeName.GetType() == typeof(PrimitiveTypeName)) { result = PrimitiveType.GetByName((PrimitiveTypeName)typeName); } else { throw new ArgumentException("typeName"); } } return result; }
internal PhpRoutineBuilder(PhpRoutine/*!*/ routine, Signature signature, TypeSignature typeSignature) { this.routine = routine; this.signature = signature; this.typeSignature = typeSignature; this.localVariables = new VariablesTable(10); this.labels = new Dictionary<VariableName, Statement>(1); }
/// <summary> /// Returns a <see cref="PhpRoutineSignature"/> for the specified argfull <see cref="MethodInfo"/>. /// </summary> /// <exception cref="ReflectionException">Invalid argfull.</exception> public static PhpRoutineSignature/*!*/ FromArgfullInfo(PhpRoutine/*!*/ routine, MethodInfo/*!*/ argfull) { // determine aliasReturn bool alias_return = false; if (argfull.ReturnType != Types.Object[0]) { if (argfull.ReturnType == Types.PhpReference[0]) alias_return = true; else throw new ReflectionException("TODO"); } ParameterInfo[] parms = argfull.GetParameters(); int parms_length = parms.Length; int parms_offset = 1; if (--parms_length < 0 || parms[0].ParameterType != Types.ScriptContext[0]) { // TODO: resource throw new ReflectionException("Invalid static argfull signature of method " + routine.FullName); } // process pseudo-generic parameters: int j = parms_offset; while (j < parms.Length && parms[j].ParameterType == Types.DTypeDesc[0]) j++; int generic_param_count = j - parms_offset; int mandatory_generic_param_count = 0; GenericParameter[] generic_params = (generic_param_count > 0) ? new GenericParameter[generic_param_count] : GenericParameter.EmptyArray; for (int i = 0; i < generic_param_count; i++) { ParameterInfo pinfo = parms[i + parms_offset]; generic_params[i] = new GenericParameter(new Name(pinfo.Name), i, routine); DTypeSpecAttribute default_type = DTypeSpecAttribute.Reflect(pinfo); if (default_type != null) { // TODO: // generic_params[i].WriteUp(default_type.TypeSpec.GetTypeDesc(null, null, null, null).Type); } else { generic_params[i].WriteUp(null); if (mandatory_generic_param_count < i) { // TODO: resource throw new ReflectionException("Invalid signature"); } else mandatory_generic_param_count++; } } parms_offset += generic_param_count; parms_length -= generic_param_count; // set up genericParams, aliasMask, typeHints, and mandatoryParamCount: BitArray alias_mask = new BitArray(parms_length, false); DType[] type_hints = new DType[parms_length]; int mandatory_param_count = 0; for (int i = 0; i < parms_length; i++) { ParameterInfo pinfo = parms[i + parms_offset]; if (pinfo.ParameterType != Types.Object[0]) { if (pinfo.ParameterType == Types.PhpReference[0]) alias_mask[i] = true; else return null; } DTypeSpecAttribute type_hint = DTypeSpecAttribute.Reflect(pinfo); if (type_hint != null) { // TODO: // type_hints[i] = type_hint.TypeSpec.GetTypeDesc(null, null, null, null).Type; } if (!pinfo.IsOptional) { if (mandatory_param_count < i) { // invalid optional parameters throw new ReflectionException("Invalid signature"); } else mandatory_param_count++; } } return new PhpRoutineSignature(generic_params, mandatory_generic_param_count). WriteUp(alias_return, alias_mask, type_hints, mandatory_param_count); }
/// <summary> /// Resolves constructor of the specified type within the current context (location). /// </summary> public DRoutine/*!*/ ResolveConstructor(DType/*!*/ type, Position position, PhpType referringType, PhpRoutine referringRoutine, out bool checkVisibilityAtRuntime) { checkVisibilityAtRuntime = false; KnownRoutine ctor; // Do resolve ctor despite of the indefiniteness of the type to make error reporting consistent // when accessing the constructors thru the new operator. switch (type.GetConstructor(referringType, out ctor)) { case GetMemberResult.OK: return ctor; case GetMemberResult.NotFound: // default ctor to be used: return new UnknownMethod(type); case GetMemberResult.BadVisibility: if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return ctor; } else { // definitive error: if (ctor.IsPrivate) { ErrorSink.Add(Errors.PrivateCtorCalled, SourceUnit, position, type.FullName, ctor.FullName, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedCtorCalled, SourceUnit, position, type.FullName, ctor.FullName, referringType.FullName); } return new UnknownMethod(type); } default: Debug.Fail(); return null; } }
/// <summary> /// Emits IL instructions that load actual parameters on the evaluation stack. /// </summary> /// <param name="codeGenerator">Code generator.</param> /// <param name="routine">PHP method being called.</param> /// <remarks> /// <para> /// The function has mandatory and optional formal arguments. /// Mandatory arguments are those formal arguments which are not preceded by /// any formal argument with default value. The others are optional. /// If a formal argument without default value is declared beyond the last mandatory argument /// it is treated as optional one by the caller. The callee checks this and throws warning. /// </para> /// Missing arguments handling: /// <list type="bullet"> /// <item>missing mandatory argument - WARNING; LOAD(null);</item> /// <item>missing optional argument - LOAD(Arg.Default);</item> /// <item>superfluous arguments are ignored</item> /// </list> /// </remarks> internal void EmitLoadOnEvalStack(CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { EmitLoadTypeArgsOnEvalStack(codeGenerator, routine); EmitLoadArgsOnEvalStack(codeGenerator, routine); }
/// <summary> /// Resolves static properties. /// </summary> public DProperty/*!*/ ResolveProperty(DType/*!*/ type, VariableName propertyName, Position position, bool staticOnly, PhpType referringType, PhpRoutine referringRoutine, out bool checkVisibilityAtRuntime) { Debug.Assert(type != null); checkVisibilityAtRuntime = false; // we cannot resolve a property unless we know the inherited members: if (type.IsDefinite) { DProperty property; GetMemberResult member_result = type.GetProperty(propertyName, referringType, out property); switch (member_result) { case GetMemberResult.OK: if (staticOnly && !property.IsStatic) goto case GetMemberResult.NotFound; return property; case GetMemberResult.NotFound: ErrorSink.Add(Errors.UnknownPropertyAccessed, SourceUnit, position, type.FullName, propertyName); return new UnknownProperty(type, propertyName.Value); case GetMemberResult.BadVisibility: if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return property; } else { // definitive error: if (property.IsPrivate) { ErrorSink.Add(Errors.PrivatePropertyAccessed, SourceUnit, position, type.FullName, propertyName.Value, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedPropertyAccessed, SourceUnit, position, type.FullName, propertyName.Value, referringType.FullName); } return new UnknownProperty(type, propertyName.Value); } default: Debug.Fail(); throw null; } } else { // warning (if any) reported by the type resolver: return new UnknownProperty(type, propertyName.Value); } }
internal void EmitLoadTypeArgsOnEvalStack(CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { ILEmitter il = codeGenerator.IL; int mandatory_count = (routine.Signature != null) ? routine.Signature.MandatoryGenericParamCount : 0; int formal_count = (routine.Signature != null) ? routine.Signature.GenericParamCount : 0; int actual_count = node.GenericParams.Count; // loads all actual parameters which are not superfluous: for (int i = 0; i < Math.Min(actual_count, formal_count); i++) node.GenericParams[i].EmitLoadTypeDesc(codeGenerator, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors); // loads missing mandatory arguments: for (int i = actual_count; i < mandatory_count; i++) { // CALL PhpException.MissingTypeArgument(<i+1>,<name>); il.LdcI4(i + 1); il.Emit(OpCodes.Ldstr, routine.FullName); codeGenerator.EmitPhpException(Methods.PhpException.MissingTypeArgument); // LOAD DTypeDesc.ObjectTypeDesc; il.Emit(OpCodes.Ldsfld, Fields.DTypeDesc.ObjectTypeDesc); } // loads missing optional arguments: for (int i = Math.Max(mandatory_count, actual_count); i < formal_count; i++) { // LOAD Arg.DefaultType; il.Emit(OpCodes.Ldsfld, Fields.Arg_DefaultType); } }
internal DConstant ResolveClassConstantName(DType/*!*/ type, VariableName constantName, Position position, PhpType referringType, PhpRoutine referringRoutine, out bool checkVisibilityAtRuntime) { checkVisibilityAtRuntime = false; // we cannot resolve a class constant unless we know the inherited members: if (type.IsDefinite) { ClassConstant constant; GetMemberResult member_result = type.GetConstant(constantName, referringType, out constant); switch (member_result) { case GetMemberResult.OK: return constant; case GetMemberResult.NotFound: ErrorSink.Add(Errors.UnknownClassConstantAccessed, SourceUnit, position, type.FullName, constantName); return new UnknownClassConstant(type, constantName.Value); case GetMemberResult.BadVisibility: if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return constant; } else { // definitive error: if (constant.IsPrivate) { ErrorSink.Add(Errors.PrivateConstantAccessed, SourceUnit, position, type.FullName, constantName.Value, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedConstantAccessed, SourceUnit, position, type.FullName, constantName.Value, referringType.FullName); } return new UnknownClassConstant(type, constantName.Value); } default: Debug.Fail(); throw null; } } else { // warning (if any) reported by the type resolver: return new UnknownClassConstant(type, constantName.Value); } }
public static void EmitLoadOnEvalStack(this CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { node.NodeCompiler<ICallSignatureCompiler>().EmitLoadOnEvalStack(node, codeGenerator, routine); }
internal void SetEntryPoint(PhpRoutine/*!*/ routine, Position position) { // pure entry point is a static parameterless "Main" method/function: if (!sourceUnit.CompilationUnit.IsPure || !routine.Name.Equals(PureAssembly.EntryPointName) || !routine.IsStatic || routine.Signature.ParamCount > 0) return; PureCompilationUnit pcu = (PureCompilationUnit)sourceUnit.CompilationUnit; if (pcu.EntryPoint != null) { ErrorSink.Add(Errors.EntryPointRedefined, SourceUnit, position); ErrorSink.Add(Errors.RelatedLocation, pcu.EntryPoint.SourceUnit, pcu.EntryPoint.Position); } else { pcu.SetEntryPoint(routine); } }
/// <summary> /// Emit check whether the argsaware routine was called properly with <see cref="PhpStack"/> initialized. /// </summary> /// <remarks>Emitted code is equivalent to <code>context.Stack.ThrowIfNotArgsaware(routine.Name.Value);</code></remarks> private void EmitArgsAwareCheck(PhpRoutine/*!*/ routine) { if (routine.IsArgsAware) { // <context>.Stack.ThrowIfNotArgsaware(routine.Name.Value) this.EmitLoadScriptContext(); // <context> this.IL.Emit(OpCodes.Ldfld, Fields.ScriptContext_Stack); // .Stack this.IL.Emit(OpCodes.Ldstr, routine.Name.Value); // routine.Name.Value this.IL.Emit(OpCodes.Call, Methods.PhpStack.ThrowIfNotArgsaware); // .call ThrowIfNotArgsaware } }
internal void SetEntryPoint(PhpRoutine/*!*/ routine, Position position) { // nothing to do here on silverlight.. }
/// <summary> /// Emits initialization of arg-full argument variables. /// </summary> private void EmitArgfullArgsInitialization(PhpRoutine/*!*/ routine) { bool optimized = (routine.Properties & RoutineProperties.HasUnoptimizedLocals) == 0; bool indirect_local_access = (routine.Properties & RoutineProperties.IndirectLocalAccess) != 0; int real_index = routine.FirstPseudoGenericParameterIndex; int index = 0; foreach (GenericParameter param in routine.Signature.GenericParams) { if (param.DefaultType != null) { Label endif_label = il.DefineLabel(); // IF ARG[real_index] == Arg.DefaultType) THEN il.Ldarg(real_index); il.Emit(OpCodes.Ldsfld, Fields.Arg_DefaultType); il.Emit(OpCodes.Bne_Un, endif_label); // ARG[real_index] = <typedesc(param.DefaultType)>; param.DefaultType.EmitLoadTypeDesc(this, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors); il.Starg(real_index); // ENDIF; il.MarkLabel(endif_label); } // add the DTypeDesc into locals /*{ EmitLoadRTVariablesTable(); il.Emit(OpCodes.Ldstr, "!" + param.Name.ToString().ToLower()); // LOAD ARG[arg_idx]; il.Ldarg(real_index); // stores argument to table: il.Emit(OpCodes.Callvirt, PhpVariable.RTVariablesTableAdder); }*/ real_index++; index++; } real_index = routine.FirstPhpParameterIndex; index = 0; foreach (FormalParam param in routine.Builder.Signature.FormalParams) { // sets variables place in the table: VariablesTable.Entry entry = routine.Builder.LocalVariables[param.Name]; bool optional = index >= routine.Signature.MandatoryParamCount; // only variables accessible by function's code are initialized; // these are // - all if function has unoptimized locals or contains indirect access // (which doesn't imply unoptimized locals) // - those which are directly used if (!optimized || indirect_local_access || entry.IsDirectlyUsed) { // if the argument is reference => the local should also be a reference: Debug.Assert(!param.PassedByRef || entry.IsPhpReference); // marks a sequence point if a parameter is initialized or type hinted: if (optional || param.TypeHint != null) { this.MarkSequencePoint(param.Span); } if (optional) { Label end_label = il.DefineLabel(); Label else_label = il.DefineLabel(); // IF (ARG[arg_idx]!=Arg.Default) THEN il.Ldarg(real_index); il.Emit(OpCodes.Ldsfld, Fields.Arg_Default); il.Emit(OpCodes.Beq_S, else_label); // emits deep copying (if not reference): EmitArgumentCopying(real_index, param); // ELSE; il.Emit(OpCodes.Br, end_label); il.MarkLabel(else_label); // ARG[arg_idx] = <default value>; EmitLoadArgumentDefaultValue(index, param, routine.FullName); il.Starg(real_index); // END IF; il.MarkLabel(end_label); } else { // emits deep copying (if not reference): EmitArgumentCopying(real_index, param); } // emits type hint test (if specified): param.EmitTypeHintTest(this); // stores argument value to the local variable or to the table // // prepares evaluation stack for call to <variables_table>.Add(<name>,ARG[arg_idx]): if (!optimized) { EmitLoadRTVariablesTable(); il.Emit(OpCodes.Ldstr, param.Name.ToString()); // LOAD ARG[arg_idx]; il.Ldarg(real_index); // "box" to reference (if the local is a reference and argument is not a reference): if (entry.IsPhpReference && !param.PassedByRef) il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); // stores argument to table: il.Emit(OpCodes.Callvirt, PhpVariable.RTVariablesTableAdder); } else if (entry.IsPhpReference && !param.PassedByRef) { // local variable is stored in a new reference local: LocalBuilder local = il.DeclareLocal(typeof(PhpReference)); entry.Variable = new Place(local); // "box" to reference (if the local is a reference and argument is not a reference): il.Ldarg(real_index); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); il.Stloc(local); } else { // local variable is stored in the argument: entry.Variable = new IndexedPlace(PlaceHolder.Argument, real_index); } } else { if (param.TypeHint != null) { if (optional) { // convert Arg.Default to proper default value (so TypeHint test will check the proper value): Label is_default = il.DefineLabel(); Label end_if = il.DefineLabel(); // if (ARG[arg_idx] = Arg.Default) THEN il.Ldarg(real_index); il.Emit(OpCodes.Ldsfld, Fields.Arg_Default); il.Emit(OpCodes.Beq_S, is_default); il.Emit(OpCodes.Br_S, end_if); { // ARG[arg_idx] = <default value>; il.MarkLabel(is_default); EmitLoadArgumentDefaultValue(index, param, routine.FullName); il.Starg(real_index); } il.MarkLabel(end_if); } // emits type hint test (if specified): param.EmitTypeHintTest(this); } } real_index++; index++; } }
internal RoutineDeclLoc(PhpRoutine/*!*/ routine, int nestingLevel) : base(nestingLevel) { this.routine = routine; }
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> /// Notices the analyzer that function declaration is entered. /// </summary> internal void EnterFunctionDeclaration(PhpRoutine/*!*/ function) { Debug.Assert(function.IsFunction); RoutineDeclLoc f = new RoutineDeclLoc(function, locationStack.Count); routineDeclStack.Push(f); locationStack.Push(f); EnterConditionalCode(); }