Base symbol representing a method or a function from source.
Inheritance: MethodSymbol
        public SourceParameterSymbol(SourceRoutineSymbol routine, FormalParam syntax, int relindex, PHPDocBlock.ParamTag ptagOpt)
        {
            Contract.ThrowIfNull(routine);
            Contract.ThrowIfNull(syntax);
            Debug.Assert(relindex >= 0);


            _routine  = routine;
            _syntax   = syntax;
            _relindex = relindex;

            _initializer = (syntax.InitValue != null)
                ? new SemanticsBinder(DeclaringCompilation, routine.ContainingFile.SyntaxTree, locals: null, routine: null, self: routine.ContainingType as SourceTypeSymbol)
                           .BindWholeExpression(syntax.InitValue, BoundAccess.Read)
                           .SingleBoundElement()
                : null;

            var phpattrs = _syntax.GetAttributes();

            _attributes = phpattrs.Count != 0
                ? new SemanticsBinder(DeclaringCompilation, routine.ContainingFile.SyntaxTree, locals: null, routine: routine, self: routine.ContainingType as SourceTypeSymbol)
                          .BindAttributes(phpattrs)
                : ImmutableArray <AttributeData> .Empty;

            PHPDoc = ptagOpt;
        }
 /// <summary>
 /// Gets value indicating the routine does not override any other routine.
 /// (static methods, private methods or sealed virtual methods not overriding anything)
 /// </summary>
 static bool IsNotOverriding(SourceRoutineSymbol routine)
 {
     return
         (routine.IsStatic ||
          routine.DeclaredAccessibility == Accessibility.Private ||
          ((routine.IsSealed || routine.ContainingType.IsSealed) && routine.OverriddenMethod == null));
 }
        /// <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);
        }
Example #4
0
        /// <summary>
        /// Creates new type context, flow context and flow state for the routine.
        /// </summary>
        public static FlowState CreateInitialState(SourceRoutineSymbol/*!*/routine)
        {
            Contract.ThrowIfNull(routine);

            // create typeCtx
            var typeCtx = routine.TypeRefContext;

            // create FlowContext 
            var flowCtx = new FlowContext(typeCtx, routine);

            // create FlowState
            var state = new FlowState(flowCtx);

            // handle input parameters type
            var parameters = routine.Parameters.OfType<SourceParameterSymbol>().ToImmutableArray();
            foreach (var p in parameters)
            {
                state.SetVar(p.Name, p.GetResultType(typeCtx));

                if (p.Syntax.PassedByRef)
                {
                    state.SetVarRef(p.Name);
                }
            }

            // $this
            if (routine.HasThis)
            {
                InitThisVar(flowCtx, state);
            }

            //
            return state;
        }
