void IncludeExpression::outputCPPImpl(CodeGenerator &cg,
                                      AnalysisResultPtr ar) {
  bool linemap = outputLineMap(cg, ar, true);

  // Includes aren't really supported in system mode
  if (cg.getOutput() == CodeGenerator::SystemCPP) {
    cg_printf("true");
    if (linemap) cg_printf(")");
    return;
  }
  const char *vars = m_privateScope ?
    "lvar_ptr(LVariableTable())" : "variables";
  bool require = (m_op == T_REQUIRE || m_op == T_REQUIRE_ONCE);
  bool once = (m_op == T_INCLUDE_ONCE || m_op == T_REQUIRE_ONCE);
  if (!getCurrentInclude(ar).empty()) {
    FileScopePtr fs = ar->findFileScope(getCurrentInclude(ar), false);
    if (fs) {
      cg_printf("%s%s(%s, %s, %s)", Option::PseudoMainPrefix,
                fs->pseudoMainName().c_str(),
                once ? "true" : "false",
                vars, cg.getGlobals(ar));
      if (linemap) cg_printf(")");
      return;
    }
  }

  // include() and require() need containing file's directory
  string currentDir = "\"\"";
  if (m_loc && m_loc->file && *m_loc->file) {
    string file = m_loc->file;
    size_t pos = file.rfind('/');
    if (pos != string::npos) {
      currentDir = '"';
      currentDir += file.substr(0, pos + 1);
      currentDir += '"';
    }
  }

  // fallback to dynamic include
  cg_printf("%s(", require ? "require" : "include");
  m_exp->outputCPP(cg, ar);
  cg_printf(", %s, %s, %s)",
            once ? "true" : "false",
            vars,
            currentDir.c_str());
  if (linemap) cg_printf(")");
}
void ClassConstantExpression::outputCPPImpl(CodeGenerator &cg,
                                            AnalysisResultPtr ar) {
  const char *globals = "g";
  if (cg.getContext() == CodeGenerator::CppParameterDefaultValueDecl ||
      cg.getContext() == CodeGenerator::CppParameterDefaultValueImpl) {
    globals = cg.getGlobals();
  }
  if (m_valid) {
    ClassScopePtr foundCls;
    string trueClassName;
    for (ClassScopePtr cls = ar->findClass(m_className);
         cls; cls = cls->getParentScope(ar)) {
      if (cls->getConstants()->isPresent(m_varName)) {
        foundCls = cls;
        trueClassName = cls->getName();
        break;
      }
    }
    ASSERT(!trueClassName.empty());
    ConstructPtr decl = foundCls->getConstants()->getValue(m_varName);
    if (decl) {
      decl->outputCPP(cg, ar);
      if (cg.getContext() == CodeGenerator::CppImplementation ||
          cg.getContext() == CodeGenerator::CppParameterDefaultValueImpl) {
        cg.printf("(%s::%s)", m_className.c_str(), m_varName.c_str());
      } else {
        cg.printf("/* %s::%s */", m_className.c_str(), m_varName.c_str());
      }
    } else {
      if (foundCls->getConstants()->isDynamic(m_varName)) {
        cg.printf("%s%s::lazy_initializer(%s)->", Option::ClassPrefix,
                  trueClassName.c_str(), globals);
      }
      cg.printf("%s%s_%s", Option::ClassConstantPrefix, trueClassName.c_str(),
                m_varName.c_str());
    }
  } else if (m_redeclared) {
    cg.printf("%s->%s%s->os_constant(\"%s\")", globals,
              Option::ClassStaticsObjectPrefix,
              m_className.c_str(), m_varName.c_str());
  } else {
    cg.printf("throw_fatal(\"unknown class constant %s::%s\")",
              m_className.c_str(), m_varName.c_str());
  }
}
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  ClassScopeRawPtr classScope = getClassScope();
  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(");
        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::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();
      }
      if (Option::GenerateCppLibCode) {
        cg.printDocComment(classScope->getDocComment());
      }
      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");
      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());
        }
        if (system || Option::EnableEval >= Option::LimitedEval) {
          cg_printf("DECLARE_INVOKES_FROM_EVAL\n");
        }

        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()) {
            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 = "";
          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 ? "\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((NEW(%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("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_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");
      }

      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(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;
  }
}
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 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);
  }
}
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());

      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(base->getOriginalName(), ar, shared_from_this());
          string lname = Util::toLower(base->getOriginalName());
          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 = (!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().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 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;
          bool needsLateInit = false;
          if (dyn) {
            conInit = " : DynamicObjectData(\"" + m_parent + "\", r)";
            hasParam = true;
          } else if (idyn) {
            conInit = " : " + string(Option::ClassPrefix) + parCls->getId() +
              "(r ? r : this)";
            hasParam = true;
          } else {
            if (redec && classScope->derivedByDynamic()) {
              conInit = "root(r ? r : this)";
              needsLateInit = true;
            }
            hasParam = true;
          }

          // this dance around the initialization list is to make
          // sure members get set in the right order - since properties
          // come first, and root comes later, we need to init the
          // properties first, and then root.
          if (needsLateInit) {
            cg_printf("%s%s(%s) : ",
                      Option::ClassPrefix,
                      clsName,
                      hasParam ? "ObjectData* r = NULL" : "");
            if (needsCppCtor) {
              cg.setContext(CodeGenerator::CppConstructor);
              ASSERT(!cg.hasInitListFirstElem());
              m_stmt->outputCPP(cg, ar);
              cg.clearInitListFirstElem();
              cg.setContext(CodeGenerator::CppDeclaration);
              cg_printf(", ");
            }
            cg_printf("%s", conInit.c_str());
          } else {
            cg_printf("%s%s(%s)%s%s",
                      Option::ClassPrefix,
                      clsName,
                      hasParam ? "ObjectData* r = NULL" : "",
                      conInit.c_str(),
                      needsCppCtor ? conInit.empty() ? " : " : ", " : "");
            if (needsCppCtor) {
              cg.setContext(CodeGenerator::CppConstructor);
              ASSERT(!cg.hasInitListFirstElem());
              m_stmt->outputCPP(cg, ar);
              cg.clearInitListFirstElem();
              cg.setContext(CodeGenerator::CppDeclaration);
            }
          }

          cg_indentBegin(" {%s",
                         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");
          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;
  }
}
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;
  }
}