예제 #1
0
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;
}
예제 #2
0
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());
  }
}
예제 #3
0
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;
}
예제 #4
0
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;
}
예제 #5
0
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;
}
예제 #7
0
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);
      }
    }
  }
}
예제 #9
0
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;
  }
}