Example #5
0
 /// <summary>
 /// Gets file symbol containing given symbol.
 /// </summary>
 public static SourceFileSymbol GetContainingFileSymbol(this Symbol s)
 {
     return(s?.OriginalDefinition switch
     {
         SourceRoutineSymbol routine => routine.ContainingFile,
         SourceTypeSymbol type => type.ContainingFile,
         _ => s != null?GetContainingFileSymbol(s.ContainingSymbol) : null,
     });
Example #6
0
        public SynthesizedStaticLocHolder(SourceRoutineSymbol routine, string locName, TypeSymbol locType = null)
        {
            Contract.ThrowIfNull(routine);

            _routine = routine;
            _locName = locName ?? "?";
            _locType = locType;
        }
Example #7
0
 /// <summary>
 /// Builds the visibility scope.
 /// </summary>
 public static VisibilityScope Create(NamedTypeSymbol self, SourceRoutineSymbol routine)
 {
     return(new VisibilityScope()
     {
         Scope = self,
         ScopeIsDynamic = self.IsTraitType() || routine is SourceLambdaSymbol || (routine?.IsGlobalScope == true),
     });
 }
Example #8
0
        public SourceLocalSymbol(SourceRoutineSymbol routine, string name, TextSpan span)
        {
            Debug.Assert(routine != null);
            Debug.Assert(!string.IsNullOrWhiteSpace(name));

            _routine = routine;
            _name    = name;
        }
        public SourceLocalSymbol(SourceRoutineSymbol routine, string name, VariableKind kind)
        {
            Debug.Assert(routine != null);
            Debug.Assert(!string.IsNullOrWhiteSpace(name));

            _routine = routine;
            _name    = name;
            _kind    = kind;
        }
Example #10
0
        /// <summary>
        /// Initializes table of locals of given routine.
        /// </summary>
        public LocalsTable(SourceRoutineSymbol routine)
        {
            Contract.ThrowIfNull(routine);

            _routine = routine;

            //
            PopuplateParameters();
        }
Example #11
0
        public SourceGeneratorSymbol(SourceRoutineSymbol originalRoutine)
            : base(originalRoutine.ContainingType, string.Format(WellKnownPchpNames.GeneratorStateMachineNameFormatString, originalRoutine.RoutineName),
                   isstatic: true, isvirtual: false,
                   returnType: originalRoutine.DeclaringCompilation.CoreTypes.Void, ps: null)
        {
            Debug.Assert(originalRoutine.IsGeneratorMethod());

            // Need to postpone settings the params because I can't access 'this' in base constructor call
            base.SetParameters(CreateParameters(originalRoutine));
        }
Example #12
0
        internal FlowContext(TypeRefContext/*!*/typeCtx, SourceRoutineSymbol routine)
        {
            Contract.ThrowIfNull(typeCtx);
            
            _typeCtx = typeCtx;
            _routine = routine;

            //
            _varsIndex = new Dictionary<VariableName, int>();
        }
Example #13
0
        public SourceParameterSymbol(SourceRoutineSymbol routine, FormalParam syntax, int index, PHPDocBlock.ParamTag ptagOpt)
        {
            Contract.ThrowIfNull(routine);
            Contract.ThrowIfNull(syntax);
            Debug.Assert(index >= 0);

            _routine = routine;
            _syntax  = syntax;
            _index   = index;
            _ptagOpt = ptagOpt;
        }
        /// <summary>
        /// Gets PHPDoc assoviated with given source symbol.
        /// </summary>
        internal static bool TryGetPHPDocBlock(this Symbol symbol, out PHPDocBlock phpdoc)
        {
            phpdoc = symbol?.OriginalDefinition switch
            {
                SourceRoutineSymbol routine => routine.PHPDocBlock,
                SourceFieldSymbol field => field.PHPDocBlock,
                SourceTypeSymbol type => type.Syntax.PHPDoc,
                                    _ => null
            };

            return(phpdoc != null);
        }
Example #15
0
        public SourceParameterSymbol(SourceRoutineSymbol routine, FormalParam syntax, int index, PHPDocBlock.ParamTag ptagOpt)
        {
            Contract.ThrowIfNull(routine);
            Contract.ThrowIfNull(syntax);
            Debug.Assert(index >= 0);

            _routine = routine;
            _syntax = syntax;
            _index = index;
            _ptagOpt = ptagOpt;
            _initializer = (syntax.InitValue != null)
                ? new SemanticsBinder(null).BindExpression(syntax.InitValue, BoundAccess.Read)
                : null;
        }
Example #16
0
        internal static MethodBody GenerateMethodBody(
            PEModuleBuilder moduleBuilder,
            SourceRoutineSymbol routine,
            int methodOrdinal,
            //ImmutableArray<LambdaDebugInfo> lambdaDebugInfo,
            //ImmutableArray<ClosureDebugInfo> closureDebugInfo,
            //StateMachineTypeSymbol stateMachineTypeOpt,
            VariableSlotAllocator variableSlotAllocatorOpt,
            DiagnosticBag diagnostics,
            //ImportChain importChainOpt,
            bool emittingPdb)
        {
            return GenerateMethodBody(moduleBuilder, routine, (builder) =>
            {
                DiagnosticBag diagnosticsForThisMethod = DiagnosticBag.GetInstance();
                var optimization = moduleBuilder.Compilation.Options.OptimizationLevel;
                var codeGen = new CodeGenerator(routine, builder, moduleBuilder, diagnosticsForThisMethod, optimization, emittingPdb);

                //if (diagnosticsForThisMethod.HasAnyErrors())
                //{
                //    // we are done here. Since there were errors we should not emit anything.
                //    return null;
                //}

                // We need to save additional debugging information for MoveNext of an async state machine.
                //var stateMachineMethod = method as SynthesizedStateMachineMethod;
                //bool isStateMachineMoveNextMethod = stateMachineMethod != null && method.Name == WellKnownMemberNames.MoveNextMethodName;

                //if (isStateMachineMoveNextMethod && stateMachineMethod.StateMachineType.KickoffMethod.IsAsync)
                //{
                //    int asyncCatchHandlerOffset;
                //    ImmutableArray<int> asyncYieldPoints;
                //    ImmutableArray<int> asyncResumePoints;
                //    codeGen.Generate(out asyncCatchHandlerOffset, out asyncYieldPoints, out asyncResumePoints);

                //    var kickoffMethod = stateMachineMethod.StateMachineType.KickoffMethod;

                //    // The exception handler IL offset is used by the debugger to treat exceptions caught by the marked catch block as "user unhandled".
                //    // This is important for async void because async void exceptions generally result in the process being terminated,
                //    // but without anything useful on the call stack. Async Task methods on the other hand return exceptions as the result of the Task.
                //    // So it is undesirable to consider these exceptions "user unhandled" since there may well be user code that is awaiting the task.
                //    // This is a heuristic since it's possible that there is no user code awaiting the task.
                //    asyncDebugInfo = new Cci.AsyncMethodBodyDebugInfo(kickoffMethod, kickoffMethod.ReturnsVoid ? asyncCatchHandlerOffset : -1, asyncYieldPoints, asyncResumePoints);
                //}
                //else
                {
                    codeGen.Generate();
                }
            }, variableSlotAllocatorOpt, diagnostics, emittingPdb);
        }
        public SourceParameterSymbol(SourceRoutineSymbol routine, FormalParam syntax, int relindex, PHPDocBlock.ParamTag ptagOpt)
        {
            Contract.ThrowIfNull(routine);
            Contract.ThrowIfNull(syntax);
            Debug.Assert(relindex >= 0);

            _routine     = routine;
            _syntax      = syntax;
            _relindex    = relindex;
            _ptagOpt     = ptagOpt;
            _initializer = (syntax.InitValue != null)
                ? new SemanticsBinder(locals: null, diagnostics: DeclaringCompilation.DeclarationDiagnostics).BindWholeExpression(syntax.InitValue, BoundAccess.Read).GetOnlyBoundElement()
                : null;
        }
        public SourceParameterSymbol(SourceRoutineSymbol routine, FormalParam syntax, int index, PHPDocBlock.ParamTag ptagOpt)
        {
            Contract.ThrowIfNull(routine);
            Contract.ThrowIfNull(syntax);
            Debug.Assert(index >= 0);

            _routine     = routine;
            _syntax      = syntax;
            _index       = index;
            _ptagOpt     = ptagOpt;
            _initializer = (syntax.InitValue != null)
                ? new SemanticsBinder(null).BindExpression(syntax.InitValue, BoundAccess.Read)
                : null;
        }
        public SourceParameterSymbol(SourceRoutineSymbol routine, FormalParam syntax, int relindex, PHPDocBlock.ParamTag ptagOpt)
        {
            Contract.ThrowIfNull(routine);
            Contract.ThrowIfNull(syntax);
            Debug.Assert(relindex >= 0);

            _routine     = routine;
            _syntax      = syntax;
            _relindex    = relindex;
            _ptagOpt     = ptagOpt;
            _initializer = (syntax.InitValue != null)
                ? new SemanticsBinder(DeclaringCompilation, locals: null, routine: routine, self: routine.ContainingType as SourceTypeSymbol)
                           .BindWholeExpression(syntax.InitValue, BoundAccess.Read)
                           .SingleBoundElement()
                : null;
        }
Example #20
0
        /// <summary>
        /// Parameters for <see cref="SourceGeneratorSymbol"/> method are defined by <c>GeneratorStateMachineDelegate</c>.
        /// </summary>
        ParameterSymbol[] CreateParameters(SourceRoutineSymbol originalRoutine)
        {
            // resolve type of $this
            var thisType = originalRoutine.GetPhpThisVariablePlaceWithoutGenerator()?.Type ?? (TypeSymbol)originalRoutine.DeclaringCompilation.ObjectType;

            // yield sm method signature
            var index = 0;

            return(new[]
            {
                ContextParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, index++),
                ThisParameter = new SpecialParameterSymbol(this, thisType, SpecialParameterSymbol.ThisName, index++),
                LocalsParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.PhpArray, SpecialParameterSymbol.LocalsName, index++),
                TmpLocalsParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.PhpArray, SpecialParameterSymbol.TemporaryLocalsName, index++),
                GeneratorParameter = new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Generator, "generator", index++),
            });
        }
        /// <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);
        }
        /// <summary>
        /// Parameters for <see cref="SourceGeneratorSymbol"/> method are defined by <c>GeneratorStateMachineDelegate</c>.
        /// </summary>
        ParameterSymbol[] getParams(SourceRoutineSymbol originalRoutine)
        {
            // resolve type of $this
            TypeSymbol thisType = originalRoutine.ThisParameter?.Type ?? (TypeSymbol)originalRoutine.DeclaringCompilation.ObjectType;

            Debug.Assert(thisType != null);

            // yield sm method signature
            var index = 0;

            return(new[]
            {
                new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Context, SpecialParameterSymbol.ContextName, index++),
                new SpecialParameterSymbol(this, thisType, SpecialParameterSymbol.ThisName, index++),
                new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.PhpArray, SpecialParameterSymbol.LocalsName, index++),
                new SpecialParameterSymbol(this, DeclaringCompilation.CoreTypes.Generator, "generator", index++),
            });
        }
