void FunctionScope::setReturnType(AnalysisResultPtr ar, TypePtr type) { // no change can be made to virtual function's prototype if (m_overriding) return; if (m_returnType) { type = Type::Coerce(ar, m_returnType, type); if (type && !Type::SameType(m_returnType, type)) { ar->incNewlyInferred(); if (!ar->isFirstPass()) { Logger::Verbose("Corrected function return type %s -> %s", m_returnType->toString().c_str(), type->toString().c_str()); } } } if (!type->getName().empty()) { FileScopePtr fs = getFileScope(); if (fs) fs->addClassDependency(ar, type->getName()); } m_returnType = type; }
ExpressionPtr IncludeExpression::postOptimize(AnalysisResultConstPtr ar) { if (!m_include.empty()) { if (!m_depsSet) { analyzeInclude(ar, m_include); m_depsSet = true; } FileScopePtr fs = ar->findFileScope(m_include); if (fs && fs->getPseudoMain()) { if (!Option::KeepStatementsWithNoEffect) { if (ExpressionPtr rep = fs->getEffectiveImpl(ar)) { recomputeEffects(); return replaceValue(rep->clone()); } } if (!Option::OutputHHBC) { m_exp.reset(); } } else { m_include = ""; } } return ExpressionPtr(); }
bool Option::Load(const char *filename) { ASSERT(filename && *filename); try { Scanner scanner(new ylmm::basic_buffer(filename), true, false); Logger::Info("loading options from %s...", filename); AnalysisResultPtr ar(new AnalysisResult()); struct stat sb; if (stat(filename, &sb)) { Logger::Error("Unable to stat file %s", filename); return false; } ParserPtr parser(new Parser(scanner, filename, sb.st_size, ar)); if (parser->parse()) { Logger::Error("Unable to parse file: %s\n%s", filename, parser->getMessage().c_str()); return false; } FileScopePtr file = ar->findFileScope(filename, false); ClassScopePtr cls = file->getClass("hphpoption"); if (!cls) { Logger::Error("Unable to find HPHPOption class in %s", filename); return false; } if (!Load(cls->getVariables())) { return false; } } catch (std::runtime_error) { Logger::Error("Unable to open file %s", filename); return false; } return true; }
void QueryExpression::doRewrites(AnalysisResultPtr ar, FileScopePtr fileScope) { // Clone the query expression for client rewriting later on. auto csqe = static_pointer_cast<QueryExpression>(this->clone()); // Find expressions that capture client state and turn them into // query parameter expression. The original expressions end up in m_queryargs. CaptureExtractor ce; auto qe = static_pointer_cast<QueryExpression>( ce.rewrite(static_pointer_cast<Expression>(shared_from_this()))); // Expose the argument (capture) expressions to static analysis rather than // the original clause list (it is still available in m_originalExpressions). m_expressions = static_pointer_cast<ExpressionList>(m_expressions->clone()); m_expressions->clearElements(); auto queryArgs = static_pointer_cast<ExpressionList>(m_expressions->clone()); for (auto e : ce.getCapturedExpressions()) { queryArgs->addElement(e); } assert(queryArgs->getCount() > 0); //syntax requires an initial from clause m_expressions->addElement(queryArgs); // Rewrite select clauses so that they become tuples of columns when // sent to the query provider. ServerSideSelectRewriter se; se.rewriteQuery(qe); // Serialize the rewritten query expression so that it can be sent to // the query provider as a string which it can unserialize and turn // into a query (unless already cached). std::ostringstream serialized; CodeGenerator cg(&serialized, CodeGenerator::Output::CodeModel, &fileScope->getName()); cg.setAstClassPrefix("Code"); qe->outputCodeModel(cg); std::string s(serialized.str().c_str(), serialized.str().length()); m_querystr = makeStaticString(s); // start again with the clone of the original query (not the one // that was rewritten for use by the query provider) and rewrite // it into a closure expression that is used by the query provider // to execute code in the context to the client, not the server. auto selectClosure = csqe->clientSideRewrite(ar, fileScope); if (selectClosure != nullptr) { m_expressions->addElement(selectClosure); } }
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; } }
void InterfaceStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) { vector<string> bases; if (m_base) m_base->getStrings(bases); for (auto &b : bases) { ar->parseOnDemandByClass(b); } StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this()); 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); } } auto classScope = std::make_shared<ClassScope>( scope, ClassScope::KindOf::Interface, m_originalName, "", bases, m_docComment, stmt, attrs); setBlockScope(classScope); scope->addClass(ar, classScope); classScope->setPersistent(false); if (m_stmt) { for (int i = 0; i < m_stmt->getCount(); i++) { IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>((*m_stmt)[i]); ph->onParseRecur(ar, scope, classScope); } checkArgumentsToPromote(scope, ExpressionListPtr(), T_INTERFACE); } }
void InterfaceStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) { vector<string> bases; if (m_base) m_base->getOriginalStrings(bases); for (auto &b : bases) { ar->parseOnDemandByClass(Util::toLower(b)); } StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this()); 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); } } ClassScopePtr classScope (new ClassScope(ClassScope::KindOfInterface, m_name, "", bases, m_docComment, stmt, attrs)); setBlockScope(classScope); scope->addClass(ar, classScope); if (Option::PersistenceHook) { classScope->setPersistent(Option::PersistenceHook(classScope, scope)); } if (m_stmt) { for (int i = 0; i < m_stmt->getCount(); i++) { IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>((*m_stmt)[i]); ph->onParseRecur(ar, classScope); } checkArgumentsToPromote(ExpressionListPtr(), T_INTERFACE); } }
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 TypedefStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) { scope->addTypeAliasName(name); }
void ClassStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr fs) { ClassScope::KindOf kindOf = ClassScope::KindOfObjectClass; switch (m_type) { case T_CLASS: kindOf = ClassScope::KindOfObjectClass; break; case T_ABSTRACT: kindOf = ClassScope::KindOfAbstractClass; break; case T_FINAL: kindOf = ClassScope::KindOfFinalClass; break; default: ASSERT(false); } vector<string> bases; if (!m_parent.empty()) { bases.push_back(m_parent); } if (m_base) m_base->getStrings(bases); StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this()); ClassScopePtr classScope(new ClassScope(kindOf, m_originalName, m_parent, bases, m_docComment, stmt)); setBlockScope(classScope); if (!fs->addClass(ar, classScope)) { m_ignored = true; return; } if (m_stmt) { bool seenConstruct = false; // flatten continuation StatementList into MethodStatements for (int i = 0; i < m_stmt->getCount(); i++) { StatementListPtr stmts = dynamic_pointer_cast<StatementList>((*m_stmt)[i]); if (stmts) { m_stmt->removeElement(i); for (int j = 0; j < stmts->getCount(); j++) { m_stmt->insertElement((*stmts)[j], i + j); } } } for (int i = 0; i < m_stmt->getCount(); i++) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth && meth->getName() == "__construct") { seenConstruct = true; break; } } for (int i = 0; i < m_stmt->getCount(); i++) { if (!seenConstruct) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth && classScope && meth->getName() == classScope->getName() && !meth->getModifiers()->isStatic()) { // class-name constructor classScope->setAttribute(ClassScope::ClassNameConstructor); } } IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>((*m_stmt)[i]); ph->onParseRecur(ar, classScope); } } }
static bool by_filename(const FileScopePtr &f1, const FileScopePtr &f2) { return f1->getName() < f2->getName(); }
void MethodStatement::analyzeProgram(AnalysisResultPtr ar) { FunctionScopeRawPtr funcScope = getFunctionScope(); if (m_params) { m_params->analyzeProgram(ar); } if (m_stmt) m_stmt->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll) { funcScope->setParamSpecs(ar); if (funcScope->isGenerator()) { MethodStatementRawPtr orig = getOrigGeneratorFunc(); VariableTablePtr variables = funcScope->getVariables(); orig->getFunctionScope()->addUse(funcScope, BlockScope::UseKindClosure); orig->getFunctionScope()->setContainsBareThis( funcScope->containsBareThis(), funcScope->containsRefThis()); orig->getFunctionScope()->setContainsThis(funcScope->containsThis()); if (ExpressionListPtr params = orig->getParams()) { for (int i = 0; i < params->getCount(); ++i) { auto param = dynamic_pointer_cast<ParameterExpression>((*params)[i]); Symbol *gp = variables->addDeclaredSymbol(param->getName(), param); gp->setGeneratorParameter(); if (param->isRef()) { gp->setRefGeneratorParameter(); gp->setReferenced(); } } } if (ClosureExpressionRawPtr closure = orig->getContainingClosure()) { if (ExpressionListPtr cvars = closure->getClosureVariables()) { for (int i = 0; i < cvars->getCount(); ++i) { auto param = dynamic_pointer_cast<ParameterExpression>((*cvars)[i]); Symbol *gp = variables->addDeclaredSymbol( param->getName(), ConstructPtr()); gp->setGeneratorParameter(); if (param->isRef()) { gp->setRefGeneratorParameter(); gp->setReferenced(); } } } } } if (Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) { funcScope->setDynamic(); } // TODO: this may have to expand to a concept of "virtual" functions... if (m_method) { funcScope->disableInline(); if (m_name.length() > 2 && m_name.substr(0,2) == "__") { bool magic = true; int paramCount = 0; if (m_name == "__destruct") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__call") { funcScope->setOverriding(Type::Variant, Type::String, Type::Array); paramCount = 2; } else if (m_name == "__set") { funcScope->setOverriding(Type::Variant, Type::String, Type::Variant); paramCount = 2; } else if (m_name == "__get") { funcScope->setOverriding(Type::Variant, Type::String); paramCount = 1; } else if (m_name == "__isset") { funcScope->setOverriding(Type::Boolean, Type::String); paramCount = 1; } else if (m_name == "__unset") { funcScope->setOverriding(Type::Variant, Type::String); paramCount = 1; } else if (m_name == "__sleep") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__wakeup") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__set_state") { funcScope->setOverriding(Type::Variant, Type::Variant); paramCount = 1; } else if (m_name == "__tostring") { funcScope->setOverriding(Type::String); } else if (m_name == "__clone") { funcScope->setOverriding(Type::Variant); } else { paramCount = -1; if (m_name != "__construct") { magic = false; } } if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) { Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this()); magic = false; } if (magic) funcScope->setMagicMethod(); } // ArrayAccess methods else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") { if (m_name == "offsetexists") { funcScope->setOverriding(Type::Boolean, Type::Variant); } else if (m_name == "offsetget") { funcScope->setOverriding(Type::Variant, Type::Variant); } else if (m_name == "offsetset") { funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant); } else if (m_name == "offsetunset") { funcScope->setOverriding(Type::Variant, Type::Variant); } } } } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) { TypePtr ret = funcScope->getReturnType(); if (ret && ret->isSpecificObject()) { FileScopePtr fs = getFileScope(); if (fs) fs->addClassDependency(ar, ret->getName()); } if (!getFunctionScope()->usesLSB()) { if (StatementPtr orig = getOrigGeneratorFunc()) { orig->getFunctionScope()->clearUsesLSB(); } } } }
void ClassStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr fs) { ClassScope::KindOf kindOf = ClassScope::KindOfObjectClass; switch (m_type) { case T_CLASS: kindOf = ClassScope::KindOfObjectClass; break; case T_ABSTRACT: kindOf = ClassScope::KindOfAbstractClass; break; case T_FINAL: kindOf = ClassScope::KindOfFinalClass; break; case T_TRAIT: kindOf = ClassScope::KindOfTrait; break; default: assert(false); } vector<string> bases; if (!m_originalParent.empty()) { bases.push_back(m_originalParent); } if (m_base) m_base->getOriginalStrings(bases); for (auto &b : bases) { ar->parseOnDemandByClass(Util::toLower(b)); } 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()); ClassScopePtr classScope(new ClassScope(kindOf, m_originalName, m_originalParent, bases, m_docComment, stmt, attrs)); setBlockScope(classScope); if (!fs->addClass(ar, classScope)) { m_ignored = true; return; } if (Option::PersistenceHook) { classScope->setPersistent(Option::PersistenceHook(classScope, fs)); } if (m_stmt) { MethodStatementPtr constructor; // flatten continuation StatementList into MethodStatements for (int i = 0; i < m_stmt->getCount(); i++) { StatementListPtr stmts = dynamic_pointer_cast<StatementList>((*m_stmt)[i]); if (stmts) { m_stmt->removeElement(i); for (int j = 0; j < stmts->getCount(); j++) { m_stmt->insertElement((*stmts)[j], i + j); } } } for (int i = 0; i < m_stmt->getCount(); i++) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth && meth->getName() == "__construct") { constructor = meth; break; } } for (int i = 0; i < m_stmt->getCount(); i++) { if (!constructor) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth && meth->getName() == classScope->getName() && !classScope->isTrait()) { // class-name constructor constructor = meth; classScope->setAttribute(ClassScope::ClassNameConstructor); } } IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>((*m_stmt)[i]); ph->onParseRecur(ar, classScope); } if (constructor && constructor->getModifiers()->isStatic()) { constructor->parseTimeFatal(Compiler::InvalidAttribute, "Constructor %s::%s() cannot be static", classScope->getOriginalName().c_str(), constructor->getOriginalName().c_str()); } } }
void MethodStatement::analyzeProgram(AnalysisResultPtr ar) { FunctionScopeRawPtr funcScope = getFunctionScope(); if (m_params) { m_params->analyzeProgram(ar); } if (m_stmt) m_stmt->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll) { funcScope->setParamSpecs(ar); if (Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) { funcScope->setDynamic(); } // TODO: this may have to expand to a concept of "virtual" functions... if (m_method) { funcScope->disableInline(); if (m_name.length() > 2 && m_name.substr(0,2) == "__") { bool magic = true; int paramCount = 0; if (m_name == "__destruct") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__call") { funcScope->setOverriding(Type::Variant, Type::String, Type::Array); paramCount = 2; } else if (m_name == "__set") { funcScope->setOverriding(Type::Variant, Type::String, Type::Variant); paramCount = 2; } else if (m_name == "__get") { funcScope->setOverriding(Type::Variant, Type::String); paramCount = 1; } else if (m_name == "__isset") { funcScope->setOverriding(Type::Boolean, Type::String); paramCount = 1; } else if (m_name == "__unset") { funcScope->setOverriding(Type::Variant, Type::String); paramCount = 1; } else if (m_name == "__sleep") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__wakeup") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__set_state") { funcScope->setOverriding(Type::Variant, Type::Variant); paramCount = 1; } else if (m_name == "__tostring") { funcScope->setOverriding(Type::String); } else if (m_name == "__clone") { funcScope->setOverriding(Type::Variant); } else { paramCount = -1; if (m_name != "__construct") { magic = false; } } if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) { Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this()); magic = false; } if (magic) funcScope->setMagicMethod(); } // ArrayAccess methods else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") { if (m_name == "offsetexists") { funcScope->setOverriding(Type::Boolean, Type::Variant); } else if (m_name == "offsetget") { funcScope->setOverriding(Type::Variant, Type::Variant); } else if (m_name == "offsetset") { funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant); } else if (m_name == "offsetunset") { funcScope->setOverriding(Type::Variant, Type::Variant); } } } } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) { TypePtr ret = funcScope->getReturnType(); if (ret && ret->isSpecificObject()) { FileScopePtr fs = getFileScope(); if (fs) fs->addClassDependency(ar, ret->getName()); } } }
void ClassStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr fs) { ClassScope::KindOf kindOf = ClassScope::KindOf::ObjectClass; switch (m_type) { case T_CLASS: kindOf = ClassScope::KindOf::ObjectClass; break; case T_ABSTRACT: kindOf = ClassScope::KindOf::AbstractClass; break; case T_STATIC: // Slight hack: see comments in hphp.y kindOf = ClassScope::KindOf::UtilClass; break; case T_FINAL: kindOf = ClassScope::KindOf::FinalClass; break; case T_TRAIT: kindOf = ClassScope::KindOf::Trait; break; case T_ENUM: kindOf = ClassScope::KindOf::Enum; break; default: assert(false); } std::vector<std::string> bases; if (!m_originalParent.empty()) { bases.push_back(m_originalParent); } if (m_base) m_base->getStrings(bases); for (auto &b : bases) { ar->parseOnDemandByClass(b); } std::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()); auto classScope = std::make_shared<ClassScope>( fs, kindOf, m_originalName, m_originalParent, bases, m_docComment, stmt, attrs); setBlockScope(classScope); if (!fs->addClass(ar, classScope)) { m_ignored = true; return; } classScope->setPersistent(false); if (m_stmt) { MethodStatementPtr constructor = nullptr; MethodStatementPtr destructor = nullptr; MethodStatementPtr clone = nullptr; // flatten continuation StatementList into MethodStatements for (int i = 0; i < m_stmt->getCount(); i++) { StatementListPtr stmts = dynamic_pointer_cast<StatementList>((*m_stmt)[i]); if (stmts) { m_stmt->removeElement(i); for (int j = 0; j < stmts->getCount(); j++) { m_stmt->insertElement((*stmts)[j], i + j); } } } for (int i = 0; i < m_stmt->getCount(); i++) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth) { if (meth->isNamed("__construct")) { constructor = meth; continue; } if (meth->isNamed("__destruct")) { destructor = meth; continue; } if (meth->isNamed("__clone")) { clone = meth; continue; } } if (constructor && destructor && clone) { break; } } for (int i = 0; i < m_stmt->getCount(); i++) { if (!constructor) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth && meth->isNamed(classScope->getOriginalName()) && !classScope->isTrait()) { // class-name constructor constructor = meth; classScope->setAttribute(ClassScope::ClassNameConstructor); } } IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>((*m_stmt)[i]); ph->onParseRecur(ar, fs, classScope); } if (constructor && constructor->getModifiers()->isStatic()) { constructor->parseTimeFatal(fs, Compiler::InvalidAttribute, "Constructor %s::%s() cannot be static", classScope->getOriginalName().c_str(), constructor->getOriginalName().c_str()); } if (destructor && destructor->getModifiers()->isStatic()) { destructor->parseTimeFatal(fs, Compiler::InvalidAttribute, "Destructor %s::%s() cannot be static", classScope->getOriginalName().c_str(), destructor->getOriginalName().c_str()); } if (clone && clone->getModifiers()->isStatic()) { clone->parseTimeFatal(fs, Compiler::InvalidAttribute, "Clone method %s::%s() cannot be static", classScope->getOriginalName().c_str(), clone->getOriginalName().c_str()); } } }
// 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; }