void MethodStatement::outputCPPStaticMethodWrapper(CodeGenerator &cg, AnalysisResultPtr ar, const char *cls) { if (!m_modifiers->isStatic() || !m_stmt) return; CodeGenerator::Context context = cg.getContext(); FunctionScopePtr funcScope = m_funcScope.lock(); bool isWrapper = context == CodeGenerator::CppTypedParamsWrapperDecl || context == CodeGenerator::CppTypedParamsWrapperImpl; bool needsWrapper = isWrapper || (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()); m_modifiers->outputCPP(cg, ar); TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg_printf("void"); } cg_printf(" %s%s(", needsWrapper && !isWrapper ? Option::TypedMethodPrefix : Option::MethodPrefix, cg.formatLabel(m_name).c_str()); if (!isWrapper) cg.setContext(CodeGenerator::CppStaticMethodWrapper); funcScope->outputCPPParamsDecl(cg, ar, m_params, true); cg_printf(") { %s%s%s(", type ? "return " : "", needsWrapper && !isWrapper ? Option::TypedMethodImplPrefix : Option::MethodImplPrefix, cg.formatLabel(m_name).c_str()); cg_printf("%s%s::s_class_name", Option::ClassPrefix, cls); cg.setContext(context); if (funcScope->isVariableArgument()) { cg_printf(", num_args"); } if (m_params) { for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); ASSERT(param); cg_printf(", %s%s", Option::VariablePrefix, param->getName().c_str()); } } if (funcScope->isVariableArgument()) { cg_printf(", args"); } cg_printf("); }\n"); if (!isWrapper && needsWrapper) { cg.setContext(CodeGenerator::CppTypedParamsWrapperDecl); outputCPPStaticMethodWrapper(cg, ar, cls); cg.setContext(context); } }
void MethodStatement::outputCPPTypeCheckWrapper(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = getFunctionScope(); TypePtr type = funcScope->getReturnType(); bool isMethod = getClassScope(); string fname = isMethod ? funcScope->getName() : funcScope->getId(cg); funcScope->outputCPP(cg, ar); cg_printf("%s%s%s(", type ? "return " : "", (isMethod ? (m_modifiers->isStatic() ? Option::TypedMethodImplPrefix : Option::TypedMethodPrefix) : Option::TypedFunctionPrefix), fname.c_str()); if (getClassScope() && m_modifiers->isStatic()) { cg_printf("cls, "); } if (funcScope->isVariableArgument()) { cg_printf("num_args, "); } assert(m_params); for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); ASSERT(param); if (i) cg_printf(", "); cg_printf("%s%s", Option::VariablePrefix, param->getName().c_str()); if (TypePtr spec = funcScope->getParamTypeSpec(i)) { if (Type::SameType(spec, funcScope->getParamType(i))) { if (spec->is(Type::KindOfArray)) { cg_printf(".getArrayData()"); } else { ClassScopePtr cls = ar->findClass(spec->getName()); assert(cls && !cls->isRedeclaring()); cg_printf(".getObjectData()"); } } } } if (funcScope->isVariableArgument()) { cg_printf(", args"); } cg_printf(");\n"); }
TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar, TypePtr type, bool coerce, FunctionScopePtr func) { ConstructPtr self = shared_from_this(); ar->getDependencyGraph()->add(DependencyGraph::KindOfFunctionCall, ar->getName(), getText(), self, func->getFullName(), func->getStmt()); TypePtr frt = func->getReturnType(); if (!frt) { m_voidReturn = true; setActualType(TypePtr()); if (!type->is(Type::KindOfAny)) { if (!m_allowVoidReturn && ar->isSecondPass() && !func->isAbstract()) { ar->getCodeError()->record(self, CodeError::UseVoidReturn, self); } m_voidWrapper = true; } } else { m_voidReturn = false; m_voidWrapper = false; type = checkTypesImpl(ar, type, frt, coerce); } m_extraArg = func->inferParamTypes(ar, self, m_params, m_valid); m_variableArgument = func->isVariableArgument(); if (m_valid) { m_implementedType.reset(); } else { m_implementedType = Type::Variant; } return type; }
bool FunctionScope::matchParams(FunctionScopePtr func) { // leaving them alone for now if (m_overriding || func->m_overriding) return false; if (isStatic() || func->isStatic()) return false; // conservative here, as we could normalize them into same counts. if (m_minParam != func->m_minParam || m_maxParam != func->m_maxParam) { return false; } if (isVariableArgument() != func->isVariableArgument() || isReferenceVariableArgument() != func->isReferenceVariableArgument() || isMixedVariableArgument() != func->isMixedVariableArgument()) { return false; } // needs perfect match for ref, hint and defaults for (int i = 0; i < m_maxParam; i++) { if (m_refs[i] != func->m_refs[i]) return false; TypePtr type1 = m_paramTypeSpecs[i]; TypePtr type2 = func->m_paramTypeSpecs[i]; if ((type1 && !type2) || (!type1 && type2) || (type1 && type2 && !Type::SameType(type1, type2))) return false; if (m_paramDefaults[i] != func->m_paramDefaults[i]) return false; } return true; }
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); if (!m_name.empty()) { ClassScopePtr cls = ar->resolveClass(m_name); if (cls) { m_name = cls->getName(); } if (!cls || cls->isRedeclaring()) { if (cls) { m_redeclared = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } if (!cls && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownClass, self); } if (m_params) m_params->inferAndCheck(ar, NEW_TYPE(Any), false); return NEW_TYPE(Object); } if (cls->isVolatile()) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_dynamic = cls->derivesFromRedeclaring(); m_validClass = true; FunctionScopePtr func = cls->findConstructor(ar, true); if (!func) { if (m_params) { if (!m_dynamic && m_params->getCount()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::BadConstructorCall, self); } m_params->setOutputCount(0); } m_params->inferAndCheck(ar, NEW_TYPE(Any), false); } } else { m_extraArg = func->inferParamTypes(ar, self, m_params, m_validClass); m_variableArgument = func->isVariableArgument(); } return Type::CreateObjectType(m_name); } else { ar->containsDynamicClass(); if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicClass, self); } if (m_params) { m_params->markParams(false); } } m_nameExp->inferAndCheck(ar, Type::String, false); if (m_params) m_params->inferAndCheck(ar, NEW_TYPE(Any), false); return Type::Variant;//NEW_TYPE(Object); }
TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar, TypePtr type, bool coerce, FunctionScopePtr func, bool arrayParams) { ConstructPtr self = shared_from_this(); TypePtr frt = func->getReturnType(); if (!frt) { m_voidReturn = true; setActualType(TypePtr()); if (!type->is(Type::KindOfAny)) { if (!m_allowVoidReturn && !func->isFirstPass() && !func->isAbstract()) { Compiler::Error(Compiler::UseVoidReturn, self); } m_voidWrapper = true; } } else { m_voidReturn = false; m_voidWrapper = false; type = checkTypesImpl(ar, type, frt, coerce); } if (arrayParams) { m_extraArg = 0; (*m_params)[0]->inferAndCheck(ar, Type::Array, false); } else { m_extraArg = func->inferParamTypes(ar, self, m_params, m_valid); } m_variableArgument = func->isVariableArgument(); if (m_valid) { m_implementedType.reset(); } else { m_implementedType = Type::Variant; } return type; }
void MethodStatement::outputSwigFFIStub(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); bool varArgs = funcScope->isVariableArgument(); bool ret = funcScope->getReturnType(); string fname = funcScope->getId(); string originalName = funcScope->getOriginalName(); int ac = funcScope->getMaxParamCount(); if (cg.getContext() == CodeGenerator::SwigFFIImpl) { printSource(cg); } cg.printf("Variant *%s(HphpSession *s", originalName.c_str()); ostringstream args; bool first = true; for (int i = 0; i < ac; i++) { cg.printf(", Variant *a%d", i); if (first) first = false; else args << ", "; args << "a" << i; } if (varArgs) { cg.printf(", Variant *va"); if (!first) args << ", "; args << "va"; } if (cg.getContext() == CodeGenerator::SwigFFIDecl) { cg.printf(");\n\n"); return; } cg.indentBegin(") {\n"); if (ret) { cg.printf("void *result;\n"); cg.printf("int kind = "); cg.printf("%s%s(&result", Option::FFIFnPrefix, fname.c_str()); if (ac > 0 || varArgs) cg.printf(", "); } else { cg.printf("%s%s(", Option::FFIFnPrefix, fname.c_str()); } cg.printf("%s);\n", args.str().c_str()); cg.printf("Variant *ret = "); if (ret) { cg.printf("hphpBuildVariant(kind, result);\n"); cg.printf("s->addVariant(ret);\n"); } else { cg.printf("hphpBuildVariant(0, 0);\n"); cg.printf("s->addVariant(ret);\n"); } cg.printf("return ret;\n"); cg.indentEnd("}\n\n"); }
TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar, TypePtr type, bool coerce, FunctionScopePtr func, bool arrayParams) { #ifdef HPHP_DETAILED_TYPE_INF_ASSERT assert(func->hasUser(getScope(), BlockScope::UseKindCaller)); #endif /* HPHP_DETAILED_TYPE_INF_ASSERT */ ConstructPtr self = shared_from_this(); TypePtr frt; { TRY_LOCK(func); func->getInferTypesMutex().assertOwnedBySelf(); assert(!func->inVisitScopes() || getScope() == func); frt = func->getReturnType(); } if (!frt) { m_voidReturn = true; setActualType(TypePtr()); if (!isUnused() && !type->is(Type::KindOfAny)) { if (!hasContext(ReturnContext) && !func->isFirstPass() && !func->isAbstract()) { if (Option::WholeProgram || !func->getContainingClass() || func->isStatic() || func->isFinal() || func->isPrivate()) { Compiler::Error(Compiler::UseVoidReturn, self); } } if (!Type::IsMappedToVariant(type)) { setExpectedType(type); } m_voidWrapper = true; } } else { m_voidReturn = false; m_voidWrapper = false; type = checkTypesImpl(ar, type, frt, coerce); assert(m_actualType); } if (arrayParams) { m_extraArg = 0; (*m_params)[0]->inferAndCheck(ar, Type::Array, false); } else { m_extraArg = func->inferParamTypes(ar, self, m_params, m_valid); } m_variableArgument = func->isVariableArgument(); if (m_valid) { m_implementedType.reset(); } else { m_implementedType = Type::Variant; } assert(type); return type; }
void ClassStatement::outputJavaFFICPPCreator(CodeGenerator &cg, AnalysisResultPtr ar, FunctionScopePtr cons) { ClassScopePtr cls = m_classScope.lock(); string packageName = Option::JavaFFIRootPackage; int ac = cons ? cons->getMaxParamCount() : 0; bool varArgs = cons && cons->isVariableArgument(); const char *clsName = getOriginalName().c_str(); string mangledName = "Java_" + packageName + "_" + clsName + "_create"; Util::replaceAll(mangledName, ".", "_"); cg.printf("JNIEXPORT jlong JNICALL\n"); cg.printf("%s(JNIEnv *env, jclass cls", mangledName.c_str()); ostringstream args; bool first = true; if (varArgs) { args << ac << " + (((Variant *)va)->isNull() ? 0" << " : ((Variant *)va)->getArrayData()->size())"; first = false; } for (int i = 0; i < ac; i++) { if (first) first = false; else { args << ", "; } cg.printf(", jlong a%d", i); args << "*(Variant *)a" << i; } if (varArgs) { if (!first) { args << ", "; } cg.printf(", jlong va"); args << "((Variant *)va)->toArray()"; } if (cg.getContext() == CodeGenerator::JavaFFICppDecl) { // java_stubs.h cg.printf(");\n\n"); return; } cg.indentBegin(") {\n"); cg.printf("ObjectData *obj = "); cg.printf("(NEW(%s%s)())->create(%s);\n", Option::ClassPrefix, cls->getId().c_str(), args.str().c_str()); cg.printf("obj->incRefCount();\n"); cg.printf("return (jlong)(NEW(Variant)(obj));\n"); cg.indentEnd("}\n\n"); }
void MethodStatement::outputCPPStaticMethodWrapper(CodeGenerator &cg, AnalysisResultPtr ar, const char *cls) { if (!m_modifiers->isStatic() || !m_stmt) return; FunctionScopePtr funcScope = m_funcScope.lock(); ar->pushScope(funcScope); m_modifiers->outputCPP(cg, ar); TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg.printf("void"); } cg.printf(" %s%s(", Option::MethodPrefix, m_name.c_str()); CodeGenerator::Context old = cg.getContext(); cg.setContext(CodeGenerator::CppStaticMethodWrapper); funcScope->outputCPPParamsDecl(cg, ar, m_params, true); cg.setContext(old); cg.printf(") { %s%s%s(\"%s\"", type ? "return " : "", Option::MethodImplPrefix, m_name.c_str(), cls); if (funcScope->isVariableArgument()) { cg.printf(", num_args"); } if (m_params) { for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); ASSERT(param); cg.printf(", %s%s", Option::VariablePrefix, param->getName().c_str()); } } if (funcScope->isVariableArgument()) { cg.printf(", args"); } cg.printf("); }\n"); ar->popScope(); }
void ClassStatement::outputJavaFFIConstructor(CodeGenerator &cg, AnalysisResultPtr ar, FunctionScopePtr cons) { int ac = cons ? cons->getMaxParamCount() : 0; bool varArgs = cons && cons->isVariableArgument(); // generates the constructor cg_printf("public %s(", getOriginalName().c_str()); ostringstream args; ostringstream params; bool first = true; for (int i = 0; i < ac; i++) { if (first) { first = false; } else { cg_printf(", "); args << ", "; params << ", "; } cg_printf("HphpVariant a%d", i); args << "a" << i << ".getVariantPtr()"; params << "long a" << i; } if (varArgs) { if (!first) { cg_printf(", "); args << ", "; params << ", "; } cg_printf("HphpVariant va"); args << "va.getVariantPtr()"; params << "long va"; } cg_indentBegin(") {\n"); cg_printf("this(create(%s));\n", args.str().c_str()); cg_indentEnd("}\n\n"); // generates the native method stub for creating the object cg_printf("private static native long create(%s);\n\n", params.str().c_str()); }
void MethodStatement::outputJavaFFICPPStub(CodeGenerator &cg, AnalysisResultPtr ar) { // TODO translate PHP namespace once that is supported string packageName = Option::JavaFFIRootPackage; FunctionScopePtr funcScope = m_funcScope.lock(); bool varArgs = funcScope->isVariableArgument(); bool ret = funcScope->getReturnType(); bool inClass = !m_className.empty(); bool isStatic = !inClass || m_modifiers->isStatic(); string fname = funcScope->getId(); 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 (fname == "__lval" || fname == "__offsetget_lval") return; const char *clsName; if (inClass) { // uses capitalized original class name ClassScopePtr cls = ar->findClass(m_className); clsName = cls->getOriginalName(); } 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("} /* function */\n\n"); }
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); m_classScope.reset(); m_funcScope.reset(); ConstructPtr self = shared_from_this(); if (!m_name.empty()) { ClassScopePtr cls = resolveClass(getScope()); m_name = m_className; if (!cls) { if (isRedeclared()) { getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } else if (getScope()->isFirstPass()) { Compiler::Error(Compiler::UnknownClass, self); } if (m_params) m_params->inferAndCheck(ar, Type::Any, false); return Type::Object; } if (cls->isVolatile() && !isPresent()) { getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_dynamic = cls->derivesFromRedeclaring(); bool valid = true; FunctionScopePtr func = cls->findConstructor(ar, true); if (!func) { if (m_params) { if (!m_dynamic && m_params->getCount()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::BadConstructorCall, self); } m_params->setOutputCount(0); } m_params->inferAndCheck(ar, Type::Some, false); } } else { m_extraArg = func->inferParamTypes(ar, self, m_params, valid); m_variableArgument = func->isVariableArgument(); } if (valid) { m_classScope = cls; m_funcScope = func; } if (!valid || m_dynamic) { m_implementedType = Type::Object; } else { m_implementedType.reset(); } return Type::CreateObjectType(m_name); } else { ar->containsDynamicClass(); if (m_params) { m_params->markParams(false); } } m_implementedType.reset(); m_nameExp->inferAndCheck(ar, Type::String, false); if (m_params) m_params->inferAndCheck(ar, Type::Any, false); return Type::Object; }
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); m_classScope.reset(); FunctionScopePtr prev = m_funcScope; m_funcScope.reset(); ConstructPtr self = shared_from_this(); if (!m_name.empty() && !isStatic()) { ClassScopePtr cls = resolveClassWithChecks(); m_name = m_className; if (!cls) { if (m_params) m_params->inferAndCheck(ar, Type::Any, false); return Type::Object; } if (getScope()->isFirstPass() && (cls->isTrait() ? !isSelf() && !isParent() : cls->isInterface() || cls->isAbstract())) { Compiler::Error(Compiler::InvalidInstantiation, self); } if (cls->isVolatile() && !isPresent()) { getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_dynamic = cls->derivesFromRedeclaring(); bool valid = true; FunctionScopePtr func = cls->findConstructor(ar, true); if (!func) { if (m_params) { if (!m_dynamic && m_params->getCount()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::BadConstructorCall, self); } } m_params->inferAndCheck(ar, Type::Some, false); } } else { if (func != prev) func->addNewObjCaller(getScope()); m_extraArg = func->inferParamTypes(ar, self, m_params, valid); m_variableArgument = func->isVariableArgument(); } if (valid) { m_classScope = cls; m_funcScope = func; } if (!valid || m_dynamic) { m_implementedType = Type::Object; } else { m_implementedType.reset(); } return Type::CreateObjectType(m_name); } else { if (m_params) { m_params->markParams(canInvokeFewArgs()); } } m_implementedType.reset(); m_nameExp->inferAndCheck(ar, Type::String, false); if (m_params) m_params->inferAndCheck(ar, Type::Any, false); return Type::Object; }
/** * Generates the Java stub method for a PHP toplevel function. * * @author qixin */ void MethodStatement::outputJavaFFIStub(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); bool varArgs = funcScope->isVariableArgument(); bool ret = funcScope->getReturnType(); bool inClass = !m_className.empty(); bool isStatic = !inClass || m_modifiers->isStatic(); string fname = funcScope->getId(); 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"); }
void MethodStatement::outputHSFFIStub(CodeGenerator &cg, AnalysisResultPtr ar) { if (!m_className.empty()) { // Haskell currently doesn't support FFI for class methods. return; } FunctionScopePtr funcScope = m_funcScope.lock(); bool varArgs = funcScope->isVariableArgument(); bool ret = funcScope->getReturnType(); string fname = funcScope->getId().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::outputCPPFFIStub(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); bool varArgs = funcScope->isVariableArgument(); bool ret = funcScope->getReturnType(); string fname = funcScope->getId(); bool inClass = !m_className.empty(); bool isStatic = !inClass || m_modifiers->isStatic(); if (inClass && m_modifiers->isAbstract()) { return; } if (fname == "__lval" || fname == "__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, m_className.c_str(), Option::MethodPrefix, fname.c_str()); } else { // instance method call cg.printf("dynamic_cast<%s%s *>(target->getObjectData())->", Option::ClassPrefix, m_className.c_str()); cg.printf("%s%s(", Option::MethodPrefix, fname.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("} /* function */\n"); } return; }
void MethodStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); ClassScopePtr scope = getClassScope(); if (outputFFI(cg, ar)) return; cg.setPHPLineNo(-1); CodeGenerator::Context context = cg.getContext(); if (context == CodeGenerator::CppImplementation) { printSource(cg); } bool isWrapper = context == CodeGenerator::CppTypedParamsWrapperDecl || context == CodeGenerator::CppTypedParamsWrapperImpl; bool needsWrapper = isWrapper || (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()); const char *prefix = needsWrapper && !isWrapper ? Option::TypedMethodPrefix : Option::MethodPrefix; switch (context) { case CodeGenerator::CppDeclaration: case CodeGenerator::CppTypedParamsWrapperDecl: { if (!m_stmt && !funcScope->isPerfectVirtual()) { cg_printf("// "); } m_modifiers->outputCPP(cg, ar); if (!m_stmt || m_name == "__offsetget_lval" || funcScope->isPerfectVirtual()) { cg_printf("virtual "); } TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg_printf("void"); } if (m_name == "__offsetget_lval") { cg_printf(" &___offsetget_lval("); } else if (m_modifiers->isStatic() && m_stmt) { // Static method wrappers get generated as support methods cg_printf(" %s%s(CStrRef cls%s", needsWrapper && !isWrapper ? Option::TypedMethodImplPrefix : Option::MethodImplPrefix, cg.formatLabel(m_name).c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg_printf(" %s%s(", prefix, cg.formatLabel(m_name).c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, true); if (m_stmt) { cg_printf(");\n"); } else if (funcScope->isPerfectVirtual()) { cg_printf(") { return throw_fatal(\"pure virtual\");}\n"); } else { cg_printf(") = 0;\n"); } if (context != CodeGenerator::CppTypedParamsWrapperDecl) { if (funcScope->isConstructor(scope) && !funcScope->isAbstract() && !scope->isInterface()) { funcScope->outputCPPCreateDecl(cg, ar); } if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) { cg.setContext(CodeGenerator::CppTypedParamsWrapperDecl); outputCPPImpl(cg, ar); cg.setContext(context); } } } break; case CodeGenerator::CppImplementation: case CodeGenerator::CppTypedParamsWrapperImpl: if (m_stmt) { TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg_printf("void"); } string origFuncName = getOriginalFullName(); string funcSection = Option::FunctionSections[origFuncName]; if (!funcSection.empty()) { cg_printf(" __attribute__ ((section (\".text.%s\")))", funcSection.c_str()); } if (m_name == "__offsetget_lval") { cg_printf(" &%s%s::___offsetget_lval(", Option::ClassPrefix, scope->getId(cg).c_str()); } else if (m_modifiers->isStatic()) { cg_printf(" %s%s::%s%s(CStrRef cls%s", Option::ClassPrefix, scope->getId(cg).c_str(), needsWrapper && !isWrapper ? Option::TypedMethodImplPrefix : Option::MethodImplPrefix, cg.formatLabel(m_name).c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg_printf(" %s%s::%s%s(", Option::ClassPrefix, scope->getId(cg).c_str(), prefix, cg.formatLabel(m_name).c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, false); cg_indentBegin(") {\n"); if (context != CodeGenerator::CppTypedParamsWrapperImpl) { if (m_stmt->hasBody()) { const char *sys = (cg.getOutput() == CodeGenerator::SystemCPP ? "_BUILTIN" : ""); if (m_modifiers->isStatic()) { cg_printf("STATIC_METHOD_INJECTION%s(%s, %s);\n", sys, scope->getOriginalName().c_str(), origFuncName.c_str()); } else if (cg.getOutput() != CodeGenerator::SystemCPP && !scope->isRedeclaring() && !scope->derivedByDynamic()) { cg_printf("INSTANCE_METHOD_INJECTION_ROOTLESS(%s, %s);\n", scope->getOriginalName().c_str(), origFuncName.c_str()); } else { cg_printf("INSTANCE_METHOD_INJECTION%s(%s, %s);\n", sys, scope->getOriginalName().c_str(), origFuncName.c_str()); } } outputCPPArgInjections(cg, ar, origFuncName.c_str(), scope, funcScope); if (m_name == "__offsetget_lval") { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[0]); cg_printf("Variant &v = %s->__lvalProxy;\n", cg.getGlobals(ar)); string lowered = Util::toLower(m_originalName); cg_printf("v = %s%s(%s%s);\n", prefix, lowered.c_str(), Option::VariablePrefix, param->getName().c_str()); cg_printf("return v;\n"); } else { if (funcScope->isConstructor(scope)) { cg_printf("bool oldInCtor = gasInCtor(true);\n"); } else if (m_name == "__destruct") { cg_printf("setInDtor();\n"); } funcScope->outputCPP(cg, ar); cg.setContext( CodeGenerator::NoContext); // no inner functions/classes if (!funcScope->isStatic() && funcScope->getVariables()-> getAttribute(VariableTable::ContainsDynamicVariable)) { cg_printf("%sthis = this;\n", Option::VariablePrefix); } outputCPPStmt(cg, ar); } cg_indentEnd("}\n"); if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) { cg.setContext(CodeGenerator::CppTypedParamsWrapperImpl); outputCPPImpl(cg, ar); } } else { outputCPPTypeCheckWrapper(cg, ar); cg_indentEnd("}\n"); } cg.setContext(context); cg.printImplSplitter(); } break; default: break; } }
void MethodStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); ClassScopePtr scope = ar->getClassScope(); string origFuncName; ar->pushScope(funcScope); if (outputFFI(cg, ar)) return; cg.setPHPLineNo(-1); if (cg.getContext() == CodeGenerator::CppImplementation) { printSource(cg); } switch (cg.getContext()) { case CodeGenerator::CppDeclaration: { if (!m_stmt) { cg.printf("// "); } m_modifiers->outputCPP(cg, ar); if (m_name == "__offsetget_lval") { cg.printf("virtual "); } TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg.printf("void"); } if (m_name == "__lval") { cg.printf(" &___lval("); } else 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(const char* cls%s", Option::MethodImplPrefix, m_name.c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg.printf(" %s%s(", Option::MethodPrefix, m_name.c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, true); if (m_stmt) { cg.printf(");\n"); } else { cg.printf(") = 0;\n"); } if (funcScope->isConstructor(scope) && !funcScope->isAbstract() && !scope->isInterface()) { funcScope->outputCPPCreateDecl(cg, ar); } } break; case CodeGenerator::CppImplementation: if (m_stmt) { TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg.printf("void"); } origFuncName = std::string(scope->getOriginalName()) + "::" + m_originalName; if (Option::HotFunctions.find(origFuncName) != Option::HotFunctions.end()) { cg.printf(" __attribute((__section__(\".text.hot\")))"); } else if (Option::ColdFunctions.find(origFuncName) != Option::ColdFunctions.end()) { cg.printf(" __attribute((__section__(\".text.cold\")))"); } if (m_name == "__lval") { cg.printf(" &%s%s::___lval(", Option::ClassPrefix, scope->getId().c_str()); } else if (m_name == "__offsetget_lval") { cg.printf(" &%s%s::___offsetget_lval(", Option::ClassPrefix, scope->getId().c_str()); } else if (m_modifiers->isStatic()) { cg.printf(" %s%s::%s%s(const char* cls%s", Option::ClassPrefix, scope->getId().c_str(), Option::MethodImplPrefix, m_name.c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg.printf(" %s%s::%s%s(", Option::ClassPrefix, scope->getId().c_str(), Option::MethodPrefix, m_name.c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, false); cg.indentBegin(") {\n"); if (m_modifiers->isStatic()) { cg.printf("STATIC_METHOD_INJECTION(%s, %s::%s);\n", scope->getOriginalName(), scope->getOriginalName(), m_originalName.c_str()); } else { cg.printf("INSTANCE_METHOD_INJECTION(%s, %s::%s);\n", scope->getOriginalName(), scope->getOriginalName(), m_originalName.c_str()); } if (Option::GenRTTIProfileData && m_params) { for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (param->hasRTTI()) { const string ¶mName = param->getName(); int id = ar->getParamRTTIEntryId(ar->getClassScope(), funcScope, paramName); if (id != -1) { cg.printf("RTTI_INJECTION(%s%s, %d);\n", Option::VariablePrefix, paramName.c_str(), id); } } } } if (m_name == "__lval" || m_name == "__offsetget_lval") { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[0]); cg.printf("Variant &v = %s->__lvalProxy;\n", cg.getOutput() == CodeGenerator::SystemCPP ? "get_system_globals()" : "get_global_variables()"); string lowered = Util::toLower(m_originalName); cg.printf("v = %s%s(%s%s);\n", Option::MethodPrefix, 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"); } else if (m_name == "__call") { ParameterExpressionPtr param; if (m_params->getCount() > 0) { param = dynamic_pointer_cast<ParameterExpression>((*m_params)[0]); cg.printf("INCALL_HELPER(%s%s);\n", Option::VariablePrefix, param->getName().c_str()); } else { cg.printf("INCALL_HELPER(\"\");\n"); } } funcScope->outputCPP(cg, ar); cg.setContext(CodeGenerator::NoContext); // no inner functions/classes if (!funcScope->isStatic() && funcScope->getVariables()-> getAttribute(VariableTable::ContainsDynamicVariable)) { cg.printf("%sthis = this;\n", Option::VariablePrefix); } outputCPPStmt(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } cg.indentEnd("} /* function */\n"); } break; default: break; } ar->popScope(); }