Beispiel #1
0
// Rewrite the outermost select clause so that it references properties of
// a result tuple constructed by the query provider. Then wrap this expression
// in a lambda so that the query provider can invoke it as a call back every
// time it produces a result tuple (row).
ClosureExpressionPtr QueryExpression::clientSideRewrite(
  AnalysisResultPtr ar, FileScopePtr fileScope) {
  // Rewrite the select expression into an expression that refers to
  // table columns (including computed columns) via properties of an
  // object produced by the query provider at runtime.
  ClientSideSelectRewriter cs;
  cs.rewriteQuery(static_pointer_cast<QueryExpression>(shared_from_this()));
  auto csSelect = cs.getClientSideSelectClause();
  // null if there is no select clause.
  if (csSelect == nullptr) return nullptr;
  ExpressionPtr selectExpr = csSelect->getExpression();

  // Now wrap up the rewritten expression into a lambda expression that
  // is passed to the query provider. When the query result is iterated,
  // the closure is called for each row in the query result in order to
  // produce the value specified by this select expression.

  // Create a return statement for the lambda body
  LabelScopePtr labelScope(new LabelScope());
  ReturnStatementPtr returnStatement(
    new ReturnStatement(BlockScopePtr(), labelScope, getRange(), selectExpr)
  );

  // Wrap up the return statement in a list for the lambda body
  StatementListPtr stmt(
    new StatementList(BlockScopePtr(), labelScope, getRange())
  );
  stmt->addElement(returnStatement);

  // Create a function statement for the lambda:

  // First create a formal parameter list, consisting of a single
  // parameter that will receive an object from the query provider
  // with a property for each table column that is referenced in the
  // expression of this select clause.
  TypeAnnotationPtr type;
  bool hhType = true;
  std::string paramName = "__query_result_row__";
  bool byRefParam = false;
  TokenID modifier = 0;
  ExpressionPtr defaultValue;
  ExpressionPtr attributeList;
  ParameterExpressionPtr parameter (
    new ParameterExpression(BlockScopePtr(), getRange(), type, hhType,
        paramName, byRefParam, modifier, defaultValue, attributeList)
  );
  ExpressionListPtr params(new ExpressionList(BlockScopePtr(), getRange()));
  params->addElement(parameter);

  // Now create a function statement object
  ModifierExpressionPtr modifiers(
    new ModifierExpression(BlockScopePtr(), getRange())
  );
  bool ref = false;
  static int counter = 0;
  std::string name = "__select__#" + std::to_string(counter++);
  TypeAnnotationPtr retTypeAnnotation;
  int attr = 0;
  std::string docComment;
  ExpressionListPtr attrList;
  FunctionStatementPtr func(
    new FunctionStatement(BlockScopePtr(), labelScope, getRange(), modifiers,
                          ref, name, params, retTypeAnnotation, stmt, attr,
                          docComment, attrList)
  );

  // The function statement needs a scope
  std::vector<UserAttributePtr> uattrs;
  FunctionScopePtr funcScope
    (new FunctionScope(ar, false, name, func, false, 1, 1,
                       nullptr, attr, docComment, fileScope, uattrs));
  fileScope->addFunction(ar, funcScope);
  func->resetScope(funcScope);
  funcScope->setOuterScope(fileScope);

  // Now construct a closure expression to create the closure value to
  // pass to the query provider.
  ExpressionListPtr captures;
  ClosureExpressionPtr closure(
    new ClosureExpression(BlockScopePtr(), getRange(), ClosureType::Short,
        func, captures)
  );
  closure->getClosureFunction()->setContainingClosure(closure);
  return closure;
}
FunctionScopePtr BuiltinSymbols::ParseHelperFunction(AnalysisResultPtr ar,
                                                     const char** &p) {
  FunctionScopePtr f = ParseExtFunction(ar, p);
  f->setHelperFunction();
  return f;
}
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 = ClassStaticsPtr(NEWOBJ(%s%s)());\n",
                  Option::ClassStaticsObjectPrefix,
                  name.c_str(),
                  Option::ClassStaticsPrefix, classScope->getId().c_str());
        cg_printf("g->%s%s = &%s%s;\n",
                  Option::ClassStaticsCallbackPrefix,
                  name.c_str(),
                  Option::ClassWrapperFunctionPrefix,
                  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();
  bool redeclared = classScope->isRedeclaring();

  switch (cg.getContext()) {
  case CodeGenerator::CppDeclaration:
    {
      if (Option::GenerateCPPMacros) {
        classScope->outputForwardDeclaration(cg);
      }

      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");
      if (Option::GenerateCPPMacros) {
        cg_printf("virtual bool o_instanceof(CStrRef s) const;\n");
      }

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

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

      // 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->outputCPPGlobalTableWrappersDecl(cg, ar);
      classScope->outputCPPDynamicClassDecl(cg);

      if (redeclared) {
        cg_indentBegin("class %s%s : public ClassStatics {\n",
                       Option::ClassStaticsPrefix, clsName);
        cg_printf("public:\n");
        cg_printf("DECLARE_OBJECT_ALLOCATION_NO_SWEEP(%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("ObjectData *createOnlyNoInit"
                       "(ObjectData* root = NULL) {\n");
        cg_printf("return %s%s(root);\n",
                  Option::CreateObjectOnlyPrefix, 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("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");
      }

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

      if (redeclared) {
        cg_printf("IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(%s%s);\n",
                  Option::ClassStaticsPrefix, clsName);
      }

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

      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(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;
  }
}
Beispiel #4
0
FunctionScopePtr MethodStatement::onInitialParse(AnalysisResultConstPtr ar,
                                                 FileScopePtr fs) {
  int minParam, maxParam;
  ConstructPtr self = shared_from_this();
  minParam = maxParam = 0;
  bool hasRef = false;
  if (m_params) {
    std::set<string> names, allDeclNames;
    int i = 0;
    maxParam = m_params->getCount();
    for (i = maxParam; i--; ) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      if (param->isRef()) hasRef = true;
      if (!param->isOptional()) {
        if (!minParam) minParam = i + 1;
      } else if (minParam && !param->hasTypeHint()) {
        Compiler::Error(Compiler::RequiredAfterOptionalParam, param);
      }
      allDeclNames.insert(param->getName());
    }

    for (i = maxParam-1; i >= 0; i--) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      if (names.find(param->getName()) != names.end()) {
        Compiler::Error(Compiler::RedundantParameter, param);
        for (int j = 0; j < 1000; j++) {
          string name = param->getName() + lexical_cast<string>(j);
          if (names.find(name) == names.end() &&
              allDeclNames.find(name) == allDeclNames.end()) {
            param->rename(name);
            break;
          }
        }
      }
      names.insert(param->getName());
    }
  }

  if (hasRef || m_ref) {
    m_attribute |= FileScope::ContainsReference;
  }

  vector<UserAttributePtr> attrs;
  if (m_attrList) {
    for (int i = 0; i < m_attrList->getCount(); ++i) {
      UserAttributePtr a =
        dynamic_pointer_cast<UserAttribute>((*m_attrList)[i]);
      attrs.push_back(a);
    }
  }

  StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this());
  FunctionScopePtr funcScope
    (new FunctionScope(ar, m_method, m_name, stmt, m_ref, minParam, maxParam,
                       m_modifiers, m_attribute, m_docComment, fs, attrs));
  if (!m_stmt) {
    funcScope->setVirtual();
  }
  setBlockScope(funcScope);

  funcScope->setParamCounts(ar, -1, -1);
  return funcScope;
}
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
                                        bool coerce) {
  reset();
  m_classScope.reset();
  FunctionScopePtr prev = m_funcScope;
  m_funcScope.reset();
  ConstructPtr self = shared_from_this();
  if (!m_name.empty() && !isStatic()) {
    ClassScopePtr cls = resolveClass();
    m_name = m_className;

    if (!cls) {
      if (isRedeclared()) {
        getScope()->getVariables()->
          setAttribute(VariableTable::NeedGlobalPointer);
      } else if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::UnknownClass, self);
      }
      if (m_params) m_params->inferAndCheck(ar, Type::Any, false);
      return Type::Object;
    }
    if (cls->isVolatile() && !isPresent()) {
      getScope()->getVariables()->
        setAttribute(VariableTable::NeedGlobalPointer);
    }
    m_dynamic = cls->derivesFromRedeclaring();
    bool valid = true;
    FunctionScopePtr func = cls->findConstructor(ar, true);
    if (!func) {
      if (m_params) {
        if (!m_dynamic && m_params->getCount()) {
          if (getScope()->isFirstPass()) {
            Compiler::Error(Compiler::BadConstructorCall, self);
          }
          m_params->setOutputCount(0);
        }
        m_params->inferAndCheck(ar, Type::Some, false);
      }
    } else {
      if (func != prev) func->addCaller(getScope());
      m_extraArg = func->inferParamTypes(ar, self, m_params,
                                         valid);
      m_variableArgument = func->isVariableArgument();
    }
    if (valid) {
      m_classScope = cls;
      m_funcScope = func;
    }
    if (!valid || m_dynamic) {
      m_implementedType = Type::Object;
    } else {
      m_implementedType.reset();
    }
    return Type::CreateObjectType(m_name);
  } else {
    ar->containsDynamicClass();
    if (m_params) {
      m_params->markParams(canInvokeFewArgs());
    }
  }

  m_implementedType.reset();
  m_nameExp->inferAndCheck(ar, Type::String, false);
  if (m_params) m_params->inferAndCheck(ar, Type::Any, false);
  return Type::Object;
}
void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p,
                                     bool sep) {
  while (*p) {
    // Parse name
    const char *cname = *p++;
    // Parse parent
    const char *cparent = *p++;
    // Parse list of interfaces
    vector<string> ifaces;
    while (*p) ifaces.push_back(*p++);
    p++;
    // Parse methods
    vector<FunctionScopePtr> methods;
    while (*p) {
      FunctionScopePtr fs = ParseExtFunction(ar, p);
      if (sep) {
        fs->setSepExtension();
      }
      bool abstract = (bool)*p++;
      if (abstract) {
        fs->addModifier(T_ABSTRACT);
      }
      int visibility = (long)*p++;
      int vismod = 0;
      if (visibility == 1) {
        vismod = T_PROTECTED;
      } else if (visibility == 2) {
        vismod = T_PRIVATE;
      }
      fs->addModifier(vismod);
      bool stat = (bool)*p++;
      if (stat) {
        fs->addModifier(T_STATIC);
      }
      methods.push_back(fs);
    }
    if (cparent && *cparent && (ifaces.empty() || ifaces[0] != cparent)) {
      ifaces.insert(ifaces.begin(), cparent);
    }
    ClassScopePtr cl(new ClassScope(ar, string(cname), string(cparent),
                                    ifaces, methods));
    p++;
    // Parse properties
    while (*p) {
      *p++; // TODO, support visibility
      const char *name = *p++;
      TypePtr type = ParseType(p);
      cl->getVariables()->add(name, type, false, ar, ExpressionPtr(),
                              ModifierExpressionPtr());
    }
    p++;
    // Parse consts
    while (*p) {
      const char *name = *p++;
      TypePtr type = ParseType(p);
      cl->getConstants()->add(name, type, ExpressionPtr(), ar, ConstructPtr());
    }
    p++;
    cl->setSystem();
    if (sep) {
      cl->setSepExtension();
    }
    s_classes[cl->getName()] = cl;
  }
}
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg,
                                                    AnalysisResultPtr ar,
                                                    bool directVariant,
                                                    int doExist) {
  bool bThis = m_object->isThis();
  bool useGetThis = false;
  FunctionScopePtr funcScope = ar->getFunctionScope();
  if (bThis) {
    if (funcScope && funcScope->isStatic()) {
      cg_printf("GET_THIS_ARROW()");
    } else {
      // in order for __set() and __get() to be called
      useGetThis = true;
    }
  }

  const char *op = ".";
  string func = Option::ObjectPrefix;
  const char *error = ", true";
  ClassScopePtr cls = ar->getClassScope();
  const char *context = "";
  if (cg.getOutput() != CodeGenerator::SystemCPP) {
    if (cls) {
      context = ", s_class_name";
    } else if (funcScope && !funcScope->inPseudoMain()) {
      context = ", empty_string";
    }
  }
  if (doExist) {
    func = doExist > 0 ? "doIsSet" : "doEmpty";
    error = "";
  } else {
    if (bThis && funcScope && funcScope->isStatic()) {
      func = Option::ObjectStaticPrefix;
      error = "";
      context = "";
    } else if (m_context & ExistContext) {
      error = ", false";
    }
    if (m_context & (LValue | RefValue | UnsetContext)) {
      func += "lval";
      error = "";
    } else {
      func += "get";
    }
  }

  if (m_property->getKindOf() == Expression::KindOfScalarExpression) {
    ScalarExpressionPtr name =
      dynamic_pointer_cast<ScalarExpression>(m_property);
    const char *propName = name->getString().c_str();
    if (m_valid && m_object->getType()->isSpecificObject()) {
      if (m_static) {
        if (!bThis) {
          ASSERT(m_class);
          if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty(");
          cg_printf("g->%s%s%s%s",
                    Option::StaticPropertyPrefix, m_class->getName().c_str(),
                    Option::IdPrefix.c_str(), propName);
          if (doExist) cg_printf(")");
        } else {
          // if $val is a class static variable (static $val), then $val
          // cannot be declared as a class variable (var $val), $this->val
          // refers to a non-static class variable and has to use get/lval.
          uint64 hash = hash_string(propName);
          if (useGetThis) cg_printf("GET_THIS_DOT()");
          cg_printf("%s(", func.c_str());
          cg_printString(propName, ar);
          cg_printf(", 0x%016llXLL%s%s)", hash, error, context);
        }
      } else {
        if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty(");
        if (!bThis) {
          ASSERT(!directVariant);
          m_object->outputCPP(cg, ar);
          cg_printf("->");
        }
        cg_printf("%s%s", Option::PropertyPrefix, propName);
        if (doExist) cg_printf(")");
      }
    } else {
      if (!bThis) {
        if (directVariant) {
          TypePtr expectedType = m_object->getExpectedType();
          ASSERT(expectedType->is(Type::KindOfObject));
          // Clear m_expectedType to avoid type cast (toObject).
          m_object->setExpectedType(TypePtr());
          m_object->outputCPP(cg, ar);
          m_object->setExpectedType(expectedType);
        } else {
          m_object->outputCPP(cg, ar);
        }
        cg_printf(op);
      } else {
        if (useGetThis) cg_printf("GET_THIS_DOT()");
      }
      uint64 hash = hash_string(propName);
      cg_printf("%s(", func.c_str());
      cg_printString(propName, ar);
      cg_printf(", 0x%016llXLL%s%s)", hash, error, context);
    }
  } else {
    if (!bThis) {
      if (directVariant) {
        TypePtr expectedType = m_object->getExpectedType();
        ASSERT(expectedType->is(Type::KindOfObject));
        // Clear m_expectedType to avoid type cast (toObject).
        m_object->setExpectedType(TypePtr());
        m_object->outputCPP(cg, ar);
        m_object->setExpectedType(expectedType);
      } else {
        m_object->outputCPP(cg, ar);
      }
      cg_printf(op);
    } else {
      if (useGetThis) cg_printf("GET_THIS_DOT()");
    }
    cg_printf("%s(", func.c_str());
    m_property->outputCPP(cg, ar);
    cg_printf(", -1LL%s%s)", error, context);
  }
}
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar,
                                              TypePtr type, bool coerce) {
  reset();

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

  if (m_name.empty()) {
    m_nameExp->inferAndCheck(ar, Type::String, 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) {
    if (getScope()->isFirstPass()) {
      if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
        Compiler::Error(Compiler::UnknownObjectMethod, self);
      }
    }

    m_classScope.reset();
    m_funcScope.reset();

    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->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) {
        if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
          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());
  }

  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 (!m_object->getType()->isSpecificObject() ||
      (func->isVirtual() && !func->isPerfectVirtual())) {
    valid = false;
  }

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

  return checkParamsAndReturn(ar, type, coerce, func, false);
}
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);
}
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar,
                                              TypePtr type, bool coerce) {
  reset();

  ConstructPtr self = shared_from_this();
  TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), true);
  m_valid = true;
  m_bindClass = true;

  if (m_name.empty()) {
    // if dynamic property or method, we have nothing to find out
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UseDynamicMethod, self);
    }
    m_nameExp->inferAndCheck(ar, Type::String, false);
    setInvokeParams(ar);
    // we have to use a variant to hold dynamic value
    return checkTypesImpl(ar, type, Type::Variant, coerce);
  }

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

  if (!cls) {
    if (ar->isFirstPass()) {
      // call resolveClass to mark functions as dynamic
      // but we cant do anything else with the result.
      resolveClass(ar, m_name);
      if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
        ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self);
      }
    }

    m_classScope.reset();
    m_funcScope.reset();

    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->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) {
        if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
          // TODO: we could try to find out class derivation is present...
          ar->getCodeError()->record(self,
                                     CodeError::DerivedObjectMethod, self);
          // we have to make sure the method is in invoke list
          setDynamicByIdentifier(ar, m_name);
        } else {
          ar->getCodeError()->record(self,
                                     CodeError::UnknownObjectMethod, self);
        }
      }

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

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

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

  // invoke() will return Variant
  if (func->isVirtual() || !m_object->getType()->isSpecificObject()) {
    valid = false;
  }

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

  return checkParamsAndReturn(ar, type, coerce, func);
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
                                             TypePtr type, bool coerce) {
  m_valid = false;

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

  if (!m_property->is(Expression::KindOfScalarExpression)) {
    m_property->inferAndCheck(ar, Type::String, false);

    // we also lost track of which class variable an expression is about, hence
    // any type inference could be wrong. Instead, we just force variants on
    // all class variables.
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(getOriginalClass(), false);
    }

    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
  string name = exp->getString();
  ASSERT(!name.empty());

  m_property->inferAndCheck(ar, Type::String, false);

  ClassScopePtr cls;
  if (objectType && !objectType->getName().empty()) {
    // what object-> has told us
    cls = ar->findExactClass(shared_from_this(), objectType->getName());
  } else {
    if ((m_context & LValue) && objectType &&
        !objectType->is(Type::KindOfObject) &&
        !objectType->is(Type::KindOfVariant) &&
        !objectType->is(Type::KindOfSome) &&
        !objectType->is(Type::KindOfAny)) {
      m_object->inferAndCheck(ar, Type::Object, true);
    }
  }

  if (!cls) {
    if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
      ar->forceClassVariants(name, getOriginalClass(), false);
    }
    return Type::Variant;
  }

  int prop = hasContext(AssignmentLHS) ? ClassScope::MayHaveUnknownPropSetter :
    hasContext(ExistContext) ? ClassScope::MayHaveUnknownPropTester :
    hasContext(UnsetContext) && hasContext(LValue) ?
    ClassScope::MayHavePropUnsetter : ClassScope::MayHaveUnknownPropGetter;
  if ((m_context & (AssignmentLHS|OprLValue)) ||
      !cls->implementsAccessor(prop)) {
    clearEffect(AccessorEffect);
  }

  // resolved to this class
  if (m_context & RefValue) {
    type = Type::Variant;
    coerce = true;
  }

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr func = m_object->getOriginalFunction();
    if (!func || func->isStatic()) {
      if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::MissingObjectContext, self);
      }
      m_actualType = Type::Variant;
      return m_actualType;
    }
  }

  if (!m_propSym || cls != m_objectClass.lock()) {
    m_objectClass = cls;
    ClassScopePtr parent;
    m_propSym = cls->findProperty(parent, name, ar, self);
    assert(m_propSym);
    if (!parent) {
      parent = cls;
    }
    m_propSymValid = m_propSym->isPresent() &&
      (!m_propSym->isPrivate() ||
       getOriginalClass() == parent) &&
      !m_propSym->isStatic();

    if (m_propSymValid) {
      parent->addUse(getScope(), BlockScope::UseKindNonStaticRef);
    }
  }

  TypePtr ret;
  if (m_propSymValid && (!cls->derivesFromRedeclaring() ||
                         m_propSym->isPrivate())) {
    ret = cls->checkProperty(m_propSym, type, coerce, ar);
    assert(m_object->getType()->isSpecificObject());
    m_valid = true;

    clearEffect(AccessorEffect);
    clearEffect(CreateEffect);
    return ret;
  } else {
    m_actualType = Type::Variant;
    return m_actualType;
  }
}
Beispiel #12
0
void ClassStatement::outputCPP(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->declareClass(\"%s\");\n",
                m_name.c_str());
    }
    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:
    {
      ClassScopePtr parCls;
      if (!m_parent.empty()) parCls = ar->findClass(m_parent);
      cg.printf("class %s%s", Option::ClassPrefix, clsName);
      bool derived = false;
      if (!m_parent.empty() && classScope->derivesFrom(ar, m_parent)) {
        if (parCls->isRedeclaring()) {
          cg.printf(" : public DynamicObjectData");
        } else {
          cg.printf(" : virtual public %s%s", Option::ClassPrefix,
                    parCls->getId().c_str());
        }
        derived = true;
      }
      if (m_base) {
        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)) {
            // temporary fix for inheriting from a re-declaring class
            string id = intfClassScope->getId();
            if (!derived) {
              derived = true;
              cg.printf(" :");
            } else {
              cg.printf(",");
            }
            cg.printf(" virtual public %s%s", Option::ClassPrefix, id.c_str());
          }
        }
      }
      if (!derived) {
        const char *op = derived ? "," : " :";
        if (classScope->derivesFromRedeclaring()) {
          cg.printf("%s public DynamicObjectData", op);
        } else {
          cg.printf("%s virtual public ObjectData", op);
        }
      }
      cg.indentBegin(" {\n");

      if (Option::GenerateCPPMacros) {
        vector<string> bases;
        getAllParents(ar, bases);

        cg.indentBegin("BEGIN_CLASS_MAP(%s)\n", clsName);
        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 = classScope->derivesFromRedeclaring() ==
          ClassScope::DirectFromRedeclared;
        bool idyn = classScope->derivesFromRedeclaring() ==
          ClassScope::IndirectFromRedeclared;
        bool redec = classScope->isRedeclaring();
        if (!classScope->derivesFromRedeclaring()) {
          cg.printf("DECLARE_CLASS(%s, %s, %s)\n", 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 (cg.getOutput() == CodeGenerator::SystemCPP ||
            Option::EnableEval >= Option::LimitedEval) {
          cg.printf("DECLARE_INVOKES_FROM_EVAL\n");
        }
        if (dyn || idyn || redec) {
          if (redec) {

            cg.indentBegin("Variant %sroot_invoke(const char* s, CArrRef ps, "
                           "int64 h, bool f = true) {\n",
                           Option::ObjectPrefix);
            cg.printf("return root->%sinvoke(s, ps, h, f);\n",
                      Option::ObjectPrefix);
            cg.indentEnd("}\n");
            cg.indentBegin("Variant %sroot_invoke_few_args(const char* s, "
                           "int64 h, int count", Option::ObjectPrefix);
            for (int i = 0; i < Option::InvokeFewArgsCount; i++) {
              cg.printf(", CVarRef a%d = null_variant", i);
            }
            cg.printf(") {\n");
            cg.printf("return root->%sinvoke_few_args(s, h, count",
                      Option::ObjectPrefix);
            for (int i = 0; i < Option::InvokeFewArgsCount; i++) {
              cg.printf(", a%d", i);
            }
            cg.printf(");\n");
            cg.indentEnd("}\n");
            if (!dyn && !idyn) cg.printf("private: ObjectData* root;\n");
            cg.printf("public:\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");
      }

      if (!classScope->derivesFromRedeclaring()){
        classScope->getVariables()->outputCPPPropertyDecl(cg, ar);
      }

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

      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 %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(%s%s(NEW(%s%s)(root))->"
                  "dynCreate(params, init));\n",
                  Option::SmartPtrPrefix, clsName,
                  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());
      }
    }
    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->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();
}
Beispiel #13
0
void FunctionStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) {
  // Correctness checks are normally done before adding function to scope.
  if (m_params) {
    for (int i = 0; i < m_params->getCount(); i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      if (param->hasTypeHint() && param->defaultValue()) {
        param->compatibleDefault();
      }
    }
  }

  // note it's important to add to scope, not a pushed FunctionContainer,
  // as a function may be declared inside a class's method, yet this function
  // is a global function, not a class method.
  FunctionScopePtr fs = onInitialParse(ar, scope);
  FunctionScope::RecordFunctionInfo(m_name, fs);
  if (!scope->addFunction(ar, fs)) {
    m_ignored = true;
    return;
  }

  if (Option::PersistenceHook) {
    fs->setPersistent(Option::PersistenceHook(fs, scope));
  }

  if (m_name == "__autoload") {
    if (m_params && m_params->getCount() != 1) {
      parseTimeFatal(Compiler::InvalidMagicMethod,
                     "__autoload() must take exactly 1 argument");
    }
  }

  if (fs->isNative()) {
    if (getStmts()) {
      parseTimeFatal(Compiler::InvalidAttribute,
                     "Native functions must not have an implementation body");
    }
    if (m_params) {
      int nParams = m_params->getCount();
      for (int i = 0; i < nParams; ++i) {
        // Variadic capture params don't need types
        // since they'll be Arrays as far as HNI is concerned.
        auto param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
        if (!param->hasUserType() && !param->isVariadic()) {
          parseTimeFatal(Compiler::InvalidAttribute,
                         "Native function calls must have type hints "
                         "on all args");
        }
      }
    }
    if (getReturnTypeConstraint().empty()) {
      parseTimeFatal(Compiler::InvalidAttribute,
                     "Native function %s() must have a return type hint",
                     getOriginalName().c_str());
    }
  } else if (!getStmts()) {
    parseTimeFatal(Compiler::InvalidAttribute,
                   "Global function %s() must contain a body",
                    getOriginalName().c_str());
  }
}
void FunctionStatement::outputCPPImpl(CodeGenerator &cg,
                                      AnalysisResultPtr ar) {
  CodeGenerator::Context context = cg.getContext();

  FunctionScopePtr funcScope = m_funcScope.lock();
  string fname = funcScope->getId(cg).c_str();
  bool pseudoMain = funcScope->inPseudoMain();
  string origFuncName = !pseudoMain ? funcScope->getOriginalName() :
          ("run_init::" + funcScope->getFileScope()->getName());
  string funcSection;

  if (outputFFI(cg, ar)) return;

  if (context == CodeGenerator::NoContext) {
    string rname = cg.formatLabel(m_name);
    if (funcScope->isRedeclaring()) {
      cg.printf("g->%s%s = &%s%s;\n", Option::CallInfoPrefix, m_name.c_str(),
          Option::CallInfoPrefix, fname.c_str());
    }
    if (funcScope->isVolatile()) {
      cg_printf("g->declareFunctionLit(");
      cg_printString(m_name, ar);
      cg_printf(");\n");
      cg_printf("g->FVF(%s) = true;\n", rname.c_str());
    }
    return;
  }

  if (context == CodeGenerator::CppDeclaration &&
      !funcScope->isInlined()) return;

  if (context == CodeGenerator::CppPseudoMain &&
      !pseudoMain) return;
  if (context == CodeGenerator::CppImplementation &&
      (funcScope->isInlined() || pseudoMain)) return;
  ar->pushScope(funcScope);

  cg.setPHPLineNo(-1);

  if (pseudoMain && !Option::GenerateCPPMain) {
    if (context == CodeGenerator::CppPseudoMain) {
      if (cg.getOutput() != CodeGenerator::SystemCPP) {
        cg.setContext(CodeGenerator::NoContext); // no inner functions/classes
        funcScope->getVariables()->setAttribute(VariableTable::ForceGlobal);
        outputCPPStmt(cg, ar);
        funcScope->getVariables()->clearAttribute(VariableTable::ForceGlobal);
        cg.setContext(CodeGenerator::CppPseudoMain);
        ar->popScope();
        return;
      }
    } else if (context == CodeGenerator::CppForwardDeclaration &&
               cg.getOutput() != CodeGenerator::SystemCPP) {
      return;
    }
  }

  if (context == CodeGenerator::CppImplementation) {
    printSource(cg);
  } else if (context == CodeGenerator::CppForwardDeclaration &&
             Option::GenerateCppLibCode) {
    cg_printf("\n");
    printSource(cg);
    cg.printDocComment(funcScope->getDocComment());
  }

  if (funcScope->isInlined()) cg_printf("inline ");

  TypePtr type = funcScope->getReturnType();
  if (type) {
    type->outputCPPDecl(cg, ar);
  } else {
    cg_printf("void");
  }

  funcSection = Option::FunctionSections[origFuncName];
  if (!funcSection.empty()) {
    cg_printf(" __attribute__ ((section (\".text.%s\")))",
              funcSection.c_str());
  }

  if (pseudoMain) {
    cg_printf(" %s%s(", Option::PseudoMainPrefix,
              funcScope->getFileScope()->pseudoMainName().c_str());
  } else {
    cg_printf(" %s%s(", Option::FunctionPrefix, fname.c_str());
  }

  switch (context) {
  case CodeGenerator::CppForwardDeclaration:
    funcScope->outputCPPParamsDecl(cg, ar, m_params, true);
    cg_printf(");\n");
    if (funcScope->hasDirectInvoke()) {
      cg_printf("Variant %s%s(void *extra, CArrRef params);\n",
                Option::InvokePrefix, fname.c_str());
    }
    break;
  case CodeGenerator::CppDeclaration:
  case CodeGenerator::CppImplementation:
  case CodeGenerator::CppPseudoMain:
    {
      funcScope->outputCPPParamsDecl(cg, ar, m_params, false);
      cg_indentBegin(") {\n");
      const char *sys =
        (cg.getOutput() == CodeGenerator::SystemCPP ? "_BUILTIN" : "");
      if (pseudoMain) {
        cg_printf("PSEUDOMAIN_INJECTION%s(%s, %s%s);\n",
                  sys, origFuncName.c_str(), Option::PseudoMainPrefix,
                  funcScope->getFileScope()->pseudoMainName().c_str());
      } else {
        if (m_stmt->hasBody()) {
          cg_printf("FUNCTION_INJECTION%s(%s);\n", sys, origFuncName.c_str());
        }
        outputCPPArgInjections(cg, ar, origFuncName.c_str(), ClassScopePtr(),
                               funcScope);
      }
      funcScope->outputCPP(cg, ar);
      cg.setContext(CodeGenerator::NoContext); // no inner functions/classes
      outputCPPStmt(cg, ar);
      cg.setContext(context);
      cg_indentEnd("} /* function */\n");
    }
    break;
  default:
    ASSERT(false);
  }

  ar->popScope();
}
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_class && 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(")");
}
Beispiel #16
0
void AnalysisResult::analyzeProgram(bool system /* = false */) {
  AnalysisResultPtr ar = shared_from_this();

  getVariables()->forceVariants(ar, VariableTable::AnyVars);
  getVariables()->setAttribute(VariableTable::ContainsLDynamicVariable);
  getVariables()->setAttribute(VariableTable::ContainsExtract);
  getVariables()->setAttribute(VariableTable::ForceGlobal);

  // Analyze Includes
  Logger::Verbose("Analyzing Includes");
  sort(m_fileScopes.begin(), m_fileScopes.end(), by_filename); // fixed order
  unsigned int i = 0;
  for (i = 0; i < m_fileScopes.size(); i++) {
    collectFunctionsAndClasses(m_fileScopes[i]);
  }

  // Keep generated code identical without randomness
  canonicalizeSymbolOrder();

  markRedeclaringClasses();

  // Analyze some special cases
  for (set<string>::const_iterator it = Option::VolatileClasses.begin();
       it != Option::VolatileClasses.end(); ++it) {
    ClassScopePtr cls = findClass(toLower(*it));
    if (cls && cls->isUserClass()) {
      cls->setVolatile();
    }
  }

  checkClassDerivations();
  resolveNSFallbackFuncs();

  // Analyze All
  Logger::Verbose("Analyzing All");
  setPhase(AnalysisResult::AnalyzeAll);
  for (i = 0; i < m_fileScopes.size(); i++) {
    m_fileScopes[i]->analyzeProgram(ar);
  }

  /*
    Note that cls->collectMethods() can add entries to m_classDecs,
    which can invalidate iterators. So we have to create an array
    and then iterate over that.
    The new entries added to m_classDecs are always empty, so it
    doesnt matter that we dont include them in the iteration
  */
  std::vector<ClassScopePtr> classes;
  classes.reserve(m_classDecs.size());
  for (StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.begin();
       iter != m_classDecs.end(); ++iter) {
    for (ClassScopePtr cls: iter->second) {
      classes.push_back(cls);
    }
  }

  // Collect methods
  for (ClassScopePtr cls: classes) {
    if (cls->isRedeclaring()) {
      cls->setStaticDynamic(ar);
    }
    StringToFunctionScopePtrMap methods;
    cls->collectMethods(ar, methods, true /* include privates */);
    bool needAbstractMethodImpl =
      (!cls->isAbstract() && !cls->isInterface() &&
       cls->derivesFromRedeclaring() == Derivation::Normal &&
       !cls->getAttribute(ClassScope::UsesUnknownTrait));
    for (StringToFunctionScopePtrMap::const_iterator iterMethod =
           methods.begin(); iterMethod != methods.end(); ++iterMethod) {
      FunctionScopePtr func = iterMethod->second;
      if (Option::WholeProgram && !func->hasImpl() && needAbstractMethodImpl) {
        FunctionScopePtr tmpFunc =
          cls->findFunction(ar, func->getName(), true, true);
        always_assert(!tmpFunc || !tmpFunc->hasImpl());
        Compiler::Error(Compiler::MissingAbstractMethodImpl,
                        func->getStmt(), cls->getStmt());
      }
      m_methodToClassDecs[iterMethod->first].push_back(cls);
    }
  }

  ClassScopePtr cls;
  string cname;
  for (auto& sysclass_cls: m_systemClasses) {
    tie(cname, cls) = sysclass_cls;
    StringToFunctionScopePtrMap methods;
    cls->collectMethods(ar, methods, true /* include privates */);
    for (StringToFunctionScopePtrMap::const_iterator iterMethod =
           methods.begin(); iterMethod != methods.end(); ++iterMethod) {
      m_methodToClassDecs[iterMethod->first].push_back(cls);
    }
  }
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
                                             TypePtr type, bool coerce) {
  m_valid = false;

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

  if (!m_property->is(Expression::KindOfScalarExpression)) {
    m_property->inferAndCheck(ar, Type::String, false);
    // we also lost track of which class variable an expression is about, hence
    // any type inference could be wrong. Instead, we just force variants on
    // all class variables.
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(getOriginalClass(), false, true);
    }
    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
  const string &name = exp->getLiteralString();
  if (name.empty()) {
    m_property->inferAndCheck(ar, Type::String, false);
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(getOriginalClass(), false, true);
    }
    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  m_property->inferAndCheck(ar, Type::String, false);

  ClassScopePtr cls;
  if (objectType && !objectType->getName().empty()) {
    // what object-> has told us
    cls = ar->findExactClass(shared_from_this(), objectType->getName());
  } else {
    if ((m_context & LValue) && objectType &&
        !objectType->is(Type::KindOfObject) &&
        !objectType->is(Type::KindOfVariant) &&
        !objectType->is(Type::KindOfSome) &&
        !objectType->is(Type::KindOfAny)) {
      m_object->inferAndCheck(ar, Type::Object, true);
    }
  }

  if (!cls) {
    if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
      ar->forceClassVariants(name, getOriginalClass(), false, true);
    }
    return Type::Variant;
  }

  // resolved to this class
  if (m_context & RefValue) {
    type = Type::Variant;
    coerce = true;
  }

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr func = m_object->getOriginalFunction();
    if (!func || func->isStatic()) {
      if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::MissingObjectContext, self);
      }
      m_actualType = Type::Variant;
      return m_actualType;
    }
  }

  assert(cls);
  if (!m_propSym || cls != m_objectClass.lock()) {
    m_objectClass = cls;
    ClassScopePtr parent;
    m_propSym = cls->findProperty(parent, name, ar);
    if (m_propSym) {
      if (!parent) {
        parent = cls;
      }
      m_symOwner = parent;
      always_assert(m_propSym->isPresent());
      m_propSymValid =
        (!m_propSym->isPrivate() || getOriginalClass() == parent) &&
        !m_propSym->isStatic();

      if (m_propSymValid) {
        m_symOwner->addUse(getScope(),
                           BlockScope::GetNonStaticRefUseKind(
                             m_propSym->getHash()));
      }
    }
  }

  TypePtr ret;
  if (m_propSymValid && (!cls->derivesFromRedeclaring() ||
                         m_propSym->isPrivate())) {
    always_assert(m_symOwner);
    TypePtr t(m_propSym->getType());
    if (t && t->is(Type::KindOfVariant)) {
      // only check property if we could possibly do some work
      ret = t;
    } else {
      if (coerce && type->is(Type::KindOfAutoSequence) &&
          (!t || t->is(Type::KindOfVoid) ||
           t->is(Type::KindOfSome) || t->is(Type::KindOfArray))) {
        type = Type::Array;
      }
      assert(getScope()->is(BlockScope::FunctionScope));
      GET_LOCK(m_symOwner);
      ret = m_symOwner->checkProperty(getScope(), m_propSym, type, coerce, ar);
    }
    always_assert(m_object->getActualType() &&
           m_object->getActualType()->isSpecificObject());
    m_valid = true;
    return ret;
  } else {
    m_actualType = Type::Variant;
    return m_actualType;
  }
}
Beispiel #18
0
ExpressionPtr FunctionCall::inliner(AnalysisResultConstPtr ar,
                                    ExpressionPtr obj, std::string localThis) {
  FunctionScopePtr fs = getFunctionScope();
  if (m_noInline || !fs || fs == m_funcScope || !m_funcScope->getStmt()) {
    return ExpressionPtr();
  }

  BlockScope::s_jobStateMutex.lock();
  if (m_funcScope->getMark() == BlockScope::MarkProcessing) {
    fs->setForceRerun(true);
    BlockScope::s_jobStateMutex.unlock();
    return ExpressionPtr();
  }
  ReadLock lock(m_funcScope->getInlineMutex());
  BlockScope::s_jobStateMutex.unlock();

  if (!m_funcScope->getInlineAsExpr()) {
    return ExpressionPtr();
  }

  if (m_funcScope->getInlineSameContext() &&
      m_funcScope->getContainingClass() &&
      m_funcScope->getContainingClass() != getClassScope()) {
    /*
      The function contains a context sensitive construct such as
      call_user_func (context sensitive because it could call
      array('parent', 'foo')) so its not safe to inline it
      into a different context.
    */
    return ExpressionPtr();
  }

  MethodStatementPtr m
    (dynamic_pointer_cast<MethodStatement>(m_funcScope->getStmt()));

  VariableTablePtr vt = fs->getVariables();
  int nAct = m_params ? m_params->getCount() : 0;
  int nMax = m_funcScope->getMaxParamCount();
  if (nAct < m_funcScope->getMinParamCount() || !m->getStmts()) {
    return ExpressionPtr();
  }

  InlineCloneInfo info(m_funcScope);
  info.elist = ExpressionListPtr(new ExpressionList(
                                   getScope(), getLocation(),
                                   ExpressionList::ListKindWrapped));
  std::ostringstream oss;
  oss << fs->nextInlineIndex() << "_" << m_name << "_";
  std::string prefix = oss.str();

  if (obj) {
    info.callWithThis = true;
    if (!obj->isThis()) {
      SimpleVariablePtr var
        (new SimpleVariable(getScope(),
                            obj->getLocation(),
                            prefix + "this"));
      var->updateSymbol(SimpleVariablePtr());
      var->getSymbol()->setHidden();
      var->getSymbol()->setUsed();
      var->getSymbol()->setReferenced();
      AssignmentExpressionPtr ae
        (new AssignmentExpression(getScope(),
                                  obj->getLocation(),
                                  var, obj, false));
      info.elist->addElement(ae);
      info.sepm[var->getName()] = var;
      info.localThis = var->getName();
    }
  } else {
    if (m_classScope) {
      if (!m_funcScope->isStatic()) {
        ClassScopeRawPtr oCls = getOriginalClass();
        FunctionScopeRawPtr oFunc = getOriginalFunction();
        if (oCls && !oFunc->isStatic() &&
            (oCls == m_classScope ||
             oCls->derivesFrom(ar, m_className, true, false))) {
          info.callWithThis = true;
          info.localThis = localThis;
        }
      }
      if (!isSelf() && !isParent() && !isStatic()) {
        info.staticClass = m_className;
      }
    }
  }

  ExpressionListPtr plist = m->getParams();

  int i;

  for (i = 0; i < nMax || i < nAct; i++) {
    ParameterExpressionPtr param
      (i < nMax ?
       dynamic_pointer_cast<ParameterExpression>((*plist)[i]) :
       ParameterExpressionPtr());
    ExpressionPtr arg = i < nAct ? (*m_params)[i] :
      Clone(param->defaultValue(), getScope());
    SimpleVariablePtr var
      (new SimpleVariable(getScope(),
                          (i < nAct ? arg.get() : this)->getLocation(),
                          prefix + (param ?
                                    param->getName() :
                                    lexical_cast<string>(i))));
    var->updateSymbol(SimpleVariablePtr());
    var->getSymbol()->setHidden();
    var->getSymbol()->setUsed();
    var->getSymbol()->setReferenced();
    bool ref =
      (i < nMax && m_funcScope->isRefParam(i)) ||
      arg->hasContext(RefParameter);
    arg->clearContext(RefParameter);
    AssignmentExpressionPtr ae
      (new AssignmentExpression(getScope(),
                                arg->getLocation(),
                                var, arg, ref));
    info.elist->addElement(ae);
    if (i < nAct && (ref || !arg->isScalar())) {
      info.sepm[var->getName()] = var;
    }
  }

  if (cloneStmtsForInline(info, m->getStmts(), prefix, ar,
                          getFunctionScope()) <= 0) {
    info.elist->addElement(makeConstant(ar, "null"));
  }

  if (info.sepm.size()) {
    ExpressionListPtr unset_list
      (new ExpressionList(getScope(), getLocation()));

    for (StringToExpressionPtrMap::iterator it = info.sepm.begin(),
           end = info.sepm.end(); it != end; ++it) {
      ExpressionPtr var = it->second->clone();
      var->clearContext((Context)(unsigned)-1);
      unset_list->addElement(var);
    }

    ExpressionPtr unset(
      new UnaryOpExpression(getScope(), getLocation(),
                            unset_list, T_UNSET, true));
    i = info.elist->getCount();
    ExpressionPtr ret = (*info.elist)[--i];
    if (ret->isScalar()) {
      info.elist->insertElement(unset, i);
    } else {
      ExpressionListPtr result_list
        (new ExpressionList(getScope(), getLocation(),
                            ExpressionList::ListKindLeft));
      if (ret->hasContext(LValue)) {
        result_list->setContext(LValue);
        result_list->setContext(ReturnContext);
      }
      result_list->addElement(ret);
      result_list->addElement(unset);
      (*info.elist)[i] = result_list;
    }
  }

  recomputeEffects();

  return replaceValue(info.elist);
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
                                             TypePtr type, bool coerce) {
  m_valid = false;

  ConstructPtr self = shared_from_this();
  TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), false);

  if (!m_property->is(Expression::KindOfScalarExpression)) {
    // if dynamic property or method, we have nothing to find out
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UseDynamicProperty, self);
    }
    m_property->inferAndCheck(ar, Type::String, false);

    // we also lost track of which class variable an expression is about, hence
    // any type inference could be wrong. Instead, we just force variants on
    // all class variables.
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants();
    }

    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
  string name = exp->getString();
  ASSERT(!name.empty());

  m_property->inferAndCheck(ar, Type::String, false);

  ClassScopePtr cls;
  if (objectType && !objectType->getName().empty()) {
    // what object-> has told us
    cls = ar->findExactClass(objectType->getName());
  } else {
    // what ->property has told us
    cls = ar->findClass(name, AnalysisResult::PropertyName);
    if (cls) {
      m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()),
                              false);
    }
  }

  if (!cls) {
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(name);
    }
    return Type::Variant;
  }

  const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" :
    hasContext(ExistContext) ? "__isset" :
    hasContext(UnsetContext) ? "__unset" : "__get";
  if (!cls->implementsAccessor(ar, accessorName)) clearEffect(AccessorEffect);

  // resolved to this class
  int present = 0;
  if (m_context & RefValue) {
    type = Type::Variant;
    coerce = true;
  }

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr func = ar->getFunctionScope();
    if (func->isStatic()) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::MissingObjectContext,
                                   self);
      }
      m_actualType = Type::Variant;
      return m_actualType;
    }
  }

  TypePtr ret;
  if (!cls->derivesFromRedeclaring()) { // Have to use dynamic.
    ret = cls->checkProperty(name, type, coerce, ar, self, present);
    // Private only valid if in the defining class
    if (present && (getOriginalScope(ar) == cls ||
                    !(present & VariableTable::VariablePrivate))) {
      m_valid = true;
      m_static = present & VariableTable::VariableStatic;
      if (m_static) {
        ar->getScope()->getVariables()->
          setAttribute(VariableTable::NeedGlobalPointer);
      }
      m_class = cls;
    }
  }

  // get() will return Variant
  if (!m_valid || !m_object->getType()->isSpecificObject()) {
    m_actualType = Type::Variant;
    return m_actualType;
  }

  clearEffect(AccessorEffect);

  if (ar->getPhase() == AnalysisResult::LastInference) {
    if (!(m_context & ObjectContext)) {
      m_object->clearContext(Expression::LValue);
    }
    setContext(Expression::NoLValueWrapper);
  }
  return ret;
}
Beispiel #20
0
TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar,
                                           TypePtr type, bool coerce,
                                           FunctionScopePtr func,
                                           bool arrayParams) {
#ifdef HPHP_DETAILED_TYPE_INF_ASSERT
  assert(func->hasUser(getScope(), BlockScope::UseKindCaller));
#endif /* HPHP_DETAILED_TYPE_INF_ASSERT */
  ConstructPtr self = shared_from_this();
  TypePtr frt;
  {
    TRY_LOCK(func);
    func->getInferTypesMutex().assertOwnedBySelf();
    assert(!func->inVisitScopes() || getScope() == func);
    frt = func->getReturnType();
  }

  // fix return type for generators and async functions here, keep the
  // infered return type in function scope to allow further optimizations
  if (func->isGenerator()) {
    frt = Type::GetType(Type::KindOfObject, "Continuation");
  } else if (func->isAsync()) {
    frt = Type::GetType(Type::KindOfObject, "WaitHandle");
  }

  if (!frt) {
    m_voidReturn = true;
    setActualType(TypePtr());
    if (!isUnused() && !type->is(Type::KindOfAny)) {
      if (!hasContext(ReturnContext) &&
          !func->isFirstPass() && !func->isAbstract()) {
        if (Option::WholeProgram || !func->getContainingClass() ||
            func->isStatic() || func->isFinal() || func->isPrivate()) {
          Compiler::Error(Compiler::UseVoidReturn, self);
        }
      }
      if (!Type::IsMappedToVariant(type)) {
        setExpectedType(type);
      }
      m_voidWrapper = true;
    }
  } else {
    m_voidReturn = false;
    m_voidWrapper = false;
    type = checkTypesImpl(ar, type, frt, coerce);
    assert(m_actualType);
  }
  if (arrayParams) {
    m_extraArg = 0;
    (*m_params)[0]->inferAndCheck(ar, Type::Array, false);
  } else {
    m_extraArg = func->inferParamTypes(ar, self, m_params, m_valid);
  }
  m_variableArgument = func->isVariableArgument();
  if (m_valid) {
    m_implementedType.reset();
  } else {
    m_implementedType = Type::Variant;
  }
  assert(type);

  return type;
}
void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) {
    m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name);
    m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name);

    VariableTablePtr variables = getScope()->getVariables();
    if (m_superGlobal) {
        variables->setAttribute(VariableTable::NeedGlobalPointer);
    } else if (m_name == "GLOBALS") {
        m_globals = true;
    } else {
        m_sym = variables->addDeclaredSymbol(
                    m_name,
                    Option::OutputHHBC ? shared_from_this() : ConstructPtr());
    }

    if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
        if (FunctionScopePtr func = getFunctionScope()) {
            if (m_name == "this" && (func->inPseudoMain() || getClassScope())) {
                func->setContainsThis();
                m_this = true;
                if (!hasContext(ObjectContext)) {
                    bool unset = hasAllContext(UnsetContext | LValue);
                    func->setContainsBareThis(
                        true,
                        hasAnyContext(RefValue | RefAssignmentLHS) ||
                        m_sym->isRefClosureVar() || unset);
                    if (variables->getAttribute(VariableTable::ContainsDynamicVariable)) {
                        ClassScopePtr cls = getClassScope();
                        TypePtr t = !cls || cls->isRedeclaring() ?
                                    Type::Variant : Type::CreateObjectType(cls->getName());
                        variables->add(m_sym, t, true, ar, shared_from_this(),
                                       getScope()->getModifiers());
                    }
                }
            }
            if (m_sym && !(m_context & AssignmentLHS) &&
                    !((m_context & UnsetContext) && (m_context & LValue))) {
                m_sym->setUsed();
            }
        }
    } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
        if (m_sym) {
            if (!m_sym->isSystem() &&
                    !(getContext() &
                      (LValue|RefValue|RefParameter|UnsetContext|ExistContext)) &&
                    m_sym->getDeclaration().get() == this &&
                    !variables->getAttribute(VariableTable::ContainsLDynamicVariable) &&
                    !getScope()->is(BlockScope::ClassScope)) {
                if (getScope()->inPseudoMain()) {
                    Compiler::Error(Compiler::UseUndeclaredGlobalVariable,
                                    shared_from_this());
                } else if (!m_sym->isClosureVar()) {
                    Compiler::Error(Compiler::UseUndeclaredVariable, shared_from_this());
                }
            }
            // check function parameter that can occur in lval context
            if (m_sym->isParameter() &&
                    m_context & (LValue | RefValue | DeepReference |
                                 UnsetContext | InvokeArgument | OprLValue |
                                 DeepOprLValue)) {
                m_sym->setLvalParam();
            }
        }
        if (m_superGlobal || m_name == "GLOBALS") {
            FunctionScopePtr func = getFunctionScope();
            if (func) func->setNeedsCheckMem();
        }
    }
}
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) {
    if (m_class) {
        m_class->analyzeProgram(ar);
        setDynamicByIdentifier(ar, m_name);
    } else {
        if (m_className.empty()) {
            addUserFunction(ar, m_name);
        } else if (m_className != "parent") {
            addUserClass(ar, m_className);
        } else {
            m_parentClass = true;
        }
    }

    if (ar->getPhase() == AnalysisResult::AnalyzeInclude) {

        CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude);

        ConstructPtr self = shared_from_this();

        // We need to know the name of the constant so that we can associate it
        // with this file before we do type inference.
        if (!m_class && m_className.empty() && m_type == DefineFunction) {
            ScalarExpressionPtr name =
                dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
            string varName;
            if (name) {
                varName = name->getIdentifier();
                if (!varName.empty()) {
                    ar->getFileScope()->declareConstant(ar, varName);
                }
            }
            // handling define("CONSTANT", ...);
            if (m_params && m_params->getCount() >= 2) {
                ScalarExpressionPtr name =
                    dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
                string varName;
                if (name) {
                    varName = name->getIdentifier();
                    if (!varName.empty()) {
                        ExpressionPtr value = (*m_params)[1];
                        ConstantTablePtr constants =
                            ar->findConstantDeclarer(varName)->getConstants();
                        if (constants != ar->getConstants()) {
                            constants->add(varName, NEW_TYPE(Some), value, ar, self);

                            if (name->hasHphpNote("Dynamic")) {
                                constants->setDynamic(ar, varName);
                            }
                        }
                    }
                }
            }
        }

        if (m_type == UnserializeFunction) {
            ar->forceClassVariants();
        }
    }

    if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
        // Look up the corresponding FunctionScope and ClassScope
        // for this function call
        {
            FunctionScopePtr func;
            ClassScopePtr cls;
            if (!m_class && m_className.empty()) {
                func = ar->findFunction(m_name);
            } else {
                cls = ar->resolveClass(m_className);
                if (cls) {
                    if (m_name == "__construct") {
                        func = cls->findConstructor(ar, true);
                    } else {
                        func = cls->findFunction(ar, m_name, true, true);
                    }
                }
            }
            if (func && !func->isRedeclaring()) {
                if (m_funcScope != func) {
                    m_funcScope = func;
                    Construct::recomputeEffects();
                }
            }
            if (cls && !cls->isRedeclaring())
                m_classScope = cls;
        }
        // check for dynamic constant and volatile function/class
        if (!m_class && m_className.empty() &&
                (m_type == DefinedFunction ||
                 m_type == FunctionExistsFunction ||
                 m_type == ClassExistsFunction ||
                 m_type == InterfaceExistsFunction) &&
                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()) {
                    string symbol = name->getLiteralString();
                    switch (m_type) {
                    case DefinedFunction: {
                        ConstantTablePtr constants = ar->getConstants();
                        if (!constants->isPresent(symbol)) {
                            // user constant
                            BlockScopePtr block = ar->findConstantDeclarer(symbol);
                            if (block) { // found the constant
                                constants = block->getConstants();
                                // set to be dynamic
                                constants->setDynamic(ar, symbol);
                            }
                        }
                        break;
                    }
                    case FunctionExistsFunction: {
                        FunctionScopePtr func = ar->findFunction(Util::toLower(symbol));
                        if (func && func->isUserFunction()) {
                            func->setVolatile();
                        }
                        break;
                    }
                    case InterfaceExistsFunction:
                    case ClassExistsFunction: {
                        ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                        if (cls && cls->isUserClass()) {
                            cls->setVolatile();
                        }
                        break;
                    }
                    default:
                        ASSERT(false);
                    }
                }
            }
        }
    }

    if (m_params) {
        if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
            if (m_funcScope) {
                ExpressionList &params = *m_params;
                int mpc = m_funcScope->getMaxParamCount();
                for (int i = params.getCount(); i--; ) {
                    ExpressionPtr p = params[i];
                    if (i < mpc ? m_funcScope->isRefParam(i) :
                            m_funcScope->isReferenceVariableArgument()) {
                        p->setContext(Expression::RefValue);
                    } else if (!(p->getContext() & Expression::RefParameter)) {
                        p->clearContext(Expression::RefValue);
                    }
                }
            } else {
                FunctionScopePtr func = ar->findFunction(m_name);
                if (func && func->isRedeclaring()) {
                    FunctionScope::RefParamInfoPtr info =
                        FunctionScope::GetRefParamInfo(m_name);
                    if (info) {
                        for (int i = m_params->getCount(); i--; ) {
                            if (info->isRefParam(i)) {
                                m_params->markParam(i, canInvokeFewArgs());
                            }
                        }
                    }
                } else {
                    m_params->markParams(false);
                }
            }
        }

        m_params->analyzeProgram(ar);
    }
}
Beispiel #23
0
bool ClassScope::NeedStaticArray(ClassScopePtr cls, FunctionScopePtr func) {
  return cls && cls->getAttribute(NotFinal) && !func->isPrivate();
}
ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) {
    if (m_class) ar->preOptimize(m_class);
    ar->preOptimize(m_nameExp);
    ar->preOptimize(m_params);
    if (ar->getPhase() != AnalysisResult::SecondPreOptimize) {
        return ExpressionPtr();
    }
    // optimize away various "exists" functions, this may trigger
    // dead code elimination and improve type-inference.
    if (!m_class && m_className.empty() &&
            (m_type == DefinedFunction ||
             m_type == FunctionExistsFunction ||
             m_type == ClassExistsFunction ||
             m_type == InterfaceExistsFunction) &&
            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()) {
                string symbol = name->getLiteralString();
                switch (m_type) {
                case DefinedFunction: {
                    ConstantTablePtr constants = ar->getConstants();
                    // system constant
                    if (constants->isPresent(symbol)) {
                        return CONSTANT("true");
                    }
                    // user constant
                    BlockScopePtr block = ar->findConstantDeclarer(symbol);
                    // not found (i.e., undefined)
                    if (!block) {
                        if (symbol.find("::") == std::string::npos) {
                            return CONSTANT("false");
                        } else {
                            // e.g., defined("self::ZERO")
                            return ExpressionPtr();
                        }
                    }
                    constants = block->getConstants();
                    // already set to be dynamic
                    if (constants->isDynamic(symbol)) return ExpressionPtr();
                    ConstructPtr decl = constants->getValue(symbol);
                    ExpressionPtr constValue = dynamic_pointer_cast<Expression>(decl);
                    if (constValue->isScalar()) {
                        return CONSTANT("true");
                    } else {
                        return ExpressionPtr();
                    }
                    break;
                }
                case FunctionExistsFunction: {
                    const std::string &lname = Util::toLower(symbol);
                    if (Option::DynamicInvokeFunctions.find(lname) ==
                            Option::DynamicInvokeFunctions.end()) {
                        FunctionScopePtr func = ar->findFunction(lname);
                        if (!func) {
                            return CONSTANT("false");
                        } else if (!func->isVolatile()) {
                            return CONSTANT("true");
                        }
                    }
                    break;
                }
                case InterfaceExistsFunction: {
                    ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                    if (!cls || !cls->isInterface()) {
                        return CONSTANT("false");
                    } else if (!cls->isVolatile()) {
                        return CONSTANT("true");
                    }
                    break;
                }
                case ClassExistsFunction: {
                    ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                    if (!cls || cls->isInterface()) {
                        return CONSTANT("false");
                    } else if (!cls->isVolatile()) {
                        return CONSTANT("true");
                    }
                    break;
                }
                default:
                    ASSERT(false);
                }
            }
        }
    }
    return ExpressionPtr();
}
void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p,
                                     bool sep) {
  while (*p) {
    // Parse name
    const char *cname = *p++;
    // Parse parent
    const char *cparent = *p++;
    if (!cparent) cparent = "";
    // Parse list of interfaces
    vector<string> ifaces;
    while (*p) ifaces.push_back(*p++);
    p++;
    // Parse methods
    FunctionScopePtrVec methods;
    while (*p) {
      FunctionScopePtr fs = ParseExtFunction(ar, p);
      if (sep) {
        fs->setSepExtension();
      }
      int flags = (int)(int64)(*p++);
      if (flags & ClassInfo::IsAbstract) {
        fs->addModifier(T_ABSTRACT);
      }
      int vismod = 0;
      if (flags & ClassInfo::IsProtected) {
        vismod = T_PROTECTED;
      } else if (flags & ClassInfo::IsPrivate) {
        vismod = T_PRIVATE;
      }
      fs->addModifier(vismod);
      if (flags & ClassInfo::IsStatic) {
        fs->addModifier(T_STATIC);
      }
      methods.push_back(fs);
    }
    if (cparent && *cparent && (ifaces.empty() || ifaces[0] != cparent)) {
      ifaces.insert(ifaces.begin(), cparent);
    }
    ClassScopePtr cl(new ClassScope(ar, cname, cparent, ifaces, methods));
    p++;
    // Parse properties
    while (*p) {
      *p++; // TODO, support visibility
      const char *name = *p++;
      TypePtr type = ParseType(p);
      cl->getVariables()->add(name, type, false, ar, ExpressionPtr(),
                              ModifierExpressionPtr());
    }
    p++;
    // Parse consts
    while (*p) {
      const char *name = *p++;
      TypePtr type = ParseType(p);
      cl->getConstants()->add(name, type, ExpressionPtr(), ar, ConstructPtr());
    }
    p++;

    int flags = (int)(int64)(*p++);
    cl->setClassInfoAttribute(flags);
    if (flags & ClassInfo::HasDocComment) {
      cl->setDocComment(*p++);
    }

    cl->setSystem();
    if (sep) {
      cl->setSepExtension();
    }
    s_classes[cl->getName()] = cl;
  }
}
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
        bool coerce) {
    reset();

    ConstructPtr self = shared_from_this();

    // handling define("CONSTANT", ...);
    if (!m_class && 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();
                if (!varName.empty()) {
                    ExpressionPtr value = (*m_params)[1];
                    TypePtr varType = value->inferAndCheck(ar, NEW_TYPE(Some), false);
                    ar->getDependencyGraph()->
                    addParent(DependencyGraph::KindOfConstant,
                              ar->getName(), varName, self);
                    ConstantTablePtr constants =
                        ar->findConstantDeclarer(varName)->getConstants();
                    if (constants != ar->getConstants()) {
                        if (value && !value->isScalar()) {
                            constants->setDynamic(ar, varName);
                            varType = Type::Variant;
                        }
                        if (constants->isDynamic(varName)) {
                            m_dynamicConstant = true;
                            ar->getScope()->getVariables()->
                            setAttribute(VariableTable::NeedGlobalPointer);
                        } else {
                            constants->setType(ar, varName, varType, true);
                        }
                        // in case the old 'value' has been optimized
                        constants->setValue(ar, varName, value);
                    }
                    return checkTypesImpl(ar, type, Type::Boolean, coerce);
                }
            }
            if (varName.empty() && ar->isFirstPass()) {
                ar->getCodeError()->record(self, CodeError::BadDefine, self);
            }
        } else if (m_type == ExtractFunction) {
            ar->getScope()->getVariables()->forceVariants(ar);
        }
    }

    FunctionScopePtr func;

    // avoid raising both MissingObjectContext and UnknownFunction
    bool errorFlagged = false;

    if (!m_class && m_className.empty()) {
        func = ar->findFunction(m_name);
    } else {
        ClassScopePtr cls = ar->resolveClass(m_className);
        if (cls && cls->isVolatile()) {
            ar->getScope()->getVariables()
            ->setAttribute(VariableTable::NeedGlobalPointer);
        }
        if (!cls || cls->isRedeclaring()) {
            if (cls) {
                m_redeclaredClass = true;
            }
            if (!cls && ar->isFirstPass()) {
                ar->getCodeError()->record(self, CodeError::UnknownClass, self);
            }
            if (m_params) {
                m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
            }
            return checkTypesImpl(ar, type, Type::Variant, coerce);
        }
        m_derivedFromRedeclaring = cls->derivesFromRedeclaring();
        m_validClass = true;

        if (m_name == "__construct") {
            // if the class is known, php will try to identify class-name ctor
            func = cls->findConstructor(ar, true);
        }
        else {
            func = cls->findFunction(ar, m_name, true, true);
        }

        if (func && !func->isStatic()) {
            ClassScopePtr clsThis = ar->getClassScope();
            FunctionScopePtr funcThis = ar->getFunctionScope();
            if (!clsThis ||
                    (clsThis->getName() != m_className &&
                     !clsThis->derivesFrom(ar, m_className)) ||
                    funcThis->isStatic()) {
                // set the method static to avoid "unknown method" runtime exception
                if (Option::StaticMethodAutoFix && !func->containsThis()) {
                    func->setStaticMethodAutoFixed();
                }
                if (ar->isFirstPass()) {
                    ar->getCodeError()->record(self, CodeError::MissingObjectContext,
                                               self);
                    errorFlagged = true;
                }
                func.reset();
            }
        }
    }
    if (!func || func->isRedeclaring()) {
        if (func) {
            m_redeclared = true;
            ar->getScope()->getVariables()->
            setAttribute(VariableTable::NeedGlobalPointer);
        }
        if (!func && !errorFlagged && ar->isFirstPass()) {
            ar->getCodeError()->record(self, CodeError::UnknownFunction, self);
        }
        if (m_params) {
            if (func) {
                FunctionScope::RefParamInfoPtr info =
                    FunctionScope::GetRefParamInfo(m_name);
                ASSERT(info);
                for (int i = m_params->getCount(); i--; ) {
                    if (info->isRefParam(i)) {
                        m_params->markParam(i, canInvokeFewArgs());
                    }
                }
            }
            m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
        }
        return checkTypesImpl(ar, type, Type::Variant, coerce);
    }
    m_builtinFunction = !func->isUserFunction();

    if (m_redeclared) {
        if (m_params) {
            m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
        }
        return checkTypesImpl(ar, type, type, coerce);
    }

    CHECK_HOOK(beforeSimpleFunctionCallCheck);

    m_valid = true;
    type = checkParamsAndReturn(ar, type, coerce, func);

    if (!m_valid && m_params) {
        m_params->markParams(false);
    }

    CHECK_HOOK(afterSimpleFunctionCallCheck);

    return type;
}
bool BuiltinSymbols::Load(AnalysisResultPtr ar, bool extOnly /* = false */) {
  if (Loaded) return true;
  Loaded = true;

  // Build function scopes for some of the runtime helper functions
  // declared in "runtime/base/builtin_functions.h"
  const char **helper = HelperFunctions;
  while (*helper) {
    FunctionScopePtr f = ParseHelperFunction(ar, helper);
    ASSERT(!s_helperFunctions[f->getName()]);
    s_helperFunctions[f->getName()] = f;
  }

  // load extension functions first, so system/classes may call them
  ParseExtFunctions(ar, ExtensionFunctions, false);

  // parse all PHP files under system/classes
  if (!extOnly) {
    ar = AnalysisResultPtr(new AnalysisResult());
    ar->loadBuiltinFunctions();
    for (const char **cls = SystemClasses; *cls; cls++) {
      string phpBaseName = "/system/classes/";
      phpBaseName += *cls;
      phpBaseName += ".php";
      string phpFileName = Option::GetSystemRoot() + phpBaseName;
      const char *baseName = s_strings.add(phpBaseName.c_str());
      const char *fileName = s_strings.add(phpFileName.c_str());
      try {
        Scanner scanner(new ylmm::basic_buffer(fileName), true, false);
        ParserPtr parser(new Parser(scanner, baseName, 0, ar));
        if (parser->parse()) {
          Logger::Error("Unable to parse file %s: %s", fileName,
                        parser->getMessage().c_str());
          ASSERT(false);
        }
      } catch (std::runtime_error) {
        Logger::Error("Unable to open file %s", fileName);
      }
    }
    ar->analyzeProgram(true);
    ar->inferTypes();
    const StringToFileScopePtrMap &files = ar->getAllFiles();
    for (StringToFileScopePtrMap::const_iterator iterFile = files.begin();
         iterFile != files.end(); iterFile++) {
      const StringToClassScopePtrVecMap &classes =
        iterFile->second->getClasses();
      for (StringToClassScopePtrVecMap::const_iterator iter = classes.begin();
           iter != classes.end(); ++iter) {
        ASSERT(iter->second.size() == 1);
        iter->second[0]->setSystem();
        ASSERT(!s_classes[iter->first]);
        s_classes[iter->first] = iter->second[0];
      }
    }

    // parse globals/variables.php and globals/constants.php
    NoSuperGlobals = true;
    s_variables = LoadGlobalSymbols("symbols.php")->getVariables();
    const FileScopePtrVec &fileScopes =
      LoadGlobalSymbols("constants.php")->getAllFilesVector();
    if (!fileScopes.empty()) {
      s_constants = fileScopes[0]->getConstants();
    } else {
      AnalysisResult ar2;
      s_constants = ConstantTablePtr(new ConstantTable(ar2));
    }
    NoSuperGlobals = false;
  } else {
    AnalysisResult ar2;
    s_variables = VariableTablePtr(new VariableTable(ar2));
    s_constants = ConstantTablePtr(new ConstantTable(ar2));
    NoSuperGlobals = true;
  }

  // load extension constants, classes and dynamics
  ParseExtConsts(ar, ExtensionConsts, false);
  ParseExtClasses(ar, ExtensionClasses, false);
  ParseExtDynamics(ar, ExtensionDeclaredDynamic, false);
  for (unsigned int i = 0; i < Option::SepExtensions.size(); i++) {
    Option::SepExtensionOptions &options = Option::SepExtensions[i];
    string soname = options.soname;
    if (soname.empty()) {
      soname = string("lib") + options.name + ".so";
    }
    if (!options.lib_path.empty()) {
      soname = options.lib_path + "/" + soname;
    }
    if (!LoadSepExtensionSymbols(ar, options.name, soname)) {
      return false;
    }
  }

  return true;
}
void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator &cg,
        AnalysisResultPtr ar) {
    if (!m_class && 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_class && 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 {
                if (m_class) {
                    cg.printf("INVOKE_STATIC_METHOD(toString(");
                    if (m_class->is(KindOfScalarExpression)) {
                        ASSERT(strcasecmp(dynamic_pointer_cast<ScalarExpression>(m_class)->
                                          getString().c_str(), "static") == 0);
                        cg.printf("\"static\"");
                    } else {
                        m_class->outputCPP(cg, ar);
                    }
                    cg.printf("), \"%s\",", 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_class && 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);
    }
}
Beispiel #29
0
void StatementList::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  FunctionScopePtr func = ar->getFunctionScope();
  bool inPseudoMain = func && func->inPseudoMain();
  std::vector<bool> isDeclaration;

  if (inPseudoMain) {
    // We need these declarations to go first, because PHP allows top level
    // function and class declarations to appear after usage.
    for (unsigned int i = 0; i < m_stmts.size(); i++) {
      StatementPtr stmt = m_stmts[i];
      bool isDecl = false;
      if (stmt->is(Statement::KindOfFunctionStatement)) {
        isDecl = true;
      } else if (stmt->is(Statement::KindOfClassStatement) ||
                 stmt->is(Statement::KindOfInterfaceStatement)) {
        ClassScopePtr cls =
          (dynamic_pointer_cast<InterfaceStatement>(stmt))->getClassScope();
        isDecl = cls->isBaseClass() || !cls->isVolatile();
      }
      if (isDecl) stmt->outputCPP(cg,ar);
      isDeclaration.push_back(isDecl);
    }
  }

  for (unsigned int i = 0; i < m_stmts.size(); i++) {
    StatementPtr stmt = m_stmts[i];
    if (stmt->is(Statement::KindOfClassStatement)) {
      if (!inPseudoMain || !isDeclaration[i]) stmt->outputCPP(cg, ar);
    } else if (!(stmt->is(Statement::KindOfFunctionStatement) ||
                 stmt->is(Statement::KindOfInterfaceStatement)) ||
               (!inPseudoMain || !isDeclaration[i])) {
      stmt->outputCPP(cg, ar);
      if (stmt->is(Statement::KindOfMethodStatement)) {
        MethodStatementPtr methodStmt =
          dynamic_pointer_cast<MethodStatement>(stmt);
        std::string methodName = methodStmt->getName();
        if (methodName == "__get") {
          FunctionScopePtr funcScope = methodStmt->getFunctionScope();
          std::string name = funcScope->getName();
          funcScope->setName("__lval");
          methodStmt->setName("__lval");
          methodStmt->outputCPP(cg, ar);
          funcScope->setName(name);
          methodStmt->setName("__get");
        } else if (methodName == "offsetget") {
          ClassScopePtr cls = ar->getClassScope();
          std::string arrayAccess("arrayaccess");
          if (cls->derivesFrom(ar, arrayAccess, false, false)) {
            FunctionScopePtr funcScope = methodStmt->getFunctionScope();
            std::string name = funcScope->getName();
            funcScope->setName("__offsetget_lval");
            methodStmt->setName("__offsetget_lval");
            methodStmt->outputCPP(cg, ar);
            funcScope->setName(name);
            methodStmt->setName("offsetget");
          }
        }
      }
    }
  }
}
void UnaryOpExpression::outputCPPImpl(CodeGenerator &cg,
                                      AnalysisResultPtr ar) {
  if ((m_op == T_INC || m_op == T_DEC) && outputCPPImplOpEqual(cg, ar)) {
    return;
  }
  if (m_op == T_ARRAY &&
      (getContext() & (RefValue|LValue)) == 0 &&
      !ar->getInsideScalarArray()) {
    int id = -1;
    int hash = -1;
    int index = -1;
    if (m_exp) {
      ExpressionListPtr pairs = dynamic_pointer_cast<ExpressionList>(m_exp);
      Variant v;
      if (pairs && pairs->isScalarArrayPairs() && pairs->getScalarValue(v)) {
        id = ar->registerScalarArray(m_exp, hash, index);
      }
    } else {
      id = ar->registerScalarArray(m_exp, hash, index); // empty array
    }
    if (id != -1) {
      ar->outputCPPScalarArrayId(cg, id, hash, index);
      return;
    }
  }

  if ((m_op == T_ISSET || m_op == T_EMPTY || m_op == T_UNSET) && m_exp) {
    if (m_exp->is(Expression::KindOfExpressionList)) {
      ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp);
      if (exps->getListKind() == ExpressionList::ListKindParam) {
        int count = exps->getCount();
        if (count > 1) {
          cg_printf("(");
        }
        for (int i = 0; i < count; i++) {
          if (m_op == T_UNSET) {
            if (i > 0) cg_printf(", ");
            (*exps)[i]->outputCPPUnset(cg, ar);
          } else {
            if (i > 0) cg_printf(" && ");
            (*exps)[i]->outputCPPExistTest(cg, ar, m_op);
          }
        }
        if (exps->getCount() > 1) {
          cg_printf(")");
        }
        return;
      }
    }
    if (m_op == T_UNSET) {
      m_exp->outputCPPUnset(cg, ar);
    } else {
      m_exp->outputCPPExistTest(cg, ar, m_op);
    }
    return;
  }

  if (m_front) {
    switch (m_op) {
    case T_CLONE:         cg_printf("f_clone(");   break;
    case T_INC:           cg_printf("++");         break;
    case T_DEC:           cg_printf("--");         break;
    case '+':             cg_printf("+");          break;
    case '-':             cg_printf("negate(");    break;
    case '!':             cg_printf("!(");         break;
    case '~':             cg_printf("~");          break;
    case '(':             cg_printf("(");          break;
    case T_INT_CAST:      cg_printf("(");          break;
    case T_DOUBLE_CAST:   cg_printf("(");          break;
    case T_STRING_CAST:   cg_printf("(");          break;
    case T_ARRAY_CAST:    cg_printf("(");          break;
    case T_OBJECT_CAST:   cg_printf("(");          break;
    case T_BOOL_CAST:     cg_printf("(");          break;
    case T_UNSET_CAST:
      if (m_exp->hasCPPTemp()) {
        cg_printf("(id(");
      } else {
        cg_printf("(");
      }
      break;
    case T_EXIT:          cg_printf("f_exit(");    break;
    case T_ARRAY:
      cg_printf("Array(");
      break;
    case T_PRINT:         cg_printf("print(");     break;
    case T_EVAL:
      if (Option::EnableEval > Option::NoEval) {
        bool instance;
        if (ar->getClassScope()) {
          FunctionScopePtr fs = ar->getFunctionScope();
          instance = fs && !fs->isStatic();
        } else {
          instance = false;
        }
        cg_printf("eval(%s, Object(%s), ",
                  ar->getScope()->inPseudoMain() ?
                  "get_variable_table()" : "variables",
                  instance ? "this" : "");
      } else {
        cg_printf("f_eval(");
      }
      break;
    case '@':
      if (m_silencer >= 0) {
        cg_printf("(%s%d.enable(),%s%d.disable(",
                  Option::SilencerPrefix, m_silencer,
                  Option::SilencerPrefix, m_silencer);
      }
      break;
    case T_FILE:
      cg_printf("get_source_filename(\"%s\")", getLocation()->file);
      break;
      break;
    default:
      ASSERT(false);
    }
  }


  if (m_exp) {
    switch (m_op) {
    case '+':
    case '-':
      if (m_exp->getActualType() &&
          (m_exp->getActualType()->is(Type::KindOfString) ||
           m_exp->getActualType()->is(Type::KindOfArray))) {
        cg_printf("(Variant)(");
        m_exp->outputCPP(cg, ar);
        cg_printf(")");
      } else {
        m_exp->outputCPP(cg, ar);
      }
      break;
    case '@':
      // Void needs to return something to silenceDec
      if (!m_exp->hasCPPTemp() && !m_exp->getActualType()) {
        cg_printf("(");
        m_exp->outputCPP(cg, ar);
        cg_printf(",null)");
      } else {
        m_exp->outputCPP(cg, ar);
      }
      break;
    default:
      m_exp->outputCPP(cg, ar);
      break;
    }
  }

  if (m_front) {
    switch (m_op) {
    case T_ARRAY:
      {
        ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp);
        if (!exps) {
          cg_printf("ArrayData::Create()");
        }
        cg_printf(")");
      }
      break;
    case T_UNSET_CAST:
      if (m_exp->hasCPPTemp()) {
        cg_printf("),null");
      } else {
        cg_printf(",null");
      }
    case T_CLONE:
    case '!':
    case '(':
    case '-':
    case T_INT_CAST:
    case T_DOUBLE_CAST:
    case T_STRING_CAST:
    case T_ARRAY_CAST:
    case T_OBJECT_CAST:
    case T_BOOL_CAST:
    case T_EXIT:
    case T_PRINT:
    case T_EVAL:
    case T_INCLUDE:
    case T_INCLUDE_ONCE:
    case T_REQUIRE:
    case T_REQUIRE_ONCE:
      cg_printf(")");
      break;
    case '@':
      if (m_silencer >= 0) {
        cg_printf("))");
      }
      break;
    default:
      break;
    }
  } else {
    switch (m_op) {
    case T_INC:           cg_printf("++"); break;
    case T_DEC:           cg_printf("--"); break;
    default:
      ASSERT(false);
    }
  }
}