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)); } }
void FunctionStatement::onParse(AnalysisResultPtr ar, BlockScopePtr scope) { // note it's important to add to file 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. FileScopePtr fileScope = dynamic_pointer_cast<FileScope>(scope); if (!fileScope->addFunction(ar, onInitialParse(ar, fileScope, false))) { m_ignored = true; return; } ar->recordFunctionSource(m_name, m_loc, fileScope->getName()); }
void FunctionStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) { // 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 FunctionStatement::onParse(AnalysisResultPtr ar) { // note it's important to add to file 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. FileScopePtr fileScope = ar->getFileScope(); if (!fileScope->addFunction(ar, onParseImpl(ar))) { m_ignored = true; return; } ar->recordFunctionSource(m_name, m_loc, fileScope->getName()); }
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 (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) { auto param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (!param->hasUserType()) { 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::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) { checkParameters(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(scope); } } } // 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_originalName, fs); if (!scope->addFunction(ar, fs)) { m_ignored = true; return; } fs->setPersistent(false); if (isNamed("__autoload")) { if (m_params && m_params->getCount() != 1) { parseTimeFatal(scope, Compiler::InvalidMagicMethod, "__autoload() must take exactly 1 argument"); } } if (fs->isNative()) { if (getStmts()) { parseTimeFatal(scope, 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(scope, Compiler::InvalidAttribute, "Native function calls must have type hints " "on all args"); } } } if (getReturnTypeConstraint().empty()) { parseTimeFatal(scope, Compiler::InvalidAttribute, "Native function %s() must have a return type hint", getOriginalName().c_str()); } } else if (!getStmts()) { parseTimeFatal(scope, Compiler::InvalidAttribute, "Global function %s() must contain a body", 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; }