示例#1
0
        /// <summary>
        /// Analyze the method call (isMemberOf != null).
        /// </summary>
        /// <param name="analyzer"></param>
        /// <param name="info"></param>
        /// <returns></returns>
        private Evaluation AnalyzeMethodCall(Analyzer/*!*/ analyzer, ref ExInfoFromParent info)
        {
            Debug.Assert(isMemberOf != null);

            // $this->
            DirectVarUse memberDirectVarUse = isMemberOf as DirectVarUse;
            if (memberDirectVarUse != null && memberDirectVarUse.IsMemberOf == null &&  // isMemberOf is single variable
                memberDirectVarUse.VarName.IsThisVariableName &&                        // isMemberOf if $this
                analyzer.CurrentType != null)                                           // called in class context of known type
            {
                // $this->{qualifiedName}(callSignature)

                bool runtimeVisibilityCheck, isCallMethod;

                routine = analyzer.ResolveMethod(
                    analyzer.CurrentType,//typeof(this)
                    qualifiedName.Name,//.Namespace?
                    Position,
                    analyzer.CurrentType, analyzer.CurrentRoutine, false,
                    out runtimeVisibilityCheck, out isCallMethod);

                Debug.Assert(runtimeVisibilityCheck == false);  // can only be set to true if CurrentType or CurrentRoutine are null

                if (!routine.IsUnknown)
                {
                    // check __call
                    if (isCallMethod)
                    {
                        // TODO: generic args
                        
                        var arg1 = new StringLiteral(this.Position, qualifiedName.Name.Value);
                        var arg2 = this.callSignature.BuildPhpArray();

                        this.callSignature = new CallSignature(
                            new List<ActualParam>(2) {
                                new ActualParam(arg1.Position, arg1, false),
                                new ActualParam(arg2.Position, arg2, false)
                            },
                            new List<TypeRef>());
                    }
                    
                    // resolve overload if applicable:
                    RoutineSignature signature;
                    overloadIndex = routine.ResolveOverload(analyzer, callSignature, position, out signature);

                    Debug.Assert(overloadIndex != DRoutine.InvalidOverloadIndex, "A function should have at least one overload");

                    // analyze parameters:
                    callSignature.Analyze(analyzer, signature, info, false);

                    // get properties:
                    analyzer.AddCurrentRoutineProperty(routine.GetCallerRequirements());

                    return new Evaluation(this);
                }
            }

            // by default, fall back to dynamic method invocation
            routine = null;
            callSignature.Analyze(analyzer, UnknownSignature.Default, info, false);

            return new Evaluation(this);
        }
