Esempio n. 1
0
 /// <summary>
 /// Ensures that the variable is of the given type(s) in the positive branch or not of this type in the negative
 /// branch. If the current branch is unfeasible, assigns an appropriate boolean to the
 /// <see cref="BoundExpression.ConstantValue"/> of <paramref name="checkExpr"/>.
 /// </summary>
 /// <param name="varRef">The reference to the variable whose types to check.</param>
 /// <param name="targetType">The target type of the variable.</param>
 /// <param name="branch">The branch to check - <see cref="ConditionBranch.ToTrue"/> is understood as the positive
 /// branch if <paramref name="isPositiveCheck"/> is true.</param>
 /// <param name="flowState">The flow state of the branch.</param>
 /// <param name="skipPositiveIfAnyType">Whether to skip a mask with <see cref="TypeRefMask.IsAnyType"/> in the
 /// positive branch (in the negative branch, it is always skipped).</param>
 /// <param name="checkExpr">The expression to have its <see cref="BoundExpression.ConstantValue"/> potentially
 /// updated.</param>
 /// <param name="isPositiveCheck">Whether the expression returns true when the type check succeeds. For example,
 /// in the case of != it would be false.</param>
 public static void HandleTypeCheckingExpression(
     BoundVariableRef varRef,
     TypeRefMask targetType,
     ConditionBranch branch,
     FlowState flowState,
     bool skipPositiveIfAnyType = false,
     BoundExpression checkExpr  = null,
     bool isPositiveCheck       = true)
 {
     HandleTypeCheckingExpression(varRef, (var) => targetType, branch, flowState, skipPositiveIfAnyType, checkExpr, isPositiveCheck);
 }
Esempio n. 2
0
 /// <summary>
 /// Sets the initial routine return type.
 /// </summary>
 /// <param name="ctx"></param>
 /// <param name="initialState"></param>
 /// <param name="phpdoc"></param>
 static void InitReturn(FlowContext /*!*/ ctx, FlowState /*!*/ initialState, PHPDocBlock phpdoc)
 {
     Debug.Assert(ctx.ReturnVarIndex >= 0);
     if (phpdoc != null)
     {
         var returnTag = phpdoc.Returns;
         if (returnTag != null && returnTag.TypeNamesArray.Length != 0)
         {
             initialState.SetVar(ctx.ReturnVarIndex, PHPDoc.GetTypeMask(ctx.TypeRefContext, returnTag.TypeNamesArray));
         }
     }
 }
Esempio n. 3
0
        /// <summary>
        /// Initializes <c>$this</c> variable, its type and initialized state.
        /// </summary>
        private static void InitThisVar(FlowContext /*!*/ ctx, FlowState /*!*/ initialState, int varIndex)
        {
            var thisVarType = ctx.TypeRefContext.GetThisTypeMask();

            if (thisVarType.IsUninitialized)
            {
                thisVarType = TypeRefMask.AnyType;
            }

            //
            initialState.SetVarUsed(varIndex);
            initialState.SetVar(varIndex, thisVarType);
        }
Esempio n. 4
0
        /// <summary>
        /// Creates new type context, flow context and flow state for the routine.
        /// </summary>
        public static FlowState CreateInitialState(SourceRoutineSymbol /*!*/ routine, FlowContext flowCtx = null)
        {
            Contract.ThrowIfNull(routine);

            // get or create typeCtx
            var typeCtx = routine.TypeRefContext;

            if (flowCtx == null)
            {
                // create FlowContext
                flowCtx = new FlowContext(typeCtx, routine);
            }

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

            // handle input parameters type
            foreach (var p in routine.SourceParameters)
            {
                var local = state.GetLocalHandle(new VariableName(p.Name));
                var ptype = p.GetResultType(typeCtx);
                if (p.IsNotNull)
                {
                    // remove 'null' type from the mask,
                    // it cannot be null
                    ptype = typeCtx.WithoutNull(ptype);
                }
                else if (p.Type.IsReferenceType)    // a reference type that can be null
                {
                    ptype |= typeCtx.GetNullTypeMask();
                }

                state.SetLocalType(local, ptype);

                if (p.Syntax.PassedByRef && !p.Syntax.IsVariadic)
                {
                    state.MarkLocalByRef(local);
                }
            }

            // $this
            if (routine.GetPhpThisVariablePlace() != null)
            {
                InitThisVar(flowCtx, state);
            }

            //
            return(state);
        }
