int MethodStatement::getLocalEffects() const { if (m_method) return NoEffect; FunctionScopeRawPtr scope = getFunctionScope(); return scope->isVolatile() ? OtherEffect | CanThrow : NoEffect; }
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); }
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; }
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(); }
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); }
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(); } } } }
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); } }
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; }
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); }
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; }
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; } }
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); } }
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; }
/** * 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"); }