示例#2
0
        /// <summary>
		/// Tries to determine a value of the node.
		/// </summary>
		/// <returns>
		/// Whether the function call can be evaluated at compile time. <B>true</B>, 
		/// if the function is a special library one and the correct number of arguments 
		/// is specified in the call and all that arguments are evaluable.
		/// </returns>
		private bool TryEvaluate(Analyzer/*!*/ analyzer, out object value)
		{
            // special cases, allow some AST transformation:
            this.AnalyzeSpecial(analyzer);

            // try evaluate function call in compile time:
            if (callSignature.AllParamsHaveValue)
            {
                PureFunctionAttribute pureAttribute;

                // PhpLibraryFunction with PureFunctionAttribute can be evaluated
                PhpLibraryFunction lib_function;

                if ((lib_function = routine as PhpLibraryFunction) != null &&
                    (pureAttribute = PureFunctionAttribute.Reflect(lib_function.Overloads[overloadIndex].Method)) != null)
                {
                    // the method to be used for evaluation
                    MethodInfo evaluableMethod = pureAttribute.CallSpecialMethod ?
                        pureAttribute.SpecialMethod :
                        lib_function.Overloads[overloadIndex].Method;

                    Debug.Assert(evaluableMethod != null);

                    if (evaluableMethod.ContainsGenericParameters)
                        throw new ArgumentException("Evaluable method '" + evaluableMethod.Name + "' cannot contain generic parameters.");

                    var parametersInfo = evaluableMethod.GetParameters();

                    object[] invokeParameters = new object[parametersInfo.Length];

                    // convert/create proper parameters value:
                    int nextCallParamIndex = 0;

                    for (int i = 0; i < parametersInfo.Length; ++i)
                    {
                        ParameterInfo paramInfo = parametersInfo[i];
                        Type paramType = paramInfo.ParameterType;

                        // only In parameters are allowed
#if !SILVERLIGHT
                        Debug.Assert(!paramInfo.IsOut && !paramInfo.IsRetval);
#else
                        Debug.Assert(!paramInfo.IsOut && !ParameterInfoEx.IsRetVal(paramInfo));
#endif

                        // perform parameter conversion:
                        Action<Converter<object, object>> PassArgument = (converter) =>
                            {
                                if (nextCallParamIndex >= callSignature.Parameters.Count)
                                    throw new ArgumentException("Not enough parameters in evaluable method.");

                                object obj = callSignature.Parameters[nextCallParamIndex++].Expression.Value;
                                invokeParameters[i] = converter(obj);
                            };

                        // special params types:
                        if (paramType == typeof(Analyzer))
                        {
                            invokeParameters[i] = analyzer;
                        }
                        else if (paramType == typeof(CallSignature))
                        {
                            invokeParameters[i] = callSignature;
                        }
                        else if (   // ... , params object[] // last parameter
                            paramType == typeof(object[]) &&
                            i == parametersInfo.Length - 1 &&
                            parametersInfo[i].IsDefined(typeof(ParamArrayAttribute), false))
                        {
                            // params object[]
                            var args = new object[callSignature.Parameters.Count - nextCallParamIndex];
                            for (int arg = 0; arg < args.Length; ++nextCallParamIndex, ++arg)
                                args[arg] = callSignature.Parameters[nextCallParamIndex].Expression.Value;

                            invokeParameters[i] = args;
                        }
                        // PHP value types:
                        else if (paramType == typeof(object))
                            PassArgument(obj => obj);
                        else if (paramType == typeof(PhpBytes))
                            PassArgument(Convert.ObjectToPhpBytes);
                        else if (paramType == typeof(string))
                            PassArgument(Convert.ObjectToString);
                        else if (paramType == typeof(int))
                            PassArgument(obj=>(object)Convert.ObjectToInteger(obj));
                        else if (paramType == typeof(bool))
                            PassArgument(obj => (object)Convert.ObjectToBoolean(obj));
                        else if (paramType == typeof(double))
                            PassArgument(obj => (object)Convert.ObjectToDouble(obj));
                        else if (paramType == typeof(long))
                            PassArgument(obj => (object)Convert.ObjectToLongInteger(obj));
                        else if (paramType == typeof(char))
                            PassArgument(obj => (object)Convert.ObjectToChar(obj));
                        else
                            throw new ArgumentException("Parameter type " + paramType.ToString() + " cannot be used in evaluable method.", paramInfo.Name);
                    }

                    // catch runtime errors
                    var oldErrorOverride = PhpException.ThrowCallbackOverride;
                    if (!(analyzer.ErrorSink is EvalErrorSink || analyzer.ErrorSink is WebErrorSink)) // avoid infinite recursion, PhpExceptions in such cases are passed
                        PhpException.ThrowCallbackOverride = (error, message) =>
                        {
                            analyzer.ErrorSink.AddInternal(
                                -2,
                                message, (error == PhpError.Error || error == PhpError.CoreError || error == PhpError.UserError) ? ErrorSeverity.Error : ErrorSeverity.Warning,
                                (int)WarningGroups.None,
                                analyzer.SourceUnit.GetMappedFullSourcePath(Position.FirstLine),
                                new ErrorPosition(
                                    analyzer.SourceUnit.GetMappedLine(Position.FirstLine), Position.FirstColumn,
                                    analyzer.SourceUnit.GetMappedLine(Position.LastLine), Position.LastColumn),
                                true
                                );
                        };
                    
                    // invoke the method and get the result
                    try
                    {
                        value = evaluableMethod.Invoke(null, invokeParameters);

                        if (evaluableMethod.ReturnType == typeof(EvaluateInfo))
                        {
                            var info = value as EvaluateInfo;

                            if (info != null && info.emitDeclareLamdaFunction && info.newRoutine != null)
                            {
                                this.routine = info.newRoutine;
                                this.inlined = InlinedFunction.CreateFunction;
                                return false;   // 
                            }

                            if (info == null)
                                return false;

                            value = info.value;
                        }
                        
                        // apply automatic cast to false if CastToFalse attribute is defined:
                        if (evaluableMethod.ReturnTypeCustomAttributes.IsDefined(typeof(CastToFalseAttribute), false))
                        {
                            if ((value == null) ||
                                (value is int && (int)value == -1))
                                value = false;
                        }
                        
                        // pass the value
                        return true;
                    }
                    finally
                    {
                        PhpException.ThrowCallbackOverride = oldErrorOverride;
                    }
                }
            }

            // function cannot be evaluated
            value = null;
            return false;

            /*

			// skips functions without "special" flag set:
			//PhpLibraryFunction lib_function = routine as PhpLibraryFunction;
			if (lib_function == null || (lib_function.Options & FunctionImplOptions.Special) == 0)
			{
				value = null;
				return false;
			}

			switch (callSignature.Parameters.Count)
			{
				case 0:
					{
						if (lib_function.Name.EqualsLowercase("phpversion"))
						{
							value = PhpVersion.Current;
							return true;
						}

						if (lib_function.Name.EqualsLowercase("pi"))
						{
							value = Math.PI;
							return true;
						}
						break;
					}

				case 1:
					{
						// tries to evaluate the parameter:
						if (!callSignature.Parameters[0].Expression.HasValue) break;

						object param = callSignature.Parameters[0].Expression.Value;

						if (lib_function.Name.EqualsLowercase("function_exists"))
						{
                            // jakub: if this returns true, it is evaluable, in case of false, we should try it during the runtime again

							// TODO:
							//Name function_name = new Name(Convert.ObjectToString(param));
							//OverloadInfo overload;

							//// only library functions can be checked; others depends on the current set of declarators:
							//ApplicationContext.Functions.Get(function_name, 0, out overload);
							//value = overload.GetUserEntryPoint != null;

							//return overload.GetUserEntryPoint != null;
							value = false;
							return false;
						}

						if (lib_function.Name.EqualsLowercase("strlen"))
						{
							value = Convert.ObjectToString(param).Length;
							return true;
						}

						if (lib_function.Name.EqualsLowercase("round"))
						{
							value = Math.Round(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("sqrt"))
						{
							value = Math.Sqrt(Convert.ObjectToDouble(param));
							return true;
						}


						if (lib_function.Name.EqualsLowercase("exp"))
						{
							value = Math.Exp(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("log"))
						{
							value = Math.Log(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("ceil"))
						{
							value = Math.Ceiling(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("floor"))
						{
							value = Math.Floor(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("deg2rad"))
						{
							value = Convert.ObjectToDouble(param) / 180 * Math.PI;
							return true;
						}

						if (lib_function.Name.EqualsLowercase("cos"))
						{
							value = Math.Cos(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("sin"))
						{
							value = Math.Sin(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("tan"))
						{
							value = Math.Tan(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("acos"))
						{
							value = Math.Acos(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("asin"))
						{
							value = Math.Asin(Convert.ObjectToDouble(param));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("atan"))
						{
							value = Math.Atan(Convert.ObjectToDouble(param));
							return true;
						}

						break;
					}

				case 2:
					{
						// tries to evaluate the parameters:
						if (!callSignature.Parameters[0].Expression.HasValue) break;
						if (!callSignature.Parameters[1].Expression.HasValue) break;

						object param1 = callSignature.Parameters[0].Expression.Value;
						object param2 = callSignature.Parameters[1].Expression.Value;

						if (lib_function.Name.EqualsLowercase("version_compare"))
						{
							value = PhpVersion.Compare(Convert.ObjectToString(param1), Convert.ObjectToString(param2));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("log"))
						{
							value = MathEx.Log(Convert.ObjectToDouble(param1), Convert.ObjectToDouble(param2));
							return true;
						}

						if (lib_function.Name.EqualsLowercase("create_function"))
						{
							// has to be a valid identifier:
							string function_name = "__" + Guid.NewGuid().ToString().Replace('-', '_');

							string prefix1, prefix2;
							DynamicCode.GetLamdaFunctionCodePrefixes(function_name, Convert.ObjectToString(param1), out prefix1, out prefix2);

							Position pos_args = callSignature.Parameters[0].Position;
							Position pos_body = callSignature.Parameters[1].Position;

							// function __XXXXXX(<args>){<fill><body>}
							string fill = GetInlinedLambdaCodeFill(pos_args, pos_body);
							string code = String.Concat(prefix2, fill, Convert.ObjectToString(param2), "}");

							// the position of the first character of the parsed code:
							// (note that escaped characters distort position a little bit, which cannot be eliminated so easily)
							Position pos = Position.Initial;
							pos.FirstOffset = pos_args.FirstOffset - prefix1.Length + 1;
							pos.FirstColumn = pos_args.FirstColumn - prefix1.Length + 1;
							pos.FirstLine = pos_args.FirstLine;

							// parses function source code:
							List<Statement> statements = analyzer.BuildAst(pos, code);

							if (statements == null)
								break;

							FunctionDecl decl_node = (FunctionDecl)statements[0];

							// modify declaration:
							this.routine = decl_node.ConvertToLambda(analyzer);

							// adds declaration to the end of the global code statement list:
							analyzer.AddLambdaFcnDeclaration(decl_node);

							this.inlined = InlinedFunction.CreateFunction;

							// we cannot replace the expression with literal (emission of lambda declaration is needed):
							value = null;
							return false;
						}

						break;
					}

				case 3:
					{
						// tries to evaluate the parameters:
						if (!callSignature.Parameters[0].Expression.HasValue) break;
						if (!callSignature.Parameters[1].Expression.HasValue) break;
						if (!callSignature.Parameters[2].Expression.HasValue) break;

						object param1 = callSignature.Parameters[0].Expression.Value;
						object param2 = callSignature.Parameters[1].Expression.Value;
						object param3 = callSignature.Parameters[2].Expression.Value;

						if (lib_function.Name.EqualsLowercase("version_compare"))
						{
							value = PhpVersion.Compare(Convert.ObjectToString(param1), Convert.ObjectToString(param2),
								Convert.ObjectToString(param3));

							return true;
						}
						break;
					}
			}

			value = null;
			return false;
             
            */
		}
