public BoundStatement BindStatement(AST.Statement stmt) { Debug.Assert(stmt != null); if (stmt is AST.EchoStmt) return new BoundExpressionStatement(new BoundEcho(BindArguments(((AST.EchoStmt)stmt).Parameters))) { PhpSyntax = stmt }; if (stmt is AST.ExpressionStmt) return new BoundExpressionStatement(BindExpression(((AST.ExpressionStmt)stmt).Expression, BoundAccess.None)) { PhpSyntax = stmt }; if (stmt is AST.JumpStmt) return BindJumpStmt((AST.JumpStmt)stmt); if (stmt is AST.FunctionDecl) return new BoundFunctionDeclStatement(stmt.GetProperty<SourceFunctionSymbol>()); // see SourceDeclarations.PopulatorVisitor if (stmt is AST.TypeDecl) return new BoundTypeDeclStatement(stmt.GetProperty<SourceTypeSymbol>()); // see SourceDeclarations.PopulatorVisitor if (stmt is AST.GlobalStmt) return new BoundGlobalVariableStatement( ((AST.GlobalStmt)stmt).VarList.Cast<AST.DirectVarUse>() .Select(s => (BoundGlobalVariable)_locals.BindVariable(s.VarName, VariableKind.GlobalVariable, null)) .ToImmutableArray()); if (stmt is AST.StaticStmt) return new BoundStaticVariableStatement( ((AST.StaticStmt)stmt).StVarList .Select(s => (BoundStaticLocal)_locals.BindVariable(s.Variable, VariableKind.StaticVariable, () => (s.Initializer != null ? BindExpression(s.Initializer) : null))) .ToImmutableArray()) { PhpSyntax = stmt }; if (stmt is AST.UnsetStmt) return new BoundUnset( ((AST.UnsetStmt)stmt).VarList .Select(v => (BoundReferenceExpression)BindExpression(v, BoundAccess.Unset)) .ToImmutableArray()) { PhpSyntax = stmt }; if (stmt is AST.ThrowStmt) return new BoundThrowStatement(BindExpression(((AST.ThrowStmt)stmt).Expression, BoundAccess.Read)) { PhpSyntax = stmt }; if (stmt is AST.PHPDocStmt) return new BoundEmptyStatement(); throw new NotImplementedException(stmt.GetType().FullName); }
/// <summary> /// Gets type mask corresponding to <c>self</c> with <c>includesSubclasses</c> flag set whether type is not final. /// </summary> private TypeRefMask GetTypeCtxMask(AST.TypeDecl typeCtx) { if (typeCtx != null) { var typeIsFinal = (typeCtx.MemberAttributes & PhpMemberAttributes.Final) != 0; return GetTypeMask(new ClassTypeRef(NameUtils.MakeQualifiedName(typeCtx)), !typeIsFinal); } else { return TypeRefMask.AnyType; } }
BoundExpression BindUnaryEx(AST.UnaryEx expr, BoundAccess access) { var operandAccess = BoundAccess.Read; switch (expr.Operation) { case AST.Operations.AtSign: operandAccess = access; break; case AST.Operations.UnsetCast: operandAccess = BoundAccess.None; break; } return new BoundUnaryEx(BindExpression(expr.Expr, operandAccess), expr.Operation) .WithAccess(access); }
BoundExpression BindInstanceOfEx(AST.InstanceOfEx x) { return new BoundInstanceOfEx(BindExpression(x.Expression, BoundAccess.Read), BindTypeRef(x.ClassNameRef)); }
BoundExpression BindBinaryEx(AST.BinaryEx expr) { if (expr.Operation == AST.Operations.Concat) { return new BoundConcatEx(BindArguments(new[] { expr.LeftExpr, expr.RightExpr })); } else { return new BoundBinaryEx( BindExpression(expr.LeftExpr, BoundAccess.Read), BindExpression(expr.RightExpr, BoundAccess.Read), expr.Operation); } }
static ConstantValue CreateConstant(AST.GlobalConstUse expr) { if (expr.Name == QualifiedName.Null) return ConstantValue.Null; if (expr.Name == QualifiedName.True) return ConstantValue.True; if (expr.Name == QualifiedName.False) return ConstantValue.False; return null; }
BoundExpression BindConcatEx(AST.ConcatEx x) { return new BoundConcatEx(BindArguments(x.Expressions)); }
BoundExpression BindListEx(AST.ListEx expr) { var vars = expr.Items .Select(lval => (lval != null) ? (BoundReferenceExpression)BindExpression(((AST.ValueItem)lval).ValueExpr, BoundAccess.Write) : null) .ToArray(); return new BoundListEx(vars).WithAccess(BoundAccess.Write); }
public static ConstantValue TryGetConstantValue(PhpCompilation compilation, AST.Expression value) { if (value is AST.Literal) return CreateConstant((AST.Literal)value); if (value is AST.GlobalConstUse) return CreateConstant((AST.GlobalConstUse)value); return null; }
BoundExpression BindNew(AST.NewEx x, BoundAccess access) { Debug.Assert(access.IsRead || access.IsReadRef || access.IsNone); return new BoundNewEx(BindTypeRef(x.ClassNameRef), BindArguments(x.CallSignature.Parameters)) .WithAccess(access); }
BoundExpression BindArrayEx(AST.ArrayEx x, BoundAccess access) { Debug.Assert(access.IsRead && !access.IsReadRef); return new BoundArrayEx(BindArrayItems(x.Items)) { PhpSyntax = x }.WithAccess(access); }
BoundExpression BindIncDec(AST.IncDecEx expr) { // bind variable reference var varref = (BoundReferenceExpression)BindExpression(expr.Variable, BoundAccess.ReadAndWrite); // resolve kind UnaryOperationKind kind; if (expr.Inc) kind = (expr.Post) ? UnaryOperationKind.OperatorPostfixIncrement : UnaryOperationKind.OperatorPrefixIncrement; else kind = (expr.Post) ? UnaryOperationKind.OperatorPostfixDecrement : UnaryOperationKind.OperatorPrefixDecrement; // return new BoundIncDecEx(varref, kind); }
BoundExpression BindVarLikeConstructUse(AST.VarLikeConstructUse expr, BoundAccess access) { if (expr is AST.SimpleVarUse) return BindSimpleVarUse((AST.SimpleVarUse)expr, access); if (expr is AST.FunctionCall) return BindFunctionCall((AST.FunctionCall)expr, access); if (expr is AST.NewEx) return BindNew((AST.NewEx)expr, access); if (expr is AST.ArrayEx) return BindArrayEx((AST.ArrayEx)expr, access); if (expr is AST.ItemUse) return BindItemUse((AST.ItemUse)expr, access); if (expr is AST.StaticFieldUse) return BindFieldUse((AST.StaticFieldUse)expr, access); if (expr is AST.ListEx) return BindListEx((AST.ListEx)expr).WithAccess(access); throw new NotImplementedException(expr.GetType().FullName); }
BoundExpression BindConditionalEx(AST.ConditionalEx expr) { return new BoundConditionalEx( BindExpression(expr.CondExpr), (expr.TrueExpr != null) ? BindExpression(expr.TrueExpr) : null, BindExpression(expr.FalseExpr)); }
public BoundTypeRef BindTypeRef(AST.TypeRef tref) { var bound = new BoundTypeRef(tref); if (tref is AST.IndirectTypeRef) { bound.TypeExpression = BindExpression(((AST.IndirectTypeRef)tref).ClassNameVar); } return bound; }
BoundRoutineCall BindFunctionCall(AST.FunctionCall x, BoundAccess access) { if (!access.IsRead && !access.IsNone) { throw new NotSupportedException(); } var boundinstance = (x.IsMemberOf != null) ? BindExpression(x.IsMemberOf, BoundAccess.Read/*Object?*/) : null; var boundargs = BindArguments(x.CallSignature.Parameters); if (x is AST.DirectFcnCall) { var f = (AST.DirectFcnCall)x; var fname = f.FullName; if (f.IsMemberOf == null) { return new BoundGlobalFunctionCall(fname.Name, fname.FallbackName, boundargs) .WithAccess(access); } else { Debug.Assert(fname.FallbackName.HasValue == false); Debug.Assert(fname.Name.QualifiedName.IsSimpleName); return new BoundInstanceFunctionCall(boundinstance, fname.Name, boundargs) .WithAccess(access); } } else if (x is AST.IndirectFcnCall) { var f = (AST.IndirectFcnCall)x; var nameExpr = BindExpression(f.NameExpr); return ((f.IsMemberOf == null) ? (BoundRoutineCall)new BoundGlobalFunctionCall(nameExpr, boundargs) : new BoundInstanceFunctionCall(boundinstance, new BoundUnaryEx(nameExpr, AST.Operations.StringCast), boundargs)) .WithAccess(access); } else if (x is AST.StaticMtdCall) { var f = (AST.StaticMtdCall)x; Debug.Assert(f.IsMemberOf == null); var boundname = (f is AST.DirectStMtdCall) ? new BoundRoutineName(new QualifiedName(((AST.DirectStMtdCall)f).MethodName)) : new BoundRoutineName(new BoundUnaryEx(BindExpression(((AST.IndirectStMtdCall)f).MethodNameVar), AST.Operations.StringCast)); return new BoundStaticFunctionCall(BindTypeRef(f.TargetType), boundname, boundargs) .WithAccess(access); } // throw new NotImplementedException(x.GetType().FullName); }
BoundExpression BindAssignEx(AST.AssignEx expr, BoundAccess access) { var target = (BoundReferenceExpression)BindExpression(expr.LValue, BoundAccess.Write); BoundExpression value; // bind value (read as value or as ref) if (expr is AST.ValueAssignEx) { value = BindExpression(((AST.ValueAssignEx)expr).RValue, BoundAccess.Read); } else { Debug.Assert(expr is AST.RefAssignEx); Debug.Assert(expr.Operation == AST.Operations.AssignRef); target.Access = target.Access.WithWriteRef(0); // note: analysis will write the write type value = BindExpression(((AST.RefAssignEx)expr).RValue, BoundAccess.ReadRef); } // if (expr.Operation == AST.Operations.AssignValue || expr.Operation == AST.Operations.AssignRef) { return new BoundAssignEx(target, value).WithAccess(access); } else { target.Access = target.Access.WithRead(); // Read & Write on target return new BoundCompoundAssignEx(target, value, expr.Operation); } }
IEnumerable<KeyValuePair<BoundExpression, BoundExpression>> BindArrayItems(AST.Item[] items) { foreach (var x in items) { var boundIndex = (x.Index != null) ? BindExpression(x.Index, BoundAccess.Read) : null; var boundValue = (x is AST.RefItem) ? BindExpression(((AST.RefItem)x).RefToGet, BoundAccess.ReadRef) : BindExpression(((AST.ValueItem)x).ValueExpr, BoundAccess.Read); yield return new KeyValuePair<BoundExpression, BoundExpression>(boundIndex, boundValue); } }
BoundExpression BindExpression(AST.Expression expr) => BindExpression(expr, BoundAccess.Read);
BoundExpression BindItemUse(AST.ItemUse x, BoundAccess access) { if (x.IsMemberOf != null) { Debug.Assert(x.Array.IsMemberOf == null); // fix this phalanger ast weirdness: x.Array.IsMemberOf = x.IsMemberOf; x.IsMemberOf = null; } var arrayAccess = BoundAccess.Read; if (access.IsWrite || access.EnsureObject || access.EnsureArray) arrayAccess = arrayAccess.WithEnsureArray(); if (access.IsQuiet) arrayAccess = arrayAccess.WithQuiet(); var boundArray = BindExpression(x.Array, arrayAccess); // boundArray.Access = boundArray.Access.WithRead(typeof(PhpArray)) return new BoundArrayItemEx( boundArray, (x.Index != null) ? BindExpression(x.Index, BoundAccess.Read) : null) .WithAccess(access); }
static BoundExpression BindLiteral(AST.Literal expr) { if (expr is AST.LongIntLiteral) return new BoundLiteral(((AST.LongIntLiteral)expr).Value); if (expr is AST.StringLiteral) return new BoundLiteral(((AST.StringLiteral)expr).Value); if (expr is AST.DoubleLiteral) return new BoundLiteral(((AST.DoubleLiteral)expr).Value); if (expr is AST.BoolLiteral) return new BoundLiteral(((AST.BoolLiteral)expr).Value); if (expr is AST.NullLiteral) return new BoundLiteral(null); if (expr is AST.BinaryStringLiteral) return new BoundLiteral(((AST.BinaryStringLiteral)expr).Value); throw new NotImplementedException(); }
BoundStatement BindJumpStmt(AST.JumpStmt stmt) { if (stmt.Type == AST.JumpStmt.Types.Return) { return new BoundReturnStatement( (stmt.Expression != null) ? BindExpression(stmt.Expression, BoundAccess.Read) // ReadRef in case routine returns an aliased value : null) { PhpSyntax = stmt }; } throw ExceptionUtilities.Unreachable; }
static ConstantValue CreateConstant(AST.Literal expr) { if (expr is AST.LongIntLiteral) return ConstantValue.Create(((AST.LongIntLiteral)expr).Value); if (expr is AST.StringLiteral) return ConstantValue.Create(((AST.StringLiteral)expr).Value); if (expr is AST.DoubleLiteral) return ConstantValue.Create(((AST.DoubleLiteral)expr).Value); if (expr is AST.BoolLiteral) return ConstantValue.Create(((AST.BoolLiteral)expr).Value); if (expr is AST.NullLiteral) return ConstantValue.Create(null); //if (expr is BinaryStringLiteral) return ConstantValue.Create(((BinaryStringLiteral)expr).Value); return null; }
/// <summary> /// Gets type mask corresponding to given TypeRef within this context. /// </summary> private TypeRefMask GetTypeMask(AST.IndirectTypeRef/*!*/tref, bool includesSubclasses) { Contract.ThrowIfNull(tref); var dvar = tref.ClassNameVar as AST.DirectVarUse; if (dvar != null && dvar.IsMemberOf == null && dvar.VarName.IsThisVariableName) return GetThisTypeMask(); // return TypeRefMask.AnyType; }
BoundExpression BindSimpleVarUse(AST.SimpleVarUse expr, BoundAccess access) { var dexpr = expr as AST.DirectVarUse; var iexpr = expr as AST.IndirectVarUse; Debug.Assert(dexpr != null || iexpr != null); var varname = (dexpr != null) ? new BoundVariableName(dexpr.VarName) : new BoundVariableName(BindExpression(iexpr.VarNameEx)); if (expr.IsMemberOf == null) { return new BoundVariableRef(varname); } else { var instanceAccess = BoundAccess.Read; if (access.IsWrite || access.EnsureObject || access.EnsureArray) instanceAccess = instanceAccess.WithEnsureObject(); if (access.IsQuiet) instanceAccess = instanceAccess.WithQuiet(); return BoundFieldRef.CreateInstanceField(BindExpression(expr.IsMemberOf, instanceAccess), varname).WithAccess(access); } }
BoundExpression BindIncludeEx(AST.IncludingEx x) { return new BoundIncludeEx(BindExpression(x.Target, BoundAccess.Read), x.InclusionType); }
/// <summary> /// Gets type mask corresponding to given TypeRef within this context. /// </summary> public TypeRefMask GetTypeMask(AST.TypeRef/*!*/tref, bool includesSubclasses = true) { Contract.ThrowIfNull(tref); if (tref != null) { if (tref is AST.PrimitiveTypeRef) { switch (((AST.PrimitiveTypeRef)tref).PrimitiveTypeName) { case AST.PrimitiveTypeRef.PrimitiveType.@int: return GetLongTypeMask(); case AST.PrimitiveTypeRef.PrimitiveType.@float: return GetDoubleTypeMask(); case AST.PrimitiveTypeRef.PrimitiveType.@string: return GetStringTypeMask(); case AST.PrimitiveTypeRef.PrimitiveType.@bool: return GetBooleanTypeMask(); case AST.PrimitiveTypeRef.PrimitiveType.array: return GetArrayTypeMask(); case AST.PrimitiveTypeRef.PrimitiveType.callable: return GetCallableTypeMask(); case AST.PrimitiveTypeRef.PrimitiveType.@void: return 0; case AST.PrimitiveTypeRef.PrimitiveType.iterable: return GetArrayTypeMask() | GetTypeMask(NameUtils.SpecialNames.Traversable, true); // array | Traversable default: throw new ArgumentException(); } } else if (tref is AST.INamedTypeRef) return GetTypeMask(((AST.INamedTypeRef)tref).ClassName, includesSubclasses); else if (tref is AST.ReservedTypeRef) return GetTypeMaskOfReservedClassName(((AST.ReservedTypeRef)tref).QualifiedName.Value.Name); // NOTE: should be translated by parser to AliasedTypeRef else if (tref is AST.AnonymousTypeRef) return GetTypeMask(((AST.AnonymousTypeRef)tref).TypeDeclaration.QualifiedName, false); else if (tref is AST.MultipleTypeRef) { TypeRefMask result = 0; foreach (var x in ((AST.MultipleTypeRef)tref).MultipleTypes) { result |= GetTypeMask(x, includesSubclasses); } return result; } else if (tref is AST.NullableTypeRef) return GetTypeMask(((AST.NullableTypeRef)tref).TargetType) | this.GetNullTypeMask(); else if (tref is AST.GenericTypeRef) return GetTypeMask(((AST.GenericTypeRef)tref).TargetType, includesSubclasses); // TODO: now we are ignoring type args else if (tref is AST.IndirectTypeRef) return GetTypeMask((AST.IndirectTypeRef)tref, true); } return TypeRefMask.AnyType; }
BoundExpression BindFieldUse(AST.StaticFieldUse x, BoundAccess access) { var typeref = BindTypeRef(x.TargetType); BoundVariableName varname; if (x is AST.DirectStFldUse) { var dx = (AST.DirectStFldUse)x; varname = new BoundVariableName(dx.PropertyName); } else if (x is AST.IndirectStFldUse) { var ix = (AST.IndirectStFldUse)x; var fieldNameExpr = BindExpression(ix.FieldNameExpr, BoundAccess.Read); varname = new BoundVariableName(fieldNameExpr); } else { throw ExceptionUtilities.UnexpectedValue(x); } return BoundFieldRef.CreateStaticField(typeref, varname); }
BoundExpression BindGlobalConstUse(AST.GlobalConstUse expr) { // translate built-in constants directly if (expr.Name == QualifiedName.True) return new BoundLiteral(true); if (expr.Name == QualifiedName.False) return new BoundLiteral(false); if (expr.Name == QualifiedName.Null) return new BoundLiteral(null); // bind constant return new BoundGlobalConst(expr.Name.ToString()); }
BoundExpression BindPseudoConst(AST.PseudoConstUse x) => new BoundPseudoConst(x.Type);