int MethodStatement::getLocalEffects() const {
  if (m_method) return NoEffect;
  FunctionScopeRawPtr scope = getFunctionScope();
  return scope->isVolatile() ? OtherEffect | CanThrow : NoEffect;
}
Example #2
0
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
                                   FileScopeRawPtr fileScope,
                                   ClassScopePtr classScope) {
  checkParameters(fileScope);

  FunctionScopeRawPtr funcScope = getFunctionScope();
  funcScope->setOuterScope(classScope);
  const bool isNative = funcScope->isNative();
  if (m_modifiers) {
    if ((m_modifiers->isExplicitlyPublic() +
         m_modifiers->isProtected() +
         m_modifiers->isPrivate()) > 1) {
      m_modifiers->parseTimeFatal(
        fileScope,
        Compiler::InvalidAttribute,
        Strings::PICK_ACCESS_MODIFIER
      );
    }

    if (m_modifiers->hasDuplicates()) {
      m_modifiers->parseTimeFatal(
        fileScope,
        Compiler::InvalidAttribute,
        Strings::PICK_ACCESS_MODIFIER);
    }

    if (classScope->isInterface()) {
      if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
          m_modifiers->isAbstract()  || m_modifiers->isFinal() ||
          isNative) {
        m_modifiers->parseTimeFatal(
          fileScope,
          Compiler::InvalidAttribute,
          "Access type for interface method %s::%s() must be omitted",
          classScope->getOriginalName().c_str(), getOriginalName().c_str());
      }
      if (m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          fileScope,
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "interface", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
      if (getStmts()) {
        getStmts()->parseTimeFatal(
          fileScope,
          Compiler::InvalidMethodDefinition,
          "Interface method %s::%s() cannot contain body",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str());
      }
    }
    if (m_modifiers->isAbstract()) {
      if (!Option::WholeProgram &&
          funcScope->userAttributes().count("__Memoize")) {
        m_modifiers->parseTimeFatal(
          fileScope,
          Compiler::InvalidAttribute,
          "Abstract method %s::%s cannot be memoized",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str());
      }
      if (m_modifiers->isPrivate() || m_modifiers->isFinal() || isNative) {
        m_modifiers->parseTimeFatal(
          fileScope,
          Compiler::InvalidAttribute,
          "Cannot declare abstract method %s::%s() %s",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str(),
          m_modifiers->isPrivate() ? "private" :
           (m_modifiers->isFinal() ? "final" : "native"));
      }
      if (!classScope->isInterface() && !classScope->isAbstract()) {
        /* note that classScope->isAbstract() returns true for traits */
        m_modifiers->parseTimeFatal(fileScope,
                                    Compiler::InvalidAttribute,
                                    "Class %s contains abstract method %s and "
                                    "must therefore be declared abstract",
                                    classScope->getOriginalName().c_str(),
                                    getOriginalName().c_str());
      }
      if (getStmts()) {
        parseTimeFatal(fileScope,
                       Compiler::InvalidAttribute,
                       "Abstract method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      if (m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          fileScope,
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "abstract", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
    }
    if (!m_modifiers->isStatic() && classScope->isStaticUtil()) {
      m_modifiers->parseTimeFatal(
        fileScope,
        Compiler::InvalidAttribute,
        "Class %s contains non-static method %s and "
        "therefore cannot be declared 'abstract final'",
        classScope->getOriginalName().c_str(),
        getOriginalName().c_str()
      );
    }

    if (isNative) {
      if (getStmts()) {
        parseTimeFatal(fileScope,
                       Compiler::InvalidAttribute,
                       "Native method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      auto is_ctordtor = isNamed("__construct") || isNamed("__destruct");
      if (!m_retTypeAnnotation && !is_ctordtor) {
        parseTimeFatal(fileScope,
                       Compiler::InvalidAttribute,
                       "Native method %s::%s() must have a return type hint",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      } else if (m_retTypeAnnotation &&
                 is_ctordtor &&
                (m_retTypeAnnotation->dataType() != KindOfNull)) {
        parseTimeFatal(fileScope,
                       Compiler::InvalidAttribute,
                       "Native method %s::%s() must return void",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
    }
  }
  if ((!m_modifiers || !m_modifiers->isAbstract()) &&
      !getStmts() && !classScope->isInterface() && !isNative) {
    parseTimeFatal(fileScope,
                   Compiler::InvalidAttribute,
                   "Non-abstract method %s::%s() must contain body",
                   classScope->getOriginalName().c_str(),
                   getOriginalName().c_str());
  }

  classScope->addFunction(ar, fileScope, funcScope);

  setSpecialMethod(fileScope, classScope);

  if (Option::DynamicInvokeFunctions.count(getOriginalFullName())) {
    funcScope->setDynamicInvoke();
  }
  if (m_params) {
    auto nParams = m_params->getCount();
    for (int i = 0; i < nParams; i++) {
      auto param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      param->parseHandler(fileScope, classScope);
      // Variadic capture params don't need types because they'll
      // be treated as Arrays as far as HNI is concerned.
      if (isNative && !param->hasUserType() && !param->isVariadic()) {
        parseTimeFatal(fileScope,
                       Compiler::InvalidAttribute,
                       "Native method calls must have type hints on all args");
      }
    }
  }
  FunctionScope::RecordFunctionInfo(m_originalName, funcScope);
}
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
                                   ClassScopePtr classScope) {

  FunctionScopeRawPtr fs = getFunctionScope();
  const bool isNative = fs->isNative();
  if (m_modifiers) {
    if ((m_modifiers->isExplicitlyPublic() +
         m_modifiers->isProtected() +
         m_modifiers->isPrivate()) > 1) {
      m_modifiers->parseTimeFatal(
        Compiler::InvalidAttribute,
        "%s: method %s::%s()",
        Strings::PICK_ACCESS_MODIFIER,
        classScope->getOriginalName().c_str(),
        getOriginalName().c_str()
      );
    }

    if (classScope->isInterface()) {
      if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
          m_modifiers->isAbstract()  || m_modifiers->isFinal() ||
          isNative) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Access type for interface method %s::%s() must be omitted",
          classScope->getOriginalName().c_str(), getOriginalName().c_str());
      }
      // FIXME: WholeProgram check is temporary (t3044335)
      if (!Option::WholeProgram && m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "interface", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
    }
    if (m_modifiers->isAbstract()) {
      if (m_modifiers->isPrivate() || m_modifiers->isFinal() || isNative) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Cannot declare abstract method %s::%s() %s",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str(),
          m_modifiers->isPrivate() ? "private" :
           (m_modifiers->isFinal() ? "final" : "native"));
      }
      if (!classScope->isInterface() && !classScope->isAbstract()) {
        /* note that classScope->isAbstract() returns true for traits */
        m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
                                    "Class %s contains abstract method %s and "
                                    "must therefore be declared abstract",
                                    classScope->getOriginalName().c_str(),
                                    getOriginalName().c_str());
      }
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Abstract method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      // FIXME: WholeProgram check is temporary (t3044335)
      if (!Option::WholeProgram && m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "abstract", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
    }
    if (isNative) {
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      if (!m_retTypeAnnotation) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method %s::%s() must have a return type hint",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
    }
  }
  if ((!m_modifiers || !m_modifiers->isAbstract()) &&
      !getStmts() && !classScope->isInterface() && !isNative) {
    parseTimeFatal(Compiler::InvalidAttribute,
                   "Non-abstract method %s::%s() must contain body",
                   classScope->getOriginalName().c_str(),
                   getOriginalName().c_str());
  }

  classScope->addFunction(ar, fs);

  m_className = classScope->getName();
  m_originalClassName = classScope->getOriginalName();

  setSpecialMethod(classScope);

  if (Option::DynamicInvokeFunctions.find(getFullName()) !=
      Option::DynamicInvokeFunctions.end()) {
    fs->setDynamicInvoke();
  }
  if (m_params) {
    for (int i = 0; i < m_params->getCount(); i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      param->parseHandler(classScope);
      if (isNative && !param->hasUserType()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method calls must have type hints on all args");
      }
    }
  }
  FunctionScope::RecordFunctionInfo(m_name, fs);
}
Example #4
0
void AnalysisResult::addSystemFunction(FunctionScopeRawPtr fs) {
  FunctionScopePtr& entry = m_functions[fs->getScopeName()];
  assert(!entry);
  entry = fs;
}
bool ObjectPropertyExpression::outputCPPObject(CodeGenerator &cg,
                                               AnalysisResultPtr ar,
                                               bool noEvalOnError) {
  if (m_object->isThis()) {
    if (m_valid) {
      if (!m_object->getOriginalClass()) {
        m_valid = false;
      } else {
        FunctionScopeRawPtr fs = m_object->getOriginalFunction();
        if (!fs || fs->isStatic()) {
          m_valid = false;
        } else if (m_object->getOriginalClass() != getClassScope() &&
                   m_object->getOriginalClass()->isRedeclaring()) {
          m_valid = false;
        }
      }
    }
    if (!m_valid) {
      if (!getClassScope() || getClassScope()->derivedByDynamic() ||
          !static_pointer_cast<SimpleVariable>(m_object)->isGuardedThis()) {
        cg_printf("GET_THIS_ARROW()");
      }
    }
  } else if (m_valid) {
    TypePtr act;
    if (!m_object->hasCPPTemp() && m_object->getImplementedType() &&
        !Type::SameType(m_object->getImplementedType(),
                         m_object->getActualType())) {
      act = m_object->getActualType();
      m_object->setActualType(m_object->getImplementedType());
      ClassScopePtr cls = ar->findExactClass(shared_from_this(),
                                             act->getName());
      cg_printf("((%s%s*)", Option::ClassPrefix, cls->getId(cg).c_str());
    }
    m_object->outputCPP(cg, ar);
    if (act) {
      if (m_object->getImplementedType()->is(Type::KindOfObject)) {
        cg_printf(".get())");
      } else {
        cg_printf(".getObjectData())");
      }
      m_object->setActualType(act);
    }
    cg_printf("->");
  } else {
    TypePtr t = m_object->getType();
    bool ok = t && (t->is(Type::KindOfObject) || t->is(Type::KindOfVariant));
    if (noEvalOnError && !ok) {
      if (!t || !t->is(Type::KindOfArray)) {
        cg_printf("(");
        if (m_object->outputCPPUnneeded(cg, ar)) cg_printf(", ");
        return true;
      }
    }
    ok = ok || !t;
    if (!ok) cg_printf("Variant(");
    m_object->outputCPP(cg, ar);
    if (!ok) cg_printf(")");
    cg_printf(".");
  }
  return false;
}
Example #6
0
void MethodStatement::outputJavaFFICPPStub(CodeGenerator &cg,
                                           AnalysisResultPtr ar) {
  // TODO translate PHP namespace once that is supported
  string packageName = Option::JavaFFIRootPackage;

  FunctionScopeRawPtr funcScope = getFunctionScope();
  bool varArgs = funcScope->isVariableArgument();
  bool ret = funcScope->getReturnType();
  bool inClass = !m_className.empty();
  bool isStatic = !inClass || m_modifiers->isStatic();
  string fname = funcScope->getId(cg);
  int ac = funcScope->getMaxParamCount();
  bool exposeNative = !(ac > 0 || varArgs || !isStatic || !ret && inClass);

  if (inClass && m_modifiers->isAbstract()) {
    // skip all the abstract methods, because hphp doesn't generate code
    // for them
    return;
  }

  if (funcScope->getName() == "__offsetget_lval") return;

  const char *clsName;
  if (inClass) {
    // uses capitalized original class name
    ClassScopePtr cls = ar->findClass(m_className);
    clsName = cls->getOriginalName().c_str();
  } else {
    clsName = "HphpMain";
  }
  string mangledName = "Java." + packageName + "." + clsName + "." + fname
    + (exposeNative ? "" : "_native");
  // all the existing "_" are replaced as "_1"
  Util::replaceAll(mangledName, "_", "_1");
  Util::replaceAll(mangledName, ".", "_");

  cg_printf("JNIEXPORT %s JNICALL\n", ret ? "jobject" : "void");
  cg_printf("%s(JNIEnv *env, %s target", mangledName.c_str(),
            (isStatic ? "jclass" : "jobject"));

  ostringstream args;
  bool first = true;
  if (!isStatic) {
    // instance method also gets an additional argument, which is a Variant
    // pointer to the target, encoded in int64
    first = false;
    cg_printf(", jlong targetPtr");
    args << "(Variant *)targetPtr";
  }
  for (int i = 0; i < ac; i++) {
    cg_printf(", jlong a%d", i);
    if (first) first = false;
    else args << ", ";
    args << "(Variant *)a" << i;
  }
  if (varArgs) {
    cg_printf(", jlong va");
    if (!first) args << ", ";
    args << "(Variant *)va";
  }

  if (cg.getContext() == CodeGenerator::JavaFFICppDecl) {
    // java_stubs.h
    cg_printf(");\n\n");
    return;
  }

  cg_indentBegin(") {\n");

  // support static/instance methods
  if (ret) {
    cg_printf("void *result;\n");
    cg_printf("int kind = ");
    cg_printf("%s%s%s(&result",
              Option::FFIFnPrefix,
              (inClass ? (m_className + "_cls_").c_str() : ""), fname.c_str());
    if (!isStatic || ac > 0 || varArgs) cg_printf(", ");
  } else {
    cg_printf("%s%s%s(", Option::FFIFnPrefix,
                         (inClass ? (m_className + "_cls_").c_str() : ""),
                         fname.c_str());
  }
  cg_printf("%s);\n", args.str().c_str());
  if (ret) {
    if (!inClass) {
      // HphpMain extends hphp.Hphp.
      cg_printf("jclass hphp = env->GetSuperclass(target);\n");
    }
    else {
      cg_printf("jclass hphp = env->FindClass(\"hphp/Hphp\");\n");
    }
    cg_printf("return exportVariantToJava(env, hphp, result, kind);\n");
  }

  cg_indentEnd("}\n");
  cg.printImplSplitter();
}
Example #7
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() :
                                    folly::to<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);
}
Example #8
0
void MethodStatement::analyzeProgram(AnalysisResultPtr ar) {
  FunctionScopeRawPtr funcScope = getFunctionScope();

  if (m_params) {
    m_params->analyzeProgram(ar);
  }
  if (m_stmt) m_stmt->analyzeProgram(ar);

  if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
    funcScope->setParamSpecs(ar);
    if (funcScope->isGenerator()) {
      MethodStatementRawPtr orig = getOrigGeneratorFunc();
      VariableTablePtr variables = funcScope->getVariables();

      Symbol *cont = variables->getSymbol(CONTINUATION_OBJECT_NAME);
      cont->setHidden();

      orig->getFunctionScope()->addUse(funcScope, BlockScope::UseKindClosure);
      orig->getFunctionScope()->setContainsBareThis(
        funcScope->containsBareThis(), funcScope->containsRefThis());
      orig->getFunctionScope()->setContainsThis(funcScope->containsThis());

      if (ExpressionListPtr params = orig->getParams()) {
        for (int i = 0; i < params->getCount(); ++i) {
          auto param = dynamic_pointer_cast<ParameterExpression>((*params)[i]);
          Symbol *gp = variables->addDeclaredSymbol(
            param->getName(), ConstructPtr());
          gp->setGeneratorParameter();
          if (param->isRef()) {
            gp->setRefGeneratorParameter();
            gp->setReferenced();
          }
        }
      }

      if (ClosureExpressionRawPtr closure = orig->getContainingClosure()) {
        if (ExpressionListPtr cvars = closure->getClosureVariables()) {
          for (int i = 0; i < cvars->getCount(); ++i) {
            auto param = dynamic_pointer_cast<ParameterExpression>((*cvars)[i]);
            Symbol *gp = variables->addDeclaredSymbol(
              param->getName(), ConstructPtr());
            gp->setGeneratorParameter();
            if (param->isRef()) {
              gp->setRefGeneratorParameter();
              gp->setReferenced();
            }
          }
        }
      }
    }
    if (funcScope->isSepExtension() ||
        Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) {
      funcScope->setDynamic();
    }
    // TODO: this may have to expand to a concept of "virtual" functions...
    if (m_method) {
      funcScope->disableInline();
      if (m_name.length() > 2 && m_name.substr(0,2) == "__") {
        bool magic = true;
        int paramCount = 0;
        if (m_name == "__destruct") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__call") {
          funcScope->setOverriding(Type::Variant, Type::String, Type::Array);
          paramCount = 2;
        } else if (m_name == "__set") {
          funcScope->setOverriding(Type::Variant, Type::String, Type::Variant);
          paramCount = 2;
        } else if (m_name == "__get") {
          funcScope->setOverriding(Type::Variant, Type::String);
          paramCount = 1;
        } else if (m_name == "__isset") {
          funcScope->setOverriding(Type::Boolean, Type::String);
          paramCount = 1;
        } else if (m_name == "__unset") {
          funcScope->setOverriding(Type::Variant, Type::String);
          paramCount = 1;
        } else if (m_name == "__sleep") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__wakeup") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__set_state") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
          paramCount = 1;
        } else if (m_name == "__tostring") {
          funcScope->setOverriding(Type::String);
        } else if (m_name == "__clone") {
          funcScope->setOverriding(Type::Variant);
        } else {
          paramCount = -1;
          if (m_name != "__construct") {
            magic = false;
          }
        }
        if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) {
          Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this());
          magic = false;
        }
        if (magic) funcScope->setMagicMethod();
      }
      // ArrayAccess methods
      else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") {
        if (m_name == "offsetexists") {
          funcScope->setOverriding(Type::Boolean, Type::Variant);
        } else if (m_name == "offsetget") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
        } else if (m_name == "offsetset") {
          funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant);
        } else if (m_name == "offsetunset") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
        }
      }
    }
  } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
    TypePtr ret = funcScope->getReturnType();
    if (ret && ret->isSpecificObject()) {
      FileScopePtr fs = getFileScope();
      if (fs) fs->addClassDependency(ar, ret->getName());
    }
    if (!getFunctionScope()->usesLSB()) {
      if (StatementPtr orig = getOrigGeneratorFunc()) {
        orig->getFunctionScope()->clearUsesLSB();
      }
    }
  }
}
Example #9
0
void MethodStatement::inferFunctionTypes(AnalysisResultPtr ar) {
  IMPLEMENT_INFER_AND_CHECK_ASSERT(getFunctionScope());

  FunctionScopeRawPtr funcScope = getFunctionScope();
  bool pseudoMain = funcScope->inPseudoMain();

  if (m_stmt && funcScope->isFirstPass()) {
    if (pseudoMain ||
        funcScope->getReturnType() ||
        m_stmt->hasRetExp()) {
      bool lastIsReturn = false;
      if (m_stmt->getCount()) {
        StatementPtr lastStmt = (*m_stmt)[m_stmt->getCount()-1];
        if (lastStmt->is(Statement::KindOfReturnStatement)) {
          lastIsReturn = true;
        }
      }
      if (!lastIsReturn) {
        ExpressionPtr constant =
          makeScalarExpression(ar, funcScope->inPseudoMain() ?
                               Variant(1) : Variant(Variant::nullInit));
        ReturnStatementPtr returnStmt =
          ReturnStatementPtr(
            new ReturnStatement(getScope(), getLocation(), constant));
        m_stmt->addElement(returnStmt);
      }
    }
  }

  if (m_params) {
    m_params->inferAndCheck(ar, Type::Any, false);
  }

  // must also include params and use vars if this is a generator. note: we are
  // OK reading the params from the AST nodes of the original generator
  // function, since we have the dependency links set up
  if (funcScope->isGenerator()) {
    // orig function params
    MethodStatementRawPtr m = getOrigGeneratorFunc();
    assert(m);

    VariableTablePtr variables = funcScope->getVariables();
    ExpressionListPtr params = m->getParams();
    if (params) {
      for (int i = 0; i < params->getCount(); i++) {
        ParameterExpressionPtr param =
          dynamic_pointer_cast<ParameterExpression>((*params)[i]);
        const string &name = param->getName();
        assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
        variables->addParamLike(name, param->getType(), ar, param,
                                funcScope->isFirstPass());
      }
    }

    // use vars
    ExpressionListPtr useVars = m->getFunctionScope()->getClosureVars();
    if (useVars) {
      for (int i = 0; i < useVars->getCount(); i++) {
        ParameterExpressionPtr param =
          dynamic_pointer_cast<ParameterExpression>((*useVars)[i]);
        const string &name = param->getName();
        assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
        variables->addParamLike(name, param->getType(), ar, param,
                                funcScope->isFirstPass());
      }
    }
  }

  if (m_stmt) {
    m_stmt->inferTypes(ar);
  }
}
Example #10
0
void MethodStatement::outputHSFFIStub(CodeGenerator &cg, AnalysisResultPtr ar) {
  if (!m_className.empty()) {
    // Haskell currently doesn't support FFI for class methods.
    return;
  }

  FunctionScopeRawPtr funcScope = getFunctionScope();
  bool varArgs = funcScope->isVariableArgument();
  bool ret = funcScope->getReturnType();
  string fname = funcScope->getId(cg).c_str();
  cg_indentBegin("foreign import ccall \"stubs.h %s%s\" %s%s\n",
                 Option::FFIFnPrefix, fname.c_str(),
                 Option::FFIFnPrefix, fname.c_str());
  cg_printf(":: ");
  if (ret) {
    cg_printf("PtrPtr a -> ");
  }
  int ac = funcScope->getMaxParamCount();
  for (int i = 0; i < ac; ++i) {
    cg_printf("HphpVariantPtr -> ");
  }
  if (varArgs) {
    cg_printf("HphpVariantPtr -> ");
  }
  if (ret) {
    cg_printf("IO CInt");
  } else {
    cg_printf("IO ()");
  }
  cg_indentEnd("\n");

  cg_printf("f_%s :: ", fname.c_str());
  bool first = true;
  if (ac > 0) {
    cg_printf("(");
  }
  for (int i = 0; i < ac; ++i) {
    if (first) {
      first = false;
    } else {
      cg_printf(", ");
    }
    cg_printf("VariantAble a%d", i);
  }
  if (ac > 0) {
    cg_printf(") => ");
  }
  for (int i = 0; i < ac; ++i) {
    cg_printf("a%d -> ", i);
  }
  if (varArgs) {
    cg_printf("[Variant] -> ");
  }
  if (ret) {
    cg_printf("IO Variant");
  } else {
    cg_printf("IO ()");
  }
  cg_printf("\n");
  cg_printf("f_%s ", fname.c_str());
  for (int i = 0; i < ac; ++i) {
    cg_printf("v%d ", i);
  }
  if (varArgs) {
    cg_printf("va ");
  }
  cg_indentBegin("=%s\n", ret ? " alloca (\\pres ->" : "");
  for (int i = 0; i < ac; ++i) {
    cg_indentBegin("withExportedVariant (toVariant v%d) (\\p%d ->\n", i, i);
  }
  if (varArgs) {
    cg_indentBegin("withVParamList va (\\pva ->\n");
  }
  cg_indentBegin("do\n");
  cg_printf("%sffi_%s", ret ? "t <- " : "", fname.c_str());
  if (ret) {
    cg_printf(" pres");
  }
  for (int i = 0; i < ac; ++i) {
    cg_printf(" p%d", i);
  }
  if (varArgs) {
    cg_printf(" pva");
  }
  if (ret) {
    cg_printf("\n");
    cg_printf("ppres <- peek pres\n");
    cg_printf("buildVariant (fromIntegral t) ppres");
  }
  cg_indentEnd(""); // end do
  if (varArgs) {
    cg_indentEnd(")"); // end varargs
  }
  for (int i = 0; i < ac; ++i) {
    cg_indentEnd(")"); // end wEV i
  }
  if (ret) {
    cg_indentEnd(")"); // end alloca
  } else {
    cg_indentEnd("");
  }
  cg_printf("\n");
  return;
}
Example #11
0
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
                                   ClassScopePtr classScope) {

  if (m_modifiers) {
    if (classScope->isInterface()) {
      if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
          m_modifiers->isAbstract()  || m_modifiers->isFinal()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Access type for interface method %s::%s() must be omitted",
          classScope->getOriginalName().c_str(), getOriginalName().c_str());
      }
    }
    if (m_modifiers->isAbstract()) {
      if (m_modifiers->isPrivate() || m_modifiers->isFinal()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Cannot declare abstract method %s::%s() %s",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str(),
          m_modifiers->isPrivate() ? "private" : "final");
      }
      if (!classScope->isInterface() && !classScope->isAbstract()) {
        /* note that classScope->isAbstract() returns true for traits */
        m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
                                    "Class %s contains abstract method %s and "
                                    "must therefore be declared abstract",
                                    classScope->getOriginalName().c_str(),
                                    getOriginalName().c_str());
      }
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Abstract method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
    }
  }
  if ((!m_modifiers || !m_modifiers->isAbstract()) &&
      !getStmts() && !classScope->isInterface()) {
    parseTimeFatal(Compiler::InvalidAttribute,
                   "Non-abstract method %s::%s() must contain body",
                   classScope->getOriginalName().c_str(),
                   getOriginalName().c_str());
  }

  FunctionScopeRawPtr fs = getFunctionScope();

  classScope->addFunction(ar, fs);

  m_className = classScope->getName();
  m_originalClassName = classScope->getOriginalName();

  setSpecialMethod(classScope);

  if (Option::DynamicInvokeFunctions.find(getFullName()) !=
      Option::DynamicInvokeFunctions.end()) {
    fs->setDynamicInvoke();
  }
  if (m_params) {
    for (int i = 0; i < m_params->getCount(); i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      param->parseHandler(classScope);
    }
  }
  FunctionScope::RecordFunctionInfo(m_name, fs);
}
Example #12
0
void MethodStatement::outputCPPFFIStub(CodeGenerator &cg,
                                       AnalysisResultPtr ar) {
  FunctionScopeRawPtr funcScope = getFunctionScope();
  ClassScopePtr clsScope = getClassScope();
  bool varArgs = funcScope->isVariableArgument();
  bool ret = funcScope->getReturnType();
  string fname = funcScope->getId(cg);
  bool inClass = !m_className.empty();
  bool isStatic = !inClass || m_modifiers->isStatic();

  if (inClass && m_modifiers->isAbstract()) {
    return;
  }

  if (funcScope->getName() == "__offsetget_lval") {
    return;
  }

  if (ret) {
    cg_printf("int");
  } else {
    cg_printf("void");
  }
  cg_printf(" %s%s%s(", Option::FFIFnPrefix,
            (inClass ? (m_className + "_cls_").c_str() : ""), fname.c_str());
  if (ret) {
    cg_printf("void** res");
  }

  bool first = !ret;

  if (!isStatic) {
    // instance methods need one additional parameter for the target
    if (first) {
      first = false;
    }
    else {
      cg_printf(", ");
    }
    cg_printf("Variant *target");
  }

  int ac = funcScope->getMaxParamCount();
  for (int i = 0; i < ac; ++i) {
    if (first) {
      first = false;
    } else {
      cg_printf(", ");
    }
    cg_printf("Variant *a%d", i);
  }
  if (varArgs) {
    if (!first) {
      cg_printf(", ");
    }
    cg_printf("Variant *va");
  }
  cg_printf(")");
  if (cg.getContext() == CodeGenerator::CppFFIDecl) {
    cg_printf(";\n");
  } else {
    cg_indentBegin(" {\n");
    if (ret) {
      cg_printf("return hphp_ffi_exportVariant(");
    }

    if (!inClass) {
      // simple function call
      cg_printf("%s%s(", Option::FunctionPrefix, fname.c_str());
    } else if (isStatic) {
      // static method call
      cg_printf("%s%s::%s%s(", Option::ClassPrefix,
                clsScope->getId(cg).c_str(),
                Option::MethodPrefix, funcScope->getName().c_str());
    } else {
      // instance method call
      cg_printf("dynamic_cast<%s%s *>(target->getObjectData())->",
                Option::ClassPrefix, clsScope->getId(cg).c_str());
      cg_printf("%s%s(", Option::MethodPrefix, funcScope->getName().c_str());
    }

    first = true;
    if (varArgs) {
      cg_printf("%d + (va->isNull() ? 0 : va->getArrayData()->size())", ac);
      first = false;
    }

    for (int i = 0; i < ac; ++i) {
      if (first) {
        first = false;
      } else {
        cg_printf(", ");
      }
      cg_printf("*a%d", i);
    }
    if (varArgs) {
      cg_printf(", va->toArray()");
    }
    if (ret) {
      cg_printf("), res");
    }
    cg_printf(");\n");
    cg_indentEnd("}\n");
    cg.printImplSplitter();
  }
  return;
}
Example #13
0
void MethodStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  FunctionScopeRawPtr funcScope = getFunctionScope();
  ClassScopePtr scope = getClassScope();

  if (outputFFI(cg, ar)) return;

  cg.setPHPLineNo(-1);

  CodeGenerator::Context context = cg.getContext();

  if (context == CodeGenerator::CppImplementation) {
    printSource(cg);
  }

  bool isWrapper = context == CodeGenerator::CppTypedParamsWrapperDecl ||
    context == CodeGenerator::CppTypedParamsWrapperImpl;

  bool needsWrapper = isWrapper ||
    (Option::HardTypeHints && funcScope->needsTypeCheckWrapper());

  const char *prefix = needsWrapper && !isWrapper ?
    Option::TypedMethodPrefix : Option::MethodPrefix;

  switch (context) {
    case CodeGenerator::CppDeclaration:
    case CodeGenerator::CppTypedParamsWrapperDecl:
    {
      if (!m_stmt && !funcScope->isPerfectVirtual()) {
        cg_printf("// ");
      }

      m_modifiers->outputCPP(cg, ar);

      if (!m_stmt || m_name == "__offsetget_lval" ||
          funcScope->isPerfectVirtual()) {
        cg_printf("virtual ");
      }
      TypePtr type = funcScope->getReturnType();
      if (type) {
        type->outputCPPDecl(cg, ar, getScope());
      } else {
        cg_printf("void");
      }
      if (m_name == "__offsetget_lval") {
        cg_printf(" &___offsetget_lval(");
      } else if (m_modifiers->isStatic() && m_stmt) {
        // Static method wrappers get generated as support methods
        cg_printf(" %s%s(CStrRef cls%s",
                  needsWrapper && !isWrapper ?
                  Option::TypedMethodImplPrefix : Option::MethodImplPrefix,
                  cg.formatLabel(m_name).c_str(),
                  funcScope->isVariableArgument() ||
                  (m_params && m_params->getCount()) ? ", " : "");
      } else {
        cg_printf(" %s%s(", prefix, cg.formatLabel(m_name).c_str());
      }
      funcScope->outputCPPParamsDecl(cg, ar, m_params, true);
      if (m_stmt) {
        cg_printf(");\n");
      } else if (funcScope->isPerfectVirtual()) {
        cg_printf(") { return throw_fatal(\"pure virtual\");}\n");
      } else {
        cg_printf(") = 0;\n");
      }

      if (context != CodeGenerator::CppTypedParamsWrapperDecl) {
        if (funcScope->isConstructor(scope)
            && !funcScope->isAbstract() && !scope->isInterface()) {
          funcScope->outputCPPCreateDecl(cg, ar);
        }
        if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) {
          cg.setContext(CodeGenerator::CppTypedParamsWrapperDecl);
          outputCPPImpl(cg, ar);
          cg.setContext(context);
        }
      }
    }
    break;
    case CodeGenerator::CppImplementation:
    case CodeGenerator::CppTypedParamsWrapperImpl:
      if (m_stmt) {
        TypePtr type = funcScope->getReturnType();
        if (type) {
          type->outputCPPDecl(cg, ar, getScope());
        } else {
          cg_printf("void");
        }
        string origFuncName = getOriginalFullName();
        if (Option::FunctionSections.find(origFuncName) !=
            Option::FunctionSections.end()) {
          string funcSection = Option::FunctionSections[origFuncName];
          if (!funcSection.empty()) {
            cg_printf(" __attribute__ ((section (\".text.%s\")))",
                      funcSection.c_str());
          }
        }

        if (m_name == "__offsetget_lval") {
          cg_printf(" &%s%s::___offsetget_lval(",
                    Option::ClassPrefix, scope->getId(cg).c_str());
        } else if (m_modifiers->isStatic()) {
          cg_printf(" %s%s::%s%s(CStrRef cls%s", Option::ClassPrefix,
                    scope->getId(cg).c_str(),
                    needsWrapper && !isWrapper ?
                    Option::TypedMethodImplPrefix : Option::MethodImplPrefix,
                    cg.formatLabel(m_name).c_str(),
                    funcScope->isVariableArgument() ||
                    (m_params && m_params->getCount()) ? ", " : "");
        } else {
          cg_printf(" %s%s::%s%s(", Option::ClassPrefix,
                    scope->getId(cg).c_str(),
                    prefix, cg.formatLabel(m_name).c_str());
        }
        funcScope->outputCPPParamsDecl(cg, ar, m_params, false);
        cg_indentBegin(") {\n");
        if (context != CodeGenerator::CppTypedParamsWrapperImpl) {
          if (m_stmt->hasBody()) {
            const char *sys =
              (cg.getOutput() == CodeGenerator::SystemCPP ? "_BUILTIN" : "");
            if (m_modifiers->isStatic()) {
              cg_printf("STATIC_METHOD_INJECTION%s(%s, %s);\n", sys,
                        scope->getOriginalName().c_str(),
                        origFuncName.c_str());
            } else if (cg.getOutput() != CodeGenerator::SystemCPP &&
                       !scope->isRedeclaring() && !scope->derivedByDynamic()) {
              cg_printf("INSTANCE_METHOD_INJECTION_ROOTLESS(%s, %s);\n",
                        scope->getOriginalName().c_str(),
                        origFuncName.c_str());
            } else if (scope->getOriginalName() != "XhprofFrame") {
              cg_printf("INSTANCE_METHOD_INJECTION%s(%s, %s);\n", sys,
                        scope->getOriginalName().c_str(),
                        origFuncName.c_str());
            }
          }
          outputCPPArgInjections(cg, ar, origFuncName.c_str(),
                                 scope, funcScope);
          if (m_name == "__offsetget_lval") {
            ParameterExpressionPtr param =
              dynamic_pointer_cast<ParameterExpression>((*m_params)[0]);
            cg_printf("Variant &v = %s->__lvalProxy;\n", cg.getGlobals(ar));
            string lowered = Util::toLower(m_originalName);
            cg_printf("v = %s%s(%s%s);\n",
                      prefix, lowered.c_str(),
                      Option::VariablePrefix, param->getName().c_str());
            cg_printf("return v;\n");
          } else {
            if (funcScope->isConstructor(scope)) {
              cg_printf("bool oldInCtor = gasInCtor(true);\n");
            } else if (m_name == "__destruct") {
              cg_printf("setInDtor();\n");
            }
            funcScope->outputCPP(cg, ar);
            cg.setContext(
              CodeGenerator::NoContext); // no inner functions/classes
            if (!funcScope->isStatic() && funcScope->getVariables()->
                getAttribute(VariableTable::ContainsDynamicVariable)) {
              Symbol *sym = funcScope->getVariables()->getSymbol("this");
              if (sym && sym->declarationSet()) {
                cg_printf("%sthis = this;\n", Option::VariablePrefix);
              }
            }
            outputCPPStmt(cg, ar);
          }
          cg_indentEnd("}\n");
          if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) {
            cg.setContext(CodeGenerator::CppTypedParamsWrapperImpl);
            outputCPPImpl(cg, ar);
          }
        } else {
          outputCPPTypeCheckWrapper(cg, ar);
          cg_indentEnd("}\n");
        }
        cg.setContext(context);
        cg.printImplSplitter();
      }
      break;
    default:
      break;
  }
}
Example #14
0
void MethodStatement::analyzeProgramImpl(AnalysisResultPtr ar) {
  FunctionScopeRawPtr funcScope = getFunctionScope();

  if (m_params) {
    m_params->analyzeProgram(ar);
    if (Option::GenRTTIProfileData &&
        ar->getPhase() == AnalysisResult::AnalyzeFinal) {
      addParamRTTI(ar);
    }
  }
  if (m_stmt) m_stmt->analyzeProgram(ar);

  if (ar->isAnalyzeInclude()) {
    funcScope->setParamCounts(ar, -1, -1);
    if (funcScope->isSepExtension() ||
        BuiltinSymbols::IsDeclaredDynamic(m_name) ||
        Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) {
      funcScope->setDynamic();
    }
    // TODO: this may have to expand to a concept of "virtual" functions...
    if (m_method) {
      funcScope->disableInline();
      if (m_name.length() > 2 && m_name.substr(0,2) == "__") {
        bool magic = true;
        int paramCount = 0;
        if (m_name == "__destruct") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__call") {
          funcScope->setOverriding(Type::Variant, Type::String, Type::Array);
          paramCount = 2;
        } else if (m_name == "__set") {
          funcScope->setOverriding(Type::Variant, Type::String, Type::Variant);
          paramCount = 2;
        } else if (m_name == "__get") {
          funcScope->setOverriding(Type::Variant, Type::String);
          paramCount = 1;
        } else if (m_name == "__isset") {
          funcScope->setOverriding(Type::Boolean, Type::String);
          paramCount = 1;
        } else if (m_name == "__unset") {
          funcScope->setOverriding(Type::Variant, Type::String);
          paramCount = 1;
        } else if (m_name == "__sleep") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__wakeup") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__set_state") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
          paramCount = 1;
        } else if (m_name == "__tostring") {
          funcScope->setOverriding(Type::String);
        } else if (m_name == "__clone") {
          funcScope->setOverriding(Type::Variant);
        } else {
          paramCount = -1;
          if (m_name != "__construct") {
            magic = false;
          }
        }
        if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) {
          Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this());
          magic = false;
        }
        if (magic) funcScope->setMagicMethod();
      }
      // ArrayAccess methods
      else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") {
        if (m_name == "offsetexists") {
          funcScope->setOverriding(Type::Boolean, Type::Variant);
        } else if (m_name == "offsetget") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
        } else if (m_name == "offsetset") {
          funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant);
        } else if (m_name == "offsetunset") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
        }
      }
    }
    FunctionScope::RecordRefParamInfo(m_name, funcScope);
  }
}
Example #15
0
void MethodStatement::analyzeProgram(AnalysisResultPtr ar) {
  FunctionScopeRawPtr funcScope = getFunctionScope();

  if (m_params) {
    m_params->analyzeProgram(ar);
  }

  funcScope->resetYieldLabelCount();
  if (m_stmt) m_stmt->analyzeProgram(ar);

  if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
    funcScope->setParamSpecs(ar);

    if (Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) {
      funcScope->setDynamic();
    }
    // TODO: this may have to expand to a concept of "virtual" functions...
    if (m_method) {
      funcScope->disableInline();
      if (m_name.length() > 2 && m_name.substr(0,2) == "__") {
        bool magic = true;
        int paramCount = 0;
        if (m_name == "__destruct") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__call") {
          funcScope->setOverriding(Type::Variant, Type::String, Type::Array);
          paramCount = 2;
        } else if (m_name == "__set") {
          funcScope->setOverriding(Type::Variant, Type::String, Type::Variant);
          paramCount = 2;
        } else if (m_name == "__get") {
          funcScope->setOverriding(Type::Variant, Type::String);
          paramCount = 1;
        } else if (m_name == "__isset") {
          funcScope->setOverriding(Type::Boolean, Type::String);
          paramCount = 1;
        } else if (m_name == "__unset") {
          funcScope->setOverriding(Type::Variant, Type::String);
          paramCount = 1;
        } else if (m_name == "__sleep") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__wakeup") {
          funcScope->setOverriding(Type::Variant);
        } else if (m_name == "__set_state") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
          paramCount = 1;
        } else if (m_name == "__tostring") {
          // do nothing
        } else if (m_name == "__clone") {
          funcScope->setOverriding(Type::Variant);
        } else {
          paramCount = -1;
          if (m_name != "__construct") {
            magic = false;
          }
        }
        if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) {
          Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this());
          magic = false;
        }
        if (magic) funcScope->setMagicMethod();
      }
      // ArrayAccess methods
      else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") {
        if (m_name == "offsetexists") {
          funcScope->setOverriding(Type::Boolean, Type::Variant);
        } else if (m_name == "offsetget") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
        } else if (m_name == "offsetset") {
          funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant);
        } else if (m_name == "offsetunset") {
          funcScope->setOverriding(Type::Variant, Type::Variant);
        }
      }
    }
  } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
    TypePtr ret = funcScope->getReturnType();
    if (ret && ret->isSpecificObject()) {
      FileScopePtr fs = getFileScope();
      if (fs) fs->addClassDependency(ar, ret->getName());
    }
  }
}
void FunctionStatement::outputCPPImpl(CodeGenerator &cg,
                                      AnalysisResultPtr ar) {
  CodeGenerator::Context context = cg.getContext();

  FunctionScopeRawPtr funcScope = getFunctionScope();
  string fname = funcScope->getId();
  bool pseudoMain = funcScope->inPseudoMain();
  string origFuncName = !pseudoMain ? funcScope->getOriginalName() :
          ("run_init::" + funcScope->getContainingFile()->getName());

  if (outputFFI(cg, ar)) return;

  if (context == CodeGenerator::NoContext) {
    funcScope->outputCPPDef(cg);
    return;
  }

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

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

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

  bool isWrapper = context == CodeGenerator::CppTypedParamsWrapperDecl ||
    context == CodeGenerator::CppTypedParamsWrapperImpl;

  bool needsWrapper = isWrapper ||
    (Option::HardTypeHints && funcScope->needsTypeCheckWrapper());

  int startLineImplementation = -1;
  if (context == CodeGenerator::CppDeclaration ||
      context == CodeGenerator::CppImplementation ||
      context == CodeGenerator::CppPseudoMain) {
    startLineImplementation = cg.getLineNo(CodeGenerator::PrimaryStream);
  }

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

  TypePtr type = funcScope->getReturnType();
  if (type) {
    bool isHeader = cg.isFileOrClassHeader();
    cg.setFileOrClassHeader(true);
    type->outputCPPDecl(cg, ar, getScope());
    cg.setFileOrClassHeader(isHeader);
  } else {
    cg_printf("void");
  }

  if (Option::FunctionSections.find(origFuncName) !=
      Option::FunctionSections.end()) {
    string 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->getContainingFile()->pseudoMainName().c_str());
  } else {
    cg_printf(" %s%s(",
              needsWrapper && !isWrapper ?
              Option::TypedFunctionPrefix : Option::FunctionPrefix,
              fname.c_str());
  }

  switch (context) {
    case CodeGenerator::CppForwardDeclaration:
    case CodeGenerator::CppTypedParamsWrapperDecl:
      funcScope->outputCPPParamsDecl(cg, ar, m_params, true);
      if (!isWrapper) {
        int opt = Option::GetOptimizationLevel(m_cppLength);
        if (opt < 3) cg_printf(") __attribute__((optimize(%d))", opt);
      }
      cg_printf(");\n");
      if (!isWrapper) {
        if (funcScope->hasDirectInvoke()) {
          cg_printf("Variant %s%s(void *extra, CArrRef params);\n",
                    Option::InvokePrefix, fname.c_str());
        }
        if (needsWrapper) {
          cg.setContext(CodeGenerator::CppTypedParamsWrapperDecl);
          outputCPPImpl(cg, ar);
          cg.setContext(context);
        }
      }
      break;
    case CodeGenerator::CppDeclaration:
    case CodeGenerator::CppImplementation:
    case CodeGenerator::CppPseudoMain:
    case CodeGenerator::CppTypedParamsWrapperImpl:
    {
      funcScope->outputCPPParamsDecl(cg, ar, m_params, false);
      cg_indentBegin(") {\n");
      if (!isWrapper) {
        const char *suffix =
          (cg.getOutput() == CodeGenerator::SystemCPP ? "_BUILTIN" : "");
        if (pseudoMain) {
          cg_printf("PSEUDOMAIN_INJECTION%s(%s, %s%s);\n",
                    suffix, origFuncName.c_str(), Option::PseudoMainPrefix,
                    funcScope->getContainingFile()->pseudoMainName().c_str());
        } else {
          if (m_stmt->hasBody()) {
            if (suffix[0] == '\0' && !funcScope->needsCheckMem()) {
              suffix = "_NOMEM";
            }
            const string &name = funcScope->getInjectionId();
            cg_printf("FUNCTION_INJECTION%s(%s);\n", suffix, name.c_str());
          }
          outputCPPArgInjections(cg, ar, origFuncName.c_str(),
                                 ClassScopePtr(), funcScope);
        }
        funcScope->outputCPP(cg, ar);
        if (funcScope->needsRefTemp()) cg.genReferenceTemp(shared_from_this());
        if (funcScope->needsObjTemp()) {
          cg_printf("ObjectData *obj_tmp UNUSED;\n");
        }
        cg.setContext(CodeGenerator::NoContext); // no inner functions/classes
        outputCPPStmt(cg, ar);
        if (funcScope->needsRefTemp()) cg.clearRefereceTemp();
        cg_indentEnd("}\n");
        ASSERT(startLineImplementation >= 0);
        m_cppLength = cg.getLineNo(CodeGenerator::PrimaryStream)
                      - startLineImplementation;
        if (needsWrapper) {
          cg.setContext(CodeGenerator::CppTypedParamsWrapperImpl);
          outputCPPImpl(cg, ar);
        }
        cg.setContext(context);
      } else {
        outputCPPTypeCheckWrapper(cg, ar);
        cg_indentEnd("}\n");
      }
      cg.printImplSplitter();
    }
    break;
    default:
      ASSERT(false);
  }
}
bool DynamicFunctionCall::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
                                       int state) {
    bool nonStatic = !m_class && m_className.empty();
    if (!nonStatic && !m_class && !m_classScope && !isRedeclared()) {
        // call to an unknown class
        // set m_noStatic to avoid pointlessly wrapping the call
        // in STATIC_CLASS_NAME_CALL()
        m_noStatic = true;
        cg.pushCallInfo(-1);
        bool ret = FunctionCall::preOutputCPP(cg, ar, state);
        cg.popCallInfo();
        return ret;
    }
    // Short circuit out if inExpression() returns false
    if (!cg.inExpression()) return true;

    cg.wrapExpressionBegin();

    m_ciTemp = cg.createNewLocalId(shared_from_this());

    if (!m_classScope && !m_className.empty() && m_cppTemp.empty() &&
            !isSelf() && ! isParent() && !isStatic()) {
        // Create a temporary to hold the class name, in case it is not a
        // StaticString.
        m_clsNameTemp = cg.createNewLocalId(shared_from_this());
        cg_printf("CStrRef clsName%d(", m_clsNameTemp);
        cg_printString(m_origClassName, ar, shared_from_this());
        cg_printf(");\n");
    }

    if (m_class) {
        int s = m_class->hasEffect() || m_nameExp->hasEffect() ?
                FixOrder : 0;
        m_class->preOutputCPP(cg, ar, s);
    }
    m_nameExp->preOutputCPP(cg, ar, 0);

    if (nonStatic) {
        cg_printf("const CallInfo *cit%d;\n", m_ciTemp);
        cg_printf("void *vt%d;\n", m_ciTemp);
        cg_printf("get_call_info_or_fail(cit%d, vt%d, ", m_ciTemp, m_ciTemp);

        if (m_nameExp->is(Expression::KindOfSimpleVariable)) {
            m_nameExp->outputCPP(cg, ar);
        } else {
            cg_printf("(");
            m_nameExp->outputCPP(cg, ar);
            cg_printf(")");
        }
        cg_printf(");\n");
    } else {
        cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp);
        if (m_class) {
            if (m_class->is(KindOfScalarExpression)) {
                ASSERT(strcasecmp(dynamic_pointer_cast<ScalarExpression>(m_class)->
                                  getString().c_str(), "static") == 0);
                cg_printf("CStrRef cls%d = "
                          "FrameInjection::GetStaticClassName(fi.getThreadInfo())",
                          m_ciTemp);
            } else {
                cg_printf("C%sRef cls%d = ",
                          m_class->getActualType() &&
                          m_class->getActualType()->is(Type::KindOfString) ?
                          "Str" : "Var", m_ciTemp);
                m_class->outputCPP(cg, ar);
            }
        } else if (m_classScope) {
            cg_printf("CStrRef cls%d = ", m_ciTemp);
            cg_printString(m_classScope->getId(), ar, shared_from_this());
        } else {
            cg_printf("CStrRef cls%d = ", m_ciTemp);
            cg_printString(m_className, ar, shared_from_this());
        }
        cg_printf(";\n");

        cg_printf("CStrRef mth%d = ", m_ciTemp);
        if (m_nameExp->is(Expression::KindOfSimpleVariable)) {
            m_nameExp->outputCPP(cg, ar);
        } else {
            cg_printf("(");
            m_nameExp->outputCPP(cg, ar);
            cg_printf(")");
        }
        cg_printf(";\n");

        bool dynamic = true;
        if (!m_class) {
            ClassScopeRawPtr origClass = getOriginalClass();
            if (!origClass) {
                dynamic = false;
            } else {
                FunctionScopeRawPtr origFunc = getOriginalFunction();
                if (origFunc) {
                    if (origFunc->isStatic() ||
                            (m_classScope != origClass &&
                             (m_className.empty() || !origClass->derivesFrom(
                                  ar, m_className, true, true)))) {
                        dynamic = false;
                    }
                }
            }
        }

        if (dynamic) {
            if ((m_class && (!m_class->getActualType() ||
                             !m_class->getActualType()->is(Type::KindOfString))) ||
                    !getOriginalFunction() ||
                    !getOriginalClass() ||
                    getOriginalFunction()->isStatic()) {
                cg_printf("mcp%d.dynamicNamedCall(cls%d, mth%d);\n",
                          m_ciTemp, m_ciTemp, m_ciTemp);
            } else {
                cg_printf("mcp%d.isObj = true;\n", m_ciTemp);
                cg_printf("mcp%d.rootObj = this;\n", m_ciTemp);
                cg_printf("mcp%d.name = &mth%d;\n", m_ciTemp, m_ciTemp);
                cg_printf("o_get_call_info_ex(cls%d, mcp%d);\n", m_ciTemp, m_ciTemp);
            }
        } else {
            cg_printf("mcp%d.staticMethodCall(cls%d, mth%d);\n",
                      m_ciTemp,
                      m_ciTemp, m_ciTemp);

            if (m_classScope) {
                cg_printf("%s%s.%sget_call_info(mcp%d);\n",
                          Option::ClassStaticsCallbackPrefix,
                          m_classScope->getId().c_str(),
                          Option::ObjectStaticPrefix, m_ciTemp);
            } else if (isRedeclared()) {
                cg_printf("g->%s%s->%sget_call_info(mcp%d);\n",
                          Option::ClassStaticsCallbackPrefix, m_className.c_str(),
                          Option::ObjectStaticPrefix, m_ciTemp);
            } else {
                assert(false);
            }
        }
        cg_printf("const CallInfo *cit%d = mcp%d.ci;\n", m_ciTemp, m_ciTemp);
    }

    if (m_params && m_params->getCount() > 0) {
        cg.pushCallInfo(m_ciTemp);
        m_params->preOutputCPP(cg, ar, 0);
        cg.popCallInfo();
    }

    if (state & FixOrder) {
        cg.pushCallInfo(m_ciTemp);
        preOutputStash(cg, ar, state);
        cg.popCallInfo();
    }

    return true;
}
Example #18
0
/**
 * Generates the Java stub method for a PHP toplevel function.
 *
 * @author qixin
 */
