/// <summary> /// Enqueues routine's start block for analysis. /// </summary> void EnqueueRoutine(SourceRoutineSymbol routine) { Contract.ThrowIfNull(routine); // lazily binds CFG and // adds their entry block to the worklist // TODO: reset LocalsTable, FlowContext and CFG _worklist.Enqueue(routine.ControlFlowGraph?.Start); // enqueue routine parameter default values routine.Parameters.OfType<SourceParameterSymbol>().Foreach(p => { if (p.Initializer != null) { EnqueueExpression(p.Initializer, routine.TypeRefContext, routine.GetNamingContext()); } }); }
TypeSymbol ResolveType() { if (IsThis) { // <this> parameter if (_routine is SourceGlobalMethodSymbol) { // "AnyType" in case of $this in global scope return(DeclaringCompilation.CoreTypes.PhpValue); } return(ContainingType); } //return DeclaringCompilation.GetTypeFromTypeRef(_routine, _routine.ControlFlowGraph.GetParamTypeMask(this)); // determine parameter type from the signature: // aliased parameter: if (_syntax.IsOut || _syntax.PassedByRef) { if (_syntax.IsVariadic) { // PhpAlias[] return(ArrayTypeSymbol.CreateSZArray(this.ContainingAssembly, DeclaringCompilation.CoreTypes.PhpAlias)); } else { // PhpAlias return(DeclaringCompilation.CoreTypes.PhpAlias); } } // 1. specified type hint var result = DeclaringCompilation.GetTypeFromTypeRef(_syntax.TypeHint); // 2. optionally type specified in PHPDoc if (result == null && _ptagOpt != null && _ptagOpt.TypeNamesArray.Length != 0 && (DeclaringCompilation.Options.PhpDocTypes & PhpDocTypes.ParameterTypes) != 0) { var typectx = _routine.TypeRefContext; var tmask = FlowAnalysis.PHPDoc.GetTypeMask(typectx, _ptagOpt.TypeNamesArray, _routine.GetNamingContext()); if (!tmask.IsVoid && !tmask.IsAnyType) { result = DeclaringCompilation.GetTypeFromTypeRef(typectx, tmask); } } // 3 default: if (result == null) { // TODO: use type from overriden method result = DeclaringCompilation.CoreTypes.PhpValue; } // variadic (result[]) if (_syntax.IsVariadic) { result = ArrayTypeSymbol.CreateSZArray(this.ContainingAssembly, result); } // return(result); }
/// <summary> /// Constructs most appropriate CLR return type of given routine. /// The method handles returning by alias, PHP7 return type, PHPDoc @return tag and result of flow analysis. /// In case the routine is an override or can be overriden, the CLR type is a value. /// </summary> internal static TypeSymbol ConstructClrReturnType(SourceRoutineSymbol routine) { var compilation = routine.DeclaringCompilation; // if the method is generator and can't be overriden then the return type must be generator // TODO: would not be necessary if GN_SGS got fixed (the routine could report the return type correctly itself) if (routine.IsGeneratorMethod()) { // if non-virtual -> return Generator directly if (IsNotOverriding(routine)) { return(compilation.CoreTypes.Generator); } else //can't be sure -> play safe with PhpValue { return(compilation.CoreTypes.PhpValue); } } // & if (routine.SyntaxSignature.AliasReturn) { return(compilation.CoreTypes.PhpAlias); } // : return type if (routine.SyntaxReturnType != null) { return(compilation.GetTypeFromTypeRef(routine.SyntaxReturnType)); } // for non virtual methods: if (IsNotOverriding(routine)) { // /** @return */ var typeCtx = routine.TypeRefContext; if (routine.PHPDocBlock != null && (compilation.Options.PhpDocTypes & PhpDocTypes.ReturnTypes) != 0) { var returnTag = routine.PHPDocBlock.Returns; if (returnTag != null && returnTag.TypeNames.Length != 0) { var tmask = PHPDoc.GetTypeMask(typeCtx, returnTag.TypeNamesArray, routine.GetNamingContext()); if (!tmask.IsVoid && !tmask.IsAnyType) { return(compilation.GetTypeFromTypeRef(typeCtx, tmask)); } } } // determine from code flow return(compilation.GetTypeFromTypeRef(typeCtx, routine.ResultTypeMask)); } else { // TODO: an override that respects the base? check routine.ResultTypeMask (flow analysis) and base implementation is it matches } // any value by default return(compilation.CoreTypes.PhpValue); }
TypeSymbol ResolveType() { if (IsThis) { // <this> parameter if (_routine is SourceGlobalMethodSymbol) { // "AnyType" in case of $this in global scope return(DeclaringCompilation.CoreTypes.PhpValue); } return(ContainingType); } //return DeclaringCompilation.GetTypeFromTypeRef(_routine, _routine.ControlFlowGraph.GetParamTypeMask(this)); // determine parameter type from the signature: // aliased parameter: if (_syntax.IsOut || _syntax.PassedByRef) { if (_syntax.IsVariadic) { // PhpAlias[] return(ArrayTypeSymbol.CreateSZArray(this.ContainingAssembly, DeclaringCompilation.CoreTypes.PhpAlias)); } else { // PhpAlias return(DeclaringCompilation.CoreTypes.PhpAlias); } } // 1. specified type hint var typeHint = _syntax.TypeHint; if (typeHint is ReservedTypeRef rtref) { // workaround for https://github.com/peachpiecompiler/peachpie/issues/281 // remove once it gets updated in parser if (rtref.Type == ReservedTypeRef.ReservedType.self) { return(_routine.ContainingType); // self } } var result = DeclaringCompilation.GetTypeFromTypeRef(typeHint, _routine.ContainingType as SourceTypeSymbol, nullable: DefaultsToNull, phpLang: true); // 2. optionally type specified in PHPDoc if (result == null && PHPDoc != null && PHPDoc.TypeNamesArray.Length != 0 && (DeclaringCompilation.Options.PhpDocTypes & PhpDocTypes.ParameterTypes) != 0) { var typectx = _routine.TypeRefContext; var tmask = FlowAnalysis.PHPDoc.GetTypeMask(typectx, PHPDoc.TypeNamesArray, _routine.GetNamingContext()); if (!tmask.IsVoid && !tmask.IsAnyType) { result = DeclaringCompilation.GetTypeFromTypeRef(typectx, tmask); } } // 3 default: if (result == null) { // TODO: use type from overriden method result = DeclaringCompilation.CoreTypes.PhpValue; } // variadic (result[]) if (_syntax.IsVariadic) { result = ArrayTypeSymbol.CreateSZArray(this.ContainingAssembly, result); } // return(result); }
/// <summary> /// Constructs most appropriate CLR return type of given routine. /// The method handles returning by alias, PHP7 return type, PHPDoc @return tag and result of flow analysis. /// In case the routine is an override or can be overriden, the CLR type is a value. /// </summary> public static TypeSymbol ConstructClrReturnType(SourceRoutineSymbol routine) { var compilation = routine.DeclaringCompilation; // & if (routine.SyntaxSignature.AliasReturn) { return(compilation.CoreTypes.PhpAlias); } // : return type if (routine.SyntaxReturnType != null) { return(compilation.GetTypeFromTypeRef(routine.SyntaxReturnType)); } // for non virtual methods: if (routine.IsStatic || routine.DeclaredAccessibility == Accessibility.Private || (routine.IsSealed && !routine.IsOverride)) { // /** @return */ var typeCtx = routine.TypeRefContext; if (routine.PHPDocBlock != null && (compilation.Options.PhpDocTypes & PhpDocTypes.ReturnTypes) != 0) { var returnTag = routine.PHPDocBlock.Returns; if (returnTag != null && returnTag.TypeNames.Length != 0) { var tmask = PHPDoc.GetTypeMask(typeCtx, returnTag.TypeNamesArray, routine.GetNamingContext()); if (!tmask.IsVoid && !tmask.IsAnyType) { return(compilation.GetTypeFromTypeRef(typeCtx, tmask)); } } } // determine from code flow return(compilation.GetTypeFromTypeRef(typeCtx, routine.ResultTypeMask)); } else { // TODO: an override that respects the base? check routine.ResultTypeMask (flow analysis) and base implementation is it matches } // any value by default return(compilation.CoreTypes.PhpValue); }