示例#3
0
        /// <summary>
        /// Analyze the function call (isMemberOf == null).
        /// </summary>
        /// <param name="analyzer"></param>
        /// <param name="info"></param>
        /// <returns></returns>
        /// <remarks>This code fragment is separated to save the stack when too long Expression chain is being compiled.</remarks>
        private Evaluation AnalyzeFunctionCall(Analyzer/*!*/ analyzer, ref ExInfoFromParent info)
        {
            Debug.Assert(isMemberOf == null);

            // resolve name:
            
            routine = analyzer.ResolveFunctionName(qualifiedName, position);

            if (routine.IsUnknown)
            {
                // note: we've to try following at run time, there can be dynamically added namespaced function matching qualifiedName
                // try fallback
                if (this.fallbackQualifiedName.HasValue)
                {
                    var fallbackroutine = analyzer.ResolveFunctionName(this.fallbackQualifiedName.Value, position);
                    if (fallbackroutine != null && !fallbackroutine.IsUnknown)
                    {
                        if (fallbackroutine is PhpLibraryFunction)  // we are calling library function directly
                            routine = fallbackroutine;
                    }
                }

                if (routine.IsUnknown)   // still unknown ?
                    Statistics.AST.AddUnknownFunctionCall(qualifiedName);
            }
            // resolve overload if applicable:
            RoutineSignature signature;
            overloadIndex = routine.ResolveOverload(analyzer, callSignature, position, out signature);

            Debug.Assert(overloadIndex != DRoutine.InvalidOverloadIndex, "A function should have at least one overload");

            if (routine is PhpLibraryFunction)
            {
                var opts = ((PhpLibraryFunction)routine).Options;
                // warning if not supported function call is detected
                if ((opts & FunctionImplOptions.NotSupported) != 0)
                    analyzer.ErrorSink.Add(Warnings.NotSupportedFunctionCalled, analyzer.SourceUnit, Position, QualifiedName.ToString());

                // warning if function requiring locals is detected (performance critical)
                if ((opts & FunctionImplOptions.NeedsVariables) != 0 && !analyzer.CurrentScope.IsGlobal)
                    analyzer.ErrorSink.Add(Warnings.UnoptimizedLocalsInFunction, analyzer.SourceUnit, Position, QualifiedName.ToString());
                
            }

            // analyze parameters:
            callSignature.Analyze(analyzer, signature, info, false);

            // get properties:
            analyzer.AddCurrentRoutineProperty(routine.GetCallerRequirements());

            // replaces the node if its value can be determined at compile-time:
            object value;
            return TryEvaluate(analyzer, out value) ?
                new Evaluation(this, value) :
                new Evaluation(this);
        }
