TypePtr ParameterExpression::getTypeSpec(AnalysisResultPtr ar, bool forInference) { const Type::TypePtrMap &types = Type::GetTypeHintTypes(); Type::TypePtrMap::const_iterator iter; TypePtr ret; if (m_type.empty() || (forInference && m_defaultValue)) { ret = Type::Some; } else if ((iter = types.find(m_type)) != types.end()) { ret = iter->second; } else { 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); } } // we still want the above to run, so to record errors and infer defaults if (m_ref && forInference) { ret = Type::Variant; } return ret; }
void SimpleVariable::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_this) { ASSERT((getContext() & ObjectContext) == 0); if (hasContext(OprLValue) || hasContext(AssignmentLHS)) { cg_printf("throw_assign_this()"); } else if (hasContext(DeepOprLValue) || hasContext(DeepAssignmentLHS) || hasContext(LValue)) { // $this[] op= ...; or $this[] = ... cg_printf("Variant(GET_THIS())"); } else { ClassScopePtr cls = getOriginalClass(); if (cls->derivedByDynamic()) { cg_printf("Object(GET_THIS())"); } else { cg_printf("GET_THIS_TYPED(%s)", cls->getId(cg).c_str()); } } } else if (m_superGlobal) { VariableTablePtr variables = getScope()->getVariables(); string name = variables->getGlobalVariableName(cg, ar, m_name); cg_printf("g->%s", name.c_str()); } else if (m_globals) { cg_printf("get_global_array_wrapper()"); } else { const char *prefix = getScope()->getVariables()->getVariablePrefix(m_sym); cg_printf("%s%s", prefix, cg.formatLabel(m_name).c_str()); } }
TypePtr ParameterExpression::getTypeSpecForClass(AnalysisResultPtr ar, bool forInference) { TypePtr ret; if (forInference) { ClassScopePtr cls = ar->findClass(m_type); if (!cls || cls->isRedeclaring() || cls->derivedByDynamic()) { if (!cls && getScope()->isFirstPass() && !ar->isTypeAliasName(m_type)) { ConstructPtr self = shared_from_this(); Compiler::Error(Compiler::UnknownClass, self); } ret = Type::Variant; } if (cls) { // Classes must be redeclaring if there are also type aliases // with the same name. assert(!ar->isTypeAliasName(m_type) || cls->isRedeclaring()); } } if (!ret) { ret = ar->isTypeAliasName(m_type) || !Option::WholeProgram ? Type::Variant : Type::CreateObjectType(m_type); } always_assert(ret); return ret; }
TypePtr ParameterExpression::getTypeSpecForClass(AnalysisResultPtr ar, bool forInference) { TypePtr ret; if (forInference) { ClassScopePtr cls = ar->findClass(m_type); if (!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); } always_assert(ret); return ret; }
TypePtr SimpleVariable::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { TypePtr ret; ConstructPtr construct = shared_from_this(); BlockScopePtr scope = getScope(); VariableTablePtr variables = scope->getVariables(); // check function parameter that can occur in lval context if (m_sym && m_sym->isParameter() && m_context & (LValue | RefValue | DeepReference | UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) { m_sym->setLvalParam(); } if (m_this) { ClassScopePtr cls = getOriginalClass(); if (!hasContext(ObjectContext) && cls->derivedByDynamic()) { ret = Type::Object; } else { ret = Type::CreateObjectType(cls->getName()); } if (!hasContext(ObjectContext) && variables->getAttribute(VariableTable::ContainsDynamicVariable)) { ret = variables->add(m_sym, ret, true, ar, construct, scope->getModifiers()); } } else if ((m_context & (LValue|Declaration)) && !(m_context & (ObjectContext|RefValue))) { if (m_globals) { ret = Type::Variant; } else if (m_superGlobal) { ret = m_superGlobalType; } else if (m_superGlobalType) { // For system ret = variables->add(m_sym, m_superGlobalType, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } else { ret = variables->add(m_sym, type, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } } else { if (m_superGlobalType) { ret = m_superGlobalType; } else if (m_globals) { ret = Type::Array; } else if (scope->is(BlockScope::ClassScope)) { // ClassVariable expression will come to this block of code ret = getClassScope()->checkProperty(m_sym, type, true, ar); } else { TypePtr tmpType = type; if (m_context & RefValue) { tmpType = Type::Variant; coerce = true; } int p; ret = variables->checkVariable(m_sym, tmpType, coerce, ar, construct, p); } } TypePtr actual = propagateTypes(ar, ret); setTypes(ar, actual, type); if (Type::SameType(actual, ret)) { m_implementedType.reset(); } else { m_implementedType = ret; } return actual; }
TypePtr SimpleVariable::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope()); resetTypes(); TypePtr ret; ConstructPtr construct = shared_from_this(); BlockScopePtr scope = getScope(); VariableTablePtr variables = scope->getVariables(); // check function parameter that can occur in lval context if (m_sym && m_sym->isParameter() && m_context & (LValue | RefValue | DeepReference | UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) { m_sym->setLvalParam(); } if (coerce && m_sym && type && type->is(Type::KindOfAutoSequence)) { TypePtr t = m_sym->getType(); if (!t || t->is(Type::KindOfVoid) || t->is(Type::KindOfSome) || t->is(Type::KindOfArray)) { type = Type::Array; } } if (m_this) { ret = Type::Object; ClassScopePtr cls = getOriginalClass(); if (cls && (hasContext(ObjectContext) || !cls->derivedByDynamic())) { ret = Type::CreateObjectType(cls->getName()); } if (!hasContext(ObjectContext) && variables->getAttribute(VariableTable::ContainsDynamicVariable)) { if (variables->getAttribute(VariableTable::ContainsLDynamicVariable)) { ret = Type::Variant; } ret = variables->add(m_sym, ret, true, ar, construct, scope->getModifiers()); } } else if ((m_context & (LValue|Declaration)) && !(m_context & (ObjectContext|RefValue))) { if (m_globals) { ret = Type::Array; } else if (m_superGlobal) { ret = m_superGlobalType; } else if (m_superGlobalType) { // For system ret = variables->add(m_sym, m_superGlobalType, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } else { ret = variables->add(m_sym, type, ((m_context & Declaration) != Declaration), ar, construct, scope->getModifiers()); } } else { if (m_superGlobalType) { ret = m_superGlobalType; } else if (m_globals) { ret = Type::Array; } else if (scope->is(BlockScope::ClassScope)) { assert(getClassScope().get() == scope.get()); // ClassVariable expression will come to this block of code ret = getClassScope()->checkProperty(getScope(), m_sym, type, true, ar); } else { TypePtr tmpType = type; if (m_context & RefValue) { tmpType = Type::Variant; coerce = true; } ret = variables->checkVariable(m_sym, tmpType, coerce, ar, construct); if (ret && (ret->is(Type::KindOfSome) || ret->is(Type::KindOfAny))) { ret = Type::Variant; } } } // if m_assertedType is set, then this is a type assertion node TypePtr inType = m_assertedType ? GetAssertedInType(ar, m_assertedType, ret) : ret; TypePtr actual = propagateTypes(ar, inType); setTypes(ar, actual, type); if (Type::SameType(actual, ret)) { m_implementedType.reset(); } else { m_implementedType = ret; } return actual; }
void MethodStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); ClassScopePtr scope = getClassScope(); if (outputFFI(cg, ar)) return; cg.setPHPLineNo(-1); CodeGenerator::Context context = cg.getContext(); if (context == CodeGenerator::CppImplementation) { printSource(cg); } bool isWrapper = context == CodeGenerator::CppTypedParamsWrapperDecl || context == CodeGenerator::CppTypedParamsWrapperImpl; bool needsWrapper = isWrapper || (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()); const char *prefix = needsWrapper && !isWrapper ? Option::TypedMethodPrefix : Option::MethodPrefix; switch (context) { case CodeGenerator::CppDeclaration: case CodeGenerator::CppTypedParamsWrapperDecl: { if (!m_stmt && !funcScope->isPerfectVirtual()) { cg_printf("// "); } m_modifiers->outputCPP(cg, ar); if (!m_stmt || m_name == "__offsetget_lval" || funcScope->isPerfectVirtual()) { cg_printf("virtual "); } TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg_printf("void"); } if (m_name == "__offsetget_lval") { cg_printf(" &___offsetget_lval("); } else if (m_modifiers->isStatic() && m_stmt) { // Static method wrappers get generated as support methods cg_printf(" %s%s(CStrRef cls%s", needsWrapper && !isWrapper ? Option::TypedMethodImplPrefix : Option::MethodImplPrefix, cg.formatLabel(m_name).c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg_printf(" %s%s(", prefix, cg.formatLabel(m_name).c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, true); if (m_stmt) { cg_printf(");\n"); } else if (funcScope->isPerfectVirtual()) { cg_printf(") { return throw_fatal(\"pure virtual\");}\n"); } else { cg_printf(") = 0;\n"); } if (context != CodeGenerator::CppTypedParamsWrapperDecl) { if (funcScope->isConstructor(scope) && !funcScope->isAbstract() && !scope->isInterface()) { funcScope->outputCPPCreateDecl(cg, ar); } if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) { cg.setContext(CodeGenerator::CppTypedParamsWrapperDecl); outputCPPImpl(cg, ar); cg.setContext(context); } } } break; case CodeGenerator::CppImplementation: case CodeGenerator::CppTypedParamsWrapperImpl: if (m_stmt) { TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg_printf("void"); } string origFuncName = getOriginalFullName(); string funcSection = Option::FunctionSections[origFuncName]; if (!funcSection.empty()) { cg_printf(" __attribute__ ((section (\".text.%s\")))", funcSection.c_str()); } if (m_name == "__offsetget_lval") { cg_printf(" &%s%s::___offsetget_lval(", Option::ClassPrefix, scope->getId(cg).c_str()); } else if (m_modifiers->isStatic()) { cg_printf(" %s%s::%s%s(CStrRef cls%s", Option::ClassPrefix, scope->getId(cg).c_str(), needsWrapper && !isWrapper ? Option::TypedMethodImplPrefix : Option::MethodImplPrefix, cg.formatLabel(m_name).c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg_printf(" %s%s::%s%s(", Option::ClassPrefix, scope->getId(cg).c_str(), prefix, cg.formatLabel(m_name).c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, false); cg_indentBegin(") {\n"); if (context != CodeGenerator::CppTypedParamsWrapperImpl) { if (m_stmt->hasBody()) { const char *sys = (cg.getOutput() == CodeGenerator::SystemCPP ? "_BUILTIN" : ""); if (m_modifiers->isStatic()) { cg_printf("STATIC_METHOD_INJECTION%s(%s, %s);\n", sys, scope->getOriginalName().c_str(), origFuncName.c_str()); } else if (cg.getOutput() != CodeGenerator::SystemCPP && !scope->isRedeclaring() && !scope->derivedByDynamic()) { cg_printf("INSTANCE_METHOD_INJECTION_ROOTLESS(%s, %s);\n", scope->getOriginalName().c_str(), origFuncName.c_str()); } else { cg_printf("INSTANCE_METHOD_INJECTION%s(%s, %s);\n", sys, scope->getOriginalName().c_str(), origFuncName.c_str()); } } outputCPPArgInjections(cg, ar, origFuncName.c_str(), scope, funcScope); if (m_name == "__offsetget_lval") { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[0]); cg_printf("Variant &v = %s->__lvalProxy;\n", cg.getGlobals(ar)); string lowered = Util::toLower(m_originalName); cg_printf("v = %s%s(%s%s);\n", prefix, lowered.c_str(), Option::VariablePrefix, param->getName().c_str()); cg_printf("return v;\n"); } else { if (funcScope->isConstructor(scope)) { cg_printf("bool oldInCtor = gasInCtor(true);\n"); } else if (m_name == "__destruct") { cg_printf("setInDtor();\n"); } funcScope->outputCPP(cg, ar); cg.setContext( CodeGenerator::NoContext); // no inner functions/classes if (!funcScope->isStatic() && funcScope->getVariables()-> getAttribute(VariableTable::ContainsDynamicVariable)) { cg_printf("%sthis = this;\n", Option::VariablePrefix); } outputCPPStmt(cg, ar); } cg_indentEnd("}\n"); if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) { cg.setContext(CodeGenerator::CppTypedParamsWrapperImpl); outputCPPImpl(cg, ar); } } else { outputCPPTypeCheckWrapper(cg, ar); cg_indentEnd("}\n"); } cg.setContext(context); cg.printImplSplitter(); } break; default: break; } }
void SimpleVariable::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { VariableTablePtr variables = getScope()->getVariables(); if (m_this) { ASSERT((getContext() & ObjectContext) == 0); if (hasContext(OprLValue) || hasContext(AssignmentLHS)) { cg_printf("throw_assign_this()"); return; } if (variables->getAttribute(VariableTable::ContainsLDynamicVariable)) { ASSERT(m_sym); const string &namePrefix = getNamePrefix(); cg_printf("%s%sthis", namePrefix.c_str(), variables->getVariablePrefix(m_sym)); } else if (hasContext(DeepOprLValue) || hasContext(DeepAssignmentLHS) || hasContext(LValue)) { // $this[] op= ...; or $this[] = ... cg_printf("Variant(GET_THIS())"); } else { ClassScopePtr cls = getOriginalClass(); if (!cls || cls->derivedByDynamic()) { cg_printf("Object(GET_THIS())"); } else { cg_printf("GET_THIS_TYPED(%s)", cls->getId().c_str()); } } } else if (m_superGlobal) { const string &name = variables->getGlobalVariableName(ar, m_name); cg_printf("g->%s", name.c_str()); } else if (m_globals) { cg_printf("get_global_array_wrapper()"); } else { ASSERT(m_sym); bool sw = false; if (m_sym->isShrinkWrapped() && m_context == Declaration) { ASSERT(!getFunctionScope()->isGenerator()); TypePtr type = m_sym->getFinalType(); type->outputCPPDecl(cg, ar, getScope()); sw = true; cg_printf(" "); } const string &prefix0 = getNamePrefix(); const char *prefix1 = variables->getVariablePrefix(m_sym); cg_printf("%s%s%s", prefix0.c_str(), prefix1, CodeGenerator::FormatLabel(m_name).c_str()); if (m_originalSym) { cg.printf(" /* %s */", m_originalSym->getName().c_str()); } if (sw) { TypePtr type = m_sym->getFinalType(); const char *initializer = type->getCPPInitializer(); if (initializer) { cg_printf(" = %s", initializer); } } } }
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; } }