void MethodStatement::outputJavaFFIStub(CodeGenerator &cg,
                                        AnalysisResultPtr ar) {
  FunctionScopeRawPtr funcScope = getFunctionScope();
  bool varArgs = funcScope->isVariableArgument();
  bool ret = funcScope->getReturnType();
  bool inClass = !m_className.empty();
  bool isStatic = !inClass || m_modifiers->isStatic();
  string fname = funcScope->getId(cg);
  string originalName = funcScope->getOriginalName();
  if (originalName.length() < fname.length()) {
    // if there are functions of the same name, fname may contain "$$..."
    // in the end
    originalName += fname.substr(originalName.length());
  }

  if (originalName == "clone"     || originalName == "equals"
   || originalName == "finalize"  || originalName == "getClass"
   || originalName == "hashCode"  || originalName == "notify"
   || originalName == "notifyAll" || originalName == "toString"
   || originalName == "wait") {
    // not to clash with Java method names
    originalName = "_" + originalName;
  }

  if (cg.getContext() == CodeGenerator::JavaFFIInterface
   || inClass && m_modifiers->isAbstract()) {
    // skip all the abstract methods, because php overriding is not very
    // compatible with Java
    return;
  }

  if (!inClass) printSource(cg);

  // This Java method extracts the Variant pointer from the HphpVariant
  // argument as a 64-bit integer, and then calls the native version.
  bool exposeNative = false;
  int ac = funcScope->getMaxParamCount();
  if (ac > 0 || varArgs || !isStatic || !ret && inClass
   || cg.getContext() == CodeGenerator::JavaFFIInterface) {
    // make methods always return something, so that they can override
    // each other
    cg_printf("public %s%s %s(",
              (isStatic ? "static " : ""),
              (!ret && !inClass ? "void" : "HphpVariant"),
              originalName.c_str());
    ostringstream args;
    bool first = true;
    if (!isStatic) {
      // instance method has an additional parameter
      args << "this.getVariantPtr()";
    }
    for (int i = 0; i < ac; i++) {
      if (first) {
        first = false;
        if (!isStatic) args << ", ";
      }
      else {
        cg_printf(", ");
        args << ", ";
      }
      cg_printf("HphpVariant a%d", i);
      args << "a" << i << ".getVariantPtr()";
    }
    if (varArgs) {
      if (!first) {
        cg_printf(", ");
        args << ", ";
      }
      else if (!isStatic) {
        args << ", ";
      }
      cg_printf("HphpVariant va");
      args << "va.getVariantPtr()";
    }

    if (cg.getContext() == CodeGenerator::JavaFFIInterface) {
      cg_printf(");\n\n");
      return;
    }

    cg_indentBegin(") {\n");
    cg_printf("%s%s_native(%s);\n", (ret ? "return " : ""),
              originalName.c_str(),
              args.str().c_str());
    if (!ret && inClass) {
      cg_printf("return HphpNull.phpNull();\n");
    }
    cg_indentEnd("}\n\n");
  }
  else {
    exposeNative = true;
  }

  // the native method stub
  cg_printf("%s %snative %s %s%s(",
            (exposeNative ? "public" : "private"),
            (isStatic ? "static " : ""), (ret ? "HphpVariant" : "void"),
            originalName.c_str(),
            (exposeNative ? "" : "_native"));
  bool first = true;
  if (!isStatic) {
    // instance method has an additional parameter
    cg_printf("long targetPtr");
    first = false;
  }
  for (int i = 0; i < ac; i++) {
    if (first) first = false;
    else cg_printf(", ");
    cg_printf("long a%d", i);
  }
  if (varArgs) {
    if (!first) cg_printf(", ");
    cg_printf("long va");
  }
  cg_printf(");\n\n");
}