示例#4
0
		/// <include file='Doc/Nodes.xml' path='doc/method[@name="Expression.Analyze"]/*'/>
		internal override Evaluation Analyze(Analyzer/*!*/ analyzer, ExInfoFromParent info)
		{
			base.Analyze(analyzer, info);

            // look for the method:
            bool isCallMethod;
            method = analyzer.ResolveMethod(
                type, methodName, position, analyzer.CurrentType, analyzer.CurrentRoutine,
                true, out runtimeVisibilityCheck, out isCallMethod);

			if (!method.IsUnknown)
			{
				// we are sure about the method //

				if (method.IsAbstract)
				{
					analyzer.ErrorSink.Add(Errors.AbstractMethodCalled, analyzer.SourceUnit, position,
						method.DeclaringType.FullName, method.FullName);
				}
			}

            // check __callStatic
            if (isCallMethod)
            {
                // TODO: generic args

                // create new CallSignature({function name},{args})
                var arg1 = new StringLiteral(this.Position, methodName.Value);
                var arg2 = this.callSignature.BuildPhpArray();

                this.callSignature = new CallSignature(
                    new List<ActualParam>(2) {
                                new ActualParam(arg1.Position, arg1, false),
                                new ActualParam(arg2.Position, arg2, false)
                            },
                    new List<TypeRef>());
            }

            // analyze the method
			RoutineSignature signature;
			overloadIndex = method.ResolveOverload(analyzer, callSignature, position, out signature);

			Debug.Assert(overloadIndex != DRoutine.InvalidOverloadIndex, "Each method should have at least one overload");

            // analyze arguments
			callSignature.Analyze(analyzer, signature, info, false);

			return new Evaluation(this);
		}
