static void outputStringExpr(CodeGenerator &cg, AnalysisResultPtr ar, ExpressionPtr exp, bool asLitStr) { if (asLitStr && exp->isLiteralString()) { const std::string &s = exp->getLiteralString(); std::string enc = string_cplus_escape(s.c_str(), s.size()); cg_printf("\"%s\", %d", enc.c_str(), (int)s.size()); return; } TypePtr et(exp->getExpectedType()); exp->setExpectedType(Type::String); exp->outputCPP(cg, ar); exp->setExpectedType(et); }
ExpressionPtr Expression::replaceValue(ExpressionPtr rep) { if (hasContext(Expression::RefValue) && isRefable(true) && !rep->isRefable(true)) { /* An assignment isRefable, but the rhs may not be. Need this to prevent "bad pass by reference" errors. */ ExpressionListPtr el(new ExpressionList(getScope(), getRange(), ExpressionList::ListKindWrapped)); el->addElement(rep); rep->clearContext(AssignmentRHS); rep = el; } if (rep->is(KindOfSimpleVariable) && !is(KindOfSimpleVariable)) { static_pointer_cast<SimpleVariable>(rep)->setAlwaysStash(); } rep->copyContext(m_context & ~(DeadStore|AccessContext)); if (TypePtr t1 = getType()) { if (TypePtr t2 = rep->getType()) { if (!Type::SameType(t1, t2)) { rep->setExpectedType(t1); } } } if (rep->getScope() != getScope()) { rep->resetScope(getScope()); } return rep; }
TypePtr AssignmentExpression:: inferTypesImpl(AnalysisResultPtr ar, TypePtr type, bool coerce, ExpressionPtr variable, ExpressionPtr value /* = ExpressionPtr() */) { TypePtr ret = type; if (value) { if (coerce) { ret = value->inferAndCheck(ar, type, coerce); } else { ret = value->inferAndCheck(ar, NEW_TYPE(Some), coerce); } } BlockScopePtr scope = ar->getScope(); if (variable->is(Expression::KindOfConstantExpression)) { // ...as in ClassConstant statement ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(variable); bool p; scope->getConstants()->check(exp->getName(), ret, true, ar, variable, p); } else if (variable->is(Expression::KindOfDynamicVariable)) { // simptodo: not too sure about this ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable); } else if (variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); if (var->getName() == "this" && ar->getClassScope()) { if (ar->isFirstPass()) { ar->getCodeError()->record(variable, CodeError::ReassignThis, variable); } } if (ar->getPhase() == AnalysisResult::LastInference && value) { if (!value->getExpectedType()) { value->setExpectedType(variable->getActualType()); } } } // if the value may involve object, consider the variable as "referenced" // so that objects are not destructed prematurely. bool referenced = true; if (value && value->isScalar()) referenced = false; if (ret && ret->isNoObjectInvolved()) referenced = false; if (referenced && variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); const std::string &name = var->getName(); VariableTablePtr variables = ar->getScope()->getVariables(); variables->addReferenced(name); } TypePtr vt = variable->inferAndCheck(ar, ret, true); if (!coerce && type->is(Type::KindOfAny)) { ret = vt; } return ret; }
void UnaryOpExpression::SetExpTypeForExistsContext(AnalysisResultPtr ar, ExpressionPtr e, bool allowPrimitives) { if (!e) return; TypePtr at(e->getActualType()); if (!allowPrimitives && at && at->isExactType() && at->isPrimitive()) { at = e->inferAndCheck(ar, Type::Variant, true); } TypePtr it(e->getImplementedType()); TypePtr et(e->getExpectedType()); if (et && et->is(Type::KindOfVoid)) e->setExpectedType(TypePtr()); if (at && (!it || Type::IsMappedToVariant(it)) && ((allowPrimitives && Type::HasFastCastMethod(at)) || (!allowPrimitives && (at->is(Type::KindOfObject) || at->is(Type::KindOfArray) || at->is(Type::KindOfString))))) { e->setExpectedType(it ? at : TypePtr()); } }
// foldConst() is callable from the parse phase as well as the analysis phase. // We take advantage of this during the parse phase to reduce very simple // expressions down to a single scalar and keep the parse tree smaller, // especially in cases of long chains of binary operators. However, we limit // the effectivness of this during parse to ensure that we eliminate only // very simple scalars that don't require analysis in later phases. For now, // that's just simply scalar values. ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if ((ar->getPhase() != AnalysisResult::ParseAllFiles) && m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getLocation(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast<BinaryOpExpression>(m_exp2); if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) { ExpressionPtr aExp = m_exp1; ExpressionPtr bExp = binOpExp->m_exp1; ExpressionPtr cExp = binOpExp->m_exp2; m_exp1 = binOpExp = Clone(binOpExp); m_exp2 = cExp; binOpExp->m_exp1 = aExp; binOpExp->m_exp2 = bExp; if (ExpressionPtr optExp = binOpExp->foldConst(ar)) { m_exp1 = optExp; } return static_pointer_cast<Expression>(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { ScalarExpressionPtr scalar1 = dynamic_pointer_cast<ScalarExpression>(m_exp1); ScalarExpressionPtr scalar2 = dynamic_pointer_cast<ScalarExpression>(m_exp2); // Some data, like the values of __CLASS__ and friends, are not available // while we're still in the initial parse phase. if (ar->getPhase() == AnalysisResult::ParseAllFiles) { if ((scalar1 && scalar1->needsTranslation()) || (scalar2 && scalar2->needsTranslation())) { return ExpressionPtr(); } } if (!Option::WholeProgram || !Option::ParseTimeOpts) { // In the VM, don't optimize __CLASS__ if within a trait, since // __CLASS__ is not resolved yet. ClassScopeRawPtr clsScope = getOriginalClass(); if (clsScope && clsScope->isTrait()) { if ((scalar1 && scalar1->getType() == T_CLASS_C) || (scalar2 && scalar2->getType() == T_CLASS_C)) { return ExpressionPtr(); } } } Variant result; switch (m_op) { case T_LOGICAL_XOR: result = logical_xor(v1, v2); break; case '|': result = bitwise_or(v1, v2); break; case '&': result = bitwise_and(v1, v2); break; case '^': result = bitwise_xor(v1, v2); break; case '.': result = concat(v1, v2); break; case T_IS_IDENTICAL: result = same(v1, v2); break; case T_IS_NOT_IDENTICAL: result = !same(v1, v2); break; case T_IS_EQUAL: result = equal(v1, v2); break; case T_IS_NOT_EQUAL: result = !equal(v1, v2); break; case '<': result = less(v1, v2); break; case T_IS_SMALLER_OR_EQUAL: result = less_or_equal(v1, v2); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = more_or_equal(v1, v2); break; case '+': result = plus(v1, v2); break; case '-': result = minus(v1, v2); break; case '*': result = multiply(v1, v2); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = divide(v1, v2); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = modulo(v1, v2); break; case T_SL: result = shift_left(v1, v2); break; case T_SR: result = shift_right(v1, v2); break; case T_BOOLEAN_OR: result = v1 || v2; break; case T_BOOLEAN_AND: result = v1 && v2; break; case T_LOGICAL_OR: result = v1 || v2; break; case T_LOGICAL_AND: result = v1 && v2; break; case T_INSTANCEOF: result = false; break; default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) { } } else if (ar->getPhase() != AnalysisResult::ParseAllFiles) { switch (m_op) { case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND); ExpressionPtr rep = useFirst ? m_exp1 : m_exp2; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); if (!useFirst) { ExpressionListPtr l( new ExpressionList( getScope(), getLocation(), ExpressionList::ListKindComma)); l->addElement(m_exp1); l->addElement(rep); l->setActualType(Type::Boolean); rep = l; } rep->setExpectedType(getExpectedType()); return replaceValue(rep); } case T_LOGICAL_XOR: case '|': case '&': case '^': case '.': case '+': case '*': optExp = foldRightAssoc(ar); if (optExp) return optExp; break; case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v2.isNull()) { return makeIsNull(ar, getLocation(), m_exp1, m_op == T_IS_NOT_IDENTICAL); } break; default: break; } } return ExpressionPtr(); }
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp, ExpressionListPtr params, bool &valid) { if (!params) { if (m_minParam > 0) { if (ar->isFirstPass()) { ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt); } valid = false; setDynamic(); } return 0; } int ret = 0; if (params->getCount() < m_minParam) { if (ar->isFirstPass()) { ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt); } valid = false; setDynamic(); } if (params->getCount() > m_maxParam) { if (isVariableArgument()) { ret = params->getCount() - m_maxParam; } else { if (ar->isFirstPass()) { ar->getCodeError()->record(CodeError::TooManyArgument, exp, m_stmt); } valid = false; setDynamic(); } } bool canSetParamType = isUserFunction() && !m_overriding; for (int i = 0; i < params->getCount(); i++) { ExpressionPtr param = (*params)[i]; if (valid && param->hasContext(Expression::InvokeArgument)) { param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::RefValue); param->clearContext(Expression::NoRefWrapper); } TypePtr expType; if (!canSetParamType && i < m_maxParam) { expType = param->inferAndCheck(ar, getParamType(i), false); } else { expType = param->inferAndCheck(ar, NEW_TYPE(Some), false); } bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument()); if ((i < m_maxParam && isRefParam(i)) || isRefVararg) { param->setContext(Expression::LValue); param->setContext(Expression::RefValue); param->inferAndCheck(ar, Type::Variant, true); } else if (!(param->getContext() & Expression::RefParameter)) { param->clearContext(Expression::LValue); param->clearContext(Expression::RefValue); param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::NoRefWrapper); } if (i < m_maxParam) { if (m_paramTypeSpecs[i] && ar->isFirstPass()) { if (!Type::Inferred(ar, expType, m_paramTypeSpecs[i])) { const char *file = m_stmt->getLocation()->file; Logger::Error("%s: parameter %d of %s requires %s, called with %s", file, i, m_name.c_str(), m_paramTypeSpecs[i]->toString().c_str(), expType->toString().c_str()); ar->getCodeError()->record(CodeError::BadArgumentType, m_stmt); } } TypePtr paramType = getParamType(i); if (canSetParamType) { paramType = setParamType(ar, i, expType); } if (!Type::IsLegalCast(ar, expType, paramType) && paramType->isNonConvertibleType()) { param->inferAndCheck(ar, paramType, true); } param->setExpectedType(paramType); } // we do a best-effort check for bad pass-by-reference and do not report // error for some vararg case (e.g., array_multisort can have either ref // or value for the same vararg). if (!isRefVararg || !isMixedVariableArgument()) { Expression::checkPassByReference(ar, param); } } return ret; }
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp, ExpressionListPtr params, bool &valid) { if (!params) { if (m_minParam > 0) { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooFewArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } return 0; } int ret = 0; if (params->getCount() < m_minParam) { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooFewArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } if (params->getCount() > m_maxParam) { if (isVariableArgument()) { ret = params->getCount() - m_maxParam; } else { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooManyArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } } bool canSetParamType = isUserFunction() && !m_overriding && !m_perfectVirtual; for (int i = 0; i < params->getCount(); i++) { ExpressionPtr param = (*params)[i]; if (i < m_maxParam && param->hasContext(Expression::RefParameter)) { /** * This should be very un-likely, since call time pass by ref is a * deprecated, not very widely used (at least in FB codebase) feature. */ TRY_LOCK_THIS(); Symbol *sym = getVariables()->addSymbol(m_paramNames[i]); sym->setLvalParam(); sym->setCallTimeRef(); } if (valid && param->hasContext(Expression::InvokeArgument)) { param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::RefValue); param->clearContext(Expression::NoRefWrapper); } bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument()); if ((i < m_maxParam && isRefParam(i)) || isRefVararg) { param->setContext(Expression::LValue); param->setContext(Expression::RefValue); param->inferAndCheck(ar, Type::Variant, true); } else if (!(param->getContext() & Expression::RefParameter)) { param->clearContext(Expression::LValue); param->clearContext(Expression::RefValue); param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::NoRefWrapper); } TypePtr expType; /** * Duplicate the logic of getParamType(i), w/o the mutation */ TypePtr paramType(i < m_maxParam && !isZendParamMode() ? m_paramTypes[i] : TypePtr()); if (!paramType) paramType = Type::Some; if (valid && !canSetParamType && i < m_maxParam && (!Option::HardTypeHints || !m_paramTypeSpecs[i])) { /** * What is this magic, you might ask? * * Here, we take advantage of implicit conversion from every type to * Variant. Essentially, we don't really care what type comes out of this * expression since it'll just get converted anyways. Doing it this way * allows us to generate less temporaries along the way. */ TypePtr optParamType(paramType->is(Type::KindOfVariant) ? Type::Some : paramType); expType = param->inferAndCheck(ar, optParamType, false); } else { expType = param->inferAndCheck(ar, Type::Some, false); } if (i < m_maxParam) { if (!Option::HardTypeHints || !m_paramTypeSpecs[i]) { if (canSetParamType) { if (!Type::SameType(paramType, expType) && !paramType->is(Type::KindOfVariant)) { TRY_LOCK_THIS(); paramType = setParamType(ar, i, expType); } else { // do nothing - how is this safe? well, if we ever observe // paramType == expType, then this means at some point in the past, // somebody called setParamType() with expType. thus, by calling // setParamType() again with expType, we contribute no "new" // information. this argument also still applies in the face of // concurrency } } // See note above. If we have an implemented type, however, we // should set the paramType to the implemented type to avoid an // un-necessary cast if (paramType->is(Type::KindOfVariant)) { TypePtr it(param->getImplementedType()); paramType = it ? it : expType; } if (valid) { if (!Type::IsLegalCast(ar, expType, paramType) && paramType->isNonConvertibleType()) { param->inferAndCheck(ar, paramType, true); } param->setExpectedType(paramType); } } } // we do a best-effort check for bad pass-by-reference and do not report // error for some vararg case (e.g., array_multisort can have either ref // or value for the same vararg). if (!isRefVararg || !isMixedVariableArgument()) { Expression::CheckPassByReference(ar, param); } } return ret; }
ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if (m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getLocation(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast<BinaryOpExpression>(m_exp2); if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) { ExpressionPtr aExp = m_exp1; ExpressionPtr bExp = binOpExp->m_exp1; ExpressionPtr cExp = binOpExp->m_exp2; m_exp1 = binOpExp = Clone(binOpExp); m_exp2 = cExp; binOpExp->m_exp1 = aExp; binOpExp->m_exp2 = bExp; if (ExpressionPtr optExp = binOpExp->foldConst(ar)) { m_exp1 = optExp; } return static_pointer_cast<Expression>(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { Variant result; switch (m_op) { case T_LOGICAL_XOR: result = logical_xor(v1, v2); break; case '|': result = bitwise_or(v1, v2); break; case '&': result = bitwise_and(v1, v2); break; case '^': result = bitwise_xor(v1, v2); break; case '.': result = concat(v1, v2); break; case T_IS_IDENTICAL: result = same(v1, v2); break; case T_IS_NOT_IDENTICAL: result = !same(v1, v2); break; case T_IS_EQUAL: result = equal(v1, v2); break; case T_IS_NOT_EQUAL: result = !equal(v1, v2); break; case '<': result = less(v1, v2); break; case T_IS_SMALLER_OR_EQUAL: result = not_more(v1, v2); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = not_less(v1, v2); break; case '+': result = plus(v1, v2); break; case '-': result = minus(v1, v2); break; case '*': result = multiply(v1, v2); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = divide(v1, v2); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = modulo(v1, v2); break; case T_SL: result = shift_left(v1, v2); break; case T_SR: result = shift_right(v1, v2); break; case T_BOOLEAN_OR: result = v1 || v2; break; case T_BOOLEAN_AND: result = v1 && v2; break; case T_LOGICAL_OR: result = v1 || v2; break; case T_LOGICAL_AND: result = v1 && v2; break; case T_INSTANCEOF: result = false; break; default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) { } } else { switch (m_op) { case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND); ExpressionPtr rep = useFirst ? m_exp1 : m_exp2; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); if (!useFirst) { ExpressionListPtr l( new ExpressionList( getScope(), getLocation(), ExpressionList::ListKindComma)); l->addElement(m_exp1); l->addElement(rep); l->setActualType(Type::Boolean); rep = l; } rep->setExpectedType(getExpectedType()); return replaceValue(rep); } case T_LOGICAL_XOR: case '|': case '&': case '^': case '.': case '+': case '*': optExp = foldRightAssoc(ar); if (optExp) return optExp; break; case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v2.isNull()) { return makeIsNull(ar, getLocation(), m_exp1, m_op == T_IS_NOT_IDENTICAL); } break; default: break; } } return ExpressionPtr(); }