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;
  }
}
Example #4
0
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());
}
Example #5
0
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());
  }
}
Example #6
0
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());
  }
}
Example #7
0
// 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;
}