/// <summary> /// Resolves list of input arguments. /// Implicit parameters passed by compiler are ignored. /// </summary> /// <param name="routine">Routine.</param> /// <param name="ctx">Type context to transfer type masks into.</param> /// <returns>List of input PHP arguments.</returns> public static IList <PhpParam> GetExpectedArguments(this IPhpRoutineSymbol routine, TypeRefContext ctx) { Contract.ThrowIfNull(routine); Contract.ThrowIfNull(ctx); List <PhpParam> result = null; int index = 0; var ps = routine.Parameters; foreach (ParameterSymbol p in ps) { if (result == null && p.IsImplicitlyDeclared && !p.IsParams) { continue; } // var phpparam = p.IsParams ? new PhpParam(p, index++, TypeRefMask.AnyType.WithIsRef(((ArrayTypeSymbol)p.Type).ElementType.Is_PhpAlias())) : new PhpParam(p, index++, TypeRefFactory.CreateMask(ctx, p.Type, notNull: p.HasNotNull)); result ??= new List <PhpParam>(ps.Length); result.Add(phpparam); } // return(result ?? (IList <PhpParam>)Array.Empty <PhpParam>()); }
/// <summary> /// Resolves list of input arguments. /// Implicit parameters passed by compiler are ignored. /// </summary> /// <param name="routine">Routine.</param> /// <param name="ctx">TYpe context to transmer type masks into.</param> /// <returns>List of input PHP arguments.</returns> public static PhpParam[] GetExpectedArguments(this IPhpRoutineSymbol routine, TypeRefContext ctx) { Contract.ThrowIfNull(routine); var ps = routine.Parameters; var table = (routine as SourceRoutineSymbol)?.LocalsTable; var result = new List <PhpParam>(ps.Length); foreach (ParameterSymbol p in ps) { if (result.Count == 0 && p.IsImplicitlyDeclared) { continue; } // default value (bound expression) ConstantValue cvalue; var psrc = p as SourceParameterSymbol; var defaultexpr = psrc != null ? psrc.Initializer : ((cvalue = p.ExplicitDefaultConstantValue) != null ? new BoundLiteral(cvalue.Value) : null); // var phpparam = new PhpParam( TypeRefFactory.CreateMask(ctx, p.Type), p.RefKind != RefKind.None, p.IsParams, defaultexpr); result.Add(phpparam); } // return(result.ToArray()); }
/// <summary> /// Gets expected return type mask of given symbol (field, function, method or property). /// </summary> /// <remarks>Returned type mask corresponds to types that can be returned by invoking given symbol.</remarks> public static TypeRefMask GetResultType(this IPhpValue symbol, TypeRefContext ctx) { Contract.ThrowIfNull(symbol); Contract.ThrowIfNull(ctx); TypeSymbol t; if (symbol is FieldSymbol) { t = ((FieldSymbol)symbol).Type; } else if (symbol is MethodSymbol) { var m = (MethodSymbol)symbol; var r = symbol as SourceRoutineSymbol; if (r != null && r.IsStatic && r.SyntaxReturnType == null) { // In case of a static function, we can return expected return type mask exactly. // Such function cannot be overriden and we know exactly what the return type will be even the CLR type covers more possibilities. return(ctx.AddToContext(r.TypeRefContext, r.ResultTypeMask)); } t = m.ReturnType; } else if (symbol is PropertySymbol) { t = ((PropertySymbol)symbol).Type; } else if (symbol is ParameterSymbol) { var ps = (ParameterSymbol)symbol; t = ps.Type; if (ps.IsParams) { Debug.Assert(t.IsSZArray()); return(ctx.GetArrayTypeMask(TypeRefFactory.CreateMask(ctx, ((ArrayTypeSymbol)t).ElementType))); } } else { throw Roslyn.Utilities.ExceptionUtilities.UnexpectedValue(symbol); } // create the type mask from the CLR type symbol var mask = TypeRefFactory.CreateMask(ctx, t); // [CastToFalse] if (symbol is IPhpRoutineSymbol && ((IPhpRoutineSymbol)symbol).CastToFalse) { mask |= ctx.GetBooleanTypeMask(); // the function may return FALSE } // return(mask); }
public virtual TypeRefMask GetResultType(TypeRefContext ctx) { var treturn = TypeRefFactory.CreateMask(ctx, this.ReturnType); if (this.CastToFalse) { treturn |= ctx.GetBooleanTypeMask(); // ISemanticFunction may return FALSE } return(treturn); }
/// <summary> /// Resolves list of input arguments. /// Implicit parameters passed by compiler are ignored. /// </summary> /// <param name="routine">Routine.</param> /// <param name="ctx">Type context to transfer type masks into.</param> /// <returns>List of input PHP arguments.</returns> public static IList <PhpParam> GetExpectedArguments(this IPhpRoutineSymbol routine, TypeRefContext ctx) { Contract.ThrowIfNull(routine); Contract.ThrowIfNull(ctx); List <PhpParam> result = null; int index = 0; var ps = routine.Parameters; foreach (ParameterSymbol p in ps) { if (result == null && p.IsImplicitlyDeclared && !p.IsParams) { continue; } // var phpparam = new PhpParam( index++, TypeRefFactory.CreateMask(ctx, p.Type, notNull: p.HasNotNullAttribute()), p.RefKind != RefKind.None, p.IsParams, isPhpRw: p.GetPhpRwAttribute() != null, defaultValue: p.Initializer); if (result == null) { result = new List <PhpParam>(ps.Length); } result.Add(phpparam); } // return(result ?? (IList <PhpParam>)Array.Empty <PhpParam>()); }
/// <summary> /// Gets expected return type mask of given symbol (field, function, method or property). /// </summary> /// <remarks>Returned type mask corresponds to types that can be returned by invoking given symbol.</remarks> public static TypeRefMask GetResultType(this IPhpValue symbol, TypeRefContext ctx) { Contract.ThrowIfNull(symbol); Contract.ThrowIfNull(ctx); TypeSymbol t; if (symbol is FieldSymbol) { t = ((FieldSymbol)symbol).Type; } else if (symbol is MethodSymbol) { var m = (MethodSymbol)symbol; var r = symbol as SourceRoutineSymbol; // if the method is generator use ConstructClrReturnType analysis for return type // TODO: would not be necessary if GN_SGS got fixed (the routine could report the return type correctly itself) if (r != null && r.IsGeneratorMethod()) { t = m.ReturnType; } else if (r != null && r.IsStatic && r.SyntaxReturnType == null) { // In case of a static function, we can return expected return type mask exactly. // Such function cannot be overriden and we know exactly what the return type will be even the CLR type covers more possibilities. return(ctx.AddToContext(r.TypeRefContext, r.ResultTypeMask)); } else { t = m.ReturnType; } } else if (symbol is PropertySymbol) { t = ((PropertySymbol)symbol).Type; } else if (symbol is ParameterSymbol) { var ps = (ParameterSymbol)symbol; t = ps.Type; if (ps.IsParams) { Debug.Assert(t.IsSZArray()); return(ctx.GetArrayTypeMask(TypeRefFactory.CreateMask(ctx, ((ArrayTypeSymbol)t).ElementType))); } } else { throw Roslyn.Utilities.ExceptionUtilities.UnexpectedValue(symbol); } // create the type mask from the CLR type symbol var mask = TypeRefFactory.CreateMask(ctx, t); // [CastToFalse] if (symbol is IPhpRoutineSymbol && ((IPhpRoutineSymbol)symbol).CastToFalse) { mask |= ctx.GetBooleanTypeMask(); // the function may return FALSE // remove NULL (NULL is changed to FALSE), note it also can't return -1 mask = ctx.WithoutNull(mask); } // return(mask); }
/// <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) { 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 ? { 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.ResolveType(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; 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 symbol) // TODO: also user constants defined in the same scope { if (name == "defined") { call.ConstantValue = ConstantValueExtensions.AsOptional(true); } else // name == "constant" { var cvalue = symbol.GetConstantValue(false); call.ConstantValue = (cvalue != null) ? new Optional <object>(cvalue.Value) : null; call.TypeRefMask = TypeRefFactory.CreateMask(analysis.TypeCtx, symbol.Type); } return; } } break; case "strlen": if (args.Length == 1 && args[0].Value.ConstantValue.TryConvertToString(out string value)) { call.ConstantValue = new Optional <object>(value.Length); } return; } } }
/// <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)) { 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; } }
public virtual TypeRefMask GetResultType(TypeRefContext ctx) { return(TypeRefFactory.CreateMask(ctx, this.ReturnType)); }