Пример #1
0
        /// <summary>
        /// Processes functions such as is_int, is_bool etc. Returns whether the function was one of these.
        /// </summary>
        private static bool HandleTypeCheckingFunctions(
            BoundGlobalFunctionCall call,
            string name,
            BoundVariableRef arg,
            ExpressionAnalysis analysis,
            ConditionBranch branch)
        {
            var typeCtx   = analysis.TypeCtx;
            var flowState = analysis.State;

            switch (name)
            {
            case "is_int":
            case "is_integer":
            case "is_long":
                HandleTypeCheckingExpression(arg, typeCtx.GetLongTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_bool":
                HandleTypeCheckingExpression(arg, typeCtx.GetBooleanTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_float":
            case "is_double":
            case "is_real":
                HandleTypeCheckingExpression(arg, typeCtx.GetDoubleTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_string":
                var stringMask = typeCtx.GetStringTypeMask() | typeCtx.GetWritableStringTypeMask();
                HandleTypeCheckingExpression(arg, stringMask, branch, flowState, checkExpr: call);
                return(true);

            case "is_resource":
                HandleTypeCheckingExpression(arg, typeCtx.GetResourceTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_null":
                HandleTypeCheckingExpression(arg, typeCtx.GetNullTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_array":
                HandleTypeCheckingExpression(
                    arg,
                    currentType => typeCtx.GetArraysFromMask(currentType),
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            case "is_object":
                // Keep IncludesSubclasses flag in the true branch and clear it in the false branch
                HandleTypeCheckingExpression(
                    arg,
                    currentType => typeCtx.GetObjectsFromMask(currentType).WithIncludesSubclasses,
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            // TODO
            //case "is_scalar":
            //    return;

            case "is_numeric":
                HandleTypeCheckingExpression(
                    arg,
                    currentType =>
                {
                    // Specify numeric types if they are present
                    var targetType = typeCtx.IsLong(currentType) ? typeCtx.GetLongTypeMask() : 0;
                    targetType    |= typeCtx.IsDouble(currentType) ? typeCtx.GetDoubleTypeMask() : 0;

                    if (branch == ConditionBranch.ToTrue)
                    {
                        // Also string types can make is_numeric return true, but not anything else
                        targetType |= typeCtx.IsReadonlyString(currentType) ? typeCtx.GetStringTypeMask() : 0;
                        targetType |= typeCtx.IsWritableString(currentType) ? typeCtx.GetWritableStringTypeMask() : 0;

                        return(targetType);
                    }
                    else
                    {
                        // For number, is_numeric always returns true -> remove numeric types from false branch
                        return(targetType);
                    }
                },
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            case "is_callable":
                HandleTypeCheckingExpression(
                    arg,
                    currentType =>
                {
                    // Closure is specified in both branches
                    TypeRefMask targetType = 0;
                    AddTypeIfInContext(typeCtx, type => type.IsLambda, false, ref targetType);

                    if (branch == ConditionBranch.ToTrue)
                    {
                        // Also string types, arrays and objects can make is_callable return true, but not anything else
                        targetType |= typeCtx.IsReadonlyString(currentType) ? typeCtx.GetStringTypeMask() : 0;
                        targetType |= typeCtx.IsWritableString(currentType) ? typeCtx.GetWritableStringTypeMask() : 0;
                        targetType |= typeCtx.GetArraysFromMask(currentType);
                        targetType |= typeCtx.GetObjectsFromMask(currentType);

                        return(targetType);
                    }
                    else
                    {
                        // For closure, is_callable always returns true -> remove the closure type from false branch,
                        // don't remove IncludeSubclasses flag
                        return(targetType);
                    }
                },
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            // TODO
            //case "is_iterable":
            //    return;

            default:
                return(false);
            }
        }
        public override object VisitGlobalFunctionCall(BoundGlobalFunctionCall x)
        {
            if (x.Name.NameValue == NameUtils.SpecialNames.dirname)
            {
                // dirname( __FILE__ ) -> __DIR__
                if (x.ArgumentsInSourceOrder.Length == 1 &&
                    x.ArgumentsInSourceOrder[0].Value is BoundPseudoConst pc &&
                    pc.ConstType == Ast.PseudoConstUse.Types.File)
                {
                    TransformationCount++;
                    return(new BoundPseudoConst(Ast.PseudoConstUse.Types.Dir).WithAccess(x.Access));
                }
            }
            else if (x.Name.NameValue == NameUtils.SpecialNames.get_parent_class)
            {
                bool TryResolveParentClassInCurrentClassContext(SourceRoutineSymbol routine, out BoundLiteral newExpression)
                {
                    // in global function, always FALSE
                    if (routine is SourceFunctionSymbol)
                    {
                        // FALSE
                        newExpression = new BoundLiteral(false.AsObject());
                        return(true);
                    }

                    // in a method, we can resolve in compile time:
                    if (routine is SourceMethodSymbol m && m.ContainingType is SourceTypeSymbol t && !t.IsTrait)
                    {
                        if (t.BaseType == null || t.BaseType.IsObjectType())
                        {
                            // FALSE
                            newExpression = new BoundLiteral(false.AsObject())
                            {
                                ConstantValue = false.AsOptional()
                            };
                            return(true);
                        }
                        else
                        {
                            // {class name}
                            var baseTypeName = t.BaseType.PhpQualifiedName().ToString();
                            newExpression = new BoundLiteral(baseTypeName)
                            {
                                ConstantValue = baseTypeName
                            };
                            return(true);
                        }
                    }

                    //
                    newExpression = default;
                    return(false);
                }

                // get_parent_class() -> {class name} | FALSE
                if (x.ArgumentsInSourceOrder.Length == 0)
                {
                    if (TryResolveParentClassInCurrentClassContext(_routine, out var newExpression))
                    {
                        TransformationCount++;
                        return(newExpression.WithContext(x));
                    }
                }

                // get_parent_class( ??? ) -> parent::class | FALSE
                if (x.ArgumentsInSourceOrder.Length == 1)
                {
                    // get_parent_class($this), get_parent_class(__CLASS__) ->  {class name} | FALSE
                    if ((x.ArgumentsInSourceOrder[0].Value is BoundVariableRef varref && varref.Variable is ThisVariableReference) ||
                        (x.ArgumentsInSourceOrder[0].Value is BoundPseudoConst pc && pc.ConstType == Ast.PseudoConstUse.Types.Class))
                    {
                        if (TryResolveParentClassInCurrentClassContext(_routine, out var newExpression))
                        {
                            TransformationCount++;
                            return(newExpression.WithContext(x));
                        }
                    }
                }
            }
            else if (x.Name.NameValue == NameUtils.SpecialNames.method_exists)
            {
                // method_exists(FALSE, ...) -> FALSE
                if (x.ArgumentsInSourceOrder.Length >= 1)
                {
                    var value = x.ArgumentsInSourceOrder[0].Value.ConstantValue;
                    if (value.HasValue && value.TryConvertToBool(out var bvalue) && !bvalue)
                    {
                        TransformationCount++;
                        return(new BoundLiteral(false.AsObject())
                        {
                            ConstantValue = false.AsOptional()
                        }.WithContext(x));
                    }
                }
            }

            //
            return(base.VisitGlobalFunctionCall(x));
        }
Пример #3
0
        /// <summary>
        /// Resolves value of the function call in compile time if possible and updates the variable type if necessary
        /// </summary>
        public static void HandleSpecialFunctionCall(BoundGlobalFunctionCall call, ExpressionAnalysis analysis, ConditionBranch branch)
        {
            // Only direct function names
            if (!HasSimpleName(call, out string name))
            {
                return;
            }

            // Type checking functions
            if (branch != ConditionBranch.AnyResult && CanBeTypeCheckingFunction(call, name, out var arg))
            {
                if (HandleTypeCheckingFunctions(call, name, arg, analysis, branch))
                {
                    return;
                }
            }

            // Functions with all arguments resolved
            if (call.ArgumentsInSourceOrder.All(a => a.Value.ConstantValue.HasValue))
            {
                // Clear out the constant value result from the previous run of this method (if it was valid, it will be reassigned below)
                call.ConstantValue = default(Optional <object>);

                string str;

                var args = call.ArgumentsInSourceOrder;
                switch (name)           // TODO: case insensitive
                {
                case "is_callable":     // bool is_callable( string $function_name )
                case "function_exists": // bool function_exists ( string $function_name )
                    if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out str))
                    {
                        // TRUE <=> function is defined unconditionally in a reference library (PE assembly)
                        var tmp = analysis.Model.ResolveFunction(NameUtils.MakeQualifiedName(str, true));
                        if (tmp is PEMethodSymbol || (tmp is AmbiguousMethodSymbol && ((AmbiguousMethodSymbol)tmp).Ambiguities.All(f => f is PEMethodSymbol))) // TODO: unconditional declaration ?
                        {
                            if (!tmp.ContainingType.IsPhpSourceFile())                                                                                         // only functions declared in libraries, not in PHP source file
                            {
                                call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                                return;
                            }
                        }
                    }
                    break;

                // bool class_exists ( string $class_name [, bool $autoload = true ] )
                case "class_exists":
                case "interface_exists":
                    if (args.Length >= 1)
                    {
                        // TRUE <=> class is defined unconditionally in a reference library (PE assembly)
                        var class_name = args[0].Value.ConstantValue.Value as string;
                        if (class_name != null)
                        {
                            var tmp = (TypeSymbol)analysis.Model.ResolveType(NameUtils.MakeQualifiedName(class_name, true));
                            if (tmp is PENamedTypeSymbol && !tmp.IsPhpUserType())       // TODO: + SourceTypeSymbol when reachable unconditional declaration
                            {
                                bool @interface = (name == "interface_exists");
                                if (tmp.TypeKind == (@interface ? TypeKind.Interface : TypeKind.Class))
                                {
                                    call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                                }
                            }
                        }
                    }
                    break;

                // bool method_exists ( string $class_name , string $method_name )
                case "method_exists":
                    if (args.Length == 2)
                    {
                        var class_name = args[0].Value.ConstantValue.Value as string;
                        if (class_name != null && args[1].Value.ConstantValue.TryConvertToString(out str))
                        {
                            var tmp = (NamedTypeSymbol)analysis.Model.ResolveType(NameUtils.MakeQualifiedName(class_name, true));
                            if (tmp is PENamedTypeSymbol)
                            {
                                if (tmp.LookupMethods(str).Any())
                                {
                                    call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                                    return;
                                }
                            }
                        }
                    }
                    break;

                case "extension_loaded":        // bool extension_loaded(name)
                    if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out str))
                    {
                        if (analysis.Model.Extensions.Contains(str, StringComparer.OrdinalIgnoreCase))
                        {
                            call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                            return;
                        }
                    }
                    break;

                case "defined":
                case "constant":
                    if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out str))
                    {
                        // TODO: const_name in form of "{CLASS}::{NAME}"
                        var tmp = analysis.Model.ResolveConstant(str);
                        if (tmp is PEFieldSymbol fld)        // TODO: also user constants defined in the same scope
                        {
                            if (name == "defined")
                            {
                                call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                            }
                            else     // name == "constant"
                            {
                                var cvalue = fld.GetConstantValue(false);
                                call.ConstantValue = (cvalue != null) ? new Optional <object>(cvalue.Value) : null;
                                call.TypeRefMask   = TypeRefFactory.CreateMask(analysis.TypeCtx, fld.Type);
                            }

                            return;
                        }
                        else if (tmp is PEPropertySymbol prop)
                        {
                            if (name == "defined")
                            {
                                call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                            }
                            else     // name == "constant"
                            {
                                call.TypeRefMask = TypeRefFactory.CreateMask(analysis.TypeCtx, prop.Type);
                            }
                        }
                    }
                    break;

                case "strlen":
                    if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out string value))
                    {
                        call.ConstantValue = new Optional <object>(value.Length);
                    }
                    return;
                }
            }
        }
Пример #4
0
 public virtual void VisitGlobalFunctionCall(BoundGlobalFunctionCall x, ConditionBranch branch)
 {
     base.VisitGlobalFunctionCall(x);
 }
Пример #5
0
 public sealed override void VisitGlobalFunctionCall(BoundGlobalFunctionCall x) => VisitGlobalFunctionCall(x, ConditionBranch.Default);
Пример #6
0
        /// <summary>
        /// Resolves value of the function call in compile time if possible and updates the variable type if necessary
        /// </summary>
        public static void HandleSpecialFunctionCall <T>(BoundGlobalFunctionCall call, ExpressionAnalysis <T> analysis, ConditionBranch branch)
        {
            // Only direct function names
            if (!HasSimpleName(call, out string name))
            {
                return;
            }

            // Type checking functions
            if (branch != ConditionBranch.AnyResult && CanBeTypeCheckingFunction(call, name, out var arg))
            {
                if (HandleTypeCheckingFunctions(call, name, arg, analysis, branch))
                {
                    return;
                }
            }

            var args = call.ArgumentsInSourceOrder;

            // Clear out the constant value result from the previous run of this method (if it was valid, it will be reassigned below)
            call.ConstantValue = default;

            string str;

            switch (name)           // TODO: case insensitive
            {
            case "is_callable":     // bool is_callable( string $function_name )
            case "function_exists": // bool function_exists ( string $function_name )
                if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out str))
                {
                    // TRUE <=> function is defined unconditionally in a reference library (PE assembly)
                    if (IsUnconditionalDeclaration(analysis.Model.ResolveFunction(NameUtils.MakeQualifiedName(str, true))))
                    {
                        call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                    }
                }
                break;

            // bool class_exists ( string $class_name [, bool $autoload = true ] )
            case "class_exists":
            case "interface_exists":
                if (args.Length >= 1)
                {
                    // TRUE <=> class is defined unconditionally in a reference library (PE assembly)
                    var class_name = args[0].Value.ConstantValue.Value as string;
                    if (!string.IsNullOrEmpty(class_name))
                    {
                        var tmp = (TypeSymbol)analysis.Model.ResolveType(NameUtils.MakeQualifiedName(class_name, true));
                        if (tmp is PENamedTypeSymbol && !tmp.IsPhpUserType())       // TODO: + SourceTypeSymbol when reachable unconditional declaration
                        {
                            bool @interface = (name == "interface_exists");
                            if (tmp.TypeKind == (@interface ? TypeKind.Interface : TypeKind.Class))
                            {
                                call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                            }
                        }
                    }
                }
                break;

            // bool method_exists ( string $class_name , string $method_name )
            case "method_exists":
                if (args.Length == 2)
                {
                    var class_name = args[0].Value.ConstantValue.Value as string;
                    if (class_name != null && args[1].Value.ConstantValue.TryConvertToString(out str))
                    {
                        var tmp = (NamedTypeSymbol)analysis.Model.ResolveType(NameUtils.MakeQualifiedName(class_name, true));
                        if (tmp is PENamedTypeSymbol && !tmp.IsPhpUserType())
                        {
                            if (tmp.LookupMethods(str).Count != 0)     // TODO: why not User Types // TODO: why not resolve FALSE as well below?
                            {
                                call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                            }
                        }
                    }
                }
                break;

            case "defined":         // defined(CONST_NAME)
            case "constant":        // constant(CONST_NAME)
                if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out str))
                {
                    // TODO: const_name in form of "{CLASS}::{NAME}"
                    // TODO: also user constants defined in the same scope?

                    // quick evaluation of PE constants that can't be changed at run time

                    var tmp = analysis.Model.ResolveConstant(str);
                    if (tmp is PEFieldSymbol fld)
                    {
                        if (name == "defined")
                        {
                            call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                        }
                        else     // name == "constant"
                        {
                            if (fld.Type.Is_Func_Context_TResult(out var tresult))
                            {
                                call.TypeRefMask = TypeRefFactory.CreateMask(analysis.TypeCtx, tresult);
                            }
                            else
                            {
                                var cvalue = fld.GetConstantValue(false);
                                call.ConstantValue = (cvalue != null) ? new Optional <object>(cvalue.Value) : null;
                                call.TypeRefMask   = TypeRefFactory.CreateMask(analysis.TypeCtx, fld.Type, notNull: fld.IsNotNull());
                            }
                        }
                    }
                    else if (tmp is PEPropertySymbol prop)
                    {
                        if (name == "defined")
                        {
                            call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                        }
                        else     // name == "constant"
                        {
                            call.TypeRefMask = TypeRefFactory.CreateMask(analysis.TypeCtx, prop.Type, notNull: prop.IsNotNull());
                        }
                    }
                }
                break;

            case "strlen":
                if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out string value) && StringUtils.IsAsciiString(value))
                {
                    call.ConstantValue = new Optional <object>(value.Length);
                }
                break;

            case "file_exists":
                if (args.Length == 1)
                {
                    if (TryResolveFile(analysis.Model, analysis.Routine, args[0].Value, out var script))
                    {
                        // there is compiled script at this path,
                        // the expression will be always true
                        call.ConstantValue = true.AsOptional();
                    }
                }
                break;
            }
        }