示例#5
0
        /// <summary>
        /// Analyze the method call (isMemberOf != null).
        /// </summary>
        /// <param name="analyzer"></param>
        /// <param name="info"></param>
        /// <returns></returns>
        private Evaluation AnalyzeMethodCall(Analyzer/*!*/ analyzer, ref ExInfoFromParent info)
        {
            Debug.Assert(isMemberOf != null);

            // resolve routine if IsMemberOf is resolved statically:
            this.isMemberOfType = this.GetIsMemberOfType(analyzer);
            if (this.AnalyzeMethodCallOnKnownType(analyzer, ref info, this.isMemberOfType))
                return new Evaluation(this);
            
            // by default, fall back to dynamic method invocation
            routine = null;
            callSignature.Analyze(analyzer, UnknownSignature.Default, info, false);

            return new Evaluation(this);
        }
示例#6
0
        private bool AnalyzeMethodCallOnKnownType(Analyzer/*!*/ analyzer, ref ExInfoFromParent info, DType type)
        {
            if (type == null || type.IsUnknown)
                return false;

            bool runtimeVisibilityCheck, isCallMethod;

            routine = analyzer.ResolveMethod(
                type, qualifiedName.Name,
                Position,
                analyzer.CurrentType, analyzer.CurrentRoutine, false,
                out runtimeVisibilityCheck, out isCallMethod);

            if (routine.IsUnknown)
                return false;

            Debug.Assert(runtimeVisibilityCheck == false);  // can only be set to true if CurrentType or CurrentRoutine are null

            // check __call
            if (isCallMethod)
            {
                // TODO: generic args

                var arg1 = new StringLiteral(this.Position, qualifiedName.Name.Value);
                var arg2 = this.callSignature.BuildPhpArray();

                this.callSignature = new CallSignature(
                    new List<ActualParam>(2) {
                                new ActualParam(arg1.Position, arg1, false),
                                new ActualParam(arg2.Position, arg2, false)
                            },
                    new List<TypeRef>());
            }

            // resolve overload if applicable:
            RoutineSignature signature;
            overloadIndex = routine.ResolveOverload(analyzer, callSignature, position, out signature);

            Debug.Assert(overloadIndex != DRoutine.InvalidOverloadIndex, "A function should have at least one overload");

            // analyze parameters:
            callSignature.Analyze(analyzer, signature, info, false);

            // get properties:
            analyzer.AddCurrentRoutineProperty(routine.GetCallerRequirements());

            return true;
        }
