void BuiltinSymbols::ImportExtClasses(AnalysisResultPtr ar) { const ClassInfo::ClassMap &classes = ClassInfo::GetClassesMap(); for (auto it = classes.begin(); it != classes.end(); ++it) { ClassScopePtr cl = ImportClassScopePtr(ar, it->second); assert(!s_classes[cl->getName()]); s_classes[cl->getName()] = cl; } }
TypePtr Type::Union(AnalysisResultConstPtr ar, TypePtr type1, TypePtr type2) { if (SameType(type1, type2)) { return type1; } int resultKind = type1->m_kindOf | type2->m_kindOf; if (resultKind == KindOfObject) { std::string resultName(""); // if they're the same, or we don't know one's name, then use // the other if (type1->m_name == type2->m_name) { resultName = type1->m_name; } else if (type1->m_name.empty() || type2->m_name.empty()) { // resultName was initialized to "", so leave it as such; // we know it's an object but not what kind. } else { // take the superclass ClassScopePtr res = ClassScope::FindCommonParent(ar, type1->m_name, type2->m_name); if (res) resultName = res->getName(); } return TypePtr(Type::CreateObjectType(resultName)); } return GetType((KindOf)resultKind); }
ClassScopePtr ObjectMethodExpression::resolveClass(AnalysisResultPtr ar, string &name) { ClassScopePtr cls = ar->findClass(name, AnalysisResult::MethodName); if (cls) { addUserClass(ar, cls->getName()); return cls; } string construct("__construct"); cls = ar->findClass(construct, AnalysisResult::MethodName); if (cls && name == cls->getName()) { name = "__construct"; cls->setAttribute(ClassScope::ClassNameConstructor); return cls; } return ClassScopePtr(); }
bool FileScope::addClass(AnalysisResultConstPtr ar, ClassScopePtr classScope) { if (ar->declareClass(classScope)) { m_classes[classScope->getName()].push_back(classScope); return true; } return false; }
void MethodStatement::onParse(AnalysisResultPtr ar, BlockScopePtr scope) { ClassScopePtr classScope = dynamic_pointer_cast<ClassScope>(scope); FunctionScopePtr fs = getFunctionScope(); fs->setParamCounts(ar, -1, -1); classScope->addFunction(ar, fs); if (m_name == "__construct") { classScope->setAttribute(ClassScope::HasConstructor); } else if (m_name == "__destruct") { classScope->setAttribute(ClassScope::HasDestructor); } if (m_name == "__call") { classScope->setAttribute(ClassScope::HasUnknownMethodHandler); } else if (m_name == "__get") { classScope->setAttribute(ClassScope::HasUnknownPropGetter); } else if (m_name == "__set") { classScope->setAttribute(ClassScope::HasUnknownPropSetter); } else if (m_name == "__call") { classScope->setAttribute(ClassScope::HasUnknownMethodHandler); } else if (m_name == "__callstatic") { classScope->setAttribute(ClassScope::HasUnknownStaticMethodHandler); } m_className = classScope->getName(); m_originalClassName = classScope->getOriginalName(); }
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); if (!m_name.empty()) { ClassScopePtr cls = ar->resolveClass(m_name); if (cls) { m_name = cls->getName(); } if (!cls || cls->isRedeclaring()) { if (cls) { m_redeclared = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } if (!cls && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownClass, self); } if (m_params) m_params->inferAndCheck(ar, NEW_TYPE(Any), false); return NEW_TYPE(Object); } if (cls->isVolatile()) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_dynamic = cls->derivesFromRedeclaring(); m_validClass = true; FunctionScopePtr func = cls->findConstructor(ar, true); if (!func) { if (m_params) { if (!m_dynamic && m_params->getCount()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::BadConstructorCall, self); } m_params->setOutputCount(0); } m_params->inferAndCheck(ar, NEW_TYPE(Any), false); } } else { m_extraArg = func->inferParamTypes(ar, self, m_params, m_validClass); m_variableArgument = func->isVariableArgument(); } return Type::CreateObjectType(m_name); } else { ar->containsDynamicClass(); if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicClass, self); } if (m_params) { m_params->markParams(false); } } m_nameExp->inferAndCheck(ar, Type::String, false); if (m_params) m_params->inferAndCheck(ar, NEW_TYPE(Any), false); return Type::Variant;//NEW_TYPE(Object); }
bool AnalysisResult::checkClassPresent(ConstructPtr cs, const std::string &name) const { if (name == "self" || name == "parent") return true; std::string lowerName = toLower(name); if (ClassScopePtr currentCls = cs->getClassScope()) { if (lowerName == currentCls->getName() || currentCls->derivesFrom(shared_from_this(), lowerName, true, false)) { return true; } } if (FileScopePtr currentFile = cs->getFileScope()) { StatementList &stmts = *currentFile->getStmt(); for (int i = stmts.getCount(); i--; ) { StatementPtr s = stmts[i]; if (s && s->is(Statement::KindOfClassStatement)) { ClassScopePtr scope = static_pointer_cast<ClassStatement>(s)->getClassScope(); if (lowerName == scope->getName()) { return true; } if (scope->derivesFrom(shared_from_this(), lowerName, true, false)) { return true; } } } } return false; }
void ParameterExpression::fixupSelfAndParentTypehints(ClassScopePtr cls) { if (m_type == "self") { m_type = cls->getName(); } else if (m_type == "parent") { if (!cls->getOriginalParent().empty()) { m_type = toLower(cls->getOriginalParent()); } } }
ClassScopePtr AnalysisResult::findExactClass(ConstructPtr cs, const std::string &name) const { ClassScopePtr cls = findClass(name); if (!cls || !cls->isRedeclaring()) return cls; if (ClassScopePtr currentCls = cs->getClassScope()) { if (cls->getName() == currentCls->getName()) { return currentCls; } } return ClassScopePtr(); }
void FunctionScope::outputCPPCreateImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopePtr scope = ar->getClassScope(); string clsNameStr = scope->getId(cg); const char *clsName = clsNameStr.c_str(); const char *consName = scope->classNameCtor() ? scope->getName().c_str() : "__construct"; cg_printf("%s%s *%s%s::create(", Option::ClassPrefix, clsName, Option::ClassPrefix, clsName); outputCPPParamsImpl(cg, ar); cg_indentBegin(") {\n"); cg_printf("CountableHelper h(this);\n"); cg_printf("init();\n"); cg_printf("%s%s(", Option::MethodPrefix, consName); outputCPPParamsCall(cg, ar, false); cg_printf(");\n"); cg_printf("return this;\n"); cg_indentEnd("}\n"); cg_indentBegin("ObjectData *%s%s::dynCreate(CArrRef params, " "bool construct /* = true */) {\n", Option::ClassPrefix, clsName); cg_printf("init();\n"); cg_indentBegin("if (construct) {\n"); cg_printf("CountableHelper h(this);\n"); OutputCPPDynamicInvokeCount(cg); outputCPPDynamicInvoke(cg, ar, Option::MethodPrefix, cg.formatLabel(consName).c_str(), true, false, false, NULL, true); cg_indentEnd("}\n"); cg_printf("return this;\n"); cg_indentEnd("}\n"); if (isDynamic() || isSepExtension()) { cg_indentBegin("void %s%s::dynConstruct(CArrRef params) {\n", Option::ClassPrefix, clsName); OutputCPPDynamicInvokeCount(cg); outputCPPDynamicInvoke(cg, ar, Option::MethodPrefix, cg.formatLabel(consName).c_str(), true, false, false, NULL, true); cg_indentEnd("}\n"); if (cg.getOutput() == CodeGenerator::SystemCPP || Option::EnableEval >= Option::LimitedEval) { cg_indentBegin("void %s%s::dynConstructFromEval(" "Eval::VariableEnvironment &env, " "const Eval::FunctionCallExpression *caller) {\n", Option::ClassPrefix, clsName); outputCPPEvalInvoke(cg, ar, Option::MethodPrefix, cg.formatLabel(consName).c_str(), NULL, false); cg_indentEnd("}\n"); } } }
void ParameterExpression::parseHandler(AnalysisResultConstPtr ar, FunctionScopePtr func, ClassScopePtr cls) { if (cls && !m_type.empty()) { if (m_type == "self") { m_type = cls->getName(); } else if (m_type == "parent") { if (!cls->getParent().empty()) { m_type = cls->getParent(); } } } func->getVariables()->addParam(m_name, TypePtr(), ar, ExpressionPtr()); }
void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) { m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name); m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name); VariableTablePtr variables = getScope()->getVariables(); if (m_superGlobal) { variables->setAttribute(VariableTable::NeedGlobalPointer); } else if (m_name == "GLOBALS") { m_globals = true; } else { m_sym = variables->addSymbol(m_name); } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (FunctionScopePtr func = getFunctionScope()) { if (m_name == "this" && getClassScope()) { func->setContainsThis(); m_this = true; if (!hasContext(ObjectContext)) { func->setContainsBareThis(); if (variables->getAttribute(VariableTable::ContainsDynamicVariable)) { ClassScopePtr cls = getClassScope(); TypePtr t = cls->isRedeclaring() ? Type::Variant : Type::CreateObjectType(cls->getName()); variables->add(m_sym, t, true, ar, shared_from_this(), getScope()->getModifiers()); } } } if (m_sym && !(m_context & AssignmentLHS) && !((m_context & UnsetContext) && (m_context & LValue))) { m_sym->setUsed(); } } } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) { if (m_sym && !m_sym->isSystem() && !(getContext() & (LValue|RefValue|RefParameter|UnsetContext|ExistContext)) && m_sym->getDeclaration().get() == this && !variables->getAttribute(VariableTable::ContainsLDynamicVariable) && !getScope()->is(BlockScope::ClassScope)) { if (getScope()->inPseudoMain()) { Compiler::Error(Compiler::UseUndeclaredGlobalVariable, shared_from_this()); } else { Compiler::Error(Compiler::UseUndeclaredVariable, shared_from_this()); } } } }
void ClassConstantExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { const char *globals = "g"; if (cg.getContext() == CodeGenerator::CppParameterDefaultValueDecl || cg.getContext() == CodeGenerator::CppParameterDefaultValueImpl) { globals = cg.getGlobals(); } if (m_valid) { ClassScopePtr foundCls; string trueClassName; for (ClassScopePtr cls = ar->findClass(m_className); cls; cls = cls->getParentScope(ar)) { if (cls->getConstants()->isPresent(m_varName)) { foundCls = cls; trueClassName = cls->getName(); break; } } ASSERT(!trueClassName.empty()); ConstructPtr decl = foundCls->getConstants()->getValue(m_varName); if (decl) { decl->outputCPP(cg, ar); if (cg.getContext() == CodeGenerator::CppImplementation || cg.getContext() == CodeGenerator::CppParameterDefaultValueImpl) { cg.printf("(%s::%s)", m_className.c_str(), m_varName.c_str()); } else { cg.printf("/* %s::%s */", m_className.c_str(), m_varName.c_str()); } } else { if (foundCls->getConstants()->isDynamic(m_varName)) { cg.printf("%s%s::lazy_initializer(%s)->", Option::ClassPrefix, trueClassName.c_str(), globals); } cg.printf("%s%s_%s", Option::ClassConstantPrefix, trueClassName.c_str(), m_varName.c_str()); } } else if (m_redeclared) { cg.printf("%s->%s%s->os_constant(\"%s\")", globals, Option::ClassStaticsObjectPrefix, m_className.c_str(), m_varName.c_str()); } else { cg.printf("throw_fatal(\"unknown class constant %s::%s\")", m_className.c_str(), m_varName.c_str()); } }
void MethodStatement::onParse(AnalysisResultPtr ar) { ClassScopePtr classScope = dynamic_pointer_cast<ClassScope>(ar->getScope()); classScope->addFunction(ar, onParseImpl(ar)); if (m_name == "__construct") { classScope->setAttribute(ClassScope::HasConstructor); } else if (m_name == "__destruct") { classScope->setAttribute(ClassScope::HasDestructor); } if (m_name == "__call") { classScope->setAttribute(ClassScope::HasUnknownMethodHandler); } else if (m_name == "__get") { classScope->setAttribute(ClassScope::HasUnknownPropHandler); } m_className = classScope->getName(); }
ClassScopePtr BlockScope::findExactClass(const std::string &className) { if (ClassScopePtr currentCls = getContainingClass()) { if (className == currentCls->getName()) { return currentCls; } } if (FileScopePtr currentFile = getContainingFile()) { StatementList &stmts = *currentFile->getStmt(); for (int i = stmts.getCount(); i--; ) { StatementPtr s = stmts[i]; if (s && s->is(Statement::KindOfClassStatement)) { ClassScopePtr scope = static_pointer_cast<ClassStatement>(s)->getClassScope(); if (className == scope->getName()) { return scope; } } } } return ClassScopePtr(); }
bool AnalysisResult::declareClass(ClassScopePtr classScope) const { assert(m_phase < AnalyzeAll); string cname = classScope->getName(); // System classes override if (m_systemClasses.find(cname) != m_systemClasses.end()) { // we need someone to hold on to a reference to it // even though we're not going to do anything with it this->lock()->m_ignoredScopes.push_back(classScope); return false; } int mask = (m_classForcedVariants[0] ? VariableTable::NonPrivateNonStaticVars : 0) | (m_classForcedVariants[1] ? VariableTable::NonPrivateStaticVars : 0); if (mask) { AnalysisResultConstPtr ar = shared_from_this(); classScope->getVariables()->forceVariants(ar, mask); } return true; }
TypePtr SimpleVariable::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { TypePtr ret; ConstructPtr construct = shared_from_this(); BlockScopePtr scope = getScope(); VariableTablePtr variables = scope->getVariables(); // check function parameter that can occur in lval context if (m_sym && m_sym->isParameter() && m_context & (LValue | RefValue | DeepReference | UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) { m_sym->setLvalParam(); } if (m_this) { ClassScopePtr cls = getOriginalClass(); if (!hasContext(ObjectContext) && cls->derivedByDynamic()) { ret = Type::Object; } else { ret = Type::CreateObjectType(cls->getName()); } if (!hasContext(ObjectContext) && variables->getAttribute(VariableTable::ContainsDynamicVariable)) { ret = variables->add(m_sym, ret, true, ar, construct, scope->getModifiers()); } } else if ((m_context & (LValue|Declaration)) && !(m_context & (ObjectContext|RefValue))) { if (m_globals) { ret = Type::Variant; } else if (m_superGlobal) { ret = m_superGlobalType; } else if (m_superGlobalType) { // For system ret = variables->add(m_sym, m_superGlobalType, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } else { ret = variables->add(m_sym, type, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } } else { if (m_superGlobalType) { ret = m_superGlobalType; } else if (m_globals) { ret = Type::Array; } else if (scope->is(BlockScope::ClassScope)) { // ClassVariable expression will come to this block of code ret = getClassScope()->checkProperty(m_sym, type, true, ar); } else { TypePtr tmpType = type; if (m_context & RefValue) { tmpType = Type::Variant; coerce = true; } int p; ret = variables->checkVariable(m_sym, tmpType, coerce, ar, construct, p); } } TypePtr actual = propagateTypes(ar, ret); setTypes(ar, actual, type); if (Type::SameType(actual, ret)) { m_implementedType.reset(); } else { m_implementedType = ret; } return actual; }
void InterfaceStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopeRawPtr classScope = getClassScope(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isVolatile()) { const vector<string> &bases = classScope->getBases(); for (unsigned i = 0; i < bases.size(); ++i) { const string &name = bases[i]; if (cg.checkHoistedClass(name) || classScope->hasKnownBase(i)) { continue; } ClassScopePtr base = ar->findClass(name); if (base && base->isVolatile()) { cg_printf("checkClassExistsThrow("); cg_printString(name, ar, shared_from_this()); cg_printf(", &%s->CDEC(%s));\n", cg.getGlobals(ar), CodeGenerator::FormatLabel(base->getName()).c_str()); } } classScope->outputCPPDef(cg); cg.addHoistedClass(m_name); } return; } string clsNameStr = classScope->getId(); const char *clsName = clsNameStr.c_str(); switch (cg.getContext()) { case CodeGenerator::CppDeclaration: { printSource(cg); if (Option::GenerateCPPMacros) { classScope->outputForwardDeclaration(cg); } if (classScope->isRedeclaring()) { classScope->outputCPPGlobalTableWrappersDecl(cg, ar); } cg_printf("class %s%s", Option::ClassPrefix, clsName); if (m_base && Option::UseVirtualDispatch && !classScope->isRedeclaring()) { const char *sep = " :"; for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && !intfClassScope->isRedeclaring() && classScope->derivesDirectlyFrom(intf)) { string id = intfClassScope->getId(); cg_printf("%s public %s%s", sep, Option::ClassPrefix, id.c_str()); sep = ","; } } } cg_indentBegin(" {\n"); if (m_stmt) m_stmt->outputCPP(cg, ar); bool hasPropTable = classScope->checkHasPropTable(ar); if (hasPropTable) { cg_printf("public: static const ClassPropTable %sprop_table;\n", Option::ObjectStaticPrefix); } cg_indentEnd("};\n"); if (hasPropTable) { classScope->outputCPPGlobalTableWrappersDecl(cg, ar); } if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsDecl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppDeclaration); } } break; case CodeGenerator::CppImplementation: { if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsImpl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } cg.addClass(getClassScope()->getName(), getClassScope()); if (classScope->isRedeclaring() || classScope->checkHasPropTable(ar)) { classScope->outputCPPGlobalTableWrappersImpl(cg, ar); } } break; case CodeGenerator::CppFFIDecl: case CodeGenerator::CppFFIImpl: // do nothing break; case CodeGenerator::JavaFFI: { // TODO support PHP namespaces, once HPHP supports it string packageName = Option::JavaFFIRootPackage; string packageDir = packageName; Util::replaceAll(packageDir, ".", "/"); string outputDir = ar->getOutputPath() + "/" + Option::FFIFilePrefix + packageDir + "/"; Util::mkdir(outputDir); // uses a different cg to generate a separate file for each PHP class string clsFile = outputDir + getOriginalName() + ".java"; std::ofstream fcls(clsFile.c_str()); CodeGenerator cgCls(&fcls, CodeGenerator::FileCPP); cgCls.setContext(CodeGenerator::JavaFFIInterface); cgCls.printf("package %s;\n\n", packageName.c_str()); cgCls.printf("import hphp.*;\n\n"); cgCls.printf("public interface %s", getOriginalName().c_str()); if (m_base) { bool first = true; for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && classScope->derivesFrom(ar, intf, false, false) && intfClassScope->isUserClass()) { if (first) { cgCls.printf(" extends "); first = false; } else { cgCls.printf(", "); } cgCls.printf(intfClassScope->getOriginalName().c_str()); } } } cgCls.indentBegin(" {\n"); if (m_stmt) m_stmt->outputCPP(cgCls, ar); cgCls.indentEnd("}\n"); fcls.close(); } break; case CodeGenerator::JavaFFICppDecl: case CodeGenerator::JavaFFICppImpl: // do nothing break; default: ASSERT(false); break; } }
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopePtr classScope = m_classScope.lock(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isRedeclaring()) { cg_printf("g->%s%s = ClassStaticsPtr(NEW(%s%s)());\n", Option::ClassStaticsObjectPrefix, cg.formatLabel(m_name).c_str(), Option::ClassStaticsPrefix, classScope->getId(cg).c_str()); cg_printf("g->%s%s = &%s%s;\n", Option::ClassStaticsCallbackPrefix, cg.formatLabel(m_name).c_str(), Option::ClassWrapperFunctionPrefix, classScope->getId(cg).c_str()); } if (classScope->isVolatile()) { cg_printf("g->CDEC(%s) = true;\n", m_name.c_str()); } const vector<string> &bases = classScope->getBases(); for (vector<string>::const_iterator it = bases.begin(); it != bases.end(); ++it) { ClassScopePtr base = ar->findClass(*it); if (base && base->isVolatile()) { cg_printf("checkClassExists(\"%s\", g);\n", base->getOriginalName().c_str()); } } return; } if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } ar->pushScope(classScope); string clsNameStr = classScope->getId(cg); const char *clsName = clsNameStr.c_str(); bool redeclared = classScope->isRedeclaring(); switch (cg.getContext()) { case CodeGenerator::CppForwardDeclaration: if (Option::GenerateCPPMacros) { cg_printf("FORWARD_DECLARE_CLASS(%s)\n", clsName); if (redeclared) { cg_printf("FORWARD_DECLARE_REDECLARED_CLASS(%s)\n", clsName); } } if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsDecl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppForwardDeclaration); } break; case CodeGenerator::CppDeclaration: { bool system = cg.getOutput() == CodeGenerator::SystemCPP; ClassScopePtr parCls; if (!m_parent.empty()) { parCls = ar->findClass(m_parent); if (parCls && parCls->isRedeclaring()) parCls.reset(); } cg_printf("class %s%s", Option::ClassPrefix, clsName); if (!m_parent.empty() && classScope->derivesDirectlyFrom(ar, m_parent)) { if (!parCls) { cg_printf(" : public DynamicObjectData"); } else { cg_printf(" : public %s%s", Option::ClassPrefix, parCls->getId(cg).c_str()); } } else { if (classScope->derivesFromRedeclaring()) { cg_printf(" : public DynamicObjectData"); } else if (system) { cg_printf(" : public ExtObjectData"); } else { cg_printf(" : public ObjectData"); } } if (m_base && Option::UseVirtualDispatch) { for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && !intfClassScope->isRedeclaring() && classScope->derivesDirectlyFrom(ar, intf) && (!parCls || !parCls->derivesFrom(ar, intf, true, false))) { string id = intfClassScope->getId(cg); cg_printf(", public %s%s", Option::ClassPrefix, id.c_str()); } } } cg_indentBegin(" {\n"); if (Option::GenerateCPPMacros) { // Get all of this class's ancestors vector<string> bases; getAllParents(ar, bases); // Eliminate duplicates sort(bases.begin(), bases.end()); bases.erase(unique(bases.begin(), bases.end()), bases.end()); cg_indentBegin("BEGIN_CLASS_MAP(%s)\n", Util::toLower(classScope->getName()).c_str()); for (unsigned int i = 0; i < bases.size(); i++) { cg_printf("PARENT_CLASS(%s)\n", bases[i].c_str()); } if (classScope->derivesFromRedeclaring()) { cg_printf("CLASS_MAP_REDECLARED()\n"); } cg_indentEnd("END_CLASS_MAP(%s)\n", clsName); } if (Option::GenerateCPPMacros) { bool dyn = (!parCls && !m_parent.empty()) || classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared; bool idyn = parCls && classScope->derivesFromRedeclaring() == ClassScope::IndirectFromRedeclared; bool redec = classScope->isRedeclaring(); if (!classScope->derivesFromRedeclaring()) { outputCPPClassDecl(cg, ar, clsName, m_originalName.c_str(), parCls ? parCls->getId(cg).c_str() : "ObjectData"); } else { cg_printf("DECLARE_DYNAMIC_CLASS(%s, %s, %s)\n", clsName, m_originalName.c_str(), dyn || !parCls ? "DynamicObjectData" : parCls->getId(cg).c_str()); } if (system || Option::EnableEval >= Option::LimitedEval) { cg_printf("DECLARE_INVOKES_FROM_EVAL\n"); } if (dyn || idyn || redec) { if (redec) { cg_printf("DECLARE_ROOT;\n"); if (!dyn && !idyn) { cg_printf("private: ObjectData* root;\n"); cg_printf("public:\n"); cg_printf("virtual ObjectData *getRoot() { return root; }\n"); } } string conInit = ":"; if (dyn) { conInit += "DynamicObjectData(\"" + m_parent + "\", r)"; } else if (idyn) { conInit += string(Option::ClassPrefix) + parCls->getId(cg) + "(r?r:this)"; } else { conInit += "root(r?r:this)"; } cg_printf("%s%s(ObjectData* r = NULL)%s {}\n", Option::ClassPrefix, clsName, conInit.c_str()); } } cg_printf("void init();\n", Option::ClassPrefix, clsName); if (classScope->needLazyStaticInitializer()) { cg_printf("static GlobalVariables *lazy_initializer" "(GlobalVariables *g);\n"); } classScope->getVariables()->outputCPPPropertyDecl(cg, ar, classScope->derivesFromRedeclaring()); if (!classScope->getAttribute(ClassScope::HasConstructor)) { FunctionScopePtr func = classScope->findFunction(ar, "__construct", false); if (func && !func->isAbstract() && !classScope->isInterface()) { ar->pushScope(func); func->outputCPPCreateDecl(cg, ar); ar->popScope(); } } if (classScope->getAttribute(ClassScope::HasDestructor)) { cg_printf("public: virtual void destruct();\n"); } // doCall if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) { cg_printf("Variant doCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } // doGet if (classScope->getAttribute(ClassScope::HasUnknownPropHandler)) { cg_printf("Variant doGet(Variant v_name, bool error);\n"); } if (classScope->isRedeclaring() && !classScope->derivesFromRedeclaring()) { cg_printf("Variant doRootCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } if (m_stmt) m_stmt->outputCPP(cg, ar); { set<string> done; classScope->outputCPPStaticMethodWrappers(cg, ar, done, clsName); } if (cg.getOutput() == CodeGenerator::SystemCPP && ar->isBaseSysRsrcClass(clsName) && !classScope->hasProperty("rsrc")) { cg_printf("public: Variant %srsrc;\n", Option::PropertyPrefix); } cg_indentEnd("};\n"); if (redeclared) { cg_indentBegin("class %s%s : public ClassStatics {\n", Option::ClassStaticsPrefix, clsName); cg_printf("public:\n"); cg_printf("DECLARE_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); cg_printf("%s%s() : ClassStatics(%d) {}\n", Option::ClassStaticsPrefix, clsName, classScope->getRedeclaringId()); cg_indentBegin("Variant %sgetInit(const char *s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sgetInit(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sget(const char *s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant &%slval(const char* s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%slval(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sinvoke(const char *c, const char *s, " "CArrRef params, int64 hash = -1, bool fatal = true) " "{\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sinvoke(c, s, params, hash, fatal);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Object create(CArrRef params, bool init = true, " "ObjectData* root = NULL) {\n"); cg_printf("return Object((NEW(%s%s)(root))->" "dynCreate(params, init));\n", Option::ClassPrefix, clsName); cg_indentEnd("}\n"); cg_indentBegin("Variant %sconstant(const char* s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sconstant(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sinvoke_from_eval(const char *c, " "const char *s, Eval::VariableEnvironment &env, " "const Eval::FunctionCallExpression *call, " "int64 hash = -1, bool fatal = true) " "{\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sinvoke_from_eval(c, s, env, call, hash, " "fatal);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentEnd("};\n"); } classScope->outputCPPGlobalTableWrappersDecl(cg, ar); } break; case CodeGenerator::CppImplementation: if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsImpl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } classScope->outputCPPSupportMethodsImpl(cg, ar); if (redeclared) { cg_printf("IMPLEMENT_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); } cg_indentBegin("void %s%s::init() {\n", Option::ClassPrefix, clsName); if (!m_parent.empty()) { if (classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared) { cg_printf("parent->init();\n"); } else { cg_printf("%s%s::init();\n", Option::ClassPrefix, m_parent.c_str()); } } if (classScope->getVariables()-> getAttribute(VariableTable::NeedGlobalPointer)) { cg.printDeclareGlobals(); } cg.setContext(CodeGenerator::CppConstructor); if (m_stmt) m_stmt->outputCPP(cg, ar); // This is lame. Exception base class needs to prepare stacktrace outside // of its PHP constructor. Every subclass of exception also needs this // stacktrace, so we're adding an artificial __init__ in exception.php // and calling it here. if (m_name == "exception") { cg_printf("{CountableHelper h(this); t___init__();}\n"); } cg_indentEnd("}\n"); if (classScope->needStaticInitializer()) { cg_indentBegin("void %s%s::os_static_initializer() {\n", Option::ClassPrefix, clsName); cg.printDeclareGlobals(); cg.setContext(CodeGenerator::CppStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_indentBegin("void %s%s() {\n", Option::ClassStaticInitializerPrefix, clsName); cg_printf("%s%s::os_static_initializer();\n", Option::ClassPrefix, clsName); cg_indentEnd("}\n"); } if (classScope->needLazyStaticInitializer()) { cg_indentBegin("GlobalVariables *%s%s::lazy_initializer(" "GlobalVariables *g) {\n", Option::ClassPrefix, clsName); cg_indentBegin("if (!g->%s%s) {\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg_printf("g->%s%s = true;\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg.setContext(CodeGenerator::CppLazyStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_printf("return g;\n"); cg_indentEnd("}\n"); } cg.setContext(CodeGenerator::CppImplementation); if (m_stmt) m_stmt->outputCPP(cg, ar); break; case CodeGenerator::CppFFIDecl: case CodeGenerator::CppFFIImpl: if (m_stmt) m_stmt->outputCPP(cg, ar); break; case CodeGenerator::JavaFFI: { if (classScope->isRedeclaring()) break; // TODO support PHP namespaces, once HPHP supports it string packageName = Option::JavaFFIRootPackage; string packageDir = packageName; Util::replaceAll(packageDir, ".", "/"); string outputDir = ar->getOutputPath() + "/" + Option::FFIFilePrefix + packageDir + "/"; Util::mkdir(outputDir); // uses a different cg to generate a separate file for each PHP class // also, uses the original capitalized class name string clsFile = outputDir + getOriginalName() + ".java"; ofstream fcls(clsFile.c_str()); CodeGenerator cgCls(&fcls, CodeGenerator::FileCPP); cgCls.setContext(CodeGenerator::JavaFFI); cgCls.printf("package %s;\n\n", packageName.c_str()); cgCls.printf("import hphp.*;\n\n"); printSource(cgCls); string clsModifier; switch (m_type) { case T_CLASS: break; case T_ABSTRACT: clsModifier = "abstract "; break; case T_FINAL: clsModifier = "final "; break; } cgCls.printf("public %sclass %s ", clsModifier.c_str(), getOriginalName().c_str()); ClassScopePtr parCls; if (!m_parent.empty()) parCls = ar->findClass(m_parent); if (!m_parent.empty() && classScope->derivesDirectlyFrom(ar, m_parent) && parCls && parCls->isUserClass() && !parCls->isRedeclaring()) { // system classes are not supported in static FFI translation // they shouldn't appear as superclasses as well cgCls.printf("extends %s", parCls->getOriginalName().c_str()); } else { cgCls.printf("extends HphpObject"); } if (m_base) { bool first = true; for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && classScope->derivesFrom(ar, intf, false, false) && intfClassScope->isUserClass()) { if (first) { cgCls.printf(" implements "); first = false; } else { cgCls.printf(", "); } cgCls.printf(intfClassScope->getOriginalName().c_str()); } } } cgCls.indentBegin(" {\n"); // constructor for initializing the variant pointer cgCls.printf("protected %s(long ptr) { super(ptr); }\n\n", getOriginalName().c_str()); FunctionScopePtr cons = classScope->findConstructor(ar, true); if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) { // if not an abstract class and not having an explicit constructor, // adds a default constructor outputJavaFFIConstructor(cgCls, ar, cons); } if (m_stmt) m_stmt->outputCPP(cgCls, ar); cgCls.indentEnd("}\n"); fcls.close(); } break; case CodeGenerator::JavaFFICppDecl: case CodeGenerator::JavaFFICppImpl: { if (classScope->isRedeclaring()) break; if (m_stmt) m_stmt->outputCPP(cg, ar); FunctionScopePtr cons = classScope->findConstructor(ar, true); if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) { outputJavaFFICPPCreator(cg, ar, cons); } } break; default: ASSERT(false); break; } ar->popScope(); }
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; }
TypePtr SimpleVariable::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope()); resetTypes(); TypePtr ret; ConstructPtr construct = shared_from_this(); BlockScopePtr scope = getScope(); VariableTablePtr variables = scope->getVariables(); // check function parameter that can occur in lval context if (m_sym && m_sym->isParameter() && m_context & (LValue | RefValue | DeepReference | UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) { m_sym->setLvalParam(); } if (coerce && m_sym && type && type->is(Type::KindOfAutoSequence)) { TypePtr t = m_sym->getType(); if (!t || t->is(Type::KindOfVoid) || t->is(Type::KindOfSome) || t->is(Type::KindOfArray)) { type = Type::Array; } } if (m_this) { ret = Type::Object; ClassScopePtr cls = getOriginalClass(); if (cls && (hasContext(ObjectContext) || !cls->derivedByDynamic())) { ret = Type::CreateObjectType(cls->getName()); } if (!hasContext(ObjectContext) && variables->getAttribute(VariableTable::ContainsDynamicVariable)) { if (variables->getAttribute(VariableTable::ContainsLDynamicVariable)) { ret = Type::Variant; } ret = variables->add(m_sym, ret, true, ar, construct, scope->getModifiers()); } } else if ((m_context & (LValue|Declaration)) && !(m_context & (ObjectContext|RefValue))) { if (m_globals) { ret = Type::Array; } else if (m_superGlobal) { ret = m_superGlobalType; } else if (m_superGlobalType) { // For system ret = variables->add(m_sym, m_superGlobalType, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } else { ret = variables->add(m_sym, type, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } } else { if (m_superGlobalType) { ret = m_superGlobalType; } else if (m_globals) { ret = Type::Array; } else if (scope->is(BlockScope::ClassScope)) { assert(getClassScope().get() == scope.get()); // ClassVariable expression will come to this block of code ret = getClassScope()->checkProperty(getScope(), m_sym, type, true, ar); } else { TypePtr tmpType = type; if (m_context & RefValue) { tmpType = Type::Variant; coerce = true; } ret = variables->checkVariable(m_sym, tmpType, coerce, ar, construct); if (ret && (ret->is(Type::KindOfSome) || ret->is(Type::KindOfAny))) { ret = Type::Variant; } } } // if m_assertedType is set, then this is a type assertion node TypePtr inType = m_assertedType ? GetAssertedInType(ar, m_assertedType, ret) : ret; TypePtr actual = propagateTypes(ar, inType); setTypes(ar, actual, type); if (Type::SameType(actual, ret)) { m_implementedType.reset(); } else { m_implementedType = ret; } return actual; }
void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) { m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name); m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name); VariableTablePtr variables = getScope()->getVariables(); if (m_superGlobal) { variables->setAttribute(VariableTable::NeedGlobalPointer); } else if (m_name == "GLOBALS") { m_globals = true; } else { m_sym = variables->addDeclaredSymbol(m_name, shared_from_this()); } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (FunctionScopePtr func = getFunctionScope()) { if (m_name == "this" && func->mayContainThis()) { func->setContainsThis(); m_this = true; if (!hasContext(ObjectContext)) { bool unset = hasAllContext(UnsetContext | LValue); func->setContainsBareThis( true, hasAnyContext(RefValue | RefAssignmentLHS) || m_sym->isRefClosureVar() || unset); if (variables->getAttribute(VariableTable::ContainsDynamicVariable)) { ClassScopePtr cls = getClassScope(); TypePtr t = !cls || cls->isRedeclaring() ? Type::Variant : Type::CreateObjectType(cls->getName()); variables->add(m_sym, t, true, ar, shared_from_this(), getScope()->getModifiers()); } } } if (m_sym && !(m_context & AssignmentLHS) && !((m_context & UnsetContext) && (m_context & LValue))) { m_sym->setUsed(); } } } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) { if (m_sym && !m_this) { if (!m_sym->isSystem() && !(getContext() & (LValue|RefValue|RefParameter|UnsetContext|ExistContext)) && m_sym->getDeclaration().get() == this) { assert(!m_sym->isParameter()); if (!variables->getAttribute(VariableTable::ContainsLDynamicVariable) && !getScope()->is(BlockScope::ClassScope)) { if (getScope()->inPseudoMain()) { Compiler::Error(Compiler::UseUndeclaredGlobalVariable, shared_from_this()); } else if (!m_sym->isClosureVar()) { Compiler::Error(Compiler::UseUndeclaredVariable, shared_from_this()); } } } // check function parameter that can occur in lval context if (m_sym->isParameter() && m_context & (LValue | RefValue | DeepReference | UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) { m_sym->setLvalParam(); } } } }
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar, ClassScopePtr classScope) { FunctionScopeRawPtr fs = getFunctionScope(); const bool isNative = fs->isNative(); if (m_modifiers) { if ((m_modifiers->isExplicitlyPublic() + m_modifiers->isProtected() + m_modifiers->isPrivate()) > 1) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "%s: method %s::%s()", Strings::PICK_ACCESS_MODIFIER, classScope->getOriginalName().c_str(), getOriginalName().c_str() ); } if (classScope->isInterface()) { if (m_modifiers->isProtected() || m_modifiers->isPrivate() || m_modifiers->isAbstract() || m_modifiers->isFinal() || isNative) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "Access type for interface method %s::%s() must be omitted", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (m_modifiers->isAsync()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, Strings::ASYNC_WITHOUT_BODY, "interface", classScope->getOriginalName().c_str(), getOriginalName().c_str() ); } } if (m_modifiers->isAbstract()) { if (m_modifiers->isPrivate() || m_modifiers->isFinal() || isNative) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "Cannot declare abstract method %s::%s() %s", classScope->getOriginalName().c_str(), getOriginalName().c_str(), m_modifiers->isPrivate() ? "private" : (m_modifiers->isFinal() ? "final" : "native")); } if (!classScope->isInterface() && !classScope->isAbstract()) { /* note that classScope->isAbstract() returns true for traits */ m_modifiers->parseTimeFatal(Compiler::InvalidAttribute, "Class %s contains abstract method %s and " "must therefore be declared abstract", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (getStmts()) { parseTimeFatal(Compiler::InvalidAttribute, "Abstract method %s::%s() cannot contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (m_modifiers->isAsync()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, Strings::ASYNC_WITHOUT_BODY, "abstract", classScope->getOriginalName().c_str(), getOriginalName().c_str() ); } } if (isNative) { if (getStmts()) { parseTimeFatal(Compiler::InvalidAttribute, "Native method %s::%s() cannot contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (!m_retTypeAnnotation) { parseTimeFatal(Compiler::InvalidAttribute, "Native method %s::%s() must have a return type hint", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } } } if ((!m_modifiers || !m_modifiers->isAbstract()) && !getStmts() && !classScope->isInterface() && !isNative) { parseTimeFatal(Compiler::InvalidAttribute, "Non-abstract method %s::%s() must contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } classScope->addFunction(ar, fs); m_className = classScope->getName(); m_originalClassName = classScope->getOriginalName(); setSpecialMethod(classScope); if (Option::DynamicInvokeFunctions.find(getFullName()) != Option::DynamicInvokeFunctions.end()) { fs->setDynamicInvoke(); } if (m_params) { for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); param->parseHandler(classScope); if (isNative && !param->hasUserType()) { parseTimeFatal(Compiler::InvalidAttribute, "Native method calls must have type hints on all args"); } } } FunctionScope::RecordFunctionInfo(m_name, fs); }
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar, ClassScopePtr classScope) { FunctionScopeRawPtr fs = getFunctionScope(); const bool isNative = fs->isNative(); if (m_modifiers) { if ((m_modifiers->isExplicitlyPublic() + m_modifiers->isProtected() + m_modifiers->isPrivate()) > 1) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, Strings::PICK_ACCESS_MODIFIER ); } if (m_modifiers->hasDuplicates()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, Strings::PICK_ACCESS_MODIFIER); } if (classScope->isInterface()) { if (m_modifiers->isProtected() || m_modifiers->isPrivate() || m_modifiers->isAbstract() || m_modifiers->isFinal() || isNative) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "Access type for interface method %s::%s() must be omitted", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (m_modifiers->isAsync()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, Strings::ASYNC_WITHOUT_BODY, "interface", classScope->getOriginalName().c_str(), getOriginalName().c_str() ); } if (getStmts()) { getStmts()->parseTimeFatal( Compiler::InvalidMethodDefinition, "Interface method %s::%s() cannot contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } } if (m_modifiers->isAbstract()) { if (m_modifiers->isPrivate() || m_modifiers->isFinal() || isNative) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "Cannot declare abstract method %s::%s() %s", classScope->getOriginalName().c_str(), getOriginalName().c_str(), m_modifiers->isPrivate() ? "private" : (m_modifiers->isFinal() ? "final" : "native")); } if (!classScope->isInterface() && !classScope->isAbstract()) { /* note that classScope->isAbstract() returns true for traits */ m_modifiers->parseTimeFatal(Compiler::InvalidAttribute, "Class %s contains abstract method %s and " "must therefore be declared abstract", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (getStmts()) { parseTimeFatal(Compiler::InvalidAttribute, "Abstract method %s::%s() cannot contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (m_modifiers->isAsync()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, Strings::ASYNC_WITHOUT_BODY, "abstract", classScope->getOriginalName().c_str(), getOriginalName().c_str() ); } } if (!m_modifiers->isStatic() && classScope->isStaticUtil()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "Class %s contains non-static method %s and " "therefore cannot be declared 'abstract final'", classScope->getOriginalName().c_str(), getOriginalName().c_str() ); } if (isNative) { if (getStmts()) { parseTimeFatal(Compiler::InvalidAttribute, "Native method %s::%s() cannot contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } auto is_ctordtor = (m_name == "__construct") || (m_name == "__destruct"); if (!m_retTypeAnnotation && !is_ctordtor) { parseTimeFatal(Compiler::InvalidAttribute, "Native method %s::%s() must have a return type hint", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } else if (m_retTypeAnnotation && is_ctordtor && (m_retTypeAnnotation->dataType() != KindOfNull)) { parseTimeFatal(Compiler::InvalidAttribute, "Native method %s::%s() must return void", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } } } if ((!m_modifiers || !m_modifiers->isAbstract()) && !getStmts() && !classScope->isInterface() && !isNative) { parseTimeFatal(Compiler::InvalidAttribute, "Non-abstract method %s::%s() must contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } classScope->addFunction(ar, fs); m_className = classScope->getName(); m_originalClassName = classScope->getOriginalName(); setSpecialMethod(classScope); if (Option::DynamicInvokeFunctions.find(getFullName()) != Option::DynamicInvokeFunctions.end()) { fs->setDynamicInvoke(); } if (m_params) { auto nParams = m_params->getCount(); for (int i = 0; i < nParams; i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); param->parseHandler(classScope); // Variadic capture params don't need types because they'll // be treated as Arrays as far as HNI is concerned. if (isNative && !param->hasUserType() && !param->isVariadic()) { parseTimeFatal(Compiler::InvalidAttribute, "Native method calls must have type hints on all args"); } } } FunctionScope::RecordFunctionInfo(m_name, fs); }
TypePtr SimpleVariable::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { TypePtr ret; ConstructPtr construct = shared_from_this(); BlockScopePtr scope = ar->getScope(); VariableTablePtr variables = scope->getVariables(); // check function parameter that can occur in lval context if (m_context & (LValue | RefValue | UnsetContext | InvokeArgument)) { FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(scope); if (func) { if (variables->isParameter(m_name)) { variables->addLvalParam(m_name); } } } if (m_name == "this") { ClassScopePtr cls = getOriginalScope(ar); if (cls) { bool isStaticFunc = false; FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(scope); if (func->isStatic()) isStaticFunc = true; if (cls->isRedeclaring()) { ret = Type::Variant; } else { ret = Type::CreateObjectType(cls->getName()); } if (!isStaticFunc || (m_context & ObjectContext)) m_this = true; } } if ((m_context & (LValue|Declaration)) && !(m_context & ObjectContext)) { if (m_superGlobal) { ret = m_superGlobalType; } else if (m_superGlobalType) { // For system if (!m_this) { ret = variables->add(m_name, m_superGlobalType, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } } else { if (m_globals) { ret = Type::Variant; // this can happen with "unset($GLOBALS)" } else if (!m_this) { ret = variables->add(m_name, type, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } } } else { if (!m_this) { if (m_superGlobalType) { ret = m_superGlobalType; } else if (m_globals) { ret = Type::Array; } else if (scope->is(BlockScope::ClassScope)) { // ClassVariable expression will come to this block of code int properties; ret = variables->checkProperty(m_name, type, true, ar, construct, properties); } else { TypePtr tmpType = type; if (m_context & RefValue) { tmpType = Type::Variant; coerce = true; } int p; ret = variables->checkVariable(m_name, tmpType, coerce, ar, construct, p); } } } TypePtr actual = propagateTypes(ar, ret); setTypes(actual, type); if (Type::SameType(actual, ret)) { m_implementedType.reset(); } else { m_implementedType = ret; } return actual; }
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; }
TypePtr Type::Coerce(AnalysisResultConstPtr ar, TypePtr type1, TypePtr type2) { if (SameType(type1, type2)) return type1; if (type1->m_kindOf == KindOfVariant || type2->m_kindOf == KindOfVariant) return Type::Variant; if (type1->m_kindOf > type2->m_kindOf) { TypePtr tmp = type1; type1 = type2; type2 = tmp; } if (type1->m_kindOf == KindOfVoid && (type2->m_kindOf == KindOfString || type2->m_kindOf == KindOfArray || type2->m_kindOf == KindOfObject)) { return type2; } if (type2->m_kindOf == KindOfSome || type2->m_kindOf == KindOfAny) return type1; if (type2->m_kindOf & KindOfAuto) { if (type1->mustBe(type2->m_kindOf & ~KindOfAuto)) { if (!(type1->m_kindOf & Type::KindOfString)) { return type1; } if (type2->m_kindOf == KindOfAutoSequence) { return Type::Sequence; } return GetType((KindOf)(type2->m_kindOf & ~KindOfAuto)); } return Type::Variant; } if (type1->mustBe(KindOfInteger)) { if (type2->mustBe(KindOfInteger)) { return type2; } else if (type2->mustBe(KindOfDouble)) { return Type::Numeric; } } if (type1->mustBe(Type::KindOfObject) && type2->mustBe(Type::KindOfObject)) { if (type1->m_name.empty()) return type1; if (type2->m_name.empty()) return type2; ClassScopePtr cls1 = ar->findClass(type1->m_name); if (cls1 && !cls1->isRedeclaring() && cls1->derivesFrom(ar, type2->m_name, true, false)) { return type2; } ClassScopePtr cls2 = ar->findClass(type2->m_name); if (cls2 && !cls2->isRedeclaring() && cls2->derivesFrom(ar, type1->m_name, true, false)) { return type1; } if (cls1 && cls2 && !cls1->isRedeclaring() && !cls2->isRedeclaring()) { ClassScopePtr parent = ClassScope::FindCommonParent(ar, type1->m_name, type2->m_name); if (parent) { return Type::CreateObjectType(parent->getName()); } } return Type::Object; } if (type1->mustBe(type2->m_kindOf)) { return type2; } CT_ASSERT(Type::KindOfString < Type::KindOfArray); if (type1->m_kindOf == Type::KindOfString && type2->m_kindOf == Type::KindOfArray) { return Type::Sequence; } return Type::Variant; }
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar, ClassScopePtr classScope) { if (m_modifiers) { if (classScope->isInterface()) { if (m_modifiers->isProtected() || m_modifiers->isPrivate() || m_modifiers->isAbstract() || m_modifiers->isFinal()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "Access type for interface method %s::%s() must be omitted", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } } if (m_modifiers->isAbstract()) { if (m_modifiers->isPrivate() || m_modifiers->isFinal()) { m_modifiers->parseTimeFatal( Compiler::InvalidAttribute, "Cannot declare abstract method %s::%s() %s", classScope->getOriginalName().c_str(), getOriginalName().c_str(), m_modifiers->isPrivate() ? "private" : "final"); } if (!classScope->isInterface() && !classScope->isAbstract()) { /* note that classScope->isAbstract() returns true for traits */ m_modifiers->parseTimeFatal(Compiler::InvalidAttribute, "Class %s contains abstract method %s and " "must therefore be declared abstract", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } if (getStmts()) { parseTimeFatal(Compiler::InvalidAttribute, "Abstract method %s::%s() cannot contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } } } if ((!m_modifiers || !m_modifiers->isAbstract()) && !getStmts() && !classScope->isInterface()) { parseTimeFatal(Compiler::InvalidAttribute, "Non-abstract method %s::%s() must contain body", classScope->getOriginalName().c_str(), getOriginalName().c_str()); } FunctionScopeRawPtr fs = getFunctionScope(); classScope->addFunction(ar, fs); m_className = classScope->getName(); m_originalClassName = classScope->getOriginalName(); setSpecialMethod(classScope); if (Option::DynamicInvokeFunctions.find(getFullName()) != Option::DynamicInvokeFunctions.end()) { fs->setDynamicInvoke(); } if (m_params) { for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); param->parseHandler(classScope); } } FunctionScope::RecordFunctionInfo(m_name, fs); }
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(")"); }
bool FunctionScope::isConstructor(ClassScopePtr cls) const { return m_stmt && cls && (getName() == "__construct" || (cls->classNameCtor() && getName() == cls->getName())); }