Пример #7
0
        /// <summary>
        /// Resolves value of the function call in compile time if possible and updates the variable type if necessary
        /// </summary>
        public static void HandleFunctionCall(BoundGlobalFunctionCall call, ExpressionAnalysis analysis, ConditionBranch branch)
        {
            // Only direct function names
            if (!HasSimpleName(call, out string name))
            {
                return;
            }

            // Type checking functions
            if (branch != ConditionBranch.AnyResult && CanBeTypeCheckingFunction(call, name, out var arg))
            {
                if (HandleTypeCheckingFunctions(call, name, arg, analysis, branch))
                {
                    return;
                }
            }

            // Functions with all arguments resolved
            if (call.ArgumentsInSourceOrder.All(a => a.Value.ConstantValue.HasValue))
            {
                // Clear out the constant value result from the previous run of this method (if it was valid, it will be reassigned below)
                call.ConstantValue = default(Optional <object>);

                var args = call.ArgumentsInSourceOrder;
                switch (name)
                {
                // bool function_exists ( string $function_name )
                case "function_exists":
                    if (args.Length == 1)
                    {
                        // TRUE <=> function is defined unconditionally in a reference library (PE assembly)
                        var function_name = args[0].Value.ConstantValue.Value as string;
                        if (function_name != null)
                        {
                            var tmp = analysis.Model.ResolveFunction(NameUtils.MakeQualifiedName(function_name, true));
                            if (tmp is PEMethodSymbol || (tmp is AmbiguousMethodSymbol && ((AmbiguousMethodSymbol)tmp).Ambiguities.All(f => f is PEMethodSymbol)))      // TODO: unconditional declaration ?
                            {
                                call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                                return;
                            }
                        }
                    }
                    break;

                // bool class_exists ( string $class_name [, bool $autoload = true ] )
                case "class_exists":
                    if (args.Length >= 1)
                    {
                        // TRUE <=> class is defined unconditionally in a reference library (PE assembly)
                        var class_name = args[0].Value.ConstantValue.Value as string;
                        if (class_name != null)
                        {
                            var tmp = analysis.Model.GetType(NameUtils.MakeQualifiedName(class_name, true));
                            if (tmp is PENamedTypeSymbol)       // TODO: unconditional declaration ?
                            {
                                call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                                return;
                            }
                        }
                    }
                    break;

                // bool method_exists ( string $class_name , string $method_name )
                case "method_exists":
                    if (args.Length == 2)
                    {
                        var class_name    = args[0].Value.ConstantValue.Value as string;
                        var function_name = args[1].Value.ConstantValue.Value as string;
                        if (class_name != null && function_name != null)
                        {
                            var tmp = (NamedTypeSymbol)analysis.Model.GetType(NameUtils.MakeQualifiedName(class_name, true));
                            if (tmp is PENamedTypeSymbol)
                            {
                                if (tmp.LookupMethods(function_name).Any())
                                {
                                    call.ConstantValue = ConstantValueExtensions.AsOptional(true);
                                    return;
                                }
                            }
                        }
                    }
                    break;
                }
            }
        }