ClassScopePtr ClassScope::findSingleTraitWithMethod(AnalysisResultPtr ar, const string &methodName) const { assert(Option::WholeProgram); ClassScopePtr trait = ClassScopePtr(); for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); if (!tCls) continue; if (tCls->hasMethod(methodName)) { if (trait) { // more than one trait contains method return ClassScopePtr(); } trait = tCls; } } return trait; }
MethodStatementPtr ClassScope::findTraitMethod(AnalysisResultPtr ar, ClassScopePtr trait, const string &methodName, std::set<ClassScopePtr> &visitedTraits) { if (visitedTraits.find(trait) != visitedTraits.end()) { return MethodStatementPtr(); } visitedTraits.insert(trait); ClassStatementPtr tStmt = dynamic_pointer_cast<ClassStatement>(trait->getStmt()); StatementListPtr tStmts = tStmt->getStmts(); // Look in the current trait for (int s = 0; s < tStmts->getCount(); s++) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*tStmts)[s]); if (meth) { // Handle methods if (meth->getName() == methodName) { return meth; } } } // Look into children traits for (int s = 0; s < tStmts->getCount(); s++) { UseTraitStatementPtr useTraitStmt = dynamic_pointer_cast<UseTraitStatement>((*tStmts)[s]); if (useTraitStmt) { vector<string> usedTraits; useTraitStmt->getUsedTraitNames(usedTraits); for (unsigned i = 0; i < usedTraits.size(); i++) { MethodStatementPtr foundMethod = findTraitMethod(ar, ar->findClass(usedTraits[i]), methodName, visitedTraits); if (foundMethod) return foundMethod; } } } return MethodStatementPtr(); // not found }
void ObjectMethodExpression::analyzeProgram(AnalysisResultPtr ar) { FunctionCall::analyzeProgram(ar); m_object->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll) { FunctionScopePtr func = m_funcScope; if (!func && m_object->isThis() && !m_name.empty()) { ClassScopePtr cls = getClassScope(); if (cls) { m_classScope = cls; m_funcScope = func = cls->findFunction(ar, m_name, true, true); if (func) { func->addCaller(getScope()); } } } markRefParams(func, m_name, canInvokeFewArgs()); } // This is OK because AnalyzeFinal is guaranteed to run for a CPP // target, regardless of opts (and we only need the following // for CPP targets) if (ar->getPhase() == AnalysisResult::AnalyzeFinal) { // necessary because we set the expected type of m_object to // Type::Some during type inference. TypePtr at(m_object->getActualType()); TypePtr it(m_object->getImplementedType()); if (!m_object->isThis() && at && at->is(Type::KindOfObject)) { if (at->isSpecificObject() && it && Type::IsMappedToVariant(it)) { // fast-cast inference ClassScopePtr scope(ar->findClass(at->getName())); if (scope) { // add a dependency to m_object's class type // to allow the fast cast to succeed addUserClass(ar, at->getName()); } } m_object->setExpectedType(at); } } }
TypePtr ParameterExpression::getTypeSpecForClass(AnalysisResultPtr ar, bool forInference) { TypePtr ret; if (forInference) { ClassScopePtr cls = ar->findClass(m_type); if (Option::SystemGen || !cls || cls->isRedeclaring() || cls->derivedByDynamic()) { if (!cls && getScope()->isFirstPass()) { ConstructPtr self = shared_from_this(); Compiler::Error(Compiler::UnknownClass, self); } ret = Type::Variant; } } if (!ret) { ret = Type::CreateObjectType(m_type); } assert(ret); return ret; }
void ClassScope::importTraitProperties(AnalysisResultPtr ar) { for (const auto& name : m_usedTraitNames) { auto tCls = ar->findClass(name); if (!tCls) continue; auto tStmt = dynamic_pointer_cast<ClassStatement>(tCls->getStmt()); auto tStmts = tStmt->getStmts(); if (!tStmts) continue; for (int s = 0; s < tStmts->getCount(); s++) { auto prop = dynamic_pointer_cast<ClassVariable>((*tStmts)[s]); if (prop) { auto cloneProp = dynamic_pointer_cast<ClassVariable>( dynamic_pointer_cast<ClassStatement>(m_stmt)->addClone(prop)); cloneProp->resetScope(shared_from_this()); cloneProp->addTraitPropsToScope(ar, dynamic_pointer_cast<ClassScope>(shared_from_this())); } } } }
void ClassScope::importTraitProperties(AnalysisResultPtr ar) { for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); if (!tCls) continue; ClassStatementPtr tStmt = dynamic_pointer_cast<ClassStatement>(tCls->getStmt()); StatementListPtr tStmts = tStmt->getStmts(); if (!tStmts) continue; for (int s = 0; s < tStmts->getCount(); s++) { ClassVariablePtr prop = dynamic_pointer_cast<ClassVariable>((*tStmts)[s]); if (prop) { ClassVariablePtr cloneProp = dynamic_pointer_cast<ClassVariable>( dynamic_pointer_cast<ClassStatement>(m_stmt)->addClone(prop)); cloneProp->resetScope(shared_from_this()); cloneProp->addTraitPropsToScope(ar, dynamic_pointer_cast<ClassScope>(shared_from_this())); } } } }
void ObjectMethodExpression::outputCPPObjectCall(CodeGenerator &cg, AnalysisResultPtr ar) { outputCPPObject(cg, ar); bool isThis = m_object->isThis(); if (!isThis) { string objType; TypePtr type = m_object->getType(); if (type->isSpecificObject() && !m_name.empty() && m_valid) { objType = type->getName(); ClassScopePtr cls = ar->findClass(objType); objType = cls->getId(); } else { objType = "ObjectData"; } 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()); } }
TypePtr ConstantTable::checkBases(const std::string &name, TypePtr type, bool coerce, AnalysisResultPtr ar, ConstructPtr construct, const std::vector<std::string> &bases, BlockScope *&defScope) { TypePtr actualType; defScope = NULL; ClassScopePtr parent = findParent(ar, name); if (parent) { actualType = parent->checkConst(name, type, coerce, ar, construct, parent->getBases(), defScope); if (defScope) return actualType; } for (int i = bases.size() - 1; i >= (parent ? 1 : 0); i--) { const string &base = bases[i]; ClassScopePtr super = ar->findClass(base); if (!super) continue; actualType = super->checkConst(name, type, coerce, ar, construct, super->getBases(), defScope); if (defScope) return actualType; } return actualType; }
bool StaticClassName::checkPresent() { if (m_self || m_parent || m_static) return true; BlockScopeRawPtr scope = originalScope(this); FileScopeRawPtr currentFile = scope->getContainingFile(); if (currentFile) { AnalysisResultPtr ar = currentFile->getContainingProgram(); ClassScopeRawPtr cls = ar->findClass(m_className); if (!cls) return false; if (!cls->isVolatile()) return true; if (currentFile->resolveClass(cls)) return true; if (currentFile->checkClass(m_className)) return true; } if (ClassScopePtr self = scope->getContainingClass()) { if (m_className == self->getName() || self->derivesFrom(scope->getContainingProgram(), m_className, true, false)) { return true; } } return false; }
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopeRawPtr classScope = getClassScope(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isVolatile()) { string name = cg.formatLabel(m_name); if (classScope->isRedeclaring()) { cg_printf("g->%s%s = ClassStaticsPtr(NEWOBJ(%s%s)());\n", Option::ClassStaticsObjectPrefix, name.c_str(), Option::ClassStaticsPrefix, classScope->getId(cg).c_str()); cg_printf("g->%s%s = &%s%s;\n", Option::ClassStaticsCallbackPrefix, name.c_str(), Option::ClassWrapperFunctionPrefix, classScope->getId(cg).c_str()); } cg_printf("g->CDEC(%s) = true;\n", name.c_str()); const vector<string> &bases = classScope->getBases(); for (vector<string>::const_iterator it = bases.begin(); it != bases.end(); ++it) { if (cg.checkHoistedClass(*it)) continue; ClassScopePtr base = ar->findClass(*it); if (base && base->isVolatile()) { cg_printf("checkClassExists("); cg_printString(base->getOriginalName(), ar, shared_from_this()); string lname = Util::toLower(base->getOriginalName()); cg_printf(", &%s->CDEC(%s), %s->FVF(__autoload));\n", cg.getGlobals(ar), cg.formatLabel(lname).c_str(), cg.getGlobals(ar)); } } } return; } if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } string clsNameStr = classScope->getId(cg); const char *clsName = clsNameStr.c_str(); bool redeclared = classScope->isRedeclaring(); switch (cg.getContext()) { case CodeGenerator::CppDeclaration: { if (Option::GenerateCPPMacros) { classScope->outputForwardDeclaration(cg); } bool system = cg.getOutput() == CodeGenerator::SystemCPP; ClassScopePtr parCls; if (!m_parent.empty()) { parCls = ar->findClass(m_parent); if (parCls && parCls->isRedeclaring()) parCls.reset(); } if (Option::GenerateCppLibCode) { cg.printDocComment(classScope->getDocComment()); } cg_printf("class %s%s", Option::ClassPrefix, clsName); if (!m_parent.empty() && classScope->derivesDirectlyFrom(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(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"); cg_printf("public:\n"); cg.printSection("Properties"); classScope->getVariables()->outputCPPPropertyDecl(cg, ar, classScope->derivesFromRedeclaring()); if (Option::GenerateCppLibCode) { cg.printSection("Methods"); classScope->outputMethodWrappers(cg, ar); cg.printSection(">>>>>>>>>> Internal Implementation <<<<<<<<<<"); cg_printf("// NOTE: Anything below is subject to change. " "Use everything above instead.\n"); } cg.printSection("Class Map"); if (Option::GenerateCPPMacros) { cg_printf("virtual bool o_instanceof(CStrRef s) const;\n"); } 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()); } bool hasGet = classScope->getAttribute( ClassScope::HasUnknownPropGetter); bool hasSet = classScope->getAttribute( ClassScope::HasUnknownPropSetter); bool hasCall = classScope->getAttribute( ClassScope::HasUnknownMethodHandler); bool hasCallStatic = classScope->getAttribute( ClassScope::HasUnknownStaticMethodHandler); if (dyn || idyn || redec || hasGet || hasSet || hasCall || hasCallStatic) { if (redec && classScope->derivedByDynamic()) { if (!dyn && !idyn) { cg_printf("private: ObjectData* root;\n"); cg_printf("public:\n"); cg_printf("virtual ObjectData *getRoot() { return root; }\n"); } } string conInit = ""; bool hasParam = false; if (dyn) { conInit = " : DynamicObjectData(\"" + m_parent + "\", r)"; hasParam = true; } else if (idyn) { conInit = " : " + string(Option::ClassPrefix) + parCls->getId(cg) + "(r ? r : this)"; hasParam = true; } else { if (redec && classScope->derivedByDynamic()) { conInit = " : root(r ? r : this)"; } hasParam = true; } cg_indentBegin("%s%s(%s)%s {%s", Option::ClassPrefix, clsName, hasParam ? "ObjectData* r = NULL" : "", conInit.c_str(), hasGet || hasSet || hasCall || hasCallStatic ? "\n" : ""); if (hasGet) cg_printf("setAttribute(UseGet);\n"); if (hasSet) cg_printf("setAttribute(UseSet);\n"); if (hasCall) cg_printf("setAttribute(HasCall);\n"); if (hasCallStatic) cg_printf("setAttribute(HasCallStatic);\n"); cg_indentEnd("}\n"); } } cg_printf("void init();\n"); if (classScope->needLazyStaticInitializer()) { cg_printf("static GlobalVariables *lazy_initializer" "(GlobalVariables *g);\n"); } if (!classScope->getAttribute(ClassScope::HasConstructor)) { FunctionScopePtr func = classScope->findFunction(ar, "__construct", false); if (func && !func->isAbstract() && !classScope->isInterface()) { func->outputCPPCreateDecl(cg, ar); } } 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"); } if (classScope->isRedeclaring() && !classScope->derivesFromRedeclaring() && classScope->derivedByDynamic()) { 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); } if (Option::GenerateCPPMacros) { classScope->outputCPPJumpTableDecl(cg, ar); } 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(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sgetInit(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sget(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant &%slval(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%slval(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Object createOnly(ObjectData* root = NULL) {\n"); cg_printf("Object r((NEWOBJ(%s%s)(root)));\n", Option::ClassPrefix, clsName); cg_printf("r->init();\n"); cg_printf("return r;\n"); 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("bool %sget_call_info(MethodCallPackage &mcp, " "int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget_call_info(mcp, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentEnd("};\n"); } if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsDecl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppDeclaration); } 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 { ClassScopePtr parCls = ar->findClass(m_parent); cg_printf("%s%s::init();\n", Option::ClassPrefix, parCls->getId(cg).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(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; } }
void ClassScope::collectMethods(AnalysisResultPtr ar, StringToFunctionScopePtrMap &funcs, bool collectPrivate /* = true */, bool forInvoke /* = false */) { // add all functions this class has for (FunctionScopePtrVec::const_iterator iter = m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) { const FunctionScopePtr &fs = *iter; if (!collectPrivate && fs->isPrivate()) continue; FunctionScopePtr &func = funcs[fs->getName()]; if (!func) { func = fs; } else { func->setVirtual(); fs->setVirtual(); fs->setHasOverride(); if (fs->isFinal()) { std::string s__MockClass = "__MockClass"; ClassScopePtr derivedClass = func->getContainingClass(); if (derivedClass->m_userAttributes.find(s__MockClass) == derivedClass->m_userAttributes.end()) { Compiler::Error(Compiler::InvalidOverride, fs->getStmt(), func->getStmt()); } } } } int n = forInvoke ? m_parent.empty() ? 0 : 1 : m_bases.size(); // walk up for (int i = 0; i < n; i++) { const string &base = m_bases[i]; ClassScopePtr super = ar->findClass(base); if (super) { if (super->isRedeclaring()) { if (forInvoke) continue; const ClassScopePtrVec &classes = ar->findRedeclaredClasses(base); StringToFunctionScopePtrMap pristine(funcs); BOOST_FOREACH(ClassScopePtr cls, classes) { cls->m_derivedByDynamic = true; StringToFunctionScopePtrMap cur(pristine); derivedMagicMethods(cls); cls->collectMethods(ar, cur, false, forInvoke); inheritedMagicMethods(cls); funcs.insert(cur.begin(), cur.end()); cls->getVariables()-> forceVariants(ar, VariableTable::AnyNonPrivateVars); } if (base == m_parent) { m_derivesFromRedeclaring = DirectFromRedeclared; getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars, false); getVariables()->setAttribute(VariableTable::NeedGlobalPointer); } else if (isInterface()) { m_derivesFromRedeclaring = DirectFromRedeclared; } setVolatile(); } else { derivedMagicMethods(super); super->collectMethods(ar, funcs, false, forInvoke); inheritedMagicMethods(super); if (super->derivesFromRedeclaring()) { if (base == m_parent) { m_derivesFromRedeclaring = IndirectFromRedeclared; getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars); } else if (isInterface()) { m_derivesFromRedeclaring = IndirectFromRedeclared; } setVolatile(); } else if (super->isVolatile()) { setVolatile(); } } } else {
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(")"); }
void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_className.empty()) { switch (m_type) { case ExtractFunction: cg.printf("extract(variables, "); FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false); cg.printf(")"); return; case CompactFunction: cg.printf("compact(variables, "); FunctionScope::outputCPPArguments(m_params, cg, ar, -1, true); cg.printf(")"); return; default: break; } } bool volatileCheck = false; ClassScopePtr cls; if (!m_className.empty()) { cls = ar->findClass(m_className); if (cls && !ar->checkClassPresent(m_origClassName)) { volatileCheck = true; cls->outputVolatileCheckBegin(cg, ar, cls->getOriginalName()); } } if (m_valid) { bool tooManyArgs = (m_params && m_params->outputCPPTooManyArgsPre(cg, ar, m_name)); if (!m_className.empty()) { cg.printf("%s%s::", Option::ClassPrefix, m_className.c_str()); if (m_name == "__construct" && cls) { FunctionScopePtr func = cls->findConstructor(ar, true); cg.printf("%s%s(", Option::MethodPrefix, func->getName().c_str()); } else { cg.printf("%s%s(", Option::MethodPrefix, m_name.c_str()); } } else { int paramCount = m_params ? m_params->getCount() : 0; if (m_name == "get_class" && ar->getClassScope() && paramCount == 0) { cg.printf("(\"%s\"", ar->getClassScope()->getOriginalName()); } else if (m_name == "get_parent_class" && ar->getClassScope() && paramCount == 0) { const std::string parentClass = ar->getClassScope()->getParent(); if (!parentClass.empty()) { cg.printf("(\"%s\"", ar->getClassScope()->getParent().c_str()); } else { cg.printf("(false"); } } else { if (m_noPrefix) { cg.printf("%s(", m_name.c_str()); } else { cg.printf("%s%s(", m_builtinFunction ? Option::BuiltinFunctionPrefix : Option::FunctionPrefix, m_name.c_str()); } } } FunctionScope::outputCPPArguments(m_params, cg, ar, m_extraArg, m_variableArgument, m_argArrayId); cg.printf(")"); if (tooManyArgs) { m_params->outputCPPTooManyArgsPost(cg, ar, m_voidReturn); } } else { if (m_className.empty()) { if (m_redeclared && !m_dynamicInvoke) { if (canInvokeFewArgs()) { cg.printf("%s->%s%s_few_args(", cg.getGlobals(ar), Option::InvokePrefix, m_name.c_str()); int left = Option::InvokeFewArgsCount; if (m_params && m_params->getCount()) { left -= m_params->getCount(); cg.printf("%d, ", m_params->getCount()); FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false); } else { cg.printf("0"); } for (int i = 0; i < left; i++) { cg.printf(", null_variant"); } cg.printf(")"); return; } else { cg.printf("%s->%s%s(", cg.getGlobals(ar), Option::InvokePrefix, m_name.c_str()); } } else { cg.printf("invoke(\"%s\", ", m_name.c_str()); } } else { bool inObj = m_parentClass && ar->getClassScope() && !dynamic_pointer_cast<FunctionScope>(ar->getScope())->isStatic(); if (m_redeclaredClass) { if (inObj) { // parent is redeclared cg.printf("parent->%sinvoke(\"%s\",", Option::ObjectPrefix, m_name.c_str()); } else { cg.printf("%s->%s%s->%sinvoke(\"%s\", \"%s\",", cg.getGlobals(ar), Option::ClassStaticsObjectPrefix, m_className.c_str(), Option::ObjectStaticPrefix, m_className.c_str(), m_name.c_str()); } } else if (m_validClass) { if (inObj) { cg.printf("%s%s::%sinvoke(\"%s\",", Option::ClassPrefix, m_className.c_str(), Option::ObjectPrefix, m_name.c_str()); } else { cg.printf("%s%s::%sinvoke(\"%s\", \"%s\",", Option::ClassPrefix, m_className.c_str(), Option::ObjectStaticPrefix, m_className.c_str(), m_name.c_str()); } } else { cg.printf("invoke_static_method(\"%s\", \"%s\",", m_className.c_str(), m_name.c_str()); } } if ((!m_params) || (m_params->getCount() == 0)) { cg.printf("Array()"); } else { FunctionScope::outputCPPArguments(m_params, cg, ar, -1, false); } bool needHash = true; if (m_className.empty()) { needHash = !(m_redeclared && !m_dynamicInvoke); } else { needHash = m_validClass || m_redeclaredClass; } if (!needHash) { cg.printf(")"); } else { cg.printf(", 0x%.16lXLL)", hash_string_i(m_name.data(), m_name.size())); } } if (volatileCheck) { cls->outputVolatileCheckEnd(cg); } }
ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) { ar->preOptimize(m_nameExp); ar->preOptimize(m_params); if (ar->getPhase() != AnalysisResult::SecondPreOptimize) { return ExpressionPtr(); } // optimize away various "exists" functions, this may trigger // dead code elimination and improve type-inference. if (m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() == 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); // system constant if (constants->isPresent(symbol)) { return CONSTANT("true"); } // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); // not found (i.e., undefined) if (!block) { if (symbol.find("::") == std::string::npos) { return CONSTANT("false"); } else { // e.g., defined("self::ZERO") return ExpressionPtr(); } } constants = block->getConstants(); // already set to be dynamic if (constants->isDynamic(symbol)) return ExpressionPtr(); ConstructPtr decl = constants->getValue(symbol); ExpressionPtr constValue = dynamic_pointer_cast<Expression>(decl); if (constValue->isScalar()) { return CONSTANT("true"); } else { return ExpressionPtr(); } break; } case FunctionExistsFunction: { const std::string &lname = Util::toLower(symbol); if (Option::DynamicInvokeFunctions.find(lname) == Option::DynamicInvokeFunctions.end()) { FunctionScopePtr func = ar->findFunction(lname); if (!func) { return CONSTANT("false"); } else if (!func->isVolatile()) { return CONSTANT("true"); } } break; } case InterfaceExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || !cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } default: ASSERT(false); } } } } return ExpressionPtr(); }
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) { if (m_className.empty()) { addUserFunction(ar, m_name); } else if (m_className != "parent") { addUserClass(ar, m_className); } else { m_parentClass = true; } if (ar->getPhase() == AnalysisResult::AnalyzeInclude) { CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude); ConstructPtr self = shared_from_this(); // We need to know the name of the constant so that we can associate it // with this file before we do type inference. if (m_className.empty() && m_type == DefineFunction) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ar->getFileScope()->declareConstant(ar, varName); } } // handling define("CONSTANT", ...); if (m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { constants->add(varName, NEW_TYPE(Some), value, ar, self); if (name->hasHphpNote("Dynamic")) { constants->setDynamic(ar, varName); } } } } } } if (m_type == UnserializeFunction) { ar->forceClassVariants(); } } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { // Look up the corresponding FunctionScope and ClassScope // for this function call { FunctionScopePtr func; ClassScopePtr cls; if (m_className.empty()) { func = ar->findFunction(m_name); } else { cls = ar->resolveClass(m_className); if (cls) { if (m_name == "__construct") { func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } } } if (func && !func->isRedeclaring()) { if (m_funcScope != func) { m_funcScope = func; Construct::recomputeEffects(); } } if (cls && !cls->isRedeclaring()) m_classScope = cls; } // check for dynamic constant and volatile function/class if (m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() >= 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); if (!constants->isPresent(symbol)) { // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); if (block) { // found the constant constants = block->getConstants(); // set to be dynamic constants->setDynamic(ar, symbol); } } break; } case FunctionExistsFunction: { FunctionScopePtr func = ar->findFunction(Util::toLower(symbol)); if (func && func->isUserFunction()) { func->setVolatile(); } break; } case InterfaceExistsFunction: case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isUserClass()) { cls->setVolatile(); } break; } default: ASSERT(false); } } } } } if (m_params) { if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (m_funcScope) { ExpressionList ¶ms = *m_params; int mpc = m_funcScope->getMaxParamCount(); for (int i = params.getCount(); i--; ) { ExpressionPtr p = params[i]; if (i < mpc ? m_funcScope->isRefParam(i) : m_funcScope->isReferenceVariableArgument()) { p->setContext(Expression::RefValue); } else if (!(p->getContext() & Expression::RefParameter)) { p->clearContext(Expression::RefValue); } } } else { FunctionScopePtr func = ar->findFunction(m_name); if (func && func->isRedeclaring()) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); if (info) { for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } } else { m_params->markParams(false); } } } m_params->analyzeProgram(ar); } }
void ClassScope::importUsedTraits(AnalysisResultPtr ar) { // Trait flattening is supposed to happen only when we have awareness of the // whole program. assert(Option::WholeProgram); if (m_traitStatus == FLATTENED) return; if (m_traitStatus == BEING_FLATTENED) { getStmt()->analysisTimeFatal( Compiler::CyclicDependentTraits, "Cyclic dependency between traits involving %s", getScopeName().c_str() ); return; } if (m_usedTraitNames.size() == 0) { m_traitStatus = FLATTENED; return; } m_traitStatus = BEING_FLATTENED; m_numDeclMethods = m_functionsVec.size(); // First, make sure that parent classes have their traits imported. if (!m_parent.empty()) { ClassScopePtr parent = ar->findClass(m_parent); if (parent) { parent->importUsedTraits(ar); } } TMIData tmid; if (isTrait()) { for (auto const& req : getClassRequiredExtends()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || rCls->isFinal() || rCls->isInterface()) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_EXTENDS, m_scopeName.c_str(), req.c_str(), req.c_str() ); } } for (auto const& req : getClassRequiredImplements()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || !(rCls->isInterface())) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_IMPLEMENTS, m_scopeName.c_str(), req.c_str(), req.c_str() ); } } } // Find trait methods to be imported. for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); if (!tCls || !(tCls->isTrait())) { setAttribute(UsesUnknownTrait); // XXX: is this useful ... for anything? getStmt()->analysisTimeFatal( Compiler::UnknownTrait, Strings::TRAITS_UNKNOWN_TRAIT, m_usedTraitNames[i].c_str() ); } // First, make sure the used trait is flattened. tCls->importUsedTraits(ar); findTraitMethodsToImport(ar, tCls, tmid); // Import any interfaces implemented. tCls->getInterfaces(ar, m_bases, /* recursive */ false); importClassRequirements(ar, tCls); } // Apply rules. applyTraitRules(tmid); // Remove methods declared on the current class from the trait import list; // the class methods take precedence. for (auto const& methName : tmid.methodNames()) { if (findFunction(ar, methName, false /* recursive */, false /* exclIntfBase */)) { // This does not affect the methodNames() vector. tmid.erase(methName); } } auto traitMethods = tmid.finish(this); std::map<string, MethodStatementPtr, stdltistr> importedTraitMethods; std::vector<std::pair<string,const TraitMethod*>> importedTraitsWithOrigName; // Actually import the methods. for (auto const& mdata : traitMethods) { if ((mdata.tm.modifiers ? mdata.tm.modifiers : mdata.tm.method->getModifiers() )->isAbstract()) { // Skip abstract methods, if the method already exists in the class. if (findFunction(ar, mdata.name, true) || importedTraitMethods.count(mdata.name)) { continue; } } auto sourceName = mdata.tm.ruleStmt ? ((TraitAliasStatement*)mdata.tm.ruleStmt.get())->getMethodName() : mdata.name; importedTraitMethods[sourceName] = MethodStatementPtr(); importedTraitsWithOrigName.push_back( std::make_pair(sourceName, &mdata.tm)); } // Make sure there won't be 2 constructors after importing auto traitConstruct = importedTraitMethods.count("__construct"); auto traitName = importedTraitMethods.count(getScopeName()); auto classConstruct = m_functions.count("__construct"); auto className = m_functions.count(getScopeName()); if ((traitConstruct && traitName) || (traitConstruct && className) || (classConstruct && traitName)) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, "%s has colliding constructor definitions coming from traits", getScopeName().c_str() ); } for (auto const& traitPair : importedTraitsWithOrigName) { auto traitMethod = traitPair.second; MethodStatementPtr newMeth = importTraitMethod( *traitMethod, ar, traitMethod->originalName ); } // Import trait properties importTraitProperties(ar); m_traitStatus = FLATTENED; }
void ClassStatement::outputCPP(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, m_name.c_str(), Option::ClassStaticsPrefix, classScope->getId().c_str()); } if (classScope->isVolatile()) { cg.printf("g->declareClass(\"%s\");\n", m_name.c_str()); } return; } if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } ar->pushScope(classScope); string clsNameStr = classScope->getId(); 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: { ClassScopePtr parCls; if (!m_parent.empty()) parCls = ar->findClass(m_parent); cg.printf("class %s%s", Option::ClassPrefix, clsName); bool derived = false; if (!m_parent.empty() && classScope->derivesFrom(ar, m_parent)) { if (parCls->isRedeclaring()) { cg.printf(" : public DynamicObjectData"); } else { cg.printf(" : virtual public %s%s", Option::ClassPrefix, parCls->getId().c_str()); } derived = true; } if (m_base) { 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)) { // temporary fix for inheriting from a re-declaring class string id = intfClassScope->getId(); if (!derived) { derived = true; cg.printf(" :"); } else { cg.printf(","); } cg.printf(" virtual public %s%s", Option::ClassPrefix, id.c_str()); } } } if (!derived) { const char *op = derived ? "," : " :"; if (classScope->derivesFromRedeclaring()) { cg.printf("%s public DynamicObjectData", op); } else { cg.printf("%s virtual public ObjectData", op); } } cg.indentBegin(" {\n"); if (Option::GenerateCPPMacros) { vector<string> bases; getAllParents(ar, bases); cg.indentBegin("BEGIN_CLASS_MAP(%s)\n", clsName); for (unsigned int i = 0; i < bases.size(); i++) { cg.printf("PARENT_CLASS(%s)\n", bases[i].c_str()); } cg.indentEnd("END_CLASS_MAP(%s)\n", clsName); } if (Option::GenerateCPPMacros) { bool dyn = classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared; bool idyn = classScope->derivesFromRedeclaring() == ClassScope::IndirectFromRedeclared; bool redec = classScope->isRedeclaring(); if (!classScope->derivesFromRedeclaring()) { cg.printf("DECLARE_CLASS(%s, %s, %s)\n", clsName, m_originalName.c_str(), m_parent.empty() ? "ObjectData" : m_parent.c_str()); } else { cg.printf("DECLARE_DYNAMIC_CLASS(%s, %s)\n", clsName, m_originalName.c_str()); } if (cg.getOutput() == CodeGenerator::SystemCPP || Option::EnableEval >= Option::LimitedEval) { cg.printf("DECLARE_INVOKES_FROM_EVAL\n"); } if (dyn || idyn || redec) { if (redec) { cg.indentBegin("Variant %sroot_invoke(const char* s, CArrRef ps, " "int64 h, bool f = true) {\n", Option::ObjectPrefix); cg.printf("return root->%sinvoke(s, ps, h, f);\n", Option::ObjectPrefix); cg.indentEnd("}\n"); cg.indentBegin("Variant %sroot_invoke_few_args(const char* s, " "int64 h, int count", Option::ObjectPrefix); for (int i = 0; i < Option::InvokeFewArgsCount; i++) { cg.printf(", CVarRef a%d = null_variant", i); } cg.printf(") {\n"); cg.printf("return root->%sinvoke_few_args(s, h, count", Option::ObjectPrefix); for (int i = 0; i < Option::InvokeFewArgsCount; i++) { cg.printf(", a%d", i); } cg.printf(");\n"); cg.indentEnd("}\n"); if (!dyn && !idyn) cg.printf("private: ObjectData* root;\n"); cg.printf("public:\n"); } string conInit = ":"; if (dyn) { conInit += "DynamicObjectData(\"" + m_parent + "\", r)"; } else if (idyn) { conInit += string(Option::ClassPrefix) + parCls->getId() + "(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"); } if (!classScope->derivesFromRedeclaring()){ classScope->getVariables()->outputCPPPropertyDecl(cg, ar); } 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"); } 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 %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(%s%s(NEW(%s%s)(root))->" "dynCreate(params, init));\n", Option::SmartPtrPrefix, clsName, 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"); } } 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()); } } cg.setContext(CodeGenerator::CppConstructor); if (m_stmt) m_stmt->outputCPP(cg, ar); 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->derivesFrom(ar, m_parent) && 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()); } 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) && intfClassScope->isUserClass()) { if (first) { cgCls.printf(" implements "); first = false; } else { cgCls.printf(", "); } cgCls.printf(intfClassScope->getOriginalName()); } } } 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(); }
void MethodStatement::outputJavaFFICPPStub(CodeGenerator &cg, AnalysisResultPtr ar) { // TODO translate PHP namespace once that is supported string packageName = Option::JavaFFIRootPackage; FunctionScopePtr funcScope = m_funcScope.lock(); bool varArgs = funcScope->isVariableArgument(); bool ret = funcScope->getReturnType(); bool inClass = !m_className.empty(); bool isStatic = !inClass || m_modifiers->isStatic(); string fname = funcScope->getId(); int ac = funcScope->getMaxParamCount(); bool exposeNative = !(ac > 0 || varArgs || !isStatic || !ret && inClass); if (inClass && m_modifiers->isAbstract()) { // skip all the abstract methods, because hphp doesn't generate code // for them return; } if (fname == "__lval" || fname == "__offsetget_lval") return; const char *clsName; if (inClass) { // uses capitalized original class name ClassScopePtr cls = ar->findClass(m_className); clsName = cls->getOriginalName(); } else { clsName = "HphpMain"; } string mangledName = "Java." + packageName + "." + clsName + "." + fname + (exposeNative ? "" : "_native"); // all the existing "_" are replaced as "_1" Util::replaceAll(mangledName, "_", "_1"); Util::replaceAll(mangledName, ".", "_"); cg.printf("JNIEXPORT %s JNICALL\n", ret ? "jobject" : "void"); cg.printf("%s(JNIEnv *env, %s target", mangledName.c_str(), (isStatic ? "jclass" : "jobject")); ostringstream args; bool first = true; if (!isStatic) { // instance method also gets an additional argument, which is a Variant // pointer to the target, encoded in int64 first = false; cg.printf(", jlong targetPtr"); args << "(Variant *)targetPtr"; } for (int i = 0; i < ac; i++) { cg.printf(", jlong a%d", i); if (first) first = false; else args << ", "; args << "(Variant *)a" << i; } if (varArgs) { cg.printf(", jlong va"); if (!first) args << ", "; args << "(Variant *)va"; } if (cg.getContext() == CodeGenerator::JavaFFICppDecl) { // java_stubs.h cg.printf(");\n\n"); return; } cg.indentBegin(") {\n"); // support static/instance methods if (ret) { cg.printf("void *result;\n"); cg.printf("int kind = "); cg.printf("%s%s%s(&result", Option::FFIFnPrefix, (inClass ? (m_className + "_cls_").c_str() : ""), fname.c_str()); if (!isStatic || ac > 0 || varArgs) cg.printf(", "); } else { cg.printf("%s%s%s(", Option::FFIFnPrefix, (inClass ? (m_className + "_cls_").c_str() : ""), fname.c_str()); } cg.printf("%s);\n", args.str().c_str()); if (ret) { if (!inClass) { // HphpMain extends hphp.Hphp. cg.printf("jclass hphp = env->GetSuperclass(target);\n"); } else { cg.printf("jclass hphp = env->FindClass(\"hphp/Hphp\");\n"); } cg.printf("return exportVariantToJava(env, hphp, result, kind);\n"); } cg.indentEnd("} /* function */\n\n"); }
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 Type::Intersection(AnalysisResultPtr ar, TypePtr from, TypePtr to) { // Special case: if we're casting to Some or Any, return the "from" type; // if we're casting to Variant, return Variant. if (to->m_kindOf == KindOfSome || to->m_kindOf == KindOfAny) { return from; } else if (to->m_kindOf == KindOfVariant) { return Variant; } int resultKind = to->m_kindOf & from->m_kindOf; std::string resultName = ""; if (resultKind & KindOfObject) { // if they're the same, or we don't know one's name, then use // the other if (to->m_name == from->m_name || from->m_name.empty()) { resultName = to->m_name; } else if (to->m_name.empty()) { resultName = from->m_name; } else { // make sure there's a subclass relation ClassScopePtr cls = ar->findClass(from->m_name); if (cls) { if (cls->derivesFrom(ar, to->m_name, true, false)) { resultName = to->m_name; } else { resultKind &= ~KindOfObject; } } } } TypePtr res; // If there is overlap (for instance, they were the same, or we've narrowed // down something like Sequenece to be more specific), then return the // intersection of the types. if (resultKind) { res = TypePtr(new Type((KindOf)resultKind, resultName)); } else if (from->mustBe(KindOfObject) && to->m_kindOf == KindOfPrimitive) { // Special case Object -> Primitive: can we tostring it? if (!from->m_name.empty()) { ClassScopePtr cls = ar->findClass(from->m_name); if (cls && cls->findFunction(ar, "__tostring", true)) { res = Type::String; } } // Otherwise, return Byte res = Byte; } else if (from->m_kindOf == KindOfBoolean && to->mustBe(KindOfNumeric | KindOfArray | KindOfString) && !IsExactType(to->m_kindOf)) { res = Byte; } else { res = to; } if (from->mustBe(KindOfBoolean) && to->m_kindOf == KindOfPrimitive) { res = Byte; } return res; }
void InterfaceStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopeRawPtr classScope = getClassScope(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isVolatile()) { cg_printf("g->CDEC(%s) = true;\n", cg.formatLabel(m_name).c_str()); } return; } string clsNameStr = classScope->getId(cg); const char *clsName = clsNameStr.c_str(); switch (cg.getContext()) { case CodeGenerator::CppForwardDeclaration: if (Option::GenerateCPPMacros) { if (!Option::UseVirtualDispatch || classScope->isRedeclaring()) { cg_printf("FORWARD_DECLARE_GENERIC_INTERFACE(%s);\n", clsName); } else { cg_printf("FORWARD_DECLARE_INTERFACE(%s);\n", clsName); } } break; case CodeGenerator::CppDeclaration: { printSource(cg); 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(ar, intf)) { string id = intfClassScope->getId(cg); cg_printf("%s public %s%s", sep, Option::ClassPrefix, id.c_str()); sep = ","; } } } cg_indentBegin(" {\n"); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("};\n"); } break; case CodeGenerator::CppImplementation: // do nothing 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"; 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 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; } }
bool DynamicFunctionCall::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state) { bool nonStatic = !m_class && m_className.empty(); if (!nonStatic && !m_class && !m_validClass && !m_redeclared) return FunctionCall::preOutputCPP(cg, ar, state); // Short circuit out if inExpression() returns false if (!ar->inExpression()) return true; if (m_class) { m_class->preOutputCPP(cg, ar, state); } m_nameExp->preOutputCPP(cg, ar, state); ar->wrapExpressionBegin(cg); m_ciTemp = cg.createNewId(shared_from_this()); bool lsb = false; ClassScopePtr cls; if (m_validClass) { cls = ar->findClass(m_className); } if (!m_classScope && !m_className.empty() && m_cppTemp.empty() && m_origClassName != "self" && m_origClassName != "parent" && m_origClassName != "static") { // Create a temporary to hold the class name, in case it is not a // StaticString. m_clsNameTemp = cg.createNewId(shared_from_this()); cg_printf("CStrRef clsName%d(", m_clsNameTemp); cg_printString(m_origClassName, ar, shared_from_this()); cg_printf(");\n"); } if (nonStatic) { cg_printf("const CallInfo *cit%d;\n", m_ciTemp); cg_printf("void *vt%d;\n", m_ciTemp); cg_printf("get_call_info_or_fail(cit%d, vt%d, ", m_ciTemp, m_ciTemp); } else { cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp); if (m_class) { if (m_class->is(KindOfScalarExpression)) { ASSERT(strcasecmp(dynamic_pointer_cast<ScalarExpression>(m_class)-> getString().c_str(), "static") == 0); cg_printf("mcp%d.staticMethodCall(", m_ciTemp); cg_printf("\"static\""); lsb = true; } else { cg_printf("mcp%d.dynamicNamedCall(", m_ciTemp); m_class->outputCPP(cg, ar); } cg_printf(", "); } else if (m_validClass) { cg_printf("mcp%d.staticMethodCall(\"%s\", ", m_ciTemp, cls->getId(cg).c_str()); } else { cg_printf("mcp%d.staticMethodCall(\"%s\", ", m_ciTemp, m_className.c_str()); } } if (m_nameExp->is(Expression::KindOfSimpleVariable)) { m_nameExp->outputCPP(cg, ar); } else { cg_printf("("); m_nameExp->outputCPP(cg, ar); cg_printf(")"); } if (!nonStatic) { cg_printf(");\n"); if (lsb) cg_printf("mcp%d.lateStaticBind(info);\n"); cg_printf("const CallInfo *&cit%d = mcp%d.ci;\n", m_ciTemp, m_ciTemp); if (m_validClass) { cg_printf("%s%s::%sget_call_info(mcp%d", Option::ClassPrefix, cls->getId(cg).c_str(), Option::ObjectStaticPrefix, m_ciTemp, m_className.c_str()); } else if (m_redeclared) { cg_printf("g->%s%s->%sget_call_info(mcp%d", Option::ClassStaticsObjectPrefix, m_className.c_str(), Option::ObjectStaticPrefix, m_ciTemp, m_className.c_str()); } } if (nonStatic || !m_class) { cg_printf(");\n"); } if (m_params && m_params->getCount() > 0) { ar->pushCallInfo(m_ciTemp); m_params->preOutputCPP(cg, ar, state); ar->popCallInfo(); } ar->pushCallInfo(m_ciTemp); preOutputStash(cg, ar, state); ar->popCallInfo(); if (!(state & FixOrder)) { cg_printf("id(%s);\n", cppTemp().c_str()); } return true; }
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (cg.getContext() == CodeGenerator::NoContext) { InterfaceStatement::outputCPPImpl(cg, ar); return; } ClassScopeRawPtr classScope = getClassScope(); if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } string clsNameStr = classScope->getId(); const char *clsName = clsNameStr.c_str(); switch (cg.getContext()) { case CodeGenerator::CppDeclaration: { if (Option::GenerateCPPMacros) { classScope->outputForwardDeclaration(cg); } classScope->outputCPPGlobalTableWrappersDecl(cg, ar); bool system = cg.getOutput() == CodeGenerator::SystemCPP; ClassScopePtr parCls; if (!m_parent.empty()) { parCls = ar->findClass(m_parent); if (parCls && parCls->isRedeclaring()) parCls.reset(); } if (Option::GenerateCppLibCode) { cg.printDocComment(classScope->getDocComment()); } cg_printf("class %s%s", Option::ClassPrefix, clsName); if (!m_parent.empty() && classScope->derivesDirectlyFrom(m_parent)) { if (!parCls) { cg_printf(" : public DynamicObjectData"); } else { cg_printf(" : public %s%s", Option::ClassPrefix, parCls->getId().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(intf) && (!parCls || !parCls->derivesFrom(ar, intf, true, false))) { string id = intfClassScope->getId(); cg_printf(", public %s%s", Option::ClassPrefix, id.c_str()); } } } cg_indentBegin(" {\n"); cg_printf("public:\n"); cg.printSection("Properties"); if (classScope->getVariables()->outputCPPPropertyDecl( cg, ar, classScope->derivesFromRedeclaring())) { cg.printSection("Destructor"); cg_printf("~%s%s() NEVER_INLINE {}", Option::ClassPrefix, clsName); } if (Option::GenerateCppLibCode) { cg.printSection("Methods"); classScope->outputMethodWrappers(cg, ar); cg.printSection(">>>>>>>>>> Internal Implementation <<<<<<<<<<"); cg_printf("// NOTE: Anything below is subject to change. " "Use everything above instead.\n"); } cg.printSection("Class Map"); bool hasEmitCppCtor = false; bool needsCppCtor = classScope->needsCppCtor(); bool needsInit = classScope->needsInitMethod(); bool disableDestructor = !classScope->canSkipCreateMethod(ar) || (!classScope->derivesFromRedeclaring() && !classScope->hasAttribute(ClassScope::HasDestructor, ar)); if (Option::GenerateCPPMacros) { bool dyn = classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared; bool idyn = parCls && classScope->derivesFromRedeclaring() == ClassScope::IndirectFromRedeclared; bool redec = classScope->isRedeclaring(); if (!parCls && !m_parent.empty()) { assert(dyn); } if (!classScope->derivesFromRedeclaring()) { outputCPPClassDecl(cg, ar, clsName, m_originalName.c_str(), parCls ? parCls->getId().c_str() : "ObjectData"); } else { cg_printf("DECLARE_DYNAMIC_CLASS(%s, %s, %s)\n", clsName, m_originalName.c_str(), dyn || !parCls ? "DynamicObjectData" : parCls->getId().c_str()); } if (classScope->checkHasPropTable()) { cg_printf("static const ClassPropTable %sprop_table;\n", Option::ObjectStaticPrefix); } bool hasGet = classScope->getAttribute( ClassScope::HasUnknownPropGetter); bool hasSet = classScope->getAttribute( ClassScope::HasUnknownPropSetter); bool hasIsset = classScope->getAttribute( ClassScope::HasUnknownPropTester); bool hasUnset = classScope->getAttribute( ClassScope::HasPropUnsetter); bool hasCall = classScope->getAttribute( ClassScope::HasUnknownMethodHandler); bool hasCallStatic = classScope->getAttribute( ClassScope::HasUnknownStaticMethodHandler); bool hasRootParam = classScope->derivedByDynamic() && (redec || dyn || idyn); string lateInit = ""; if (redec && classScope->derivedByDynamic()) { if (!dyn && !idyn && (!parCls || parCls->isUserClass())) { cg_printf("private: ObjectData* root;\n"); cg_printf("public:\n"); cg_printf("virtual ObjectData *getRoot() { return root; }\n"); lateInit = "root(r ? r : this)"; } } string callbacks = Option::ClassStaticsCallbackPrefix + clsNameStr; string conInit = ""; if (dyn) { conInit = "DynamicObjectData(cb, \"" + CodeGenerator::EscapeLabel(m_parent) + "\", "; if (hasRootParam) { conInit += "r)"; } else { conInit += "this)"; } } else if (parCls) { conInit = string(Option::ClassPrefix) + parCls->getId() + "("; if (parCls->derivedByDynamic() && (parCls->isRedeclaring() || parCls->derivesFromRedeclaring() != ClassScope::FromNormal)) { if (hasRootParam) { conInit += "r ? r : "; } conInit += "this, "; } conInit += "cb)"; } else { if (system) { conInit = "ExtObjectData(cb)"; } else { if (hasRootParam) { conInit = "ObjectData(cb, r)"; } else { conInit = "ObjectData(cb, false)"; } } } cg_printf("%s%s(%sconst ObjectStaticCallbacks *cb = &%s%s) : %s", Option::ClassPrefix, clsName, hasRootParam ? "ObjectData* r = NULL," : "", callbacks.c_str(), redec ? ".oscb" : "", conInit.c_str()); if (needsCppCtor) { cg_printf(", "); cg.setContext(CodeGenerator::CppConstructor); ASSERT(!cg.hasInitListFirstElem()); m_stmt->outputCPP(cg, ar); cg.clearInitListFirstElem(); cg.setContext(CodeGenerator::CppDeclaration); } if (!lateInit.empty()) { cg_printf(", %s", lateInit.c_str()); } cg_indentBegin(" {%s", hasGet || hasSet || hasIsset || hasUnset || hasCall || hasCallStatic || disableDestructor || hasRootParam ? "\n" : ""); if (hasRootParam) { cg_printf("setId(r);\n"); } if (hasGet) cg_printf("setAttribute(UseGet);\n"); if (hasSet) cg_printf("setAttribute(UseSet);\n"); if (hasIsset) cg_printf("setAttribute(UseIsset);\n"); if (hasUnset) cg_printf("setAttribute(UseUnset);\n"); if (hasCall) cg_printf("setAttribute(HasCall);\n"); if (hasCallStatic) cg_printf("setAttribute(HasCallStatic);\n"); if (disableDestructor) { cg_printf("if (!hhvm) setAttribute(NoDestructor);\n"); } cg_indentEnd("}\n"); hasEmitCppCtor = true; } if (needsCppCtor && !hasEmitCppCtor) { cg_printf("%s%s() : ", Option::ClassPrefix, clsName); cg.setContext(CodeGenerator::CppConstructor); ASSERT(!cg.hasInitListFirstElem()); m_stmt->outputCPP(cg, ar); cg.clearInitListFirstElem(); cg.setContext(CodeGenerator::CppDeclaration); cg_printf(" {%s}\n", disableDestructor ? " if (!hhvm) setAttribute(NoDestructor); " : ""); } if (needsInit) { cg_printf("void init();\n"); } // doCall if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) { cg_printf("Variant doCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } if (classScope->getAttribute(ClassScope::HasInvokeMethod)) { FunctionScopePtr func = classScope->findFunction(ar, "__invoke", false); ASSERT(func); if (!func->isAbstract()) { cg_printf("const CallInfo *" "t___invokeCallInfoHelper(void *&extra);\n"); } } if (classScope->isRedeclaring() && !classScope->derivesFromRedeclaring() && classScope->derivedByDynamic()) { cg_printf("Variant doRootCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } if (m_stmt) m_stmt->outputCPP(cg, ar); { std::set<string> done; classScope->outputCPPStaticMethodWrappers(cg, ar, done, clsName); } if (Option::GenerateCPPMacros) { classScope->outputCPPJumpTableDecl(cg, ar); } cg_indentEnd("};\n"); classScope->outputCPPDynamicClassDecl(cg); 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); } classScope->outputCPPSupportMethodsImpl(cg, ar); bool needsInit = classScope->needsInitMethod(); if (needsInit) { 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 { ClassScopePtr parCls = ar->findClass(m_parent); cg_printf("%s%s::init();\n", Option::ClassPrefix, parCls->getId().c_str()); } } if (classScope->getVariables()-> getAttribute(VariableTable::NeedGlobalPointer)) { cg.printDeclareGlobals(); } cg.setContext(CodeGenerator::CppInitializer); 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"); } 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"; std::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(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; } }
void ClassScope::importUsedTraits(AnalysisResultPtr ar) { // Trait flattening is supposed to happen only when we have awareness of // the whole program. assert(Option::WholeProgram); if (m_traitStatus == FLATTENED) return; if (m_traitStatus == BEING_FLATTENED) { getStmt()->analysisTimeFatal( Compiler::CyclicDependentTraits, "Cyclic dependency between traits involving %s", getOriginalName().c_str() ); return; } if (m_usedTraitNames.size() == 0) { m_traitStatus = FLATTENED; return; } m_traitStatus = BEING_FLATTENED; // First, make sure that parent classes have their traits imported if (!m_parent.empty()) { ClassScopePtr parent = ar->findClass(m_parent); if (parent) { parent->importUsedTraits(ar); } } if (isTrait()) { for (auto const& req : getTraitRequiredExtends()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || rCls->isFinal() || rCls->isInterface()) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_EXTENDS, m_originalName.c_str(), req.c_str(), req.c_str() ); } } for (auto const& req : getTraitRequiredImplements()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || !(rCls->isInterface())) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_IMPLEMENTS, m_originalName.c_str(), req.c_str(), req.c_str() ); } } } // Find trait methods to be imported for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); if (!tCls || !(tCls->isTrait())) { setAttribute(UsesUnknownTrait); // XXX: is this useful ... for anything? getStmt()->analysisTimeFatal( Compiler::UnknownTrait, Strings::TRAITS_UNKNOWN_TRAIT, m_usedTraitNames[i].c_str() ); } // First, make sure the used trait is flattened tCls->importUsedTraits(ar); findTraitMethodsToImport(ar, tCls); // Import any interfaces implemented tCls->getInterfaces(ar, m_bases, false); } for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { // Requirements must be checked in a separate loop because the // interfaces required by one trait may be implemented by another trait // whose "use" appears later in the class' scope ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); importTraitRequirements(ar, tCls); } // Apply rules applyTraitRules(ar); // Remove trait abstract methods provided by other traits and duplicates removeSpareTraitAbstractMethods(ar); // Apply precedence of current class over used traits for (MethodToTraitListMap::iterator iter = m_importMethToTraitMap.begin(); iter != m_importMethToTraitMap.end(); ) { MethodToTraitListMap::iterator thisiter = iter; iter++; if (findFunction(ar, thisiter->first, 0, 0) != FunctionScopePtr()) { m_importMethToTraitMap.erase(thisiter); } } std::map<string, MethodStatementPtr> importedTraitMethods; std::vector<std::pair<string,const TraitMethod*>> importedTraitsWithOrigName; // Actually import the methods for (MethodToTraitListMap::const_iterator iter = m_importMethToTraitMap.begin(); iter != m_importMethToTraitMap.end(); iter++) { // The rules may rule out a method from all traits. // In this case, simply don't import the method. if (iter->second.size() == 0) { continue; } // Consistency checking: each name must only refer to one imported method if (iter->second.size() > 1) { getStmt()->analysisTimeFatal( Compiler::MethodInMultipleTraits, Strings::METHOD_IN_MULTIPLE_TRAITS, iter->first.c_str() ); } else { TraitMethodList::const_iterator traitMethIter = iter->second.begin(); if ((traitMethIter->m_modifiers ? traitMethIter->m_modifiers : traitMethIter->m_method->getModifiers())->isAbstract()) { // Skip abstract methods, if method already exists in the class if (findFunction(ar, iter->first, true) || importedTraitMethods.count(iter->first)) { continue; } } string sourceName = traitMethIter->m_ruleStmt ? toLower(((TraitAliasStatement*)traitMethIter->m_ruleStmt.get())-> getMethodName()) : iter->first; importedTraitMethods[sourceName] = MethodStatementPtr(); importedTraitsWithOrigName.push_back( make_pair(sourceName, &*traitMethIter)); } } // Make sure there won't be 2 constructors after importing auto traitConstruct = importedTraitMethods.count("__construct"); auto traitName = importedTraitMethods.count(getName()); auto classConstruct = m_functions.count("__construct"); auto className = m_functions.count(getName()); if ((traitConstruct && traitName) || (traitConstruct && className) || (classConstruct && traitName)) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, "%s has colliding constructor definitions coming from traits", getOriginalName().c_str() ); } for (unsigned i = 0; i < importedTraitsWithOrigName.size(); i++) { const string &sourceName = importedTraitsWithOrigName[i].first; const TraitMethod *traitMethod = importedTraitsWithOrigName[i].second; MethodStatementPtr newMeth = importTraitMethod( *traitMethod, ar, toLower(traitMethod->m_originalName), importedTraitMethods); if (newMeth) { importedTraitMethods[sourceName] = newMeth; } } // Import trait properties importTraitProperties(ar); m_traitStatus = FLATTENED; }
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(); }
void ClassScope::collectMethods(AnalysisResultPtr ar, StringToFunctionScopePtrMap &funcs, bool collectPrivate) { // add all functions this class has for (FunctionScopePtrVec::const_iterator iter = m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) { const FunctionScopePtr &fs = *iter; if (!collectPrivate && fs->isPrivate()) continue; FunctionScopePtr &func = funcs[fs->getScopeName()]; if (!func) { func = fs; } else { func->setVirtual(); fs->setVirtual(); fs->setHasOverride(); if (fs->isFinal()) { std::string s__MockClass = "__MockClass"; ClassScopePtr derivedClass = func->getContainingClass(); if (derivedClass->m_userAttributes.find(s__MockClass) == derivedClass->m_userAttributes.end()) { Compiler::Error(Compiler::InvalidOverride, fs->getStmt(), func->getStmt()); } } } } int n = m_bases.size(); for (int i = 0; i < n; i++) { const string &base = m_bases[i]; ClassScopePtr super = ar->findClass(base); if (super) { if (super->isRedeclaring()) { const ClassScopePtrVec &classes = ar->findRedeclaredClasses(base); StringToFunctionScopePtrMap pristine(funcs); for (auto& cls : classes) { StringToFunctionScopePtrMap cur(pristine); derivedMagicMethods(cls); cls->collectMethods(ar, cur, false); inheritedMagicMethods(cls); funcs.insert(cur.begin(), cur.end()); } m_derivesFromRedeclaring = Derivation::Redeclaring; setVolatile(); } else { derivedMagicMethods(super); super->collectMethods(ar, funcs, false); inheritedMagicMethods(super); if (super->derivesFromRedeclaring() == Derivation::Redeclaring) { m_derivesFromRedeclaring = Derivation::Redeclaring; setVolatile(); } else if (super->isVolatile()) { setVolatile(); } } } else { Compiler::Error(Compiler::UnknownBaseClass, m_stmt, base); if (base == m_parent) { ar->declareUnknownClass(m_parent); m_derivesFromRedeclaring = Derivation::Redeclaring; setVolatile(); } else { /* * TODO(#3685260): this should not be removing interfaces from * the base list. */ if (isInterface()) { m_derivesFromRedeclaring = Derivation::Redeclaring; } m_bases.erase(m_bases.begin() + i); n--; i--; } } } }