ArrayElementExpression::ArrayElementExpression (EXPRESSION_CONSTRUCTOR_PARAMETERS, ExpressionPtr variable, ExpressionPtr offset) : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES), m_variable(variable), m_offset(offset), m_global(false), m_dynamicGlobal(false), m_localEffects(AccessorEffect) { m_variable->setContext(Expression::AccessContext); if (m_variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable); if (var->getName() == "GLOBALS") { m_global = true; m_dynamicGlobal = true; if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) { ScalarExpressionPtr offset = dynamic_pointer_cast<ScalarExpression>(m_offset); if (offset->isLiteralString()) { m_globalName = offset->getIdentifier(); if (!m_globalName.empty()) { m_dynamicGlobal = false; } } } } } }
void SwitchStatement::analyzeProgramImpl(AnalysisResultPtr ar) { m_exp->analyzeProgram(ar); if (m_cases) m_cases->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll && m_exp->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr exp = dynamic_pointer_cast<SimpleVariable>(m_exp); if (exp && exp->getSymbol() && exp->getSymbol()->isClassName()) { // Mark some classes as volitle since the name is used in switch for (int i = 0; i < m_cases->getCount(); i++) { CaseStatementPtr stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); ASSERT(stmt); ExpressionPtr caseCond = stmt->getCondition(); if (caseCond && caseCond->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(caseCond); if (name && name->isLiteralString()) { string className = name->getLiteralString(); ClassScopePtr cls = ar->findClass(Util::toLower(className)); if (cls && cls->isUserClass()) { cls->setVolatile(); } } } } // Also note this down as code error ConstructPtr self = shared_from_this(); Compiler::Error(Compiler::ConditionalClassLoading, self); } } }
bool BinaryOpExpression::outputCPPImplOpEqual(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_exp1->is(Expression::KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(m_exp1); if (exp->isSuperGlobal() || exp->isDynamicGlobal()) return false; if (TypePtr t = exp->getVariable()->getActualType()) { TypePtr it(exp->getVariable()->getImplementedType()); if (t->is(Type::KindOfArray) && (!it || it->is(Type::KindOfArray) || Type::IsMappedToVariant(it) /* fast cast will kick in */)) { return false; } } // turning $a['elem'] Op= $b into $a.setOpEqual('elem', $b); exp->getVariable()->outputCPP(cg, ar); if (exp->getOffset()) { cg_printf(".setOpEqual(%d, ", m_op); exp->getOffset()->outputCPP(cg, ar); cg_printf(", ("); } else { cg_printf(".appendOpEqual(%d, (", m_op); } m_exp2->outputCPP(cg, ar); cg_printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off); if (sc) { if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); return true; } if (m_exp1->is(Expression::KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast<ObjectPropertyExpression>(m_exp1)); if (var->isValid()) return false; var->outputCPPObject(cg, ar); cg_printf("o_assign_op<%s,%d>(", isUnused() ? "void" : "Variant", m_op); var->outputCPPProperty(cg, ar); cg_printf(", "); m_exp2->outputCPP(cg, ar); cg_printf("%s)", originalClassName(cg, true).c_str()); return true; } return false; }
bool Option::Load(string &option, ExpressionPtr value) { ScalarExpressionPtr v = dynamic_pointer_cast<ScalarExpression>(value); if (!v || !v->isLiteralString()) { Logger::Error("Line %d: invalid string: %s", value->getLocation()->line1, value->getText().c_str()); return false; } option = v->getLiteralString(); return true; }
bool BinaryOpExpression::outputCPPImplOpEqual(CodeGenerator &cg, AnalysisResultPtr ar) { if (!m_exp1->is(Expression::KindOfArrayElementExpression)) return false; ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(m_exp1); if (exp->isSuperGlobal() || exp->isDynamicGlobal()) return false; bool linemap = outputLineMap(cg, ar); // turning $a['elem'] Op= $b into $a.setOpEqual('elem', $b); exp->getVariable()->outputCPP(cg, ar); if (exp->getOffset()) { cg_printf(".setOpEqual(%d, ", m_op); exp->getOffset()->outputCPP(cg, ar); cg_printf(", ("); } else { cg_printf(".appendOpEqual(%d, (", m_op); } m_exp2->outputCPP(cg, ar); cg_printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off); if (sc) { int64 hash = sc->getHash(); if (hash >= 0) { cg_printf(", 0x%016llXLL", hash); } else { cg_printf(", -1"); } if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); if (linemap) cg_printf(")"); return true; }
void ArrayElementExpression::outputCPPExistTest(CodeGenerator &cg, AnalysisResultPtr ar, int op) { switch (op) { case T_ISSET: cg_printf("isset("); break; case T_EMPTY: cg_printf("empty("); break; default: ASSERT(false); } if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = getScope()->getVariables(); string name = variables->getGlobalVariableName(cg, ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("((LVariableTable *)g)->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { m_variable->outputCPP(cg, ar); cg_printf(", "); m_offset->outputCPP(cg, ar); ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (sc) { if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); }
bool Option::Load(vector<string> &option, ExpressionPtr value) { ExpressionListPtr elements; if (!GetArrayElements(value, elements)) return false; for (int i = 0; i < elements->getCount(); i++) { ExpressionPtr e = (*elements)[i]; ArrayPairExpressionPtr pair = dynamic_pointer_cast<ArrayPairExpression>(e); ScalarExpressionPtr v; if (pair) v = dynamic_pointer_cast<ScalarExpression>(pair->getValue()); if (!pair || !v || !v->isLiteralString()) { Logger::Error("Line %d: invalid element: %s", e->getLocation()->line1, e->getText().c_str()); return false; } option.push_back(v->getLiteralString()); } return true; }
bool ArrayPairExpression::outputCPPName(CodeGenerator &cg, AnalysisResultPtr ar) { assert(m_name); ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_name); if (sc) { if (sc->isLiteralString()) { string s = sc->getLiteralString(); int64 res; if (is_strictly_integer(s.c_str(), s.size(), res)) { cg_printf("%sL", s.c_str()); } else { m_name->outputCPP(cg, ar); } return true; } if (sc->isLiteralInteger()) { m_name->outputCPP(cg, ar); return true; } } m_name->outputCPP(cg, ar); return false; }
void ArrayElementExpression::outputCPPUnset(CodeGenerator &cg, AnalysisResultPtr ar) { if (isSuperGlobal()) { Expression::outputCPPUnset(cg, ar); } else { TypePtr expected = m_variable->getExpectedType(); TypePtr implemented = m_variable->getImplementedType(); bool wrap = false; if (TypePtr t = m_variable->getActualType()) { if (t->is(Type::KindOfObject)) { if (!m_variable->getImplementedType() || !m_variable->getImplementedType()->is(Type::KindOfVariant)) { cg_printf("((Variant)("); wrap = true; } m_variable->setImplementedType(TypePtr()); m_variable->setExpectedType(TypePtr()); } } m_variable->outputCPP(cg, ar); if (wrap) cg_printf("))"); m_variable->setExpectedType(expected); m_variable->setImplementedType(implemented); cg_printf(".weakRemove("); m_offset->outputCPP(cg, ar); ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s->isStrictlyInteger(n)) { cg_printf(", true"); } } cg_printf(")"); } }
void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = getScope()->getVariables(); string name = variables->getGlobalVariableName(cg, ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("((LVariableTable *)g)->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { TypePtr type = m_variable->getActualType(); if (hasContext(UnsetContext)) { cg_printf("unsetLval("); m_variable->outputCPP(cg, ar); cg_printf(", "); } else { if (m_variable->is(Expression::KindOfScalarExpression) || (type && (type->isInteger() || type->is(Type::KindOfDouble) || type->is(Type::KindOfObject) || type->is(Type::KindOfBoolean)))) { cg_printf(type && type->is(Type::KindOfString) ? "((String)" : "((Variant)"); m_variable->outputCPP(cg, ar); cg_printf(")"); } else { TypePtr act; if (!m_variable->hasCPPTemp() && m_variable->getImplementedType() && type->is(Type::KindOfArray) && !Type::SameType(m_variable->getImplementedType(), type)) { act = type; type = m_variable->getImplementedType(); m_variable->setActualType(m_variable->getImplementedType()); } m_variable->outputCPP(cg, ar); if (act) { m_variable->setActualType(act); } } } if (m_offset) { bool lvalAt = false; bool rvalAt = false; bool byRef = false; bool arrRef = false; const char *sep = ", AccessFlags::"; if (hasContext(UnsetContext)) { // do nothing } else if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) { cg_printf(".argvalAt(cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum); } else if (m_context & (LValue|RefValue|DeepReference)) { cg_printf(".lvalAt("); lvalAt = true; } else { byRef = (m_context & AccessContext) && (!type || !type->is(Type::KindOfString)); arrRef = byRef && type && type->is(Type::KindOfArray); cg_printf(".rval%s%s(", arrRef || !byRef ? "At" : "", byRef ? "Ref" : ""); rvalAt = true; } m_offset->outputCPP(cg, ar); if (!type || !type->is(Type::KindOfString)) { if (rvalAt) { if (byRef && !arrRef) { const string &tmp = cg.getReferenceTemp(); cg_printf(", %s", tmp.empty() ? "Variant()" : tmp.c_str()); } if (!hasContext(ExistContext)) { cg_printf(", AccessFlags::Error"); // raise undefined index error sep = "_"; } } else if (lvalAt) { if (hasContext(AccessContext)) { // Dont copy the array if the element is an object, or // is referenced. // This is safe in AccessContext (the parent is an ArrayElement, // or an ObjectProperty) because applying [] to an object will // either invoke OffsetGet, or fatal, and modifications to a // referenced element would be reflected in all copies // of the array anyway. cg_printf(", AccessFlags::CheckExist"); sep = "_"; } } ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { if (lvalAt || rvalAt) { cg_printf("%sKey", sep); } else { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); } else { cg_printf(".lvalAt()"); } } }
/** * ArrayElementExpression comes from: * * reference_variable[|expr] * ->object_dim_list[|expr] * encaps T_VARIABLE[expr] * encaps ${T_STRING[expr]} */ TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { ConstructPtr self = shared_from_this(); if (m_offset && !(m_context & (UnsetContext | ExistContext | InvokeArgument | LValue | RefValue))) { setEffect(DiagnosticEffect); } if (m_context & (AssignmentLHS|OprLValue)) { clearEffect(AccessorEffect); } else if (m_context & (LValue | RefValue)) { setEffect(CreateEffect); } // handling $GLOBALS[...] if (m_variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable); if (var->getName() == "GLOBALS") { clearEffect(AccessorEffect); m_global = true; m_dynamicGlobal = true; getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); VariableTablePtr vars = ar->getVariables(); if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) { ScalarExpressionPtr offset = dynamic_pointer_cast<ScalarExpression>(m_offset); if (offset->isLiteralString()) { m_globalName = offset->getIdentifier(); if (!m_globalName.empty()) { m_dynamicGlobal = false; clearEffect(DiagnosticEffect); getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); TypePtr ret; if (coerce) { ret = vars->add(m_globalName, type, true, ar, self, ModifierExpressionPtr()); } else { int p; ret = vars->checkVariable(m_globalName, type, coerce, ar, self, p); } getScope()->getVariables()->addSuperGlobal(m_globalName); return ret; } } } else { vars->setAttribute(VariableTable::ContainsDynamicVariable); } if (hasContext(LValue) || hasContext(RefValue)) { ar->getVariables()->forceVariants(ar, VariableTable::AnyVars); ar->getVariables()-> setAttribute(VariableTable::ContainsLDynamicVariable); } if (m_offset) { m_offset->inferAndCheck(ar, Type::Primitive, false); } return m_implementedType = Type::Variant; // so not to lose values } } if ((hasContext(LValue) || hasContext(RefValue)) && !hasContext(UnsetContext)) { m_variable->setContext(LValue); } TypePtr varType; if (m_offset) { varType = m_variable->inferAndCheck(ar, coerce ? Type::AutoSequence : Type::Sequence, coerce); m_offset->inferAndCheck(ar, Type::Some, false); } else { if (hasContext(ExistContext) || hasContext(UnsetContext)) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::InvalidArrayElement, self); } } m_variable->inferAndCheck(ar, Type::Array, true); } if (varType && Type::SameType(varType, Type::String)) { clearEffect(AccessorEffect); m_implementedType.reset(); return Type::String; } if (varType && Type::SameType(varType, Type::Array)) { clearEffect(AccessorEffect); } TypePtr ret = propagateTypes(ar, Type::Variant); m_implementedType = Type::Variant; return ret; // so not to lose values }
void AssignmentExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { BlockScopePtr scope = ar->getScope(); bool ref = (m_ref && !m_value->is(Expression::KindOfNewObjectExpression)); bool setElement = false; // turning $a['elem'] = $b into $a.set('elem', $b); bool type_cast = false; bool setNull = false; TypePtr m_actualType; if (m_variable->is(Expression::KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(m_variable); m_actualType = m_variable->getActualType(); if (m_actualType && m_actualType->getKindOf() == Type::KindOfVariant && !ref) { //type_cast = true; } if (!exp->isSuperGlobal() && !exp->isDynamicGlobal()) { exp->getVariable()->outputCPP(cg, ar); if (exp->getOffset()) { cg.printf(".set("); exp->getOffset()->outputCPP(cg, ar); cg.printf(", ("); } else { cg.printf(".append(("); } if (type_cast) { m_actualType->outputCPPCast(cg, ar); cg.printf("("); } if (ref && m_value->isRefable()) cg.printf("ref("); m_value->outputCPP(cg, ar); if (ref && m_value->isRefable()) cg.printf(")"); if (type_cast) cg.printf(")"); cg.printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off); if (sc) { int64 hash = sc->getHash(); if (hash >= 0) { cg.printf(", 0x%016llXLL", hash); } else { cg.printf(", -1"); } if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg.printf(", true"); // skip toKey() at run time } } } } cg.printf(")"); setElement = true; } } if (m_variable->is(Expression::KindOfSimpleVariable) && m_value->is(Expression::KindOfConstantExpression)) { ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(m_value); if (exp->isNull()) setNull = true; } if (!setElement) { if (setNull) { cg.printf("setNull("); m_variable->outputCPP(cg, ar); } else { cg.printf("("); m_variable->outputCPP(cg, ar); cg.printf(" = "); if (type_cast) { m_actualType->outputCPPCast(cg, ar); cg.printf("("); } if (ref && m_value->isRefable()) cg.printf("ref("); m_value->outputCPP(cg, ar); if (ref && m_value->isRefable()) cg.printf(")"); if (type_cast) cg.printf(")"); } cg.printf(")"); } }
void SimpleFunctionCall::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { bool linemap = outputLineMap(cg, ar, true); if (!m_lambda.empty()) { cg.printf("\"%s\"", m_lambda.c_str()); if (linemap) cg.printf(")"); return; } if (m_className.empty()) { if (m_type == DefineFunction && m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); ExpressionPtr value = (*m_params)[1]; if (varName.empty()) { cg.printf("throw_fatal(\"bad define\")"); } else if (m_dynamicConstant) { cg.printf("g->declareConstant(\"%s\", g->%s%s, ", varName.c_str(), Option::ConstantPrefix, varName.c_str()); value->outputCPP(cg, ar); cg.printf(")"); } else { bool needAssignment = true; bool isSystem = ar->getConstants()->isSystem(varName); if (isSystem || ((!ar->isConstantRedeclared(varName)) && value->isScalar())) { needAssignment = false; } if (needAssignment) { cg.printf("%s%s = ", Option::ConstantPrefix, varName.c_str()); value->outputCPP(cg, ar); } } } else { cg.printf("throw_fatal(\"bad define\")"); } if (linemap) cg.printf(")"); return; } if (m_name == "func_num_args") { cg.printf("num_args"); if (linemap) cg.printf(")"); return; } switch (m_type) { case VariableArgumentFunction: { FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(ar->getScope()); if (func) { cg.printf("%s(", m_name.c_str()); func->outputCPPParamsCall(cg, ar, true); if (m_params) { cg.printf(","); m_params->outputCPP(cg, ar); } cg.printf(")"); if (linemap) cg.printf(")"); return; } } break; case FunctionExistsFunction: case ClassExistsFunction: case InterfaceExistsFunction: { bool literalString = false; string symbol; if (m_params && m_params->getCount() == 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { literalString = true; symbol = name->getLiteralString(); } } } if (literalString) { switch (m_type) { case FunctionExistsFunction: { const std::string &lname = Util::toLower(symbol); bool dynInvoke = Option::DynamicInvokeFunctions.find(lname) != Option::DynamicInvokeFunctions.end(); if (!dynInvoke) { FunctionScopePtr func = ar->findFunction(lname); if (func) { if (!func->isDynamic()) { if (func->isRedeclaring()) { const char *name = func->getName().c_str(); cg.printf("(%s->%s%s != invoke_failed_%s)", cg.getGlobals(ar), Option::InvokePrefix, name, name); break; } cg.printf("true"); break; } } else { cg.printf("false"); break; } } cg.printf("f_function_exists(\"%s\")", lname.c_str()); } break; case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && !cls->isInterface()) { const char *name = cls->getName().c_str(); cg.printf("f_class_exists(\"%s\")", name); } else { cg.printf("false"); } } break; case InterfaceExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isInterface()) { const char *name = cls->getName().c_str(); cg.printf("f_interface_exists(\"%s\")", name); } else { cg.printf("false"); } } break; default: break; } if (linemap) cg.printf(")"); return; } } break; case GetDefinedVarsFunction: cg.printf("get_defined_vars(variables)"); if (linemap) cg.printf(")"); return; default: break; } } outputCPPParamOrderControlled(cg, ar); if (linemap) cg.printf(")"); }
/** * ArrayElementExpression comes from: * * reference_variable[|expr] * ->object_dim_list[|expr] * encaps T_VARIABLE[expr] * encaps ${T_STRING[expr]} */ TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { ConstructPtr self = shared_from_this(); // handling $GLOBALS[...] if (m_variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable); if (var->getName() == "GLOBALS") { clearEffect(AccessorEffect); m_global = true; m_dynamicGlobal = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); VariableTablePtr vars = ar->getVariables(); if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) { ScalarExpressionPtr offset = dynamic_pointer_cast<ScalarExpression>(m_offset); if (offset->isLiteralString()) { m_globalName = offset->getIdentifier(); if (!m_globalName.empty()) { m_dynamicGlobal = false; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); TypePtr ret; ConstructPtr decl = vars->getDeclaration(m_globalName); if (decl) { ar->getDependencyGraph()-> add(DependencyGraph::KindOfGlobalVariable, ar->getName(), m_globalName, self, m_globalName, decl); } if (coerce) { ret = vars->add(m_globalName, type, true, ar, self, ModifierExpressionPtr()); } else { int p; ret = vars->checkVariable(m_globalName, type, coerce, ar, self, p); } ar->getScope()->getVariables()->addSuperGlobal(m_globalName); return ret; } } } else { vars->setAttribute(VariableTable::ContainsDynamicVariable); } if (hasContext(LValue) || hasContext(RefValue)) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseLDynamicVariable, self); } ar->getVariables()->forceVariants(ar); ar->getVariables()-> setAttribute(VariableTable::ContainsLDynamicVariable); } else { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseRDynamicVariable, self); } } if (m_offset) { m_offset->inferAndCheck(ar, NEW_TYPE(Primitive), false); } return m_implementedType = Type::Variant; // so not to lose values } } if ((hasContext(LValue) || hasContext(RefValue)) && !hasContext(UnsetContext)) { m_variable->setContext(LValue); } TypePtr varType; if (m_offset) { varType = m_variable->inferAndCheck(ar, NEW_TYPE(Sequence), false); m_offset->inferAndCheck(ar, NEW_TYPE(Some), false); } else { if (hasContext(ExistContext) || hasContext(UnsetContext)) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::InvalidArrayElement, self); } } m_variable->inferAndCheck(ar, Type::Array, true); } if (varType && Type::SameType(varType, Type::String)) { clearEffect(AccessorEffect); m_implementedType.reset(); return Type::String; } if (varType && Type::SameType(varType, Type::Array)) { clearEffect(AccessorEffect); } if (hasContext(LValue) || hasContext(RefValue)) setEffect(CreateEffect); TypePtr ret = propagateTypes(ar, Type::Variant); m_implementedType = Type::Variant; return ret; // so not to lose values }
bool CaseStatement::isLiteralString() const { if (!m_condition->is(Expression::KindOfScalarExpression)) return false; ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_condition); return exp->isLiteralString(); }
void AssignmentExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { BlockScopePtr scope = ar->getScope(); bool ref = (m_ref && m_value->isRefable()); bool setNull = false; bool arrayLike = false; if (m_variable->is(Expression::KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(m_variable); if (!exp->isSuperGlobal() && !exp->isDynamicGlobal()) { exp->getVariable()->outputCPP(cg, ar); if (exp->getOffset()) { cg_printf(".set("); exp->getOffset()->outputCPP(cg, ar); cg_printf(", ("); } else { cg_printf(".append(("); } wrapValue(cg, ar, m_value, ref, true); cg_printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off); if (sc) { if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); return; } } else if (m_variable->is(Expression::KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast<ObjectPropertyExpression>(m_variable)); if (!var->isValid()) { var->outputCPPObject(cg, ar); cg_printf("o_set("); var->outputCPPProperty(cg, ar); cg_printf(", %s", ref ? "ref(" : ""); m_value->outputCPP(cg, ar); cg_printf("%s, %s)", ref ? ")" : "", ar->getClassScope() ? "s_class_name" : "empty_string"); return; } } else if (m_variable->is(Expression::KindOfSimpleVariable) && m_value->is(Expression::KindOfConstantExpression)) { ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(m_value); if (exp->isNull()) setNull = true; } bool wrapped = true; if (setNull) { cg_printf("setNull("); m_variable->outputCPP(cg, ar); } else { if ((wrapped = !isUnused())) { cg_printf("("); } m_variable->outputCPP(cg, ar); cg_printf(" = "); wrapValue(cg, ar, m_value, ref, arrayLike); } if (wrapped) { cg_printf(")"); } }
void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = getScope()->getVariables(); string name = variables->getGlobalVariableName(ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("((LVariableTable *)g)->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { TypePtr type = m_variable->getType(); if (hasContext(UnsetContext)) { cg_printf("unsetLval("); m_variable->outputCPP(cg, ar); cg_printf(", "); } else { if (m_variable->is(Expression::KindOfScalarExpression) || (type && (type->isInteger() || type->is(Type::KindOfDouble) || type->is(Type::KindOfObject) || type->is(Type::KindOfBoolean)))) { cg_printf(type && type->is(Type::KindOfString) ? "((String)" : "((Variant)"); m_variable->outputCPP(cg, ar); cg_printf(")"); } else { m_variable->outputCPP(cg, ar); } } if (m_offset) { bool lvalAt = false; bool rvalAt = false; bool byRef = false; bool arrRef = false; const char *sep = ", AccessFlags::"; bool isArrayType = type && type->is(Type::KindOfArray); bool isStringType = type && type->is(Type::KindOfString); bool isRealChainRoot = isChainRoot() && hasCPPCseTemp(); TypePtr t; bool hasCseStore = isRealChainRoot && GetCseTempInfo( ar, static_pointer_cast<Expression>(shared_from_this()), t); if (hasContext(UnsetContext)) { // do nothing } else if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) { ASSERT(!isRealChainRoot); // TODO: handle this case cg_printf(".argvalAt(cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum); } else if (m_context & (LValue|RefValue|DeepReference)) { // if we see an array access element in LValue context, the // type inference pass will never infer its type to be a string ASSERT(!isStringType); if (isRealChainRoot && !isArrayType) { // chain roots for non array types (variants) should call // lvalRef() cg_printf(".lvalRef("); } else { cg_printf(".lvalAt("); } lvalAt = true; } else { byRef = ((m_context & AccessContext) || isRealChainRoot) && !isStringType; arrRef = byRef && isArrayType; cg_printf(".rval%s%s(", arrRef || !byRef ? "At" : "", byRef ? "Ref" : ""); rvalAt = true; } m_offset->outputCPP(cg, ar); if (!isStringType) { if (rvalAt) { if (byRef && !arrRef) { string tmp; if (hasCseStore) { tmp = string(Option::CseTempStoragePrefix) + m_cppCseTemp; } else { tmp = cg.getReferenceTemp(); } cg_printf(", %s", tmp.empty() ? "Variant()" : tmp.c_str()); } if (!hasContext(ExistContext)) { cg_printf(", AccessFlags::Error"); // raise undefined index error sep = "_"; } } else if (lvalAt) { if (hasCseStore && !isArrayType) { cg_printf(", %s%s", Option::CseTempStoragePrefix, m_cppCseTemp.c_str()); } if (hasContext(AccessContext)) { // Dont copy the array if the element is an object, or // is referenced. // This is safe in AccessContext (the parent is an ArrayElement, // or an ObjectProperty) because applying [] to an object will // either invoke OffsetGet, or fatal, and modifications to a // referenced element would be reflected in all copies // of the array anyway. cg_printf(", AccessFlags::CheckExist"); sep = "_"; } } ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { if (lvalAt || rvalAt) { cg_printf("%sKey", sep); } else { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); } else { cg_printf(".lvalAt()"); } } }
bool AssignmentExpression::SpecialAssignment(CodeGenerator &cg, AnalysisResultPtr ar, ExpressionPtr lval, ExpressionPtr rval, const char *rvalStr, bool ref) { if (lval->is(KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(lval); if (!exp->isSuperGlobal() && !exp->isDynamicGlobal()) { exp->getVariable()->outputCPP(cg, ar); if (exp->getOffset()) { cg_printf(".set("); exp->getOffset()->outputCPP(cg, ar); cg_printf(", ("); } else { cg_printf(".append(("); } if (rval) { wrapValue(cg, ar, rval, ref, (exp->getVariable()->is(KindOfArrayElementExpression) || exp->getVariable()->is(KindOfObjectPropertyExpression)) && (exp->getVariable()->getContainedEffects() && (CreateEffect|AccessorEffect))); } else { cg_printf(ref ? "ref(%s)" : "%s", rvalStr); } cg_printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off); if (sc) { if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); return true; } } else if (lval->is(KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast<ObjectPropertyExpression>(lval)); if (!var->isValid()) { bool nonPrivate = var->isNonPrivate(ar); var->outputCPPObject(cg, ar); if (nonPrivate) { cg_printf("o_setPublic("); } else { cg_printf("o_set("); } var->outputCPPProperty(cg, ar); cg_printf(", %s", ref ? "ref(" : ""); if (rval) { rval->outputCPP(cg, ar); } else { cg_printf(ref ? "ref(%s)" : "%s", rvalStr); } if (nonPrivate) { cg_printf("%s)", ref ? ")" : ""); } else { cg_printf("%s%s)", ref ? ")" : "", lval->originalClassName(cg, true).c_str()); } return true; } } return false; }
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) { if (m_className.empty()) { addUserFunction(ar, m_name); } else if (m_className != "parent") { addUserClass(ar, m_className); } else { m_parentClass = true; } if (ar->getPhase() == AnalysisResult::AnalyzeInclude) { CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude); ConstructPtr self = shared_from_this(); // We need to know the name of the constant so that we can associate it // with this file before we do type inference. if (m_className.empty() && m_type == DefineFunction) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ar->getFileScope()->declareConstant(ar, varName); } } // handling define("CONSTANT", ...); if (m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { constants->add(varName, NEW_TYPE(Some), value, ar, self); if (name->hasHphpNote("Dynamic")) { constants->setDynamic(ar, varName); } } } } } } if (m_type == UnserializeFunction) { ar->forceClassVariants(); } } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { // Look up the corresponding FunctionScope and ClassScope // for this function call { FunctionScopePtr func; ClassScopePtr cls; if (m_className.empty()) { func = ar->findFunction(m_name); } else { cls = ar->resolveClass(m_className); if (cls) { if (m_name == "__construct") { func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } } } if (func && !func->isRedeclaring()) { if (m_funcScope != func) { m_funcScope = func; Construct::recomputeEffects(); } } if (cls && !cls->isRedeclaring()) m_classScope = cls; } // check for dynamic constant and volatile function/class if (m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() >= 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); if (!constants->isPresent(symbol)) { // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); if (block) { // found the constant constants = block->getConstants(); // set to be dynamic constants->setDynamic(ar, symbol); } } break; } case FunctionExistsFunction: { FunctionScopePtr func = ar->findFunction(Util::toLower(symbol)); if (func && func->isUserFunction()) { func->setVolatile(); } break; } case InterfaceExistsFunction: case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isUserClass()) { cls->setVolatile(); } break; } default: ASSERT(false); } } } } } if (m_params) { if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (m_funcScope) { ExpressionList ¶ms = *m_params; int mpc = m_funcScope->getMaxParamCount(); for (int i = params.getCount(); i--; ) { ExpressionPtr p = params[i]; if (i < mpc ? m_funcScope->isRefParam(i) : m_funcScope->isReferenceVariableArgument()) { p->setContext(Expression::RefValue); } else if (!(p->getContext() & Expression::RefParameter)) { p->clearContext(Expression::RefValue); } } } else { FunctionScopePtr func = ar->findFunction(m_name); if (func && func->isRedeclaring()) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); if (info) { for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } } else { m_params->markParams(false); } } } m_params->analyzeProgram(ar); } }
void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = ar->getScope()->getVariables(); string name = variables->getGlobalVariableName(cg, ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("get_variable_table()->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { TypePtr type = m_variable->getActualType(); if (hasContext(UnsetContext)) { cg_printf("unsetLval("); m_variable->outputCPP(cg, ar); cg_printf(", "); } else { if (m_variable->is(Expression::KindOfScalarExpression) || (type && (type->isInteger() || type->is(Type::KindOfDouble) || type->is(Type::KindOfObject) || type->is(Type::KindOfBoolean)))) { cg_printf(type && type->is(Type::KindOfString) ? "((String)" : "((Variant)"); m_variable->outputCPP(cg, ar); cg_printf(")"); } else { m_variable->outputCPP(cg, ar); } } if (m_offset) { bool lvalAt = false; bool rvalAt = false; if (hasContext(UnsetContext)) { // do nothing } else if (m_context & InvokeArgument) { cg_printf(".refvalAt("); } else if (m_context & (LValue|RefValue)) { cg_printf(".lvalAt("); lvalAt = true; } else { cg_printf(".rvalAt("); rvalAt = true; } m_offset->outputCPP(cg, ar); if (!type || !type->is(Type::KindOfString)) { bool prehashed = false; ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (sc) { int64 hash = sc->getHash(); if (hash >= 0) { cg_printf(", 0x%016llXLL", hash); prehashed = true; } } if (!prehashed) { cg_printf(", -1"); } if (rvalAt) { if (!hasContext(ExistContext)) { cg_printf(", true"); // raise undefined index error } else { cg_printf(", false"); } } else if (lvalAt) { if (hasContext(ObjectContext)) { // object target might not trigger an array copy cg_printf(", true"); } else { cg_printf(", false"); } } if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } cg_printf(")"); } else { cg_printf(".lvalAt()"); } } }
ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) { ar->preOptimize(m_nameExp); ar->preOptimize(m_params); if (ar->getPhase() != AnalysisResult::SecondPreOptimize) { return ExpressionPtr(); } // optimize away various "exists" functions, this may trigger // dead code elimination and improve type-inference. if (m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() == 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); // system constant if (constants->isPresent(symbol)) { return CONSTANT("true"); } // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); // not found (i.e., undefined) if (!block) { if (symbol.find("::") == std::string::npos) { return CONSTANT("false"); } else { // e.g., defined("self::ZERO") return ExpressionPtr(); } } constants = block->getConstants(); // already set to be dynamic if (constants->isDynamic(symbol)) return ExpressionPtr(); ConstructPtr decl = constants->getValue(symbol); ExpressionPtr constValue = dynamic_pointer_cast<Expression>(decl); if (constValue->isScalar()) { return CONSTANT("true"); } else { return ExpressionPtr(); } break; } case FunctionExistsFunction: { const std::string &lname = Util::toLower(symbol); if (Option::DynamicInvokeFunctions.find(lname) == Option::DynamicInvokeFunctions.end()) { FunctionScopePtr func = ar->findFunction(lname); if (!func) { return CONSTANT("false"); } else if (!func->isVolatile()) { return CONSTANT("true"); } } break; } case InterfaceExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || !cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } default: ASSERT(false); } } } } return ExpressionPtr(); }