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;
  }
}
Example #2
0
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  ClassScopeRawPtr classScope = getClassScope();
  if (cg.getContext() == CodeGenerator::NoContext) {
    if (classScope->isVolatile()) {
      string name = CodeGenerator::FormatLabel(m_name);
      if (classScope->isRedeclaring()) {
        cg_printf("g->%s%s = &%s%s;\n",
                  Option::ClassStaticsCallbackPrefix,
                  name.c_str(),
                  Option::ClassStaticsCallbackPrefix,
                  classScope->getId().c_str());
      }
      cg_printf("g->CDEC(%s) = true;\n", name.c_str());
      cg.addHoistedClass(name);

      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("checkClassExistsThrow(");
          cg_printString(*it, ar, shared_from_this());
          string lname = Util::toLower(*it);
          cg_printf(", &%s->CDEC(%s));\n",
                    cg.getGlobals(ar),
                    CodeGenerator::FormatLabel(lname).c_str());
        }
      }
    }
    return;
  }

  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");
      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");

      bool hasEmitCppCtor = false;
      bool needsCppCtor = classScope->needsCppCtor();
      bool needsInit    = classScope->needsInitMethod();

      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,
                    CodeGenerator::EscapeLabel(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 {
            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 ?
                       "\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");
        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(" {}\n");
      }

      if (needsInit) {
        cg_printf("void init();\n");
      }

      if (!classScope->getAttribute(ClassScope::HasConstructor)) {
        FunctionScopePtr func = classScope->findFunction(ar, "__construct",
                                                         false);
        if (func && !func->isAbstract() && !classScope->isInterface()) {
          func->outputCPPCreateDecl(cg, ar);
        }
      }

      // 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);
      {
        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";
      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;
  }
}