예제 #1
0
bool ClassScope::NeedStaticArray(ClassScopePtr cls, FunctionScopePtr func) {
  return cls && cls->getAttribute(NotFinal) && !func->isPrivate();
}
예제 #2
0
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  ClassScopePtr classScope = m_classScope.lock();
  if (cg.getContext() == CodeGenerator::NoContext) {
    if (classScope->isRedeclaring()) {
      cg.printf("g->%s%s = ClassStaticsPtr(NEW(%s%s)());\n",
                Option::ClassStaticsObjectPrefix, m_name.c_str(),
                Option::ClassStaticsPrefix, classScope->getId().c_str());
    }
    if (classScope->isVolatile()) {
      cg.printf("g->CDEC(%s) = true;\n", m_name.c_str());
    }
    const vector<string> &bases = classScope->getBases();
    for (vector<string>::const_iterator it = bases.begin();
         it != bases.end(); ++it) {
      ClassScopePtr base = ar->findClass(*it);
      if (base && base->isVolatile()) {
        cg.printf("checkClassExists(\"%s\", g);\n", base->getOriginalName());
      }
    }
    return;
  }

  if (cg.getContext() != CodeGenerator::CppForwardDeclaration) {
    printSource(cg);
  }

  ar->pushScope(classScope);
  string clsNameStr = classScope->getId();
  const char *clsName = clsNameStr.c_str();
  bool redeclared = classScope->isRedeclaring();
  switch (cg.getContext()) {
  case CodeGenerator::CppForwardDeclaration:
    if (Option::GenerateCPPMacros) {
      cg.printf("FORWARD_DECLARE_CLASS(%s)\n", clsName);
      if (redeclared) {
        cg.printf("FORWARD_DECLARE_REDECLARED_CLASS(%s)\n", clsName);
      }
    }
    if (m_stmt) {
      cg.setContext(CodeGenerator::CppClassConstantsDecl);
      m_stmt->outputCPP(cg, ar);
      cg.setContext(CodeGenerator::CppForwardDeclaration);
    }
    break;
  case CodeGenerator::CppDeclaration:
    {
      bool system = cg.getOutput() == CodeGenerator::SystemCPP;
      ClassScopePtr parCls;
      if (!m_parent.empty()) parCls = ar->findClass(m_parent);
      cg.printf("class %s%s", Option::ClassPrefix, clsName);
      if (!m_parent.empty() && classScope->derivesFrom(ar, m_parent)) {
        if (!parCls || parCls->isRedeclaring()) {
          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");
        }
      }
      cg.indentBegin(" {\n");

      if (Option::GenerateCPPMacros) {
        // Get all of this class's ancestors
        vector<string> bases;
        getAllParents(ar, bases);
        // Eliminate duplicates
        sort(bases.begin(), bases.end());
        bases.erase(unique(bases.begin(), bases.end()), bases.end());

        cg.indentBegin("BEGIN_CLASS_MAP(%s)\n",
                       Util::toLower(classScope->getName()).c_str());
        for (unsigned int i = 0; i < bases.size(); i++) {
          cg.printf("PARENT_CLASS(%s)\n", bases[i].c_str());
        }
        cg.indentEnd("END_CLASS_MAP(%s)\n", clsName);
      }

      if (Option::GenerateCPPMacros) {
        bool dyn = (!parCls && !m_parent.empty()) ||
          classScope->derivesFromRedeclaring() ==
          ClassScope::DirectFromRedeclared;
        bool idyn = classScope->derivesFromRedeclaring() ==
          ClassScope::IndirectFromRedeclared;
        bool redec = classScope->isRedeclaring();
        if (!classScope->derivesFromRedeclaring()) {
          outputCPPClassDecl(cg, ar, clsName, m_originalName.c_str(),
                             m_parent.empty() ?
                             "ObjectData" : m_parent.c_str());
        } else {
          cg.printf("DECLARE_DYNAMIC_CLASS(%s, %s)\n", clsName,
                    m_originalName.c_str());
        }
        if (system || Option::EnableEval >= Option::LimitedEval) {
          cg.printf("DECLARE_INVOKES_FROM_EVAL\n");
        }
        if (dyn || idyn || redec) {
          if (redec) {
            cg.printf("DECLARE_ROOT;\n");
             if (!dyn && !idyn) {
               cg.printf("private: ObjectData* root;\n");
               cg.printf("public:\n");
               cg.printf("virtual ObjectData *getRoot() { return root; }\n");
             }
          }

          string conInit = ":";
          if (dyn) {
            conInit += "DynamicObjectData(\"" + m_parent + "\", r)";
          } else if (idyn) {
            conInit += string(Option::ClassPrefix) + parCls->getId() +
              "(r?r:this)";
          } else {
            conInit += "root(r?r:this)";
          }

          cg.printf("%s%s(ObjectData* r = NULL)%s {}\n",
                    Option::ClassPrefix, clsName,
                    conInit.c_str());
        }
      }

      cg.printf("void init();\n",
                Option::ClassPrefix, clsName);

      if (classScope->needLazyStaticInitializer()) {
        cg.printf("static GlobalVariables *lazy_initializer"
                  "(GlobalVariables *g);\n");
      }

      classScope->getVariables()->outputCPPPropertyDecl(cg, ar,
          classScope->derivesFromRedeclaring());

      if (!classScope->getAttribute(ClassScope::HasConstructor)) {
        FunctionScopePtr func = classScope->findFunction(ar, "__construct",
                                                         false);
        if (func && !func->isAbstract() && !classScope->isInterface()) {
          ar->pushScope(func);
          func->outputCPPCreateDecl(cg, ar);
          ar->popScope();
        }
      }
      if (classScope->getAttribute(ClassScope::HasDestructor)) {
        cg.printf("public: virtual void destruct();\n");
      }

      // doCall
      if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) {
        cg.printf("Variant doCall(Variant v_name, Variant v_arguments, "
                  "bool fatal);\n");
      }
      // doGet
      if (classScope->getAttribute(ClassScope::HasUnknownPropHandler)) {
        cg.printf("Variant doGet(Variant v_name, bool error);\n");
      }


      if (classScope->isRedeclaring() &&
          !classScope->derivesFromRedeclaring()) {
        cg.printf("Variant doRootCall(Variant v_name, Variant v_arguments, "
                  "bool fatal);\n");
      }

      if (m_stmt) m_stmt->outputCPP(cg, ar);
      {
        set<string> done;
        classScope->outputCPPStaticMethodWrappers(cg, ar, done, clsName);
      }

      if (cg.getOutput() == CodeGenerator::SystemCPP &&
          ar->isBaseSysRsrcClass(clsName) &&
          !classScope->hasProperty("rsrc")) {
        cg.printf("public: Variant %srsrc;\n", Option::PropertyPrefix);
      }

      cg.indentEnd("};\n");

      if (redeclared) {
        cg.indentBegin("class %s%s : public ClassStatics {\n",
                       Option::ClassStaticsPrefix, clsName);
        cg.printf("public:\n");
        cg.printf("DECLARE_OBJECT_ALLOCATION(%s%s);\n",
                  Option::ClassStaticsPrefix, clsName);
        cg.printf("%s%s() : ClassStatics(%d) {}\n",
                  Option::ClassStaticsPrefix, clsName,
                  classScope->getRedeclaringId());
        cg.indentBegin("Variant %sgetInit(const char *s, int64 hash = -1) {\n",
                       Option::ObjectStaticPrefix);
        cg.printf("return %s%s::%sgetInit(s, hash);\n", Option::ClassPrefix,
                  clsName, Option::ObjectStaticPrefix);
        cg.indentEnd("}\n");
        cg.indentBegin("Variant %sget(const char *s, int64 hash = -1) {\n",
                       Option::ObjectStaticPrefix);
        cg.printf("return %s%s::%sget(s, hash);\n", Option::ClassPrefix,
                  clsName, Option::ObjectStaticPrefix);
        cg.indentEnd("}\n");
        cg.indentBegin("Variant &%slval(const char* s, int64 hash = -1) {\n",
                  Option::ObjectStaticPrefix);
        cg.printf("return %s%s::%slval(s, hash);\n", Option::ClassPrefix,
                  clsName, Option::ObjectStaticPrefix);
        cg.indentEnd("}\n");
        cg.indentBegin("Variant %sinvoke(const char *c, const char *s, "
                       "CArrRef params, int64 hash = -1, bool fatal = true) "
                       "{\n",
                  Option::ObjectStaticPrefix);
        cg.printf("return %s%s::%sinvoke(c, s, params, hash, fatal);\n",
                  Option::ClassPrefix, clsName,
                  Option::ObjectStaticPrefix);
        cg.indentEnd("}\n");
        cg.indentBegin("Object create(CArrRef params, bool init = true, "
                       "ObjectData* root = NULL) {\n");
        cg.printf("return Object((NEW(%s%s)(root))->"
                  "dynCreate(params, init));\n",
                  Option::ClassPrefix, clsName);
        cg.indentEnd("}\n");
        cg.indentBegin("Variant %sconstant(const char* s) {\n",
                       Option::ObjectStaticPrefix);
        cg.printf("return %s%s::%sconstant(s);\n", Option::ClassPrefix,
                  clsName, Option::ObjectStaticPrefix);
        cg.indentEnd("}\n");
        cg.indentBegin("Variant %sinvoke_from_eval(const char *c, "
                       "const char *s, Eval::VariableEnvironment &env, "
                       "const Eval::FunctionCallExpression *call, "
                       "int64 hash = -1, bool fatal = true) "
                       "{\n",
                       Option::ObjectStaticPrefix);
        cg.printf("return %s%s::%sinvoke_from_eval(c, s, env, call, hash, "
                  "fatal);\n",
                  Option::ClassPrefix, clsName,
                  Option::ObjectStaticPrefix);
        cg.indentEnd("}\n");
        cg.indentEnd("};\n");
      }
    }
    break;
  case CodeGenerator::CppImplementation:
    if (m_stmt) {
      cg.setContext(CodeGenerator::CppClassConstantsImpl);
      m_stmt->outputCPP(cg, ar);
      cg.setContext(CodeGenerator::CppImplementation);
    }

    classScope->outputCPPSupportMethodsImpl(cg, ar);

    if (redeclared) {
      cg.printf("IMPLEMENT_OBJECT_ALLOCATION(%s%s);\n",
                Option::ClassStaticsPrefix, clsName);
    }

    cg.indentBegin("void %s%s::init() {\n",
                   Option::ClassPrefix, clsName);
    if (!m_parent.empty()) {
      if (classScope->derivesFromRedeclaring() ==
          ClassScope::DirectFromRedeclared) {
        cg.printf("parent->init();\n");
      } else {
        cg.printf("%s%s::init();\n", Option::ClassPrefix, m_parent.c_str());
      }
    }
    if (classScope->getVariables()->
        getAttribute(VariableTable::NeedGlobalPointer)) {
      cg.printDeclareGlobals();
    }
    cg.setContext(CodeGenerator::CppConstructor);
    if (m_stmt) m_stmt->outputCPP(cg, ar);
    cg.indentEnd("}\n");

    if (classScope->needStaticInitializer()) {
      cg.indentBegin("void %s%s::os_static_initializer() {\n",
                     Option::ClassPrefix, clsName);
      cg.printDeclareGlobals();
      cg.setContext(CodeGenerator::CppStaticInitializer);
      if (m_stmt) m_stmt->outputCPP(cg, ar);
      cg.indentEnd("}\n");
      cg.indentBegin("void %s%s() {\n",
                     Option::ClassStaticInitializerPrefix, clsName);
      cg.printf("%s%s::os_static_initializer();\n",  Option::ClassPrefix,
                clsName);
      cg.indentEnd("}\n");
    }
    if (classScope->needLazyStaticInitializer()) {
      cg.indentBegin("GlobalVariables *%s%s::lazy_initializer("
                     "GlobalVariables *g) {\n", Option::ClassPrefix, clsName);
      cg.indentBegin("if (!g->%s%s) {\n",
                     Option::ClassStaticInitializerFlagPrefix, clsName);
      cg.printf("g->%s%s = true;\n", Option::ClassStaticInitializerFlagPrefix,
                clsName);
      cg.setContext(CodeGenerator::CppLazyStaticInitializer);
      if (m_stmt) m_stmt->outputCPP(cg, ar);
      cg.indentEnd("}\n");
      cg.printf("return g;\n");
      cg.indentEnd("}\n");
    }
    cg.setContext(CodeGenerator::CppImplementation);
    if (m_stmt) m_stmt->outputCPP(cg, ar);

    break;
  case CodeGenerator::CppFFIDecl:
  case CodeGenerator::CppFFIImpl:
    if (m_stmt) m_stmt->outputCPP(cg, ar);
    break;
  case CodeGenerator::JavaFFI:
    {
      if (classScope->isRedeclaring()) break;

      // TODO support PHP namespaces, once HPHP supports it
      string packageName = Option::JavaFFIRootPackage;
      string packageDir = packageName;
      Util::replaceAll(packageDir, ".", "/");

      string outputDir = ar->getOutputPath() + "/" + Option::FFIFilePrefix +
        packageDir + "/";
      Util::mkdir(outputDir);

      // uses a different cg to generate a separate file for each PHP class
      // also, uses the original capitalized class name
      string clsFile = outputDir + getOriginalName() + ".java";
      ofstream fcls(clsFile.c_str());
      CodeGenerator cgCls(&fcls, CodeGenerator::FileCPP);
      cgCls.setContext(CodeGenerator::JavaFFI);

      cgCls.printf("package %s;\n\n", packageName.c_str());
      cgCls.printf("import hphp.*;\n\n");

      printSource(cgCls);

      string clsModifier;
      switch (m_type) {
      case T_CLASS:
        break;
      case T_ABSTRACT:
        clsModifier = "abstract ";
        break;
      case T_FINAL:
        clsModifier = "final ";
        break;
      }
      cgCls.printf("public %sclass %s ", clsModifier.c_str(),
                   getOriginalName().c_str());

      ClassScopePtr parCls;
      if (!m_parent.empty()) parCls = ar->findClass(m_parent);
      if (!m_parent.empty() && classScope->derivesFrom(ar, m_parent)
          && parCls && parCls->isUserClass() && !parCls->isRedeclaring()) {
        // system classes are not supported in static FFI translation
        // they shouldn't appear as superclasses as well
        cgCls.printf("extends %s", parCls->getOriginalName());
      }
      else {
        cgCls.printf("extends HphpObject");
      }
      if (m_base) {
        bool first = true;
        for (int i = 0; i < m_base->getCount(); i++) {
          ScalarExpressionPtr exp =
            dynamic_pointer_cast<ScalarExpression>((*m_base)[i]);
          const char *intf = exp->getString().c_str();
          ClassScopePtr intfClassScope = ar->findClass(intf);
          if (intfClassScope && classScope->derivesFrom(ar, intf)
           && intfClassScope->isUserClass()) {
            if (first) {
              cgCls.printf(" implements ");
              first = false;
            }
            else {
              cgCls.printf(", ");
            }
            cgCls.printf(intfClassScope->getOriginalName());
          }
        }
      }

      cgCls.indentBegin(" {\n");

      // constructor for initializing the variant pointer
      cgCls.printf("protected %s(long ptr) { super(ptr); }\n\n",
                   getOriginalName().c_str());

      FunctionScopePtr cons = classScope->findConstructor(ar, true);
      if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) {
        // if not an abstract class and not having an explicit constructor,
        // adds a default constructor
        outputJavaFFIConstructor(cgCls, ar, cons);
      }

      if (m_stmt) m_stmt->outputCPP(cgCls, ar);
      cgCls.indentEnd("}\n");

      fcls.close();
    }
    break;
  case CodeGenerator::JavaFFICppDecl:
  case CodeGenerator::JavaFFICppImpl:
    {
      if (classScope->isRedeclaring()) break;

      if (m_stmt) m_stmt->outputCPP(cg, ar);
      FunctionScopePtr cons = classScope->findConstructor(ar, true);
      if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) {
        outputJavaFFICPPCreator(cg, ar, cons);
      }
    }
    break;
  default:
    ASSERT(false);
    break;
  }

  ar->popScope();
}
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar,
                                              TypePtr type, bool coerce) {
  assert(type);
  IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());
  resetTypes();
  reset();

  ConstructPtr self = shared_from_this();
  TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false);
  m_valid = true;
  m_bindClass = true;

  if (m_name.empty()) {
    m_nameExp->inferAndCheck(ar, Type::Some, false);
    setInvokeParams(ar);
    // we have to use a variant to hold dynamic value
    return checkTypesImpl(ar, type, Type::Variant, coerce);
  }

  ClassScopePtr cls;
  if (objectType && !objectType->getName().empty()) {
    if (m_classScope && !strcasecmp(objectType->getName().c_str(),
                                    m_classScope->getName().c_str())) {
      cls = m_classScope;
    } else {
      cls = ar->findExactClass(shared_from_this(), objectType->getName());
    }
  }

  if (!cls) {
    m_classScope.reset();
    m_funcScope.reset();

    m_valid = false;
    setInvokeParams(ar);
    return checkTypesImpl(ar, type, Type::Variant, coerce);
  }

  if (m_classScope != cls) {
    m_classScope = cls;
    m_funcScope.reset();
  }

  FunctionScopePtr func = m_funcScope;
  if (!func) {
    func = cls->findFunction(ar, m_name, true, true);
    if (!func) {
      if (!cls->isTrait() &&
          !cls->getAttribute(ClassScope::MayHaveUnknownMethodHandler) &&
          !cls->getAttribute(ClassScope::HasUnknownMethodHandler) &&
          !cls->getAttribute(ClassScope::InheritsUnknownMethodHandler)) {
        if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
          if (!Option::AllDynamic) {
            setDynamicByIdentifier(ar, m_name);
          }
        } else {
          Compiler::Error(Compiler::UnknownObjectMethod, self);
        }
      }

      m_valid = false;
      setInvokeParams(ar);
      return checkTypesImpl(ar, type, Type::Variant, coerce);
    }
    m_funcScope = func;
    func->addCaller(getScope(), !type->is(Type::KindOfAny));
  }

  bool valid = true;
  m_bindClass = func->isStatic();

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr localfunc = getFunctionScope();
    if (localfunc->isStatic()) {
      if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::MissingObjectContext, self);
      }
      valid = false;
    }
  }

  // invoke() will return Variant
  if (cls->isInterface() ||
      (func->isVirtual() &&
       (!Option::WholeProgram || func->isAbstract() ||
        (func->hasOverride() && cls->getAttribute(ClassScope::NotFinal))) &&
       !func->isPerfectVirtual())) {
    valid = false;
  }

  if (!valid) {
    setInvokeParams(ar);
    checkTypesImpl(ar, type, Type::Variant, coerce);
    m_valid = false; // so we use invoke() syntax
    if (!Option::AllDynamic) {
      func->setDynamic();
    }
    assert(m_actualType);
    return m_actualType;
  }

  assert(func);
  return checkParamsAndReturn(ar, type, coerce, func, false);
}