// Rewrite the outermost select clause so that it references properties of // a result tuple constructed by the query provider. Then wrap this expression // in a lambda so that the query provider can invoke it as a call back every // time it produces a result tuple (row). ClosureExpressionPtr QueryExpression::clientSideRewrite( AnalysisResultPtr ar, FileScopePtr fileScope) { // Rewrite the select expression into an expression that refers to // table columns (including computed columns) via properties of an // object produced by the query provider at runtime. ClientSideSelectRewriter cs; cs.rewriteQuery(static_pointer_cast<QueryExpression>(shared_from_this())); auto csSelect = cs.getClientSideSelectClause(); // null if there is no select clause. if (csSelect == nullptr) return nullptr; ExpressionPtr selectExpr = csSelect->getExpression(); // Now wrap up the rewritten expression into a lambda expression that // is passed to the query provider. When the query result is iterated, // the closure is called for each row in the query result in order to // produce the value specified by this select expression. // Create a return statement for the lambda body LabelScopePtr labelScope(new LabelScope()); ReturnStatementPtr returnStatement( new ReturnStatement(BlockScopePtr(), labelScope, getRange(), selectExpr) ); // Wrap up the return statement in a list for the lambda body StatementListPtr stmt( new StatementList(BlockScopePtr(), labelScope, getRange()) ); stmt->addElement(returnStatement); // Create a function statement for the lambda: // First create a formal parameter list, consisting of a single // parameter that will receive an object from the query provider // with a property for each table column that is referenced in the // expression of this select clause. TypeAnnotationPtr type; bool hhType = true; std::string paramName = "__query_result_row__"; bool byRefParam = false; TokenID modifier = 0; ExpressionPtr defaultValue; ExpressionPtr attributeList; ParameterExpressionPtr parameter ( new ParameterExpression(BlockScopePtr(), getRange(), type, hhType, paramName, byRefParam, modifier, defaultValue, attributeList) ); ExpressionListPtr params(new ExpressionList(BlockScopePtr(), getRange())); params->addElement(parameter); // Now create a function statement object ModifierExpressionPtr modifiers( new ModifierExpression(BlockScopePtr(), getRange()) ); bool ref = false; static int counter = 0; std::string name = "__select__#" + std::to_string(counter++); TypeAnnotationPtr retTypeAnnotation; int attr = 0; std::string docComment; ExpressionListPtr attrList; FunctionStatementPtr func( new FunctionStatement(BlockScopePtr(), labelScope, getRange(), modifiers, ref, name, params, retTypeAnnotation, stmt, attr, docComment, attrList) ); // The function statement needs a scope std::vector<UserAttributePtr> uattrs; FunctionScopePtr funcScope (new FunctionScope(ar, false, name, func, false, 1, 1, nullptr, attr, docComment, fileScope, uattrs)); fileScope->addFunction(ar, funcScope); func->resetScope(funcScope); funcScope->setOuterScope(fileScope); // Now construct a closure expression to create the closure value to // pass to the query provider. ExpressionListPtr captures; ClosureExpressionPtr closure( new ClosureExpression(BlockScopePtr(), getRange(), ClosureType::Short, func, captures) ); closure->getClosureFunction()->setContainingClosure(closure); return closure; }
FunctionScopePtr BuiltinSymbols::ParseHelperFunction(AnalysisResultPtr ar, const char** &p) { FunctionScopePtr f = ParseExtFunction(ar, p); f->setHelperFunction(); return f; }
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopeRawPtr classScope = getClassScope(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isVolatile()) { string name = CodeGenerator::FormatLabel(m_name); if (classScope->isRedeclaring()) { cg_printf("g->%s%s = ClassStaticsPtr(NEWOBJ(%s%s)());\n", Option::ClassStaticsObjectPrefix, name.c_str(), Option::ClassStaticsPrefix, classScope->getId().c_str()); cg_printf("g->%s%s = &%s%s;\n", Option::ClassStaticsCallbackPrefix, name.c_str(), Option::ClassWrapperFunctionPrefix, classScope->getId().c_str()); } cg_printf("g->CDEC(%s) = true;\n", name.c_str()); const vector<string> &bases = classScope->getBases(); for (vector<string>::const_iterator it = bases.begin(); it != bases.end(); ++it) { if (cg.checkHoistedClass(*it)) continue; ClassScopePtr base = ar->findClass(*it); if (base && base->isVolatile()) { cg_printf("checkClassExistsThrow("); cg_printString(base->getOriginalName(), ar, shared_from_this()); string lname = Util::toLower(base->getOriginalName()); cg_printf(", &%s->CDEC(%s));\n", cg.getGlobals(ar), CodeGenerator::FormatLabel(lname).c_str()); } } } return; } if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } string clsNameStr = classScope->getId(); const char *clsName = clsNameStr.c_str(); bool redeclared = classScope->isRedeclaring(); switch (cg.getContext()) { case CodeGenerator::CppDeclaration: { if (Option::GenerateCPPMacros) { classScope->outputForwardDeclaration(cg); } bool system = cg.getOutput() == CodeGenerator::SystemCPP; ClassScopePtr parCls; if (!m_parent.empty()) { parCls = ar->findClass(m_parent); if (parCls && parCls->isRedeclaring()) parCls.reset(); } if (Option::GenerateCppLibCode) { cg.printDocComment(classScope->getDocComment()); } cg_printf("class %s%s", Option::ClassPrefix, clsName); if (!m_parent.empty() && classScope->derivesDirectlyFrom(m_parent)) { if (!parCls) { cg_printf(" : public DynamicObjectData"); } else { cg_printf(" : public %s%s", Option::ClassPrefix, parCls->getId().c_str()); } } else { if (classScope->derivesFromRedeclaring()) { cg_printf(" : public DynamicObjectData"); } else if (system) { cg_printf(" : public ExtObjectData"); } else { cg_printf(" : public ObjectData"); } } if (m_base && Option::UseVirtualDispatch) { for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && !intfClassScope->isRedeclaring() && classScope->derivesDirectlyFrom(intf) && (!parCls || !parCls->derivesFrom(ar, intf, true, false))) { string id = intfClassScope->getId(); cg_printf(", public %s%s", Option::ClassPrefix, id.c_str()); } } } cg_indentBegin(" {\n"); cg_printf("public:\n"); cg.printSection("Properties"); classScope->getVariables()->outputCPPPropertyDecl(cg, ar, classScope->derivesFromRedeclaring()); if (Option::GenerateCppLibCode) { cg.printSection("Methods"); classScope->outputMethodWrappers(cg, ar); cg.printSection(">>>>>>>>>> Internal Implementation <<<<<<<<<<"); cg_printf("// NOTE: Anything below is subject to change. " "Use everything above instead.\n"); } cg.printSection("Class Map"); if (Option::GenerateCPPMacros) { cg_printf("virtual bool o_instanceof(CStrRef s) const;\n"); } bool hasEmitCppCtor = false; bool needsCppCtor = classScope->needsCppCtor(); bool needsInit = classScope->needsInitMethod(); if (Option::GenerateCPPMacros) { bool dyn = (!parCls && !m_parent.empty()) || classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared; bool idyn = parCls && classScope->derivesFromRedeclaring() == ClassScope::IndirectFromRedeclared; bool redec = classScope->isRedeclaring(); if (!classScope->derivesFromRedeclaring()) { outputCPPClassDecl(cg, ar, clsName, m_originalName.c_str(), parCls ? parCls->getId().c_str() : "ObjectData"); } else { cg_printf("DECLARE_DYNAMIC_CLASS(%s, %s, %s)\n", clsName, m_originalName.c_str(), dyn || !parCls ? "DynamicObjectData" : parCls->getId().c_str()); } bool hasGet = classScope->getAttribute( ClassScope::HasUnknownPropGetter); bool hasSet = classScope->getAttribute( ClassScope::HasUnknownPropSetter); bool hasCall = classScope->getAttribute( ClassScope::HasUnknownMethodHandler); bool hasCallStatic = classScope->getAttribute( ClassScope::HasUnknownStaticMethodHandler); if (dyn || idyn || redec || hasGet || hasSet || hasCall || hasCallStatic) { if (redec && classScope->derivedByDynamic()) { if (!dyn && !idyn) { cg_printf("private: ObjectData* root;\n"); cg_printf("public:\n"); cg_printf("virtual ObjectData *getRoot() { return root; }\n"); } } string conInit = ""; bool hasParam = false; bool needsLateInit = false; if (dyn) { conInit = " : DynamicObjectData(\"" + m_parent + "\", r)"; hasParam = true; } else if (idyn) { conInit = " : " + string(Option::ClassPrefix) + parCls->getId() + "(r ? r : this)"; hasParam = true; } else { if (redec && classScope->derivedByDynamic()) { conInit = "root(r ? r : this)"; needsLateInit = true; } hasParam = true; } // this dance around the initialization list is to make // sure members get set in the right order - since properties // come first, and root comes later, we need to init the // properties first, and then root. if (needsLateInit) { cg_printf("%s%s(%s) : ", Option::ClassPrefix, clsName, hasParam ? "ObjectData* r = NULL" : ""); if (needsCppCtor) { cg.setContext(CodeGenerator::CppConstructor); ASSERT(!cg.hasInitListFirstElem()); m_stmt->outputCPP(cg, ar); cg.clearInitListFirstElem(); cg.setContext(CodeGenerator::CppDeclaration); cg_printf(", "); } cg_printf("%s", conInit.c_str()); } else { cg_printf("%s%s(%s)%s%s", Option::ClassPrefix, clsName, hasParam ? "ObjectData* r = NULL" : "", conInit.c_str(), needsCppCtor ? conInit.empty() ? " : " : ", " : ""); if (needsCppCtor) { cg.setContext(CodeGenerator::CppConstructor); ASSERT(!cg.hasInitListFirstElem()); m_stmt->outputCPP(cg, ar); cg.clearInitListFirstElem(); cg.setContext(CodeGenerator::CppDeclaration); } } cg_indentBegin(" {%s", hasGet || hasSet || hasCall || hasCallStatic ? "\n" : ""); if (hasGet) cg_printf("setAttribute(UseGet);\n"); if (hasSet) cg_printf("setAttribute(UseSet);\n"); if (hasCall) cg_printf("setAttribute(HasCall);\n"); if (hasCallStatic) cg_printf("setAttribute(HasCallStatic);\n"); cg_indentEnd("}\n"); hasEmitCppCtor = true; } } if (needsCppCtor && !hasEmitCppCtor) { cg_printf("%s%s() : ", Option::ClassPrefix, clsName); cg.setContext(CodeGenerator::CppConstructor); ASSERT(!cg.hasInitListFirstElem()); m_stmt->outputCPP(cg, ar); cg.clearInitListFirstElem(); cg.setContext(CodeGenerator::CppDeclaration); cg_printf(" {}\n"); } if (needsInit) { cg_printf("void init();\n"); } if (classScope->needLazyStaticInitializer()) { cg_printf("static GlobalVariables *lazy_initializer" "(GlobalVariables *g);\n"); } if (!classScope->getAttribute(ClassScope::HasConstructor)) { FunctionScopePtr func = classScope->findFunction(ar, "__construct", false); if (func && !func->isAbstract() && !classScope->isInterface()) { func->outputCPPCreateDecl(cg, ar); } } // doCall if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) { cg_printf("Variant doCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } if (classScope->getAttribute(ClassScope::HasInvokeMethod)) { FunctionScopePtr func = classScope->findFunction(ar, "__invoke", false); ASSERT(func); if (!func->isAbstract()) { cg_printf("const CallInfo *" "t___invokeCallInfoHelper(void *&extra);\n"); } } if (classScope->isRedeclaring() && !classScope->derivesFromRedeclaring() && classScope->derivedByDynamic()) { cg_printf("Variant doRootCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } if (m_stmt) m_stmt->outputCPP(cg, ar); { set<string> done; classScope->outputCPPStaticMethodWrappers(cg, ar, done, clsName); } if (Option::GenerateCPPMacros) { classScope->outputCPPJumpTableDecl(cg, ar); } cg_indentEnd("};\n"); classScope->outputCPPGlobalTableWrappersDecl(cg, ar); classScope->outputCPPDynamicClassDecl(cg); if (redeclared) { cg_indentBegin("class %s%s : public ClassStatics {\n", Option::ClassStaticsPrefix, clsName); cg_printf("public:\n"); cg_printf("DECLARE_OBJECT_ALLOCATION_NO_SWEEP(%s%s);\n", Option::ClassStaticsPrefix, clsName); cg_printf("%s%s() : ClassStatics(%d) {}\n", Option::ClassStaticsPrefix, clsName, classScope->getRedeclaringId()); cg_indentBegin("Variant %sgetInit(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sgetInit(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sget(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant &%slval(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%slval(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("ObjectData *createOnlyNoInit" "(ObjectData* root = NULL) {\n"); cg_printf("return %s%s(root);\n", Option::CreateObjectOnlyPrefix, clsName); cg_indentEnd("}\n"); cg_indentBegin("Variant %sconstant(const char* s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sconstant(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("bool %sget_call_info(MethodCallPackage &mcp, " "int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget_call_info(mcp, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentEnd("};\n"); } if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsDecl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppDeclaration); } } break; case CodeGenerator::CppImplementation: { if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsImpl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } classScope->outputCPPSupportMethodsImpl(cg, ar); if (redeclared) { cg_printf("IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(%s%s);\n", Option::ClassStaticsPrefix, clsName); } bool needsInit = classScope->needsInitMethod(); if (needsInit) { cg_indentBegin("void %s%s::init() {\n", Option::ClassPrefix, clsName); if (!m_parent.empty()) { if (classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared) { cg_printf("parent->init();\n"); } else { ClassScopePtr parCls = ar->findClass(m_parent); cg_printf("%s%s::init();\n", Option::ClassPrefix, parCls->getId().c_str()); } } if (classScope->getVariables()-> getAttribute(VariableTable::NeedGlobalPointer)) { cg.printDeclareGlobals(); } cg.setContext(CodeGenerator::CppInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); // This is lame. Exception base class needs to prepare stacktrace // outside of its PHP constructor. Every subclass of exception also // needs this stacktrace, so we're adding an artificial __init__ in // exception.php and calling it here. if (m_name == "exception") { cg_printf("{CountableHelper h(this); t___init__();}\n"); } cg_indentEnd("}\n"); } if (classScope->needStaticInitializer()) { cg_indentBegin("void %s%s::os_static_initializer() {\n", Option::ClassPrefix, clsName); cg.printDeclareGlobals(); cg.setContext(CodeGenerator::CppStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_indentBegin("void %s%s() {\n", Option::ClassStaticInitializerPrefix, clsName); cg_printf("%s%s::os_static_initializer();\n", Option::ClassPrefix, clsName); cg_indentEnd("}\n"); } if (classScope->needLazyStaticInitializer()) { cg_indentBegin("GlobalVariables *%s%s::lazy_initializer(" "GlobalVariables *g) {\n", Option::ClassPrefix, clsName); cg_indentBegin("if (!g->%s%s) {\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg_printf("g->%s%s = true;\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg.setContext(CodeGenerator::CppLazyStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_printf("return g;\n"); cg_indentEnd("}\n"); } cg.setContext(CodeGenerator::CppImplementation); if (m_stmt) m_stmt->outputCPP(cg, ar); } break; case CodeGenerator::CppFFIDecl: case CodeGenerator::CppFFIImpl: if (m_stmt) m_stmt->outputCPP(cg, ar); break; case CodeGenerator::JavaFFI: { if (classScope->isRedeclaring()) break; // TODO support PHP namespaces, once HPHP supports it string packageName = Option::JavaFFIRootPackage; string packageDir = packageName; Util::replaceAll(packageDir, ".", "/"); string outputDir = ar->getOutputPath() + "/" + Option::FFIFilePrefix + packageDir + "/"; Util::mkdir(outputDir); // uses a different cg to generate a separate file for each PHP class // also, uses the original capitalized class name string clsFile = outputDir + getOriginalName() + ".java"; ofstream fcls(clsFile.c_str()); CodeGenerator cgCls(&fcls, CodeGenerator::FileCPP); cgCls.setContext(CodeGenerator::JavaFFI); cgCls.printf("package %s;\n\n", packageName.c_str()); cgCls.printf("import hphp.*;\n\n"); printSource(cgCls); string clsModifier; switch (m_type) { case T_CLASS: break; case T_ABSTRACT: clsModifier = "abstract "; break; case T_FINAL: clsModifier = "final "; break; } cgCls.printf("public %sclass %s ", clsModifier.c_str(), getOriginalName().c_str()); ClassScopePtr parCls; if (!m_parent.empty()) parCls = ar->findClass(m_parent); if (!m_parent.empty() && classScope->derivesDirectlyFrom(m_parent) && parCls && parCls->isUserClass() && !parCls->isRedeclaring()) { // system classes are not supported in static FFI translation // they shouldn't appear as superclasses as well cgCls.printf("extends %s", parCls->getOriginalName().c_str()); } else { cgCls.printf("extends HphpObject"); } if (m_base) { bool first = true; for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && classScope->derivesFrom(ar, intf, false, false) && intfClassScope->isUserClass()) { if (first) { cgCls.printf(" implements "); first = false; } else { cgCls.printf(", "); } cgCls.printf(intfClassScope->getOriginalName().c_str()); } } } cgCls.indentBegin(" {\n"); // constructor for initializing the variant pointer cgCls.printf("protected %s(long ptr) { super(ptr); }\n\n", getOriginalName().c_str()); FunctionScopePtr cons = classScope->findConstructor(ar, true); if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) { // if not an abstract class and not having an explicit constructor, // adds a default constructor outputJavaFFIConstructor(cgCls, ar, cons); } if (m_stmt) m_stmt->outputCPP(cgCls, ar); cgCls.indentEnd("}\n"); fcls.close(); } break; case CodeGenerator::JavaFFICppDecl: case CodeGenerator::JavaFFICppImpl: { if (classScope->isRedeclaring()) break; if (m_stmt) m_stmt->outputCPP(cg, ar); FunctionScopePtr cons = classScope->findConstructor(ar, true); if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) { outputJavaFFICPPCreator(cg, ar, cons); } } break; default: ASSERT(false); break; } }
FunctionScopePtr MethodStatement::onInitialParse(AnalysisResultConstPtr ar, FileScopePtr fs) { int minParam, maxParam; ConstructPtr self = shared_from_this(); minParam = maxParam = 0; bool hasRef = false; if (m_params) { std::set<string> names, allDeclNames; int i = 0; maxParam = m_params->getCount(); for (i = maxParam; i--; ) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (param->isRef()) hasRef = true; if (!param->isOptional()) { if (!minParam) minParam = i + 1; } else if (minParam && !param->hasTypeHint()) { Compiler::Error(Compiler::RequiredAfterOptionalParam, param); } allDeclNames.insert(param->getName()); } for (i = maxParam-1; i >= 0; i--) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (names.find(param->getName()) != names.end()) { Compiler::Error(Compiler::RedundantParameter, param); for (int j = 0; j < 1000; j++) { string name = param->getName() + lexical_cast<string>(j); if (names.find(name) == names.end() && allDeclNames.find(name) == allDeclNames.end()) { param->rename(name); break; } } } names.insert(param->getName()); } } if (hasRef || m_ref) { m_attribute |= FileScope::ContainsReference; } vector<UserAttributePtr> attrs; if (m_attrList) { for (int i = 0; i < m_attrList->getCount(); ++i) { UserAttributePtr a = dynamic_pointer_cast<UserAttribute>((*m_attrList)[i]); attrs.push_back(a); } } StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this()); FunctionScopePtr funcScope (new FunctionScope(ar, m_method, m_name, stmt, m_ref, minParam, maxParam, m_modifiers, m_attribute, m_docComment, fs, attrs)); if (!m_stmt) { funcScope->setVirtual(); } setBlockScope(funcScope); funcScope->setParamCounts(ar, -1, -1); return funcScope; }
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); m_classScope.reset(); FunctionScopePtr prev = m_funcScope; m_funcScope.reset(); ConstructPtr self = shared_from_this(); if (!m_name.empty() && !isStatic()) { ClassScopePtr cls = resolveClass(); m_name = m_className; if (!cls) { if (isRedeclared()) { getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } else if (getScope()->isFirstPass()) { Compiler::Error(Compiler::UnknownClass, self); } if (m_params) m_params->inferAndCheck(ar, Type::Any, false); return Type::Object; } if (cls->isVolatile() && !isPresent()) { getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_dynamic = cls->derivesFromRedeclaring(); bool valid = true; FunctionScopePtr func = cls->findConstructor(ar, true); if (!func) { if (m_params) { if (!m_dynamic && m_params->getCount()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::BadConstructorCall, self); } m_params->setOutputCount(0); } m_params->inferAndCheck(ar, Type::Some, false); } } else { if (func != prev) func->addCaller(getScope()); m_extraArg = func->inferParamTypes(ar, self, m_params, valid); m_variableArgument = func->isVariableArgument(); } if (valid) { m_classScope = cls; m_funcScope = func; } if (!valid || m_dynamic) { m_implementedType = Type::Object; } else { m_implementedType.reset(); } return Type::CreateObjectType(m_name); } else { ar->containsDynamicClass(); if (m_params) { m_params->markParams(canInvokeFewArgs()); } } m_implementedType.reset(); m_nameExp->inferAndCheck(ar, Type::String, false); if (m_params) m_params->inferAndCheck(ar, Type::Any, false); return Type::Object; }
void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p, bool sep) { while (*p) { // Parse name const char *cname = *p++; // Parse parent const char *cparent = *p++; // Parse list of interfaces vector<string> ifaces; while (*p) ifaces.push_back(*p++); p++; // Parse methods vector<FunctionScopePtr> methods; while (*p) { FunctionScopePtr fs = ParseExtFunction(ar, p); if (sep) { fs->setSepExtension(); } bool abstract = (bool)*p++; if (abstract) { fs->addModifier(T_ABSTRACT); } int visibility = (long)*p++; int vismod = 0; if (visibility == 1) { vismod = T_PROTECTED; } else if (visibility == 2) { vismod = T_PRIVATE; } fs->addModifier(vismod); bool stat = (bool)*p++; if (stat) { fs->addModifier(T_STATIC); } methods.push_back(fs); } if (cparent && *cparent && (ifaces.empty() || ifaces[0] != cparent)) { ifaces.insert(ifaces.begin(), cparent); } ClassScopePtr cl(new ClassScope(ar, string(cname), string(cparent), ifaces, methods)); p++; // Parse properties while (*p) { *p++; // TODO, support visibility const char *name = *p++; TypePtr type = ParseType(p); cl->getVariables()->add(name, type, false, ar, ExpressionPtr(), ModifierExpressionPtr()); } p++; // Parse consts while (*p) { const char *name = *p++; TypePtr type = ParseType(p); cl->getConstants()->add(name, type, ExpressionPtr(), ar, ConstructPtr()); } p++; cl->setSystem(); if (sep) { cl->setSepExtension(); } s_classes[cl->getName()] = cl; } }
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg, AnalysisResultPtr ar, bool directVariant, int doExist) { bool bThis = m_object->isThis(); bool useGetThis = false; FunctionScopePtr funcScope = ar->getFunctionScope(); if (bThis) { if (funcScope && funcScope->isStatic()) { cg_printf("GET_THIS_ARROW()"); } else { // in order for __set() and __get() to be called useGetThis = true; } } const char *op = "."; string func = Option::ObjectPrefix; const char *error = ", true"; ClassScopePtr cls = ar->getClassScope(); const char *context = ""; if (cg.getOutput() != CodeGenerator::SystemCPP) { if (cls) { context = ", s_class_name"; } else if (funcScope && !funcScope->inPseudoMain()) { context = ", empty_string"; } } if (doExist) { func = doExist > 0 ? "doIsSet" : "doEmpty"; error = ""; } else { if (bThis && funcScope && funcScope->isStatic()) { func = Option::ObjectStaticPrefix; error = ""; context = ""; } else if (m_context & ExistContext) { error = ", false"; } if (m_context & (LValue | RefValue | UnsetContext)) { func += "lval"; error = ""; } else { func += "get"; } } if (m_property->getKindOf() == Expression::KindOfScalarExpression) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(m_property); const char *propName = name->getString().c_str(); if (m_valid && m_object->getType()->isSpecificObject()) { if (m_static) { if (!bThis) { ASSERT(m_class); if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty("); cg_printf("g->%s%s%s%s", Option::StaticPropertyPrefix, m_class->getName().c_str(), Option::IdPrefix.c_str(), propName); if (doExist) cg_printf(")"); } else { // if $val is a class static variable (static $val), then $val // cannot be declared as a class variable (var $val), $this->val // refers to a non-static class variable and has to use get/lval. uint64 hash = hash_string(propName); if (useGetThis) cg_printf("GET_THIS_DOT()"); cg_printf("%s(", func.c_str()); cg_printString(propName, ar); cg_printf(", 0x%016llXLL%s%s)", hash, error, context); } } else { if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty("); if (!bThis) { ASSERT(!directVariant); m_object->outputCPP(cg, ar); cg_printf("->"); } cg_printf("%s%s", Option::PropertyPrefix, propName); if (doExist) cg_printf(")"); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg_printf(op); } else { if (useGetThis) cg_printf("GET_THIS_DOT()"); } uint64 hash = hash_string(propName); cg_printf("%s(", func.c_str()); cg_printString(propName, ar); cg_printf(", 0x%016llXLL%s%s)", hash, error, context); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg_printf(op); } else { if (useGetThis) cg_printf("GET_THIS_DOT()"); } cg_printf("%s(", func.c_str()); m_property->outputCPP(cg, ar); cg_printf(", -1LL%s%s)", error, context); } }
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Object, false); m_valid = true; m_bindClass = true; if (m_name.empty()) { m_nameExp->inferAndCheck(ar, Type::String, false); setInvokeParams(ar); // we have to use a variant to hold dynamic value return checkTypesImpl(ar, type, Type::Variant, coerce); } ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { if (m_classScope && !strcasecmp(objectType->getName().c_str(), m_classScope->getName().c_str())) { cls = m_classScope; } else { cls = ar->findExactClass(shared_from_this(), objectType->getName()); } } if (!cls) { if (getScope()->isFirstPass()) { if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) { Compiler::Error(Compiler::UnknownObjectMethod, self); } } m_classScope.reset(); m_funcScope.reset(); setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } if (m_classScope != cls) { m_classScope = cls; m_funcScope.reset(); } FunctionScopePtr func = m_funcScope; if (!func) { func = cls->findFunction(ar, m_name, true, true); if (!func) { if (!cls->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) { if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) { setDynamicByIdentifier(ar, m_name); } else { Compiler::Error(Compiler::UnknownObjectMethod, self); } } m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } m_funcScope = func; func->addCaller(getScope()); } bool valid = true; m_bindClass = func->isStatic(); // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr localfunc = getFunctionScope(); if (localfunc->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } valid = false; } } // invoke() will return Variant if (!m_object->getType()->isSpecificObject() || (func->isVirtual() && !func->isPerfectVirtual())) { valid = false; } if (!valid) { setInvokeParams(ar); checkTypesImpl(ar, type, Type::Variant, coerce); m_valid = false; // so we use invoke() syntax func->setDynamic(); return m_actualType; } return checkParamsAndReturn(ar, type, coerce, func, false); }
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { assert(type); IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope()); resetTypes(); reset(); ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false); m_valid = true; m_bindClass = true; if (m_name.empty()) { m_nameExp->inferAndCheck(ar, Type::Some, false); setInvokeParams(ar); // we have to use a variant to hold dynamic value return checkTypesImpl(ar, type, Type::Variant, coerce); } ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { if (m_classScope && !strcasecmp(objectType->getName().c_str(), m_classScope->getName().c_str())) { cls = m_classScope; } else { cls = ar->findExactClass(shared_from_this(), objectType->getName()); } } if (!cls) { m_classScope.reset(); m_funcScope.reset(); m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } if (m_classScope != cls) { m_classScope = cls; m_funcScope.reset(); } FunctionScopePtr func = m_funcScope; if (!func) { func = cls->findFunction(ar, m_name, true, true); if (!func) { if (!cls->isTrait() && !cls->getAttribute(ClassScope::MayHaveUnknownMethodHandler) && !cls->getAttribute(ClassScope::HasUnknownMethodHandler) && !cls->getAttribute(ClassScope::InheritsUnknownMethodHandler)) { if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) { if (!Option::AllDynamic) { setDynamicByIdentifier(ar, m_name); } } else { Compiler::Error(Compiler::UnknownObjectMethod, self); } } m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } m_funcScope = func; func->addCaller(getScope(), !type->is(Type::KindOfAny)); } bool valid = true; m_bindClass = func->isStatic(); // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr localfunc = getFunctionScope(); if (localfunc->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } valid = false; } } // invoke() will return Variant if (cls->isInterface() || (func->isVirtual() && (!Option::WholeProgram || func->isAbstract() || (func->hasOverride() && cls->getAttribute(ClassScope::NotFinal))) && !func->isPerfectVirtual())) { valid = false; } if (!valid) { setInvokeParams(ar); checkTypesImpl(ar, type, Type::Variant, coerce); m_valid = false; // so we use invoke() syntax if (!Option::AllDynamic) { func->setDynamic(); } assert(m_actualType); return m_actualType; } assert(func); return checkParamsAndReturn(ar, type, coerce, func, false); }
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), true); m_valid = true; m_bindClass = true; if (m_name.empty()) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicMethod, self); } m_nameExp->inferAndCheck(ar, Type::String, false); setInvokeParams(ar); // we have to use a variant to hold dynamic value return checkTypesImpl(ar, type, Type::Variant, coerce); } ClassScopePtr cls = m_classScope; if (objectType && !objectType->getName().empty()) { cls = ar->findExactClass(objectType->getName()); } if (!cls) { if (ar->isFirstPass()) { // call resolveClass to mark functions as dynamic // but we cant do anything else with the result. resolveClass(ar, m_name); if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) { ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self); } } m_classScope.reset(); m_funcScope.reset(); setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } if (m_classScope != cls) { m_classScope = cls; m_funcScope.reset(); } FunctionScopePtr func = m_funcScope; if (!func) { func = cls->findFunction(ar, m_name, true, true); if (!func) { if (!cls->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) { if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) { // TODO: we could try to find out class derivation is present... ar->getCodeError()->record(self, CodeError::DerivedObjectMethod, self); // we have to make sure the method is in invoke list setDynamicByIdentifier(ar, m_name); } else { ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self); } } m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } m_funcScope = func; } bool valid = true; m_bindClass = func->isStatic(); // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr localfunc = ar->getFunctionScope(); if (localfunc->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } valid = false; } } // invoke() will return Variant if (func->isVirtual() || !m_object->getType()->isSpecificObject()) { valid = false; } if (!valid) { setInvokeParams(ar); checkTypesImpl(ar, type, Type::Variant, coerce); m_valid = false; // so we use invoke() syntax func->setDynamic(); return m_actualType; } return checkParamsAndReturn(ar, type, coerce, func); }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false); if (!m_property->is(Expression::KindOfScalarExpression)) { m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(getOriginalClass(), false); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); string name = exp->getString(); ASSERT(!name.empty()); m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(shared_from_this(), objectType->getName()); } else { if ((m_context & LValue) && objectType && !objectType->is(Type::KindOfObject) && !objectType->is(Type::KindOfVariant) && !objectType->is(Type::KindOfSome) && !objectType->is(Type::KindOfAny)) { m_object->inferAndCheck(ar, Type::Object, true); } } if (!cls) { if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) { ar->forceClassVariants(name, getOriginalClass(), false); } return Type::Variant; } int prop = hasContext(AssignmentLHS) ? ClassScope::MayHaveUnknownPropSetter : hasContext(ExistContext) ? ClassScope::MayHaveUnknownPropTester : hasContext(UnsetContext) && hasContext(LValue) ? ClassScope::MayHavePropUnsetter : ClassScope::MayHaveUnknownPropGetter; if ((m_context & (AssignmentLHS|OprLValue)) || !cls->implementsAccessor(prop)) { clearEffect(AccessorEffect); } // resolved to this class if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = m_object->getOriginalFunction(); if (!func || func->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } if (!m_propSym || cls != m_objectClass.lock()) { m_objectClass = cls; ClassScopePtr parent; m_propSym = cls->findProperty(parent, name, ar, self); assert(m_propSym); if (!parent) { parent = cls; } m_propSymValid = m_propSym->isPresent() && (!m_propSym->isPrivate() || getOriginalClass() == parent) && !m_propSym->isStatic(); if (m_propSymValid) { parent->addUse(getScope(), BlockScope::UseKindNonStaticRef); } } TypePtr ret; if (m_propSymValid && (!cls->derivesFromRedeclaring() || m_propSym->isPrivate())) { ret = cls->checkProperty(m_propSym, type, coerce, ar); assert(m_object->getType()->isSpecificObject()); m_valid = true; clearEffect(AccessorEffect); clearEffect(CreateEffect); return ret; } else { m_actualType = Type::Variant; return m_actualType; } }
void ClassStatement::outputCPP(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopePtr classScope = m_classScope.lock(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isRedeclaring()) { cg.printf("g->%s%s = ClassStaticsPtr(NEW(%s%s)());\n", Option::ClassStaticsObjectPrefix, m_name.c_str(), Option::ClassStaticsPrefix, classScope->getId().c_str()); } if (classScope->isVolatile()) { cg.printf("g->declareClass(\"%s\");\n", m_name.c_str()); } return; } if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } ar->pushScope(classScope); string clsNameStr = classScope->getId(); const char *clsName = clsNameStr.c_str(); bool redeclared = classScope->isRedeclaring(); switch (cg.getContext()) { case CodeGenerator::CppForwardDeclaration: if (Option::GenerateCPPMacros) { cg.printf("FORWARD_DECLARE_CLASS(%s)\n", clsName); if (redeclared) { cg.printf("FORWARD_DECLARE_REDECLARED_CLASS(%s)\n", clsName); } } if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsDecl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppForwardDeclaration); } break; case CodeGenerator::CppDeclaration: { ClassScopePtr parCls; if (!m_parent.empty()) parCls = ar->findClass(m_parent); cg.printf("class %s%s", Option::ClassPrefix, clsName); bool derived = false; if (!m_parent.empty() && classScope->derivesFrom(ar, m_parent)) { if (parCls->isRedeclaring()) { cg.printf(" : public DynamicObjectData"); } else { cg.printf(" : virtual public %s%s", Option::ClassPrefix, parCls->getId().c_str()); } derived = true; } if (m_base) { for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && classScope->derivesFrom(ar, intf)) { // temporary fix for inheriting from a re-declaring class string id = intfClassScope->getId(); if (!derived) { derived = true; cg.printf(" :"); } else { cg.printf(","); } cg.printf(" virtual public %s%s", Option::ClassPrefix, id.c_str()); } } } if (!derived) { const char *op = derived ? "," : " :"; if (classScope->derivesFromRedeclaring()) { cg.printf("%s public DynamicObjectData", op); } else { cg.printf("%s virtual public ObjectData", op); } } cg.indentBegin(" {\n"); if (Option::GenerateCPPMacros) { vector<string> bases; getAllParents(ar, bases); cg.indentBegin("BEGIN_CLASS_MAP(%s)\n", clsName); for (unsigned int i = 0; i < bases.size(); i++) { cg.printf("PARENT_CLASS(%s)\n", bases[i].c_str()); } cg.indentEnd("END_CLASS_MAP(%s)\n", clsName); } if (Option::GenerateCPPMacros) { bool dyn = classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared; bool idyn = classScope->derivesFromRedeclaring() == ClassScope::IndirectFromRedeclared; bool redec = classScope->isRedeclaring(); if (!classScope->derivesFromRedeclaring()) { cg.printf("DECLARE_CLASS(%s, %s, %s)\n", clsName, m_originalName.c_str(), m_parent.empty() ? "ObjectData" : m_parent.c_str()); } else { cg.printf("DECLARE_DYNAMIC_CLASS(%s, %s)\n", clsName, m_originalName.c_str()); } if (cg.getOutput() == CodeGenerator::SystemCPP || Option::EnableEval >= Option::LimitedEval) { cg.printf("DECLARE_INVOKES_FROM_EVAL\n"); } if (dyn || idyn || redec) { if (redec) { cg.indentBegin("Variant %sroot_invoke(const char* s, CArrRef ps, " "int64 h, bool f = true) {\n", Option::ObjectPrefix); cg.printf("return root->%sinvoke(s, ps, h, f);\n", Option::ObjectPrefix); cg.indentEnd("}\n"); cg.indentBegin("Variant %sroot_invoke_few_args(const char* s, " "int64 h, int count", Option::ObjectPrefix); for (int i = 0; i < Option::InvokeFewArgsCount; i++) { cg.printf(", CVarRef a%d = null_variant", i); } cg.printf(") {\n"); cg.printf("return root->%sinvoke_few_args(s, h, count", Option::ObjectPrefix); for (int i = 0; i < Option::InvokeFewArgsCount; i++) { cg.printf(", a%d", i); } cg.printf(");\n"); cg.indentEnd("}\n"); if (!dyn && !idyn) cg.printf("private: ObjectData* root;\n"); cg.printf("public:\n"); } string conInit = ":"; if (dyn) { conInit += "DynamicObjectData(\"" + m_parent + "\", r)"; } else if (idyn) { conInit += string(Option::ClassPrefix) + parCls->getId() + "(r?r:this)"; } else { conInit += "root(r?r:this)"; } cg.printf("%s%s(ObjectData* r = NULL)%s {}\n", Option::ClassPrefix, clsName, conInit.c_str()); } } cg.printf("void init();\n", Option::ClassPrefix, clsName); if (classScope->needLazyStaticInitializer()) { cg.printf("static GlobalVariables *lazy_initializer" "(GlobalVariables *g);\n"); } if (!classScope->derivesFromRedeclaring()){ classScope->getVariables()->outputCPPPropertyDecl(cg, ar); } if (!classScope->getAttribute(ClassScope::HasConstructor)) { FunctionScopePtr func = classScope->findFunction(ar, "__construct", false); if (func && !func->isAbstract() && !classScope->isInterface()) { ar->pushScope(func); func->outputCPPCreateDecl(cg, ar); ar->popScope(); } } if (classScope->getAttribute(ClassScope::HasDestructor)) { cg.printf("public: virtual void destruct();\n"); } // doCall if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) { cg.printf("Variant doCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } if (m_stmt) m_stmt->outputCPP(cg, ar); { set<string> done; classScope->outputCPPStaticMethodWrappers(cg, ar, done, clsName); } if (cg.getOutput() == CodeGenerator::SystemCPP && ar->isBaseSysRsrcClass(clsName) && !classScope->hasProperty("rsrc")) { cg.printf("public: Variant %srsrc;\n", Option::PropertyPrefix); } cg.indentEnd("};\n"); if (redeclared) { cg.indentBegin("class %s%s : public ClassStatics {\n", Option::ClassStaticsPrefix, clsName); cg.printf("public:\n"); cg.printf("DECLARE_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); cg.printf("%s%s() : ClassStatics(%d) {}\n", Option::ClassStaticsPrefix, clsName, classScope->getRedeclaringId()); cg.indentBegin("Variant %sget(const char *s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg.printf("return %s%s::%sget(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg.indentEnd("}\n"); cg.indentBegin("Variant &%slval(const char* s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg.printf("return %s%s::%slval(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg.indentEnd("}\n"); cg.indentBegin("Variant %sinvoke(const char *c, const char *s, " "CArrRef params, int64 hash = -1, bool fatal = true) " "{\n", Option::ObjectStaticPrefix); cg.printf("return %s%s::%sinvoke(c, s, params, hash, fatal);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg.indentEnd("}\n"); cg.indentBegin("Object create(CArrRef params, bool init = true, " "ObjectData* root = NULL) {\n"); cg.printf("return Object(%s%s(NEW(%s%s)(root))->" "dynCreate(params, init));\n", Option::SmartPtrPrefix, clsName, Option::ClassPrefix, clsName); cg.indentEnd("}\n"); cg.indentBegin("Variant %sconstant(const char* s) {\n", Option::ObjectStaticPrefix); cg.printf("return %s%s::%sconstant(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg.indentEnd("}\n"); cg.indentBegin("Variant %sinvoke_from_eval(const char *c, " "const char *s, Eval::VariableEnvironment &env, " "const Eval::FunctionCallExpression *call, " "int64 hash = -1, bool fatal = true) " "{\n", Option::ObjectStaticPrefix); cg.printf("return %s%s::%sinvoke_from_eval(c, s, env, call, hash, " "fatal);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg.indentEnd("}\n"); cg.indentEnd("};\n"); } } break; case CodeGenerator::CppImplementation: if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsImpl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } classScope->outputCPPSupportMethodsImpl(cg, ar); if (redeclared) { cg.printf("IMPLEMENT_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); } cg.indentBegin("void %s%s::init() {\n", Option::ClassPrefix, clsName); if (!m_parent.empty()) { if (classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared) { cg.printf("parent->init();\n"); } else { cg.printf("%s%s::init();\n", Option::ClassPrefix, m_parent.c_str()); } } cg.setContext(CodeGenerator::CppConstructor); if (m_stmt) m_stmt->outputCPP(cg, ar); cg.indentEnd("}\n"); if (classScope->needStaticInitializer()) { cg.indentBegin("void %s%s::os_static_initializer() {\n", Option::ClassPrefix, clsName); cg.printDeclareGlobals(); cg.setContext(CodeGenerator::CppStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg.indentEnd("}\n"); cg.indentBegin("void %s%s() {\n", Option::ClassStaticInitializerPrefix, clsName); cg.printf("%s%s::os_static_initializer();\n", Option::ClassPrefix, clsName); cg.indentEnd("}\n"); } if (classScope->needLazyStaticInitializer()) { cg.indentBegin("GlobalVariables *%s%s::lazy_initializer(" "GlobalVariables *g) {\n", Option::ClassPrefix, clsName); cg.indentBegin("if (!g->%s%s) {\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg.printf("g->%s%s = true;\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg.setContext(CodeGenerator::CppLazyStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg.indentEnd("}\n"); cg.printf("return g;\n"); cg.indentEnd("}\n"); } cg.setContext(CodeGenerator::CppImplementation); if (m_stmt) m_stmt->outputCPP(cg, ar); break; case CodeGenerator::CppFFIDecl: case CodeGenerator::CppFFIImpl: if (m_stmt) m_stmt->outputCPP(cg, ar); break; case CodeGenerator::JavaFFI: { if (classScope->isRedeclaring()) break; // TODO support PHP namespaces, once HPHP supports it string packageName = Option::JavaFFIRootPackage; string packageDir = packageName; Util::replaceAll(packageDir, ".", "/"); string outputDir = ar->getOutputPath() + "/" + Option::FFIFilePrefix + packageDir + "/"; Util::mkdir(outputDir); // uses a different cg to generate a separate file for each PHP class // also, uses the original capitalized class name string clsFile = outputDir + getOriginalName() + ".java"; ofstream fcls(clsFile.c_str()); CodeGenerator cgCls(&fcls, CodeGenerator::FileCPP); cgCls.setContext(CodeGenerator::JavaFFI); cgCls.printf("package %s;\n\n", packageName.c_str()); cgCls.printf("import hphp.*;\n\n"); printSource(cgCls); string clsModifier; switch (m_type) { case T_CLASS: break; case T_ABSTRACT: clsModifier = "abstract "; break; case T_FINAL: clsModifier = "final "; break; } cgCls.printf("public %sclass %s ", clsModifier.c_str(), getOriginalName().c_str()); ClassScopePtr parCls; if (!m_parent.empty()) parCls = ar->findClass(m_parent); if (!m_parent.empty() && classScope->derivesFrom(ar, m_parent) && parCls->isUserClass() && !parCls->isRedeclaring()) { // system classes are not supported in static FFI translation // they shouldn't appear as superclasses as well cgCls.printf("extends %s", parCls->getOriginalName()); } else { cgCls.printf("extends HphpObject"); } if (m_base) { bool first = true; for (int i = 0; i < m_base->getCount(); i++) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>((*m_base)[i]); const char *intf = exp->getString().c_str(); ClassScopePtr intfClassScope = ar->findClass(intf); if (intfClassScope && classScope->derivesFrom(ar, intf) && intfClassScope->isUserClass()) { if (first) { cgCls.printf(" implements "); first = false; } else { cgCls.printf(", "); } cgCls.printf(intfClassScope->getOriginalName()); } } } cgCls.indentBegin(" {\n"); // constructor for initializing the variant pointer cgCls.printf("protected %s(long ptr) { super(ptr); }\n\n", getOriginalName().c_str()); FunctionScopePtr cons = classScope->findConstructor(ar, true); if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) { // if not an abstract class and not having an explicit constructor, // adds a default constructor outputJavaFFIConstructor(cgCls, ar, cons); } if (m_stmt) m_stmt->outputCPP(cgCls, ar); cgCls.indentEnd("}\n"); fcls.close(); } break; case CodeGenerator::JavaFFICppDecl: case CodeGenerator::JavaFFICppImpl: { if (classScope->isRedeclaring()) break; if (m_stmt) m_stmt->outputCPP(cg, ar); FunctionScopePtr cons = classScope->findConstructor(ar, true); if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) { outputJavaFFICPPCreator(cg, ar, cons); } } break; default: ASSERT(false); break; } ar->popScope(); }
void FunctionStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) { // Correctness checks are normally done before adding function to scope. if (m_params) { for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (param->hasTypeHint() && param->defaultValue()) { param->compatibleDefault(); } } } // note it's important to add to scope, not a pushed FunctionContainer, // as a function may be declared inside a class's method, yet this function // is a global function, not a class method. FunctionScopePtr fs = onInitialParse(ar, scope); FunctionScope::RecordFunctionInfo(m_name, fs); if (!scope->addFunction(ar, fs)) { m_ignored = true; return; } if (Option::PersistenceHook) { fs->setPersistent(Option::PersistenceHook(fs, scope)); } if (m_name == "__autoload") { if (m_params && m_params->getCount() != 1) { parseTimeFatal(Compiler::InvalidMagicMethod, "__autoload() must take exactly 1 argument"); } } if (fs->isNative()) { if (getStmts()) { parseTimeFatal(Compiler::InvalidAttribute, "Native functions must not have an implementation body"); } if (m_params) { int nParams = m_params->getCount(); for (int i = 0; i < nParams; ++i) { // Variadic capture params don't need types // since they'll be Arrays as far as HNI is concerned. auto param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (!param->hasUserType() && !param->isVariadic()) { parseTimeFatal(Compiler::InvalidAttribute, "Native function calls must have type hints " "on all args"); } } } if (getReturnTypeConstraint().empty()) { parseTimeFatal(Compiler::InvalidAttribute, "Native function %s() must have a return type hint", getOriginalName().c_str()); } } else if (!getStmts()) { parseTimeFatal(Compiler::InvalidAttribute, "Global function %s() must contain a body", getOriginalName().c_str()); } }
void FunctionStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { CodeGenerator::Context context = cg.getContext(); FunctionScopePtr funcScope = m_funcScope.lock(); string fname = funcScope->getId(cg).c_str(); bool pseudoMain = funcScope->inPseudoMain(); string origFuncName = !pseudoMain ? funcScope->getOriginalName() : ("run_init::" + funcScope->getFileScope()->getName()); string funcSection; if (outputFFI(cg, ar)) return; if (context == CodeGenerator::NoContext) { string rname = cg.formatLabel(m_name); if (funcScope->isRedeclaring()) { cg.printf("g->%s%s = &%s%s;\n", Option::CallInfoPrefix, m_name.c_str(), Option::CallInfoPrefix, fname.c_str()); } if (funcScope->isVolatile()) { cg_printf("g->declareFunctionLit("); cg_printString(m_name, ar); cg_printf(");\n"); cg_printf("g->FVF(%s) = true;\n", rname.c_str()); } return; } if (context == CodeGenerator::CppDeclaration && !funcScope->isInlined()) return; if (context == CodeGenerator::CppPseudoMain && !pseudoMain) return; if (context == CodeGenerator::CppImplementation && (funcScope->isInlined() || pseudoMain)) return; ar->pushScope(funcScope); cg.setPHPLineNo(-1); if (pseudoMain && !Option::GenerateCPPMain) { if (context == CodeGenerator::CppPseudoMain) { if (cg.getOutput() != CodeGenerator::SystemCPP) { cg.setContext(CodeGenerator::NoContext); // no inner functions/classes funcScope->getVariables()->setAttribute(VariableTable::ForceGlobal); outputCPPStmt(cg, ar); funcScope->getVariables()->clearAttribute(VariableTable::ForceGlobal); cg.setContext(CodeGenerator::CppPseudoMain); ar->popScope(); return; } } else if (context == CodeGenerator::CppForwardDeclaration && cg.getOutput() != CodeGenerator::SystemCPP) { return; } } if (context == CodeGenerator::CppImplementation) { printSource(cg); } else if (context == CodeGenerator::CppForwardDeclaration && Option::GenerateCppLibCode) { cg_printf("\n"); printSource(cg); cg.printDocComment(funcScope->getDocComment()); } if (funcScope->isInlined()) cg_printf("inline "); TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg_printf("void"); } funcSection = Option::FunctionSections[origFuncName]; if (!funcSection.empty()) { cg_printf(" __attribute__ ((section (\".text.%s\")))", funcSection.c_str()); } if (pseudoMain) { cg_printf(" %s%s(", Option::PseudoMainPrefix, funcScope->getFileScope()->pseudoMainName().c_str()); } else { cg_printf(" %s%s(", Option::FunctionPrefix, fname.c_str()); } switch (context) { case CodeGenerator::CppForwardDeclaration: funcScope->outputCPPParamsDecl(cg, ar, m_params, true); cg_printf(");\n"); if (funcScope->hasDirectInvoke()) { cg_printf("Variant %s%s(void *extra, CArrRef params);\n", Option::InvokePrefix, fname.c_str()); } break; case CodeGenerator::CppDeclaration: case CodeGenerator::CppImplementation: case CodeGenerator::CppPseudoMain: { funcScope->outputCPPParamsDecl(cg, ar, m_params, false); cg_indentBegin(") {\n"); const char *sys = (cg.getOutput() == CodeGenerator::SystemCPP ? "_BUILTIN" : ""); if (pseudoMain) { cg_printf("PSEUDOMAIN_INJECTION%s(%s, %s%s);\n", sys, origFuncName.c_str(), Option::PseudoMainPrefix, funcScope->getFileScope()->pseudoMainName().c_str()); } else { if (m_stmt->hasBody()) { cg_printf("FUNCTION_INJECTION%s(%s);\n", sys, origFuncName.c_str()); } outputCPPArgInjections(cg, ar, origFuncName.c_str(), ClassScopePtr(), funcScope); } funcScope->outputCPP(cg, ar); cg.setContext(CodeGenerator::NoContext); // no inner functions/classes outputCPPStmt(cg, ar); cg.setContext(context); cg_indentEnd("} /* function */\n"); } break; default: ASSERT(false); } ar->popScope(); }
void SimpleFunctionCall::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { bool linemap = outputLineMap(cg, ar, true); if (!m_lambda.empty()) { cg.printf("\"%s\"", m_lambda.c_str()); if (linemap) cg.printf(")"); return; } if (!m_class && m_className.empty()) { if (m_type == DefineFunction && m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); ExpressionPtr value = (*m_params)[1]; if (varName.empty()) { cg.printf("throw_fatal(\"bad define\")"); } else if (m_dynamicConstant) { cg.printf("g->declareConstant(\"%s\", g->%s%s, ", varName.c_str(), Option::ConstantPrefix, varName.c_str()); value->outputCPP(cg, ar); cg.printf(")"); } else { bool needAssignment = true; bool isSystem = ar->getConstants()->isSystem(varName); if (isSystem || ((!ar->isConstantRedeclared(varName)) && value->isScalar())) { needAssignment = false; } if (needAssignment) { cg.printf("%s%s = ", Option::ConstantPrefix, varName.c_str()); value->outputCPP(cg, ar); } } } else { cg.printf("throw_fatal(\"bad define\")"); } if (linemap) cg.printf(")"); return; } if (m_name == "func_num_args") { cg.printf("num_args"); if (linemap) cg.printf(")"); return; } switch (m_type) { case VariableArgumentFunction: { FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(ar->getScope()); if (func) { cg.printf("%s(", m_name.c_str()); func->outputCPPParamsCall(cg, ar, true); if (m_params) { cg.printf(","); m_params->outputCPP(cg, ar); } cg.printf(")"); if (linemap) cg.printf(")"); return; } } break; case FunctionExistsFunction: case ClassExistsFunction: case InterfaceExistsFunction: { bool literalString = false; string symbol; if (m_params && m_params->getCount() == 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { literalString = true; symbol = name->getLiteralString(); } } } if (literalString) { switch (m_type) { case FunctionExistsFunction: { const std::string &lname = Util::toLower(symbol); bool dynInvoke = Option::DynamicInvokeFunctions.find(lname) != Option::DynamicInvokeFunctions.end(); if (!dynInvoke) { FunctionScopePtr func = ar->findFunction(lname); if (func) { if (!func->isDynamic()) { if (func->isRedeclaring()) { const char *name = func->getName().c_str(); cg.printf("(%s->%s%s != invoke_failed_%s)", cg.getGlobals(ar), Option::InvokePrefix, name, name); break; } cg.printf("true"); break; } } else { cg.printf("false"); break; } } cg.printf("f_function_exists(\"%s\")", lname.c_str()); } break; case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && !cls->isInterface()) { const char *name = cls->getName().c_str(); cg.printf("f_class_exists(\"%s\")", name); } else { cg.printf("false"); } } break; case InterfaceExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isInterface()) { const char *name = cls->getName().c_str(); cg.printf("f_interface_exists(\"%s\")", name); } else { cg.printf("false"); } } break; default: break; } if (linemap) cg.printf(")"); return; } } break; case GetDefinedVarsFunction: cg.printf("get_defined_vars(variables)"); if (linemap) cg.printf(")"); return; default: break; } } outputCPPParamOrderControlled(cg, ar); if (linemap) cg.printf(")"); }
void AnalysisResult::analyzeProgram(bool system /* = false */) { AnalysisResultPtr ar = shared_from_this(); getVariables()->forceVariants(ar, VariableTable::AnyVars); getVariables()->setAttribute(VariableTable::ContainsLDynamicVariable); getVariables()->setAttribute(VariableTable::ContainsExtract); getVariables()->setAttribute(VariableTable::ForceGlobal); // Analyze Includes Logger::Verbose("Analyzing Includes"); sort(m_fileScopes.begin(), m_fileScopes.end(), by_filename); // fixed order unsigned int i = 0; for (i = 0; i < m_fileScopes.size(); i++) { collectFunctionsAndClasses(m_fileScopes[i]); } // Keep generated code identical without randomness canonicalizeSymbolOrder(); markRedeclaringClasses(); // Analyze some special cases for (set<string>::const_iterator it = Option::VolatileClasses.begin(); it != Option::VolatileClasses.end(); ++it) { ClassScopePtr cls = findClass(toLower(*it)); if (cls && cls->isUserClass()) { cls->setVolatile(); } } checkClassDerivations(); resolveNSFallbackFuncs(); // Analyze All Logger::Verbose("Analyzing All"); setPhase(AnalysisResult::AnalyzeAll); for (i = 0; i < m_fileScopes.size(); i++) { m_fileScopes[i]->analyzeProgram(ar); } /* Note that cls->collectMethods() can add entries to m_classDecs, which can invalidate iterators. So we have to create an array and then iterate over that. The new entries added to m_classDecs are always empty, so it doesnt matter that we dont include them in the iteration */ std::vector<ClassScopePtr> classes; classes.reserve(m_classDecs.size()); for (StringToClassScopePtrVecMap::const_iterator iter = m_classDecs.begin(); iter != m_classDecs.end(); ++iter) { for (ClassScopePtr cls: iter->second) { classes.push_back(cls); } } // Collect methods for (ClassScopePtr cls: classes) { if (cls->isRedeclaring()) { cls->setStaticDynamic(ar); } StringToFunctionScopePtrMap methods; cls->collectMethods(ar, methods, true /* include privates */); bool needAbstractMethodImpl = (!cls->isAbstract() && !cls->isInterface() && cls->derivesFromRedeclaring() == Derivation::Normal && !cls->getAttribute(ClassScope::UsesUnknownTrait)); for (StringToFunctionScopePtrMap::const_iterator iterMethod = methods.begin(); iterMethod != methods.end(); ++iterMethod) { FunctionScopePtr func = iterMethod->second; if (Option::WholeProgram && !func->hasImpl() && needAbstractMethodImpl) { FunctionScopePtr tmpFunc = cls->findFunction(ar, func->getName(), true, true); always_assert(!tmpFunc || !tmpFunc->hasImpl()); Compiler::Error(Compiler::MissingAbstractMethodImpl, func->getStmt(), cls->getStmt()); } m_methodToClassDecs[iterMethod->first].push_back(cls); } } ClassScopePtr cls; string cname; for (auto& sysclass_cls: m_systemClasses) { tie(cname, cls) = sysclass_cls; StringToFunctionScopePtrMap methods; cls->collectMethods(ar, methods, true /* include privates */); for (StringToFunctionScopePtrMap::const_iterator iterMethod = methods.begin(); iterMethod != methods.end(); ++iterMethod) { m_methodToClassDecs[iterMethod->first].push_back(cls); } } }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false); if (!m_property->is(Expression::KindOfScalarExpression)) { m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(getOriginalClass(), false, true); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); const string &name = exp->getLiteralString(); if (name.empty()) { m_property->inferAndCheck(ar, Type::String, false); if (m_context & (LValue | RefValue)) { ar->forceClassVariants(getOriginalClass(), false, true); } return Type::Variant; // we have to use a variant to hold dynamic value } m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(shared_from_this(), objectType->getName()); } else { if ((m_context & LValue) && objectType && !objectType->is(Type::KindOfObject) && !objectType->is(Type::KindOfVariant) && !objectType->is(Type::KindOfSome) && !objectType->is(Type::KindOfAny)) { m_object->inferAndCheck(ar, Type::Object, true); } } if (!cls) { if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) { ar->forceClassVariants(name, getOriginalClass(), false, true); } return Type::Variant; } // resolved to this class if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = m_object->getOriginalFunction(); if (!func || func->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } assert(cls); if (!m_propSym || cls != m_objectClass.lock()) { m_objectClass = cls; ClassScopePtr parent; m_propSym = cls->findProperty(parent, name, ar); if (m_propSym) { if (!parent) { parent = cls; } m_symOwner = parent; always_assert(m_propSym->isPresent()); m_propSymValid = (!m_propSym->isPrivate() || getOriginalClass() == parent) && !m_propSym->isStatic(); if (m_propSymValid) { m_symOwner->addUse(getScope(), BlockScope::GetNonStaticRefUseKind( m_propSym->getHash())); } } } TypePtr ret; if (m_propSymValid && (!cls->derivesFromRedeclaring() || m_propSym->isPrivate())) { always_assert(m_symOwner); TypePtr t(m_propSym->getType()); if (t && t->is(Type::KindOfVariant)) { // only check property if we could possibly do some work ret = t; } else { if (coerce && type->is(Type::KindOfAutoSequence) && (!t || t->is(Type::KindOfVoid) || t->is(Type::KindOfSome) || t->is(Type::KindOfArray))) { type = Type::Array; } assert(getScope()->is(BlockScope::FunctionScope)); GET_LOCK(m_symOwner); ret = m_symOwner->checkProperty(getScope(), m_propSym, type, coerce, ar); } always_assert(m_object->getActualType() && m_object->getActualType()->isSpecificObject()); m_valid = true; return ret; } else { m_actualType = Type::Variant; return m_actualType; } }
ExpressionPtr FunctionCall::inliner(AnalysisResultConstPtr ar, ExpressionPtr obj, std::string localThis) { FunctionScopePtr fs = getFunctionScope(); if (m_noInline || !fs || fs == m_funcScope || !m_funcScope->getStmt()) { return ExpressionPtr(); } BlockScope::s_jobStateMutex.lock(); if (m_funcScope->getMark() == BlockScope::MarkProcessing) { fs->setForceRerun(true); BlockScope::s_jobStateMutex.unlock(); return ExpressionPtr(); } ReadLock lock(m_funcScope->getInlineMutex()); BlockScope::s_jobStateMutex.unlock(); if (!m_funcScope->getInlineAsExpr()) { return ExpressionPtr(); } if (m_funcScope->getInlineSameContext() && m_funcScope->getContainingClass() && m_funcScope->getContainingClass() != getClassScope()) { /* The function contains a context sensitive construct such as call_user_func (context sensitive because it could call array('parent', 'foo')) so its not safe to inline it into a different context. */ return ExpressionPtr(); } MethodStatementPtr m (dynamic_pointer_cast<MethodStatement>(m_funcScope->getStmt())); VariableTablePtr vt = fs->getVariables(); int nAct = m_params ? m_params->getCount() : 0; int nMax = m_funcScope->getMaxParamCount(); if (nAct < m_funcScope->getMinParamCount() || !m->getStmts()) { return ExpressionPtr(); } InlineCloneInfo info(m_funcScope); info.elist = ExpressionListPtr(new ExpressionList( getScope(), getLocation(), ExpressionList::ListKindWrapped)); std::ostringstream oss; oss << fs->nextInlineIndex() << "_" << m_name << "_"; std::string prefix = oss.str(); if (obj) { info.callWithThis = true; if (!obj->isThis()) { SimpleVariablePtr var (new SimpleVariable(getScope(), obj->getLocation(), prefix + "this")); var->updateSymbol(SimpleVariablePtr()); var->getSymbol()->setHidden(); var->getSymbol()->setUsed(); var->getSymbol()->setReferenced(); AssignmentExpressionPtr ae (new AssignmentExpression(getScope(), obj->getLocation(), var, obj, false)); info.elist->addElement(ae); info.sepm[var->getName()] = var; info.localThis = var->getName(); } } else { if (m_classScope) { if (!m_funcScope->isStatic()) { ClassScopeRawPtr oCls = getOriginalClass(); FunctionScopeRawPtr oFunc = getOriginalFunction(); if (oCls && !oFunc->isStatic() && (oCls == m_classScope || oCls->derivesFrom(ar, m_className, true, false))) { info.callWithThis = true; info.localThis = localThis; } } if (!isSelf() && !isParent() && !isStatic()) { info.staticClass = m_className; } } } ExpressionListPtr plist = m->getParams(); int i; for (i = 0; i < nMax || i < nAct; i++) { ParameterExpressionPtr param (i < nMax ? dynamic_pointer_cast<ParameterExpression>((*plist)[i]) : ParameterExpressionPtr()); ExpressionPtr arg = i < nAct ? (*m_params)[i] : Clone(param->defaultValue(), getScope()); SimpleVariablePtr var (new SimpleVariable(getScope(), (i < nAct ? arg.get() : this)->getLocation(), prefix + (param ? param->getName() : lexical_cast<string>(i)))); var->updateSymbol(SimpleVariablePtr()); var->getSymbol()->setHidden(); var->getSymbol()->setUsed(); var->getSymbol()->setReferenced(); bool ref = (i < nMax && m_funcScope->isRefParam(i)) || arg->hasContext(RefParameter); arg->clearContext(RefParameter); AssignmentExpressionPtr ae (new AssignmentExpression(getScope(), arg->getLocation(), var, arg, ref)); info.elist->addElement(ae); if (i < nAct && (ref || !arg->isScalar())) { info.sepm[var->getName()] = var; } } if (cloneStmtsForInline(info, m->getStmts(), prefix, ar, getFunctionScope()) <= 0) { info.elist->addElement(makeConstant(ar, "null")); } if (info.sepm.size()) { ExpressionListPtr unset_list (new ExpressionList(getScope(), getLocation())); for (StringToExpressionPtrMap::iterator it = info.sepm.begin(), end = info.sepm.end(); it != end; ++it) { ExpressionPtr var = it->second->clone(); var->clearContext((Context)(unsigned)-1); unset_list->addElement(var); } ExpressionPtr unset( new UnaryOpExpression(getScope(), getLocation(), unset_list, T_UNSET, true)); i = info.elist->getCount(); ExpressionPtr ret = (*info.elist)[--i]; if (ret->isScalar()) { info.elist->insertElement(unset, i); } else { ExpressionListPtr result_list (new ExpressionList(getScope(), getLocation(), ExpressionList::ListKindLeft)); if (ret->hasContext(LValue)) { result_list->setContext(LValue); result_list->setContext(ReturnContext); } result_list->addElement(ret); result_list->addElement(unset); (*info.elist)[i] = result_list; } } recomputeEffects(); return replaceValue(info.elist); }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), false); if (!m_property->is(Expression::KindOfScalarExpression)) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicProperty, self); } m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); string name = exp->getString(); ASSERT(!name.empty()); m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(objectType->getName()); } else { // what ->property has told us cls = ar->findClass(name, AnalysisResult::PropertyName); if (cls) { m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()), false); } } if (!cls) { if (m_context & (LValue | RefValue)) { ar->forceClassVariants(name); } return Type::Variant; } const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" : hasContext(ExistContext) ? "__isset" : hasContext(UnsetContext) ? "__unset" : "__get"; if (!cls->implementsAccessor(ar, accessorName)) clearEffect(AccessorEffect); // resolved to this class int present = 0; if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = ar->getFunctionScope(); if (func->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } TypePtr ret; if (!cls->derivesFromRedeclaring()) { // Have to use dynamic. ret = cls->checkProperty(name, type, coerce, ar, self, present); // Private only valid if in the defining class if (present && (getOriginalScope(ar) == cls || !(present & VariableTable::VariablePrivate))) { m_valid = true; m_static = present & VariableTable::VariableStatic; if (m_static) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_class = cls; } } // get() will return Variant if (!m_valid || !m_object->getType()->isSpecificObject()) { m_actualType = Type::Variant; return m_actualType; } clearEffect(AccessorEffect); if (ar->getPhase() == AnalysisResult::LastInference) { if (!(m_context & ObjectContext)) { m_object->clearContext(Expression::LValue); } setContext(Expression::NoLValueWrapper); } return ret; }
TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar, TypePtr type, bool coerce, FunctionScopePtr func, bool arrayParams) { #ifdef HPHP_DETAILED_TYPE_INF_ASSERT assert(func->hasUser(getScope(), BlockScope::UseKindCaller)); #endif /* HPHP_DETAILED_TYPE_INF_ASSERT */ ConstructPtr self = shared_from_this(); TypePtr frt; { TRY_LOCK(func); func->getInferTypesMutex().assertOwnedBySelf(); assert(!func->inVisitScopes() || getScope() == func); frt = func->getReturnType(); } // fix return type for generators and async functions here, keep the // infered return type in function scope to allow further optimizations if (func->isGenerator()) { frt = Type::GetType(Type::KindOfObject, "Continuation"); } else if (func->isAsync()) { frt = Type::GetType(Type::KindOfObject, "WaitHandle"); } if (!frt) { m_voidReturn = true; setActualType(TypePtr()); if (!isUnused() && !type->is(Type::KindOfAny)) { if (!hasContext(ReturnContext) && !func->isFirstPass() && !func->isAbstract()) { if (Option::WholeProgram || !func->getContainingClass() || func->isStatic() || func->isFinal() || func->isPrivate()) { Compiler::Error(Compiler::UseVoidReturn, self); } } if (!Type::IsMappedToVariant(type)) { setExpectedType(type); } m_voidWrapper = true; } } else { m_voidReturn = false; m_voidWrapper = false; type = checkTypesImpl(ar, type, frt, coerce); assert(m_actualType); } if (arrayParams) { m_extraArg = 0; (*m_params)[0]->inferAndCheck(ar, Type::Array, false); } else { m_extraArg = func->inferParamTypes(ar, self, m_params, m_valid); } m_variableArgument = func->isVariableArgument(); if (m_valid) { m_implementedType.reset(); } else { m_implementedType = Type::Variant; } assert(type); return type; }
void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) { m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name); m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name); VariableTablePtr variables = getScope()->getVariables(); if (m_superGlobal) { variables->setAttribute(VariableTable::NeedGlobalPointer); } else if (m_name == "GLOBALS") { m_globals = true; } else { m_sym = variables->addDeclaredSymbol( m_name, Option::OutputHHBC ? shared_from_this() : ConstructPtr()); } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (FunctionScopePtr func = getFunctionScope()) { if (m_name == "this" && (func->inPseudoMain() || getClassScope())) { func->setContainsThis(); m_this = true; if (!hasContext(ObjectContext)) { bool unset = hasAllContext(UnsetContext | LValue); func->setContainsBareThis( true, hasAnyContext(RefValue | RefAssignmentLHS) || m_sym->isRefClosureVar() || unset); if (variables->getAttribute(VariableTable::ContainsDynamicVariable)) { ClassScopePtr cls = getClassScope(); TypePtr t = !cls || cls->isRedeclaring() ? Type::Variant : Type::CreateObjectType(cls->getName()); variables->add(m_sym, t, true, ar, shared_from_this(), getScope()->getModifiers()); } } } if (m_sym && !(m_context & AssignmentLHS) && !((m_context & UnsetContext) && (m_context & LValue))) { m_sym->setUsed(); } } } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) { if (m_sym) { if (!m_sym->isSystem() && !(getContext() & (LValue|RefValue|RefParameter|UnsetContext|ExistContext)) && m_sym->getDeclaration().get() == this && !variables->getAttribute(VariableTable::ContainsLDynamicVariable) && !getScope()->is(BlockScope::ClassScope)) { if (getScope()->inPseudoMain()) { Compiler::Error(Compiler::UseUndeclaredGlobalVariable, shared_from_this()); } else if (!m_sym->isClosureVar()) { Compiler::Error(Compiler::UseUndeclaredVariable, shared_from_this()); } } // check function parameter that can occur in lval context if (m_sym->isParameter() && m_context & (LValue | RefValue | DeepReference | UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) { m_sym->setLvalParam(); } } if (m_superGlobal || m_name == "GLOBALS") { FunctionScopePtr func = getFunctionScope(); if (func) func->setNeedsCheckMem(); } } }
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) { if (m_class) { m_class->analyzeProgram(ar); setDynamicByIdentifier(ar, m_name); } else { if (m_className.empty()) { addUserFunction(ar, m_name); } else if (m_className != "parent") { addUserClass(ar, m_className); } else { m_parentClass = true; } } if (ar->getPhase() == AnalysisResult::AnalyzeInclude) { CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude); ConstructPtr self = shared_from_this(); // We need to know the name of the constant so that we can associate it // with this file before we do type inference. if (!m_class && m_className.empty() && m_type == DefineFunction) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ar->getFileScope()->declareConstant(ar, varName); } } // handling define("CONSTANT", ...); if (m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { constants->add(varName, NEW_TYPE(Some), value, ar, self); if (name->hasHphpNote("Dynamic")) { constants->setDynamic(ar, varName); } } } } } } if (m_type == UnserializeFunction) { ar->forceClassVariants(); } } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { // Look up the corresponding FunctionScope and ClassScope // for this function call { FunctionScopePtr func; ClassScopePtr cls; if (!m_class && m_className.empty()) { func = ar->findFunction(m_name); } else { cls = ar->resolveClass(m_className); if (cls) { if (m_name == "__construct") { func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } } } if (func && !func->isRedeclaring()) { if (m_funcScope != func) { m_funcScope = func; Construct::recomputeEffects(); } } if (cls && !cls->isRedeclaring()) m_classScope = cls; } // check for dynamic constant and volatile function/class if (!m_class && m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() >= 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); if (!constants->isPresent(symbol)) { // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); if (block) { // found the constant constants = block->getConstants(); // set to be dynamic constants->setDynamic(ar, symbol); } } break; } case FunctionExistsFunction: { FunctionScopePtr func = ar->findFunction(Util::toLower(symbol)); if (func && func->isUserFunction()) { func->setVolatile(); } break; } case InterfaceExistsFunction: case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isUserClass()) { cls->setVolatile(); } break; } default: ASSERT(false); } } } } } if (m_params) { if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (m_funcScope) { ExpressionList ¶ms = *m_params; int mpc = m_funcScope->getMaxParamCount(); for (int i = params.getCount(); i--; ) { ExpressionPtr p = params[i]; if (i < mpc ? m_funcScope->isRefParam(i) : m_funcScope->isReferenceVariableArgument()) { p->setContext(Expression::RefValue); } else if (!(p->getContext() & Expression::RefParameter)) { p->clearContext(Expression::RefValue); } } } else { FunctionScopePtr func = ar->findFunction(m_name); if (func && func->isRedeclaring()) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); if (info) { for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } } else { m_params->markParams(false); } } } m_params->analyzeProgram(ar); } }
bool ClassScope::NeedStaticArray(ClassScopePtr cls, FunctionScopePtr func) { return cls && cls->getAttribute(NotFinal) && !func->isPrivate(); }
ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) { if (m_class) ar->preOptimize(m_class); ar->preOptimize(m_nameExp); ar->preOptimize(m_params); if (ar->getPhase() != AnalysisResult::SecondPreOptimize) { return ExpressionPtr(); } // optimize away various "exists" functions, this may trigger // dead code elimination and improve type-inference. if (!m_class && m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() == 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); // system constant if (constants->isPresent(symbol)) { return CONSTANT("true"); } // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); // not found (i.e., undefined) if (!block) { if (symbol.find("::") == std::string::npos) { return CONSTANT("false"); } else { // e.g., defined("self::ZERO") return ExpressionPtr(); } } constants = block->getConstants(); // already set to be dynamic if (constants->isDynamic(symbol)) return ExpressionPtr(); ConstructPtr decl = constants->getValue(symbol); ExpressionPtr constValue = dynamic_pointer_cast<Expression>(decl); if (constValue->isScalar()) { return CONSTANT("true"); } else { return ExpressionPtr(); } break; } case FunctionExistsFunction: { const std::string &lname = Util::toLower(symbol); if (Option::DynamicInvokeFunctions.find(lname) == Option::DynamicInvokeFunctions.end()) { FunctionScopePtr func = ar->findFunction(lname); if (!func) { return CONSTANT("false"); } else if (!func->isVolatile()) { return CONSTANT("true"); } } break; } case InterfaceExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || !cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } default: ASSERT(false); } } } } return ExpressionPtr(); }
void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p, bool sep) { while (*p) { // Parse name const char *cname = *p++; // Parse parent const char *cparent = *p++; if (!cparent) cparent = ""; // Parse list of interfaces vector<string> ifaces; while (*p) ifaces.push_back(*p++); p++; // Parse methods FunctionScopePtrVec methods; while (*p) { FunctionScopePtr fs = ParseExtFunction(ar, p); if (sep) { fs->setSepExtension(); } int flags = (int)(int64)(*p++); if (flags & ClassInfo::IsAbstract) { fs->addModifier(T_ABSTRACT); } int vismod = 0; if (flags & ClassInfo::IsProtected) { vismod = T_PROTECTED; } else if (flags & ClassInfo::IsPrivate) { vismod = T_PRIVATE; } fs->addModifier(vismod); if (flags & ClassInfo::IsStatic) { fs->addModifier(T_STATIC); } methods.push_back(fs); } if (cparent && *cparent && (ifaces.empty() || ifaces[0] != cparent)) { ifaces.insert(ifaces.begin(), cparent); } ClassScopePtr cl(new ClassScope(ar, cname, cparent, ifaces, methods)); p++; // Parse properties while (*p) { *p++; // TODO, support visibility const char *name = *p++; TypePtr type = ParseType(p); cl->getVariables()->add(name, type, false, ar, ExpressionPtr(), ModifierExpressionPtr()); } p++; // Parse consts while (*p) { const char *name = *p++; TypePtr type = ParseType(p); cl->getConstants()->add(name, type, ExpressionPtr(), ar, ConstructPtr()); } p++; int flags = (int)(int64)(*p++); cl->setClassInfoAttribute(flags); if (flags & ClassInfo::HasDocComment) { cl->setDocComment(*p++); } cl->setSystem(); if (sep) { cl->setSepExtension(); } s_classes[cl->getName()] = cl; } }
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); // handling define("CONSTANT", ...); if (!m_class && m_className.empty()) { if (m_type == DefineFunction && m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; TypePtr varType = value->inferAndCheck(ar, NEW_TYPE(Some), false); ar->getDependencyGraph()-> addParent(DependencyGraph::KindOfConstant, ar->getName(), varName, self); ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { if (value && !value->isScalar()) { constants->setDynamic(ar, varName); varType = Type::Variant; } if (constants->isDynamic(varName)) { m_dynamicConstant = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } else { constants->setType(ar, varName, varType, true); } // in case the old 'value' has been optimized constants->setValue(ar, varName, value); } return checkTypesImpl(ar, type, Type::Boolean, coerce); } } if (varName.empty() && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::BadDefine, self); } } else if (m_type == ExtractFunction) { ar->getScope()->getVariables()->forceVariants(ar); } } FunctionScopePtr func; // avoid raising both MissingObjectContext and UnknownFunction bool errorFlagged = false; if (!m_class && m_className.empty()) { func = ar->findFunction(m_name); } else { ClassScopePtr cls = ar->resolveClass(m_className); if (cls && cls->isVolatile()) { ar->getScope()->getVariables() ->setAttribute(VariableTable::NeedGlobalPointer); } if (!cls || cls->isRedeclaring()) { if (cls) { m_redeclaredClass = true; } if (!cls && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownClass, self); } if (m_params) { m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, Type::Variant, coerce); } m_derivedFromRedeclaring = cls->derivesFromRedeclaring(); m_validClass = true; if (m_name == "__construct") { // if the class is known, php will try to identify class-name ctor func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } if (func && !func->isStatic()) { ClassScopePtr clsThis = ar->getClassScope(); FunctionScopePtr funcThis = ar->getFunctionScope(); if (!clsThis || (clsThis->getName() != m_className && !clsThis->derivesFrom(ar, m_className)) || funcThis->isStatic()) { // set the method static to avoid "unknown method" runtime exception if (Option::StaticMethodAutoFix && !func->containsThis()) { func->setStaticMethodAutoFixed(); } if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); errorFlagged = true; } func.reset(); } } } if (!func || func->isRedeclaring()) { if (func) { m_redeclared = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } if (!func && !errorFlagged && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownFunction, self); } if (m_params) { if (func) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); ASSERT(info); for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, Type::Variant, coerce); } m_builtinFunction = !func->isUserFunction(); if (m_redeclared) { if (m_params) { m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, type, coerce); } CHECK_HOOK(beforeSimpleFunctionCallCheck); m_valid = true; type = checkParamsAndReturn(ar, type, coerce, func); if (!m_valid && m_params) { m_params->markParams(false); } CHECK_HOOK(afterSimpleFunctionCallCheck); return type; }
bool BuiltinSymbols::Load(AnalysisResultPtr ar, bool extOnly /* = false */) { if (Loaded) return true; Loaded = true; // Build function scopes for some of the runtime helper functions // declared in "runtime/base/builtin_functions.h" const char **helper = HelperFunctions; while (*helper) { FunctionScopePtr f = ParseHelperFunction(ar, helper); ASSERT(!s_helperFunctions[f->getName()]); s_helperFunctions[f->getName()] = f; } // load extension functions first, so system/classes may call them ParseExtFunctions(ar, ExtensionFunctions, false); // parse all PHP files under system/classes if (!extOnly) { ar = AnalysisResultPtr(new AnalysisResult()); ar->loadBuiltinFunctions(); for (const char **cls = SystemClasses; *cls; cls++) { string phpBaseName = "/system/classes/"; phpBaseName += *cls; phpBaseName += ".php"; string phpFileName = Option::GetSystemRoot() + phpBaseName; const char *baseName = s_strings.add(phpBaseName.c_str()); const char *fileName = s_strings.add(phpFileName.c_str()); try { Scanner scanner(new ylmm::basic_buffer(fileName), true, false); ParserPtr parser(new Parser(scanner, baseName, 0, ar)); if (parser->parse()) { Logger::Error("Unable to parse file %s: %s", fileName, parser->getMessage().c_str()); ASSERT(false); } } catch (std::runtime_error) { Logger::Error("Unable to open file %s", fileName); } } ar->analyzeProgram(true); ar->inferTypes(); const StringToFileScopePtrMap &files = ar->getAllFiles(); for (StringToFileScopePtrMap::const_iterator iterFile = files.begin(); iterFile != files.end(); iterFile++) { const StringToClassScopePtrVecMap &classes = iterFile->second->getClasses(); for (StringToClassScopePtrVecMap::const_iterator iter = classes.begin(); iter != classes.end(); ++iter) { ASSERT(iter->second.size() == 1); iter->second[0]->setSystem(); ASSERT(!s_classes[iter->first]); s_classes[iter->first] = iter->second[0]; } } // parse globals/variables.php and globals/constants.php NoSuperGlobals = true; s_variables = LoadGlobalSymbols("symbols.php")->getVariables(); const FileScopePtrVec &fileScopes = LoadGlobalSymbols("constants.php")->getAllFilesVector(); if (!fileScopes.empty()) { s_constants = fileScopes[0]->getConstants(); } else { AnalysisResult ar2; s_constants = ConstantTablePtr(new ConstantTable(ar2)); } NoSuperGlobals = false; } else { AnalysisResult ar2; s_variables = VariableTablePtr(new VariableTable(ar2)); s_constants = ConstantTablePtr(new ConstantTable(ar2)); NoSuperGlobals = true; } // load extension constants, classes and dynamics ParseExtConsts(ar, ExtensionConsts, false); ParseExtClasses(ar, ExtensionClasses, false); ParseExtDynamics(ar, ExtensionDeclaredDynamic, false); for (unsigned int i = 0; i < Option::SepExtensions.size(); i++) { Option::SepExtensionOptions &options = Option::SepExtensions[i]; string soname = options.soname; if (soname.empty()) { soname = string("lib") + options.name + ".so"; } if (!options.lib_path.empty()) { soname = options.lib_path + "/" + soname; } if (!LoadSepExtensionSymbols(ar, options.name, soname)) { return false; } } return true; }
void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator &cg, AnalysisResultPtr ar) { if (!m_class && m_className.empty()) { switch (m_type) { case ExtractFunction: cg.printf("extract(variables, "); FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false); cg.printf(")"); return; case CompactFunction: cg.printf("compact(variables, "); FunctionScope::outputCPPArguments(m_params, cg, ar, -1, true); cg.printf(")"); return; default: break; } } bool volatileCheck = false; ClassScopePtr cls; if (!m_className.empty()) { cls = ar->findClass(m_className); if (cls && !ar->checkClassPresent(m_origClassName)) { volatileCheck = true; cls->outputVolatileCheckBegin(cg, ar, cls->getOriginalName()); } } if (m_valid) { bool tooManyArgs = (m_params && m_params->outputCPPTooManyArgsPre(cg, ar, m_name)); if (!m_className.empty()) { cg.printf("%s%s::", Option::ClassPrefix, m_className.c_str()); if (m_name == "__construct" && cls) { FunctionScopePtr func = cls->findConstructor(ar, true); cg.printf("%s%s(", Option::MethodPrefix, func->getName().c_str()); } else { cg.printf("%s%s(", Option::MethodPrefix, m_name.c_str()); } } else { int paramCount = m_params ? m_params->getCount() : 0; if (m_name == "get_class" && ar->getClassScope() && paramCount == 0) { cg.printf("(\"%s\"", ar->getClassScope()->getOriginalName()); } else if (m_name == "get_parent_class" && ar->getClassScope() && paramCount == 0) { const std::string parentClass = ar->getClassScope()->getParent(); if (!parentClass.empty()) { cg.printf("(\"%s\"", ar->getClassScope()->getParent().c_str()); } else { cg.printf("(false"); } } else { if (m_noPrefix) { cg.printf("%s(", m_name.c_str()); } else { cg.printf("%s%s(", m_builtinFunction ? Option::BuiltinFunctionPrefix : Option::FunctionPrefix, m_name.c_str()); } } } FunctionScope::outputCPPArguments(m_params, cg, ar, m_extraArg, m_variableArgument, m_argArrayId); cg.printf(")"); if (tooManyArgs) { m_params->outputCPPTooManyArgsPost(cg, ar, m_voidReturn); } } else { if (!m_class && m_className.empty()) { if (m_redeclared && !m_dynamicInvoke) { if (canInvokeFewArgs()) { cg.printf("%s->%s%s_few_args(", cg.getGlobals(ar), Option::InvokePrefix, m_name.c_str()); int left = Option::InvokeFewArgsCount; if (m_params && m_params->getCount()) { left -= m_params->getCount(); cg.printf("%d, ", m_params->getCount()); FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false); } else { cg.printf("0"); } for (int i = 0; i < left; i++) { cg.printf(", null_variant"); } cg.printf(")"); return; } else { cg.printf("%s->%s%s(", cg.getGlobals(ar), Option::InvokePrefix, m_name.c_str()); } } else { cg.printf("invoke(\"%s\", ", m_name.c_str()); } } else { bool inObj = m_parentClass && ar->getClassScope() && !dynamic_pointer_cast<FunctionScope>(ar->getScope())->isStatic(); if (m_redeclaredClass) { if (inObj) { // parent is redeclared cg.printf("parent->%sinvoke(\"%s\",", Option::ObjectPrefix, m_name.c_str()); } else { cg.printf("%s->%s%s->%sinvoke(\"%s\", \"%s\",", cg.getGlobals(ar), Option::ClassStaticsObjectPrefix, m_className.c_str(), Option::ObjectStaticPrefix, m_className.c_str(), m_name.c_str()); } } else if (m_validClass) { if (inObj) { cg.printf("%s%s::%sinvoke(\"%s\",", Option::ClassPrefix, m_className.c_str(), Option::ObjectPrefix, m_name.c_str()); } else { cg.printf("%s%s::%sinvoke(\"%s\", \"%s\",", Option::ClassPrefix, m_className.c_str(), Option::ObjectStaticPrefix, m_className.c_str(), m_name.c_str()); } } else { if (m_class) { cg.printf("INVOKE_STATIC_METHOD(toString("); if (m_class->is(KindOfScalarExpression)) { ASSERT(strcasecmp(dynamic_pointer_cast<ScalarExpression>(m_class)-> getString().c_str(), "static") == 0); cg.printf("\"static\""); } else { m_class->outputCPP(cg, ar); } cg.printf("), \"%s\",", m_name.c_str()); } else { cg.printf("invoke_static_method(\"%s\", \"%s\",", m_className.c_str(), m_name.c_str()); } } } if ((!m_params) || (m_params->getCount() == 0)) { cg.printf("Array()"); } else { FunctionScope::outputCPPArguments(m_params, cg, ar, -1, false); } bool needHash = true; if (!m_class && m_className.empty()) { needHash = !(m_redeclared && !m_dynamicInvoke); } else { needHash = m_validClass || m_redeclaredClass; } if (!needHash) { cg.printf(")"); } else { cg.printf(", 0x%.16lXLL)", hash_string_i(m_name.data(), m_name.size())); } } if (volatileCheck) { cls->outputVolatileCheckEnd(cg); } }
void StatementList::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr func = ar->getFunctionScope(); bool inPseudoMain = func && func->inPseudoMain(); std::vector<bool> isDeclaration; if (inPseudoMain) { // We need these declarations to go first, because PHP allows top level // function and class declarations to appear after usage. for (unsigned int i = 0; i < m_stmts.size(); i++) { StatementPtr stmt = m_stmts[i]; bool isDecl = false; if (stmt->is(Statement::KindOfFunctionStatement)) { isDecl = true; } else if (stmt->is(Statement::KindOfClassStatement) || stmt->is(Statement::KindOfInterfaceStatement)) { ClassScopePtr cls = (dynamic_pointer_cast<InterfaceStatement>(stmt))->getClassScope(); isDecl = cls->isBaseClass() || !cls->isVolatile(); } if (isDecl) stmt->outputCPP(cg,ar); isDeclaration.push_back(isDecl); } } for (unsigned int i = 0; i < m_stmts.size(); i++) { StatementPtr stmt = m_stmts[i]; if (stmt->is(Statement::KindOfClassStatement)) { if (!inPseudoMain || !isDeclaration[i]) stmt->outputCPP(cg, ar); } else if (!(stmt->is(Statement::KindOfFunctionStatement) || stmt->is(Statement::KindOfInterfaceStatement)) || (!inPseudoMain || !isDeclaration[i])) { stmt->outputCPP(cg, ar); if (stmt->is(Statement::KindOfMethodStatement)) { MethodStatementPtr methodStmt = dynamic_pointer_cast<MethodStatement>(stmt); std::string methodName = methodStmt->getName(); if (methodName == "__get") { FunctionScopePtr funcScope = methodStmt->getFunctionScope(); std::string name = funcScope->getName(); funcScope->setName("__lval"); methodStmt->setName("__lval"); methodStmt->outputCPP(cg, ar); funcScope->setName(name); methodStmt->setName("__get"); } else if (methodName == "offsetget") { ClassScopePtr cls = ar->getClassScope(); std::string arrayAccess("arrayaccess"); if (cls->derivesFrom(ar, arrayAccess, false, false)) { FunctionScopePtr funcScope = methodStmt->getFunctionScope(); std::string name = funcScope->getName(); funcScope->setName("__offsetget_lval"); methodStmt->setName("__offsetget_lval"); methodStmt->outputCPP(cg, ar); funcScope->setName(name); methodStmt->setName("offsetget"); } } } } } }
void UnaryOpExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if ((m_op == T_INC || m_op == T_DEC) && outputCPPImplOpEqual(cg, ar)) { return; } if (m_op == T_ARRAY && (getContext() & (RefValue|LValue)) == 0 && !ar->getInsideScalarArray()) { int id = -1; int hash = -1; int index = -1; if (m_exp) { ExpressionListPtr pairs = dynamic_pointer_cast<ExpressionList>(m_exp); Variant v; if (pairs && pairs->isScalarArrayPairs() && pairs->getScalarValue(v)) { id = ar->registerScalarArray(m_exp, hash, index); } } else { id = ar->registerScalarArray(m_exp, hash, index); // empty array } if (id != -1) { ar->outputCPPScalarArrayId(cg, id, hash, index); return; } } if ((m_op == T_ISSET || m_op == T_EMPTY || m_op == T_UNSET) && m_exp) { if (m_exp->is(Expression::KindOfExpressionList)) { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (exps->getListKind() == ExpressionList::ListKindParam) { int count = exps->getCount(); if (count > 1) { cg_printf("("); } for (int i = 0; i < count; i++) { if (m_op == T_UNSET) { if (i > 0) cg_printf(", "); (*exps)[i]->outputCPPUnset(cg, ar); } else { if (i > 0) cg_printf(" && "); (*exps)[i]->outputCPPExistTest(cg, ar, m_op); } } if (exps->getCount() > 1) { cg_printf(")"); } return; } } if (m_op == T_UNSET) { m_exp->outputCPPUnset(cg, ar); } else { m_exp->outputCPPExistTest(cg, ar, m_op); } return; } if (m_front) { switch (m_op) { case T_CLONE: cg_printf("f_clone("); break; case T_INC: cg_printf("++"); break; case T_DEC: cg_printf("--"); break; case '+': cg_printf("+"); break; case '-': cg_printf("negate("); break; case '!': cg_printf("!("); break; case '~': cg_printf("~"); break; case '(': cg_printf("("); break; case T_INT_CAST: cg_printf("("); break; case T_DOUBLE_CAST: cg_printf("("); break; case T_STRING_CAST: cg_printf("("); break; case T_ARRAY_CAST: cg_printf("("); break; case T_OBJECT_CAST: cg_printf("("); break; case T_BOOL_CAST: cg_printf("("); break; case T_UNSET_CAST: if (m_exp->hasCPPTemp()) { cg_printf("(id("); } else { cg_printf("("); } break; case T_EXIT: cg_printf("f_exit("); break; case T_ARRAY: cg_printf("Array("); break; case T_PRINT: cg_printf("print("); break; case T_EVAL: if (Option::EnableEval > Option::NoEval) { bool instance; if (ar->getClassScope()) { FunctionScopePtr fs = ar->getFunctionScope(); instance = fs && !fs->isStatic(); } else { instance = false; } cg_printf("eval(%s, Object(%s), ", ar->getScope()->inPseudoMain() ? "get_variable_table()" : "variables", instance ? "this" : ""); } else { cg_printf("f_eval("); } break; case '@': if (m_silencer >= 0) { cg_printf("(%s%d.enable(),%s%d.disable(", Option::SilencerPrefix, m_silencer, Option::SilencerPrefix, m_silencer); } break; case T_FILE: cg_printf("get_source_filename(\"%s\")", getLocation()->file); break; break; default: ASSERT(false); } } if (m_exp) { switch (m_op) { case '+': case '-': if (m_exp->getActualType() && (m_exp->getActualType()->is(Type::KindOfString) || m_exp->getActualType()->is(Type::KindOfArray))) { cg_printf("(Variant)("); m_exp->outputCPP(cg, ar); cg_printf(")"); } else { m_exp->outputCPP(cg, ar); } break; case '@': // Void needs to return something to silenceDec if (!m_exp->hasCPPTemp() && !m_exp->getActualType()) { cg_printf("("); m_exp->outputCPP(cg, ar); cg_printf(",null)"); } else { m_exp->outputCPP(cg, ar); } break; default: m_exp->outputCPP(cg, ar); break; } } if (m_front) { switch (m_op) { case T_ARRAY: { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (!exps) { cg_printf("ArrayData::Create()"); } cg_printf(")"); } break; case T_UNSET_CAST: if (m_exp->hasCPPTemp()) { cg_printf("),null"); } else { cg_printf(",null"); } case T_CLONE: case '!': case '(': case '-': case T_INT_CAST: case T_DOUBLE_CAST: case T_STRING_CAST: case T_ARRAY_CAST: case T_OBJECT_CAST: case T_BOOL_CAST: case T_EXIT: case T_PRINT: case T_EVAL: case T_INCLUDE: case T_INCLUDE_ONCE: case T_REQUIRE: case T_REQUIRE_ONCE: cg_printf(")"); break; case '@': if (m_silencer >= 0) { cg_printf("))"); } break; default: break; } } else { switch (m_op) { case T_INC: cg_printf("++"); break; case T_DEC: cg_printf("--"); break; default: ASSERT(false); } } }