Esempio n. 5
0
        /// <summary>
        /// Ensures that the variable is of the given type(s) in the positive branch or not of this type in the negative
        /// branch. If the current branch is unfeasible, assigns an appropriate boolean to the
        /// <see cref="BoundExpression.ConstantValue"/> of <paramref name="checkExpr"/>.
        /// </summary>
        /// <param name="varRef">The reference to the variable whose types to check.</param>
        /// <param name="targetTypeCallback">The callback that receives the current type mask of the variable and returns
        /// the target one.</param>
        /// <param name="branch">The branch to check - <see cref="ConditionBranch.ToTrue"/> is understood as the positive
        /// branch if <paramref name="isPositiveCheck"/> is true.</param>
        /// <param name="flowState">The flow state of the branch.</param>
        /// <param name="skipPositiveIfAnyType">Whether to skip a mask with <see cref="TypeRefMask.IsAnyType"/> in the
        /// positive branch (in the negative branch, it is always skipped).</param>
        /// <param name="checkExpr">The expression to have its <see cref="BoundExpression.ConstantValue"/> potentially
        /// updated.</param>
        /// <param name="isPositiveCheck">Whether the expression returns true when the type check succeeds. For example,
        /// in the case of != it would be false.</param>
        public static void HandleTypeCheckingExpression(
            BoundVariableRef varRef,
            Func <TypeRefMask, TypeRefMask> targetTypeCallback,
            ConditionBranch branch,
            FlowState flowState,
            bool skipPositiveIfAnyType = false,
            BoundExpression checkExpr  = null,
            bool isPositiveCheck       = true)
        {
            if (!TryGetVariableHandle(varRef.Variable, flowState, out VariableHandle handle))
            {
                return;
            }

            var currentType = flowState.GetLocalType(handle);
            var targetType  = targetTypeCallback(currentType);

            // Model negative type checks (such as $x != null) by inverting branches for the core checking function
            var branchHlp = isPositiveCheck ? branch : branch.NegativeBranch();

            bool isFeasible = HandleTypeChecking(currentType, targetType, branchHlp, flowState, handle, skipPositiveIfAnyType);

            // If the constant value was not meant to be updated, skip its computation
            if (checkExpr == null)
            {
                return;
            }

            if (!currentType.IsRef)
            {
                // If the true branch proves to be unfeasible, the function always returns false and vice versa
                var resultConstVal = isFeasible ? default(Optional <object>) : new Optional <object>(!branch.TargetValue().Value);

                // Each branch can clean only the constant value it produced during its analysis (in order not to lose result
                // of the other branch): true branch can produce false value and vice versa
                if (!resultConstVal.EqualsOptional(checkExpr.ConstantValue) &&
                    (resultConstVal.HasValue ||
                     checkExpr.ConstantValue.Value is false && branch == ConditionBranch.ToTrue ||
                     checkExpr.ConstantValue.Value is true && branch == ConditionBranch.ToFalse))
                {
                    checkExpr.ConstantValue = resultConstVal;
                }
            }
            else
            {
                // We cannot reason about the result of the check if the variable can be modified by reference
                checkExpr.ConstantValue = default(Optional <object>);
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Initializes <c>$this</c> variable, its type and initialized state.
        /// </summary>
        private static void InitThisVar(FlowContext /*!*/ ctx, FlowState /*!*/ initialState)
        {
            var thisVarType = ctx.TypeRefContext.GetThisTypeMask();

            if (thisVarType.IsUninitialized)
            {
                thisVarType = TypeRefMask.AnyType;
            }

            //
            var thisHandle = ctx.GetVarIndex(VariableName.ThisVariableName);

            initialState.SetLocalType(thisHandle, thisVarType); // set $this type
            initialState.VisitLocal(thisHandle);                // mark as visited (used) to not report as unused
        }
Esempio n. 7
0
        public override void VisitCFGConditionalEdge(ConditionalEdge x)
        {
            // build state for TrueBlock and FalseBlock properly, take minimal evaluation into account
            var state = _state;

            // true branch
            _state = state.Clone();
            VisitCondition(x.Condition, ConditionBranch.ToTrue);
            TraverseToBlock(x, _state, x.TrueTarget);

            // false branch
            _state = state.Clone();
            VisitCondition(x.Condition, ConditionBranch.ToFalse);
            TraverseToBlock(x, _state, x.FalseTarget);
        }
Esempio n. 8
0
        /// <summary>
        /// Initializes <c>$this</c> variable, its type and initialized state.
        /// </summary>
        private static void InitThisVar(FlowContext /*!*/ ctx, FlowState /*!*/ initialState)
        {
            var thisVarType = ctx.TypeRefContext.GetThisTypeMask();

            if (thisVarType.IsUninitialized)
            {
                thisVarType = TypeRefMask.AnyType;
            }

            //
            var thisIdx = ctx.GetVarIndex(VariableName.ThisVariableName);

            initialState.SetVarUsed(thisIdx);
            initialState.SetVar(thisIdx, thisVarType);
        }
Esempio n. 9
0
        private static bool TryGetVariableHandle(BoundVariable boundvar, FlowState state, out VariableHandle varHandle)
        {
            if (boundvar.Name != null)  // direct variable name
            {
                if (boundvar.VariableKind == VariableKind.LocalVariable || boundvar.VariableKind == VariableKind.Parameter)
                {
                    varHandle = state.GetLocalHandle(boundvar.Name);
                    return(true);
                }
            }

            //
            varHandle = default(VariableHandle);
            return(false);
        }
Esempio n. 10
0
        private static bool TryGetVariableHandle(IVariableReference boundvar, FlowState state, out VariableHandle varHandle)
        {
            if (boundvar is LocalVariableReference local && local.BoundName.IsDirect)  // direct variable name
            {
                if (local.VariableKind == VariableKind.LocalVariable ||
                    local.VariableKind == VariableKind.Parameter ||
                    local.VariableKind == VariableKind.LocalTemporalVariable)
                {
                    varHandle = state.GetLocalHandle(local.BoundName.NameValue);
                    return(true);
                }
            }

            //
            varHandle = default(VariableHandle);
            return(false);
        }
Esempio n. 11
0
        public override void VisitCFGForeachMoveNextEdge(ForeachMoveNextEdge x)
        {
            var state = _state;
            // get type information from Enumeree to determine types value variable
            var elementType = HandleTraversableUse(x.EnumereeEdge.Enumeree);

            if (elementType.IsVoid)
            {
                elementType = TypeRefMask.AnyType;
            }

            // Body branch
            _state = state.Clone();
            // set key variable and value variable at current state

            var valueVar = x.ValueVariable;

            if (valueVar is BoundListEx)
            {
                throw new NotImplementedException();
                //VisitListEx(valueVar.List, elementType);
            }
            else
            {
                valueVar.Access = valueVar.Access.WithWrite(valueVar.Access.IsWriteRef ? elementType.WithRefFlag : elementType);
                Accept(valueVar);

                //
                var keyVar = x.KeyVariable;
                if (keyVar != null)
                {
                    keyVar.Access = keyVar.Access.WithWrite(TypeRefMask.AnyType);
                    Accept(keyVar);
                }
            }
            TraverseToBlock(x, _state, x.BodyBlock);

            // End branch
            _state = state.Clone();
            TraverseToBlock(x, _state, x.NextBlock);
        }
Esempio n. 12
0
        /// <summary>
        /// Creates new type context, flow context and flow state for the routine.
        /// </summary>
        public static FlowState CreateInitialState(SourceRoutineSymbol /*!*/ routine, FlowContext flowCtx = null)
        {
            Contract.ThrowIfNull(routine);

            // get or create typeCtx
            var typeCtx = routine.TypeRefContext;

            // create or reuse FlowContext
            flowCtx ??= new FlowContext(typeCtx, routine);

            // pre-allocate locals map // https://github.com/peachpiecompiler/peachpie/issues/1002
            foreach (var variable in routine.LocalsTable.Variables)
            {
                if (variable is Semantics.LocalVariableReference local && local.VariableKind == VariableKind.LocalVariable)
                {
                    flowCtx.GetVarIndex(variable.BoundName.NameValue);
                }
            }

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

            // populate input parameters type
            foreach (var p in routine.SourceParameters)
            {
                var local = state.GetLocalHandle(new VariableName(p.Name));
                var ptype = p.GetResultType(typeCtx);
                state.SetLocalType(local, ptype);
            }

            // $this
            if (routine.GetPhpThisVariablePlace() != null)
            {
                InitThisVar(flowCtx, state);
            }

            //
            return(state);
        }
Esempio n. 13
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.SourceParameters;

            foreach (var p in parameters)
            {
                var local = state.GetLocalHandle(new VariableName(p.Name));
                state.SetLocalType(local, p.GetResultType(typeCtx));

                if (p.Syntax.PassedByRef && !p.Syntax.IsVariadic)
                {
                    state.MarkLocalByRef(local);
                }
            }

            // $this
            if (routine.GetPhpThisVariablePlace() != null)
            {
                InitThisVar(flowCtx, state);
            }

            //
            return(state);
        }
Esempio n. 14
0
        private static bool HandleTypeChecking(
            TypeRefMask currentType,
            TypeRefMask targetType,
            ConditionBranch branch,
            FlowState flowState,
            VariableHandle handle,
            bool skipTrueIfAnyType)
        {
            // Information whether this path can ever be taken
            bool isFeasible = true;

            if (branch == ConditionBranch.ToTrue)
            {
                // In the true branch the IsAnyType case can be optionally skipped
                if (skipTrueIfAnyType && currentType.IsAnyType)
                {
                    return(isFeasible);
                }

                // Intersect the possible types with those checked by the function, always keeping the IsRef flag.
                // IncludesSubclasses is kept only if it is specified in targetType.
                TypeRefMask resultType = (currentType & (targetType | TypeRefMask.IsRefMask));

                if (resultType.IsVoid)
                {
                    // Clearing the type out in this branch means the variable will never be of that type.
                    // In order to prevent errors in analysis and code generation, set the type to the one specified.
                    resultType = targetType | (currentType & TypeRefMask.IsRefMask);

                    isFeasible = false;
                }

                flowState.SetLocalType(handle, resultType);
            }
            else
            {
                Debug.Assert(branch == ConditionBranch.ToFalse);

                // In the false branch we cannot handle the IsAnyType case
                if (currentType.IsAnyType)
                {
                    return(isFeasible);
                }

                // Remove the types and flags excluded by the fact that the function returned false
                TypeRefMask resultType = currentType & (~targetType);

                if (resultType.IsVoid)
                {
                    // Clearing the type out in this branch means the variable will always be of that type
                    // In order to prevent errors in analysis and code generation, do not alter the type in this case.

                    isFeasible = false;
                }
                else
                {
                    flowState.SetLocalType(handle, resultType);
                }
            }

            return(isFeasible);
        }
Esempio n. 15
0
        /// <summary>
        /// Creates new type context, flow context and flow state for the routine.
        /// </summary>
        public static FlowState CreateInitialState(SourceRoutineSymbol routine)
        {
            Contract.ThrowIfNull(routine);

            var containingType = routine.ContainingType as SourceNamedTypeSymbol;

            // collect locals
            var locals    = LocalsBinder.BindLocals(routine);
            var returnIdx = locals.IndexOf(x => x.VariableKind == VariableKind.ReturnVariable);

            // create typeCtx
            var typeCtx = routine.TypeRefContext;

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

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

            // handle parameters passed by reference
            var parameters = routine.Parameters.OfType <SourceParameterSymbol>().ToImmutableArray();

            foreach (var p in parameters)
            {
                if (p.Syntax.PassedByRef)
                {
                    state.SetVarRef(p.Name);
                }
            }

            // mark $this as initialized
            // mark global variables as ByRef, used
            // mark function parameters as used, initialized, typed
            // construct initial state for variables

            int paramIdx = 0;

            for (int i = 0; i < locals.Length; i++)
            {
                switch (locals[i].VariableKind)
                {
                case VariableKind.GlobalVariable:
                    state.SetVarRef(i);     // => used, byref, initialized
                    break;

                case VariableKind.Parameter:
                    //state.SetVarUsed(i);
                    var paramtag = PHPDoc.GetParamTag(routine.PHPDocBlock, paramIdx, locals[i].Name);
                    state.SetVar(i, GetParamType(typeCtx, paramtag, parameters[paramIdx].Syntax, default(CallInfo), paramIdx));
                    paramIdx++;
                    break;

                //case VariableKind.UseParameter:
                //    state.SetVar(i, TypeRefMask.AnyType);
                //    break;
                case VariableKind.ThisParameter:
                    InitThisVar(flowCtx, state, i);
                    break;

                case VariableKind.StaticVariable:
                    state.SetVarInitialized(i);
                    break;

                case VariableKind.ReturnVariable:
                    InitReturn(flowCtx, state, routine.PHPDocBlock);
                    break;
                }
            }
            //
            return(state);
        }