示例#7
0
		internal override Evaluation Analyze(Analyzer/*!*/ analyzer, ExInfoFromParent info)
		{
            Debug.Assert(this.IsMemberOf == null);

			access = info.Access;

			this.typeArgsResolved = classNameRef.Analyze(analyzer);

			DType type = classNameRef.ResolvedType;
			RoutineSignature signature;

			if (typeArgsResolved)
				analyzer.AnalyzeConstructedType(type);

			if (type != null)
			{
				bool error_reported = false;

				// make checks if we are sure about character of the type:
				if (type.IsIdentityDefinite)
				{
					if (type.IsAbstract || type.IsInterface)
					{
						analyzer.ErrorSink.Add(Errors.AbstractClassOrInterfaceInstantiated, analyzer.SourceUnit,
							position, type.FullName);
						error_reported = true;
					}
				}

                // disallow instantiation of Closure
                if (type.RealType == typeof(PHP.Library.SPL.Closure))
                {
                    analyzer.ErrorSink.Add(Errors.ClosureInstantiated, analyzer.SourceUnit, position, type.FullName);
                    error_reported = true;
                }

				// type name resolved, look the constructor up:
				constructor = analyzer.ResolveConstructor(type, position, analyzer.CurrentType, analyzer.CurrentRoutine,
				  out runtimeVisibilityCheck);

				if (constructor.ResolveOverload(analyzer, callSignature, position, out signature) == DRoutine.InvalidOverloadIndex)
				{
					if (!error_reported)
					{
						analyzer.ErrorSink.Add(Errors.ClassHasNoVisibleCtor, analyzer.SourceUnit,
						  position, type.FullName);
					}
				}
			}
			else
			{
				signature = UnknownSignature.Default;
			}

			callSignature.Analyze(analyzer, signature, info, false);

			return new Evaluation(this);
		}