void IncludeExpression::analyzeInclude(AnalysisResultPtr ar, const std::string &include) { ASSERT(ar->getPhase() == AnalysisResult::AnalyzeInclude); ConstructPtr self = shared_from_this(); FileScopePtr file = ar->findFileScope(include, true); if (!file) { ar->getCodeError()->record(self, CodeError::PHPIncludeFileNotFound, self); return; } if (include.find("compiler/") != 0 || include.find("/compiler/") == string::npos) { ar->getCodeError()->record(self, CodeError::PHPIncludeFileNotInLib, self, ConstructPtr(), include.c_str()); } if (!isFileLevel()) { // Not unsupported but potentially bad ar->getCodeError()->record(self, CodeError::UseDynamicInclude, self); } ar->getDependencyGraph()->add (DependencyGraph::KindOfProgramMaxInclude, ar->getName(), file->getName(), StatementPtr()); ar->getDependencyGraph()->addParent (DependencyGraph::KindOfProgramMinInclude, ar->getName(), file->getName(), StatementPtr()); FunctionScopePtr func = ar->getFunctionScope(); ar->getFileScope()->addIncludeDependency(ar, m_include, func && func->isInlined()); }
void SimpleFunctionCall::onParse(AnalysisResultPtr ar) { if (m_class) return; FileScopePtr fs = ar->getFileScope(); ConstructPtr self = shared_from_this(); if (m_className.empty()) { CodeErrorPtr codeError = ar->getCodeError(); switch (m_type) { case CreateFunction: if (m_params->getCount() == 2 && (*m_params)[0]->isLiteralString() && (*m_params)[1]->isLiteralString()) { FunctionScopePtr func = ar->getFunctionScope(); if (func) func->disableInline(); string params = (*m_params)[0]->getLiteralString(); string body = (*m_params)[1]->getLiteralString(); m_lambda = CodeGenerator::GetNewLambda(); string code = "function " + m_lambda + "(" + params + ") " "{" + body + "}"; ar->appendExtraCode(code); } break; case VariableArgumentFunction: ar->getFileScope()->setAttribute(FileScope::VariableArgument); break; case ExtractFunction: ar->getCodeError()->record(self, CodeError::UseExtract, self); ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable); ar->getFileScope()->setAttribute(FileScope::ContainsExtract); break; case CompactFunction: ar->getFileScope()->setAttribute(FileScope::ContainsDynamicVariable); ar->getFileScope()->setAttribute(FileScope::ContainsCompact); break; case ShellExecFunction: ar->getCodeError()->record(self, CodeError::UseShellExec, self); break; case GetDefinedVarsFunction: ar->getFileScope()->setAttribute(FileScope::ContainsGetDefinedVars); ar->getFileScope()->setAttribute(FileScope::ContainsCompact); break; default: CHECK_HOOK(onSimpleFunctionCallFuncType); break; } } string call = getText(); string name = m_name; if (!m_className.empty()) { name = m_className + "::" + name; } ar->getDependencyGraph()->add(DependencyGraph::KindOfFunctionCall, call, shared_from_this(), name); }
int CodeGenerator::createNewLocalId(AnalysisResultPtr ar) { FunctionScopePtr func = ar->getFunctionScope(); if (func) { return func->nextInlineIndex(); } FileScopePtr fs = ar->getFileScope(); if (fs) { return createNewId(fs->getName()); } return createNewId(""); }
void StatementList::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr func = ar->getFunctionScope(); bool inPseudoMain = func && func->inPseudoMain(); std::vector<bool> isDeclaration; if (inPseudoMain) { // We need these declarations to go first, because PHP allows top level // function and class declarations to appear after usage. for (unsigned int i = 0; i < m_stmts.size(); i++) { StatementPtr stmt = m_stmts[i]; bool isDecl = false; if (stmt->is(Statement::KindOfFunctionStatement)) { isDecl = true; } else if (stmt->is(Statement::KindOfClassStatement) || stmt->is(Statement::KindOfInterfaceStatement)) { ClassScopePtr cls = (dynamic_pointer_cast<InterfaceStatement>(stmt))->getClassScope(); isDecl = cls->isBaseClass() || !cls->isVolatile(); } if (isDecl) stmt->outputCPP(cg,ar); isDeclaration.push_back(isDecl); } } for (unsigned int i = 0; i < m_stmts.size(); i++) { StatementPtr stmt = m_stmts[i]; if (stmt->is(Statement::KindOfClassStatement)) { if (!inPseudoMain || !isDeclaration[i]) stmt->outputCPP(cg, ar); } else if (!(stmt->is(Statement::KindOfFunctionStatement) || stmt->is(Statement::KindOfInterfaceStatement)) || (!inPseudoMain || !isDeclaration[i])) { stmt->outputCPP(cg, ar); if (stmt->is(Statement::KindOfMethodStatement)) { MethodStatementPtr methodStmt = dynamic_pointer_cast<MethodStatement>(stmt); std::string methodName = methodStmt->getName(); if (methodName == "offsetget") { ClassScopePtr cls = ar->getClassScope(); std::string arrayAccess("arrayaccess"); if (cls->derivesFrom(ar, arrayAccess, false, false)) { FunctionScopePtr funcScope = methodStmt->getFunctionScope(); std::string name = funcScope->getName(); funcScope->setName("__offsetget_lval"); methodStmt->setName("__offsetget_lval"); methodStmt->outputCPP(cg, ar); funcScope->setName(name); methodStmt->setName("offsetget"); } } } } } }
TypePtr ParameterExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { ASSERT(type->is(Type::KindOfSome) || type->is(Type::KindOfAny)); TypePtr ret = getTypeSpec(ar, true); if (m_defaultValue && !m_ref) { ret = m_defaultValue->inferAndCheck(ar, ret, false); } // parameters are like variables, but we need to remember these are // parameters so when variable table is generated, they are not generated // as declared variables. VariableTablePtr variables = ar->getScope()->getVariables(); if (ar->isFirstPass()) { ret = variables->addParam(m_name, ret, ar, shared_from_this()); } else { // Functions that can be called dynamically have to have // variant parameters, even if they have a type hint if (ar->getFunctionScope()->isDynamic() || ar->getFunctionScope()->isRedeclaring() || ar->getFunctionScope()->isVirtual()) { variables->forceVariant(ar, m_name, VariableTable::AnyVars); } int p; ret = variables->checkVariable(m_name, ret, true, ar, shared_from_this(), p); if (ar->isSecondPass() && ret->is(Type::KindOfSome)) { // This is probably too conservative. The problem is that // a function never called will have parameter types of Any. // Functions that it calls won't be able to accept variant unless // it is forced here. variables->forceVariant(ar, m_name, VariableTable::AnyVars); ret = Type::Variant; } else if (ar->getPhase() == AnalysisResult::LastInference && !ret->getName().empty()) { addUserClass(ar, ret->getName(), true); } } return ret; }
void ParameterExpression::analyzeProgram(AnalysisResultPtr ar) { if (!m_type.empty()) addUserClass(ar, m_type); if (m_defaultValue) m_defaultValue->analyzeProgram(ar); if (ar->isFirstPass()) { // Have to use non const ref params for magic methods FunctionScopePtr fs = ar->getFunctionScope(); if (fs->isMagicMethod() || fs->getName() == "offsetget") { fs->getVariables()->addLvalParam(m_name); } } }
void InterfaceStatement::checkVolatile(AnalysisResultPtr ar) { ClassScopePtr classScope = m_classScope.lock(); // redeclared classes/interfaces are automatically volatile if (!classScope->isVolatile()) { if (checkVolatileBases(ar)) { // if any base is volatile, the class is volatile classScope->setVolatile(); } } if (classScope->isVolatile()) { ar->getFunctionScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } }
void GlobalStatement::inferTypes(AnalysisResultPtr ar) { BlockScopePtr scope = ar->getScope(); scope->getVariables()->setAttribute(VariableTable::InsideGlobalStatement); for (int i = 0; i < m_exp->getCount(); i++) { ExpressionPtr exp = (*m_exp)[i]; if (exp->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp); VariableTablePtr variables = scope->getVariables(); const std::string &name = var->getName(); /* If we have already seen this variable in the current scope and it is not a global variable, record this variable as "redeclared" which will force Variant type. */ variables->checkRedeclared(name, KindOfGlobalStatement); /* If this is not a top-level global statement, the variable also needs to be Variant type. This should not be a common use case in php code. */ if (!isTopLevel()) { variables->addNestedGlobal(name); } var->setContext(Expression::Declaration); var->inferAndCheck(ar, NEW_TYPE(Any), true); if (variables->needLocalCopy(name)) { variables->forceVariant(ar, name); variables->setAttribute(VariableTable::NeedGlobalPointer); } ConstructPtr decl = ar->getVariables()->getDeclaration(var->getName()); if (decl) { ar->getDependencyGraph()->add(DependencyGraph::KindOfGlobalVariable, ar->getName(), var->getName(), var, var->getName(), decl); } } else { if (ar->isFirstPass()) { ar->getCodeError()->record(shared_from_this(), CodeError::UseDynamicGlobal, exp); } m_dynamicGlobal = true; } } FunctionScopePtr func = ar->getFunctionScope(); scope->getVariables()->clearAttribute(VariableTable::InsideGlobalStatement); }
void FunctionStatement::analyzeProgramImpl(AnalysisResultPtr ar) { // registering myself as a parent in dependency graph, so that // (1) we can tell orphaned parents // (2) overwrite non-master copy of function declarations if (ar->isFirstPass()) { if (m_loc) { ar->getDependencyGraph()->addParent(DependencyGraph::KindOfFunctionCall, "", m_name, shared_from_this()); } // else it's pseudoMain or artificial functions we added } FunctionScopePtr func = ar->getFunctionScope(); // containing function scope FunctionScopePtr fs = m_funcScope.lock(); // redeclared functions are automatically volatile if (func && fs->isVolatile()) { func->getVariables()->setAttribute(VariableTable::NeedGlobalPointer); } MethodStatement::analyzeProgramImpl(ar); }
void ClassStatement::analyzeProgram(AnalysisResultPtr ar) { vector<string> bases; if (!m_parent.empty()) bases.push_back(m_parent); if (m_base) m_base->getStrings(bases); for (unsigned int i = 0; i < bases.size(); i++) { string className = bases[i]; addUserClass(ar, bases[i]); } ClassScopePtr classScope = m_classScope.lock(); if (hasHphpNote("Volatile")) classScope->setVolatile(); FunctionScopePtr func = ar->getFunctionScope(); // redeclared classes are automatically volatile if (classScope->isVolatile()) { func->getVariables()->setAttribute(VariableTable::NeedGlobalPointer); } if (m_stmt) { ar->pushScope(classScope); m_stmt->analyzeProgram(ar); ar->popScope(); } DependencyGraphPtr dependencies = ar->getDependencyGraph(); for (unsigned int i = 0; i < bases.size(); i++) { ClassScopePtr cls = ar->findClass(bases[i]); if (cls) { if (dependencies->checkCircle(DependencyGraph::KindOfClassDerivation, m_originalName, cls->getOriginalName())) { ClassScopePtr classScope = m_classScope.lock(); ar->getCodeError()->record(CodeError::InvalidDerivation, shared_from_this(), ConstructPtr(), cls->getOriginalName()); m_parent = ""; m_base = ExpressionListPtr(); classScope->clearBases(); } else if (cls->isUserClass()) { dependencies->add(DependencyGraph::KindOfClassDerivation, ar->getName(), m_originalName, shared_from_this(), cls->getOriginalName(), cls->getStmt()); } } } }
void ObjectPropertyExpression::outputCPPUnset(CodeGenerator &cg, AnalysisResultPtr ar) { bool bThis = m_object->isThis(); if (bThis) { FunctionScopePtr func = ar->getFunctionScope(); if (func && func->isStatic()) { cg.printf("GET_THIS_ARROW()"); } } else { m_object->outputCPP(cg, ar); cg_printf("->"); } cg_printf("t___unset("); bool direct = m_property->isUnquotedScalar(); if (direct) { cg_printf("\""); } m_property->outputCPP(cg, ar); if (direct) { cg_printf("\""); } cg_printf(")"); }
void UnaryOpExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if ((m_op == T_INC || m_op == T_DEC) && outputCPPImplOpEqual(cg, ar)) { return; } if (m_op == T_ARRAY && (getContext() & (RefValue|LValue)) == 0 && !ar->getInsideScalarArray()) { int id = -1; int hash = -1; int index = -1; if (m_exp) { ExpressionListPtr pairs = dynamic_pointer_cast<ExpressionList>(m_exp); Variant v; if (pairs && pairs->isScalarArrayPairs() && pairs->getScalarValue(v)) { id = ar->registerScalarArray(m_exp, hash, index); } } else { id = ar->registerScalarArray(m_exp, hash, index); // empty array } if (id != -1) { ar->outputCPPScalarArrayId(cg, id, hash, index); return; } } if ((m_op == T_ISSET || m_op == T_EMPTY || m_op == T_UNSET) && m_exp) { if (m_exp->is(Expression::KindOfExpressionList)) { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (exps->getListKind() == ExpressionList::ListKindParam) { int count = exps->getCount(); if (count > 1) { cg_printf("("); } for (int i = 0; i < count; i++) { if (m_op == T_UNSET) { if (i > 0) cg_printf(", "); (*exps)[i]->outputCPPUnset(cg, ar); } else { if (i > 0) cg_printf(" && "); (*exps)[i]->outputCPPExistTest(cg, ar, m_op); } } if (exps->getCount() > 1) { cg_printf(")"); } return; } } if (m_op == T_UNSET) { m_exp->outputCPPUnset(cg, ar); } else { m_exp->outputCPPExistTest(cg, ar, m_op); } return; } if (m_front) { switch (m_op) { case T_CLONE: cg_printf("f_clone("); break; case T_INC: cg_printf("++"); break; case T_DEC: cg_printf("--"); break; case '+': cg_printf("+"); break; case '-': cg_printf("negate("); break; case '!': cg_printf("!("); break; case '~': cg_printf("~"); break; case '(': cg_printf("("); break; case T_INT_CAST: cg_printf("("); break; case T_DOUBLE_CAST: cg_printf("("); break; case T_STRING_CAST: cg_printf("("); break; case T_ARRAY_CAST: cg_printf("("); break; case T_OBJECT_CAST: cg_printf("("); break; case T_BOOL_CAST: cg_printf("("); break; case T_UNSET_CAST: if (m_exp->hasCPPTemp()) { cg_printf("(id("); } else { cg_printf("("); } break; case T_EXIT: cg_printf("f_exit("); break; case T_ARRAY: cg_printf("Array("); break; case T_PRINT: cg_printf("print("); break; case T_EVAL: if (Option::EnableEval > Option::NoEval) { bool instance; if (ar->getClassScope()) { FunctionScopePtr fs = ar->getFunctionScope(); instance = fs && !fs->isStatic(); } else { instance = false; } cg_printf("eval(%s, Object(%s), ", ar->getScope()->inPseudoMain() ? "get_variable_table()" : "variables", instance ? "this" : ""); } else { cg_printf("f_eval("); } break; case '@': if (m_silencer >= 0) { cg_printf("(%s%d.enable(),%s%d.disable(", Option::SilencerPrefix, m_silencer, Option::SilencerPrefix, m_silencer); } break; case T_FILE: cg_printf("get_source_filename(\"%s\")", getLocation()->file); break; break; default: ASSERT(false); } } if (m_exp) { switch (m_op) { case '+': case '-': if (m_exp->getActualType() && (m_exp->getActualType()->is(Type::KindOfString) || m_exp->getActualType()->is(Type::KindOfArray))) { cg_printf("(Variant)("); m_exp->outputCPP(cg, ar); cg_printf(")"); } else { m_exp->outputCPP(cg, ar); } break; case '@': // Void needs to return something to silenceDec if (!m_exp->hasCPPTemp() && !m_exp->getActualType()) { cg_printf("("); m_exp->outputCPP(cg, ar); cg_printf(",null)"); } else { m_exp->outputCPP(cg, ar); } break; default: m_exp->outputCPP(cg, ar); break; } } if (m_front) { switch (m_op) { case T_ARRAY: { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (!exps) { cg_printf("ArrayData::Create()"); } cg_printf(")"); } break; case T_UNSET_CAST: if (m_exp->hasCPPTemp()) { cg_printf("),null"); } else { cg_printf(",null"); } case T_CLONE: case '!': case '(': case '-': case T_INT_CAST: case T_DOUBLE_CAST: case T_STRING_CAST: case T_ARRAY_CAST: case T_OBJECT_CAST: case T_BOOL_CAST: case T_EXIT: case T_PRINT: case T_EVAL: case T_INCLUDE: case T_INCLUDE_ONCE: case T_REQUIRE: case T_REQUIRE_ONCE: cg_printf(")"); break; case '@': if (m_silencer >= 0) { cg_printf("))"); } break; default: break; } } else { switch (m_op) { case T_INC: cg_printf("++"); break; case T_DEC: cg_printf("--"); break; default: ASSERT(false); } } }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), false); if (!m_property->is(Expression::KindOfScalarExpression)) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicProperty, self); } m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); string name = exp->getString(); ASSERT(!name.empty()); m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(objectType->getName()); } else { // what ->property has told us cls = ar->findClass(name, AnalysisResult::PropertyName); if (cls) { objectType = m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()), false); } if ((m_context & LValue) && objectType && !objectType->is(Type::KindOfObject) && !objectType->is(Type::KindOfVariant) && !objectType->is(Type::KindOfSome) && !objectType->is(Type::KindOfAny)) { m_object->inferAndCheck(ar, NEW_TYPE(Object), true); } } if (!cls) { if (m_context & (LValue | RefValue)) { ar->forceClassVariants(name); } return Type::Variant; } const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" : hasContext(ExistContext) ? "__isset" : hasContext(UnsetContext) ? "__unset" : "__get"; if (!cls->implementsAccessor(ar, accessorName)) clearEffect(AccessorEffect); // resolved to this class int present = 0; if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = ar->getFunctionScope(); if (func->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } TypePtr ret; if (!cls->derivesFromRedeclaring()) { // Have to use dynamic. ret = cls->checkProperty(name, type, coerce, ar, self, present); // Private only valid if in the defining class if (present && (getOriginalScope(ar) == cls || !(present & VariableTable::VariablePrivate))) { m_valid = true; m_static = present & VariableTable::VariableStatic; if (m_static) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_class = cls; } } // get() will return Variant if (!m_valid || !m_object->getType()->isSpecificObject()) { m_actualType = Type::Variant; return m_actualType; } clearEffect(AccessorEffect); if (ar->getPhase() == AnalysisResult::LastInference) { if (!(m_context & ObjectContext)) { m_object->clearContext(Expression::LValue); } setContext(Expression::NoLValueWrapper); } return ret; }
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg, AnalysisResultPtr ar, bool directVariant, int doExist) { bool bThis = m_object->isThis(); bool useGetThis = false; FunctionScopePtr funcScope = ar->getFunctionScope(); if (bThis) { if (funcScope && funcScope->isStatic()) { cg_printf("GET_THIS_ARROW()"); } else { // in order for __set() and __get() to be called useGetThis = true; } } const char *op = "."; string func = Option::ObjectPrefix; const char *error = ", true"; ClassScopePtr cls = ar->getClassScope(); const char *context = ""; if (cg.getOutput() != CodeGenerator::SystemCPP) { if (cls) { context = ", s_class_name"; } else if (funcScope && !funcScope->inPseudoMain()) { context = ", empty_string"; } } if (doExist) { func = doExist > 0 ? "doIsSet" : "doEmpty"; error = ""; } else { if (bThis && funcScope && funcScope->isStatic()) { func = Option::ObjectStaticPrefix; error = ""; context = ""; } else if (m_context & ExistContext) { error = ", false"; } if (m_context & (LValue | RefValue | UnsetContext)) { func += "lval"; error = ""; } else { func += "get"; } } if (m_property->getKindOf() == Expression::KindOfScalarExpression) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(m_property); const char *propName = name->getString().c_str(); if (m_valid && m_object->getType()->isSpecificObject()) { if (m_static) { if (!bThis) { ASSERT(m_class); if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty("); cg_printf("g->%s%s%s%s", Option::StaticPropertyPrefix, m_class->getName().c_str(), Option::IdPrefix.c_str(), propName); if (doExist) cg_printf(")"); } else { // if $val is a class static variable (static $val), then $val // cannot be declared as a class variable (var $val), $this->val // refers to a non-static class variable and has to use get/lval. if (useGetThis) cg_printf("GET_THIS_DOT()"); cg_printf("%s(", func.c_str()); cg_printString(propName, ar); cg_printf("%s%s)", error, context); } } else { if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty("); if (!bThis) { ASSERT(!directVariant); m_object->outputCPP(cg, ar); cg_printf("->"); } cg_printf("%s%s", Option::PropertyPrefix, propName); if (doExist) cg_printf(")"); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg_printf(op); } else { if (useGetThis) cg_printf("GET_THIS_DOT()"); } cg_printf("%s(", func.c_str()); cg_printString(propName, ar); cg_printf("%s%s)", error, context); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg_printf(op); } else { if (useGetThis) cg_printf("GET_THIS_DOT()"); } cg_printf("%s(", func.c_str()); m_property->outputCPP(cg, ar); cg_printf("%s%s)", error, context); } }
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg, AnalysisResultPtr ar, bool directVariant) { bool bThis = m_object->isThis(); const char *op = "."; string func = Option::ObjectPrefix; const char *error = ""; if (m_context & ExistContext) { error = ", false"; // suppress non-object property error } if (bThis && ar->getFunctionScope()->isStatic()) { func = Option::ObjectStaticPrefix; error = ""; } if (m_context & (LValue | RefValue)) { func += "lval"; error = ""; } else { func += "get"; } if (m_property->getKindOf() == Expression::KindOfScalarExpression) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(m_property); const char *propName = name->getString().c_str(); if (m_valid && m_object->getType()->isSpecificObject()) { if (m_static) { if (!bThis) { ASSERT(m_class); cg.printf("g->%s%s%s%s", Option::StaticPropertyPrefix, m_class->getName().c_str(), Option::IdPrefix.c_str(), propName); } else { // if $val is a class static variable (static $val), then $val // cannot be declared as a class variable (var $val), $this->val // refers to a non-static class variable and has to use get/lval. uint64 hash = hash_string(propName); cg.printf("%s(\"%s\", 0x%016llXLL)", func.c_str(), propName, hash); } } else { if (!bThis) { ASSERT(!directVariant); cg.printf("AS_CLASS("); m_object->outputCPP(cg, ar); cg.printf(",%s%s)->", Option::ClassPrefix, m_object->getType()->getName().c_str()); } cg.printf("%s%s", Option::PropertyPrefix, propName); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg.printf(op); } uint64 hash = hash_string(propName); cg.printf("%s(\"%s\", 0x%016llXLL%s)", func.c_str(), propName, hash, error); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg.printf(op); } cg.printf("%s(", func.c_str()); m_property->outputCPP(cg, ar); cg.printf(", -1LL%s)", error); } }
void ListAssignment::analyzeProgram(AnalysisResultPtr ar) { if (m_variables) m_variables->analyzeProgram(ar); if (m_array) m_array->analyzeProgram(ar); FunctionScopePtr func = ar->getFunctionScope(); if (func) func->disableInline(); }
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); // handling define("CONSTANT", ...); 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(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; TypePtr varType = value->inferAndCheck(ar, NEW_TYPE(Some), false); ar->getDependencyGraph()-> addParent(DependencyGraph::KindOfConstant, ar->getName(), varName, self); ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { if (value && !value->isScalar()) { constants->setDynamic(ar, varName); varType = Type::Variant; } if (constants->isDynamic(varName)) { m_dynamicConstant = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } else { constants->setType(ar, varName, varType, true); } // in case the old 'value' has been optimized constants->setValue(ar, varName, value); } return checkTypesImpl(ar, type, Type::Boolean, coerce); } } if (varName.empty() && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::BadDefine, self); } } else if (m_type == ExtractFunction) { ar->getScope()->getVariables()->forceVariants(ar); } } FunctionScopePtr func; // avoid raising both MissingObjectContext and UnknownFunction bool errorFlagged = false; if (m_className.empty()) { func = ar->findFunction(m_name); } else { ClassScopePtr cls = ar->resolveClass(m_className); if (cls && cls->isVolatile()) { ar->getScope()->getVariables() ->setAttribute(VariableTable::NeedGlobalPointer); } if (!cls || cls->isRedeclaring()) { if (cls) { m_redeclaredClass = true; } if (!cls && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownClass, self); } if (m_params) { m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, Type::Variant, coerce); } m_derivedFromRedeclaring = cls->derivesFromRedeclaring(); m_validClass = true; if (m_name == "__construct") { // if the class is known, php will try to identify class-name ctor func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } if (func && !func->isStatic()) { ClassScopePtr clsThis = ar->getClassScope(); FunctionScopePtr funcThis = ar->getFunctionScope(); if (!clsThis || (clsThis->getName() != m_className && !clsThis->derivesFrom(ar, m_className)) || funcThis->isStatic()) { // set the method static to avoid "unknown method" runtime exception if (Option::StaticMethodAutoFix && !func->containsThis()) { func->setStatic(); } if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); errorFlagged = true; } func.reset(); } } } if (!func || func->isRedeclaring()) { if (func) { m_redeclared = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } if (!func && !errorFlagged && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownFunction, self); } if (m_params) { if (func) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); ASSERT(info); for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, Type::Variant, coerce); } m_builtinFunction = !func->isUserFunction(); if (m_redeclared) { if (m_params) { m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, type, coerce); } CHECK_HOOK(beforeSimpleFunctionCallCheck); m_valid = true; type = checkParamsAndReturn(ar, type, coerce, func); if (!m_valid && m_params) { m_params->markParams(false); } CHECK_HOOK(afterSimpleFunctionCallCheck); return type; }
void ObjectMethodExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { bool isThis = m_object->isThis(); if (isThis && ar->getFunctionScope()->isStatic()) { bool linemap = outputLineMap(cg, ar, true); cg_printf("throw_fatal(\"Using $this when not in object context\")"); if (linemap) cg_printf(")"); return; } bool fewParams = canInvokeFewArgs(); bool linemap = outputLineMap(cg, ar, true); if (!isThis) { if (directVariantProxy(ar) && !m_object->hasCPPTemp()) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); if (m_bindClass) { cg_printf(". BIND_CLASS_DOT "); } else { cg_printf("."); } } else { string objType; TypePtr type = m_object->getType(); if (type->isSpecificObject() && !m_name.empty() && m_valid) { objType = type->getName(); } else { objType = "ObjectData"; } m_object->outputCPP(cg, ar); if (m_bindClass) { cg_printf("-> BIND_CLASS_ARROW(%s) ", objType.c_str()); } else { cg_printf("->"); } } } else if (m_bindClass && m_classScope) { cg_printf(" BIND_CLASS_ARROW(%s) ", m_classScope->getId().c_str()); } if (!m_name.empty()) { if (m_valid && m_object->getType()->isSpecificObject()) { cg_printf("%s%s(", Option::MethodPrefix, m_name.c_str()); FunctionScope::outputCPPArguments(m_params, cg, ar, m_extraArg, m_variableArgument, m_argArrayId); cg_printf(")"); } else { if (fewParams) { uint64 hash = hash_string_i(m_name.data(), m_name.size()); cg_printf("%s%sinvoke_few_args(\"%s\"", Option::ObjectPrefix, isThis ? "root_" : "", m_origName.c_str()); cg_printf(", 0x%016llXLL, ", hash); if (m_params && m_params->getCount()) { cg_printf("%d, ", m_params->getCount()); FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false); } else { cg_printf("0"); } cg_printf(")"); } else { cg_printf("%s%sinvoke(\"%s\"", Option::ObjectPrefix, isThis ? "root_" : "", m_origName.c_str()); cg_printf(", "); if (m_params && m_params->getCount()) { FunctionScope::outputCPPArguments(m_params, cg, ar, -1, false); } else { cg_printf("Array()"); } uint64 hash = hash_string_i(m_name.data(), m_name.size()); cg_printf(", 0x%016llXLL)", hash); } } } else { if (fewParams) { cg_printf("%s%sinvoke_few_args(", Option::ObjectPrefix, isThis ? "root_" : ""); m_nameExp->outputCPP(cg, ar); cg_printf(", -1LL, "); if (m_params && m_params->getCount()) { cg_printf("%d, ", m_params->getCount()); FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false); } else { cg_printf("0"); } cg_printf(")"); } else { cg_printf("%s%sinvoke((", Option::ObjectPrefix, isThis ? "root_" : ""); m_nameExp->outputCPP(cg, ar); cg_printf(")"); cg_printf(", "); if (m_params && m_params->getCount()) { FunctionScope::outputCPPArguments(m_params, cg, ar, -1, false); } else { cg_printf("Array()"); } cg_printf(", -1LL)"); } } if (linemap) cg_printf(")"); }
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), true); m_valid = true; m_bindClass = true; if (m_name.empty()) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicMethod, self); } m_nameExp->inferAndCheck(ar, Type::String, false); setInvokeParams(ar); // we have to use a variant to hold dynamic value return checkTypesImpl(ar, type, Type::Variant, coerce); } ClassScopePtr cls = m_classScope; if (objectType && !objectType->getName().empty()) { cls = ar->findExactClass(objectType->getName()); } if (!cls) { if (ar->isFirstPass()) { // call resolveClass to mark functions as dynamic // but we cant do anything else with the result. resolveClass(ar, m_name); if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) { ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self); } } m_classScope.reset(); m_funcScope.reset(); setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } if (m_classScope != cls) { m_classScope = cls; m_funcScope.reset(); } FunctionScopePtr func = m_funcScope; if (!func) { func = cls->findFunction(ar, m_name, true, true); if (!func) { if (!cls->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) { if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) { // TODO: we could try to find out class derivation is present... ar->getCodeError()->record(self, CodeError::DerivedObjectMethod, self); // we have to make sure the method is in invoke list setDynamicByIdentifier(ar, m_name); } else { ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self); } } m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } m_funcScope = func; } bool valid = true; m_bindClass = func->isStatic(); // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr localfunc = ar->getFunctionScope(); if (localfunc->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } valid = false; } } // invoke() will return Variant if (func->isVirtual() || !m_object->getType()->isSpecificObject()) { valid = false; } if (!valid) { setInvokeParams(ar); checkTypesImpl(ar, type, Type::Variant, coerce); m_valid = false; // so we use invoke() syntax func->setDynamic(); return m_actualType; } return checkParamsAndReturn(ar, type, coerce, func); }