Example #23
0
        /// <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());
                }
            });
        }
Example #24
0
 public SynthesizedLocalSymbol(SourceRoutineSymbol routine, string name, TypeSymbol type)
     : base(routine, name + "'", default(TextSpan))
 {
     Contract.ThrowIfNull(type);
     _type = type;
 }
 /// <summary>
 /// Gets value indicating the routine was found containing <c>yield</c>
 /// hence it is considered as a generator state machine method.
 /// </summary>
 /// <param name="routine">The analysed routine.</param>
 /// <returns>Value indicating the routine gets a generator.</returns>
 internal static bool IsGeneratorMethod(this SourceRoutineSymbol routine) => (routine.Flags & RoutineFlags.IsGenerator) != 0;
Example #26
0
 public SourceReturnSymbol(SourceRoutineSymbol routine)
     : base(routine, SpecialName, VariableKind.ReturnVariable)
 {
 }
 public SynthesizedLocalSymbol(SourceRoutineSymbol routine, string name, TypeSymbol type)
     : base(routine, name + "#", VariableKind.LocalVariable)
 {
     Contract.ThrowIfNull(type);
     _type = type;
 }
        public SynthesizedStaticLocHolder(SourceRoutineSymbol routine, string locName, TypeSymbol locType)
        {
            Contract.ThrowIfNull(routine);
            Contract.ThrowIfNull(locType);

            _routine = routine;
            _locName = locName ?? "?";
            _locType = locType;
        }
Example #29
0
 /// <summary>
 /// Builds the visibility scope.
 /// </summary>
 public VisibilityScope(NamedTypeSymbol self, SourceRoutineSymbol routine)
 {
     Scope          = self;
     ScopeIsDynamic = self.IsTraitType() || routine is SourceLambdaSymbol || (routine?.IsGlobalScope == true);
 }
Example #30
0
        /// <summary>
        /// Generates analyzed method.
        /// </summary>
        void EmitMethodBody(SourceRoutineSymbol routine)
        {
            Contract.ThrowIfNull(routine);
            Debug.Assert(routine.ControlFlowGraph != null);
            Debug.Assert(routine.ControlFlowGraph.Start.FlowState != null);

            var body = MethodGenerator.GenerateMethodBody(_moduleBuilder, routine, 0, null, _diagnostics, _emittingPdb);
            _moduleBuilder.SetMethodBody(routine, body);
        }