/*
     * When naming an anonymous function, the process works loosely by walking
     * up the AST and then translating that to a string. The stringification
     * happens from some far-up assignment and then going back down the parse
     * tree to the function definition point.
     *
     * This function will walk up the parse tree, gathering relevant nodes used
     * for naming, and return the assignment node if there is one. The provided
     * array and size will be filled in, and the returned node could be NULL if
     * no assignment is found. The first element of the array will be the
     * innermost node relevant to naming, and the last element will be the
     * outermost node.
     */
    ParseNode *gatherNameable(ParseNode **nameable, size_t *size) {
        *size = 0;

        for (int pos = nparents - 1; pos >= 0; pos--) {
            ParseNode *cur = parents[pos];
            if (cur->isAssignment())
                return cur;

            switch (cur->getKind()) {
                case PNK_NAME:     return cur;  /* found the initialized declaration */
                case PNK_FUNCTION: return NULL; /* won't find an assignment or declaration */

                case PNK_RETURN:
                    /*
                     * Normally the relevant parent of a node is its direct parent, but
                     * sometimes with code like:
                     *
                     *    var foo = (function() { return function() {}; })();
                     *
                     * the outer function is just a helper to create a scope for the
                     * returned function. Hence the name of the returned function should
                     * actually be 'foo'.  This loop sees if the current node is a
                     * PNK_RETURN, and if there is a direct function call we skip to
                     * that.
                     */
                    for (int tmp = pos - 1; tmp > 0; tmp--) {
                        if (isDirectCall(tmp, cur)) {
                            pos = tmp;
                            break;
                        } else if (call(cur)) {
                            /* Don't skip too high in the tree */
                            break;
                        }
                        cur = parents[tmp];
                    }
                    break;

                case PNK_COLON:
                    /*
                     * If this is a PNK_COLON, but our parent is not a PNK_OBJECT,
                     * then this is a label and we're done naming. Otherwise we
                     * record the PNK_COLON but skip the PNK_OBJECT so we're not
                     * flagged as a contributor.
                     */
                    if (pos == 0 || !parents[pos - 1]->isKind(PNK_OBJECT))
                        return NULL;
                    pos--;
                    /* fallthrough */

                /* Save any other nodes we encounter on the way up. */
                default:
                    JS_ASSERT(*size < MaxParents);
                    nameable[(*size)++] = cur;
                    break;
            }

        }

        return NULL;
    }
Example #2
0
bool
ModuleBuilder::processExport(frontend::ParseNode* pn)
{
    MOZ_ASSERT(pn->isArity(PN_UNARY));

    ParseNode* kid = pn->pn_kid;
    bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT;

    switch (kid->getKind()) {
      case PNK_EXPORT_SPEC_LIST:
        MOZ_ASSERT(!isDefault);
        for (ParseNode* spec = kid->pn_head; spec; spec = spec->pn_next) {
            MOZ_ASSERT(spec->isKind(PNK_EXPORT_SPEC));
            RootedAtom localName(cx_, spec->pn_left->pn_atom);
            RootedAtom exportName(cx_, spec->pn_right->pn_atom);
            if (!appendLocalExportEntry(exportName, localName))
                return false;
        }
        break;

      case PNK_FUNCTION: {
          RootedFunction func(cx_, kid->pn_funbox->function());
          RootedAtom localName(cx_, func->atom());
          RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
          if (!appendLocalExportEntry(exportName, localName))
              return false;
          break;
      }

      case PNK_CLASS: {
          const ClassNode& cls = kid->as<ClassNode>();
          MOZ_ASSERT(cls.names());
          RootedAtom localName(cx_, cls.names()->innerBinding()->pn_atom);
          RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
          if (!appendLocalExportEntry(exportName, localName))
              return false;
          break;
      }

      case PNK_VAR:
      case PNK_CONST:
      case PNK_GLOBALCONST:
      case PNK_LET: {
          MOZ_ASSERT(kid->isArity(PN_LIST));
          for (ParseNode* var = kid->pn_head; var; var = var->pn_next) {
              if (var->isKind(PNK_ASSIGN))
                  var = var->pn_left;
              MOZ_ASSERT(var->isKind(PNK_NAME));
              RootedAtom localName(cx_, var->pn_atom);
              RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
              if (!appendLocalExportEntry(exportName, localName))
                  return false;
          }
          break;
      }

      default:
        MOZ_ASSERT(isDefault);
        RootedAtom localName(cx_, cx_->names().starDefaultStar);
        RootedAtom exportName(cx_, cx_->names().default_);
        if (!appendLocalExportEntry(exportName, localName))
            return false;
        break;
    }
    return true;
}
Example #3
0
bool
ModuleBuilder::buildAndInit(frontend::ParseNode* moduleNode, HandleModuleObject module)
{
    MOZ_ASSERT(moduleNode->isKind(PNK_MODULE));

    ParseNode* stmtsNode = moduleNode->pn_expr;
    MOZ_ASSERT(stmtsNode->isKind(PNK_STATEMENTLIST));
    MOZ_ASSERT(stmtsNode->isArity(PN_LIST));

    for (ParseNode* pn = stmtsNode->pn_head; pn; pn = pn->pn_next) {
        switch (pn->getKind()) {
          case PNK_IMPORT:
            if (!processImport(pn))
                return false;
            break;

          case PNK_EXPORT:
          case PNK_EXPORT_DEFAULT:
            if (!processExport(pn))
                return false;
            break;

          case PNK_EXPORT_FROM:
            if (!processExportFrom(pn))
                return false;
            break;

          default:
            break;
        }
    }

    for (const auto& e : exportEntries_) {
        RootedExportEntry exp(cx_, e);
        if (!exp->moduleRequest()) {
            RootedImportEntry importEntry(cx_, importEntryFor(exp->localName()));
            if (!importEntry) {
                if (!localExportEntries_.append(exp))
                    return false;
            } else {
                if (importEntry->importName() == cx_->names().star) {
                    if (!localExportEntries_.append(exp))
                        return false;
                } else {
                    RootedAtom exportName(cx_, exp->exportName());
                    RootedAtom moduleRequest(cx_, importEntry->moduleRequest());
                    RootedAtom importName(cx_, importEntry->importName());
                    RootedExportEntry exportEntry(cx_);
                    exportEntry = ExportEntryObject::create(cx_,
                                                            exportName,
                                                            moduleRequest,
                                                            importName,
                                                            nullptr);
                    if (!exportEntry || !indirectExportEntries_.append(exportEntry))
                        return false;
                }
            }
        } else if (exp->importName() == cx_->names().star) {
            if (!starExportEntries_.append(exp))
                return false;
        } else {
            if (!indirectExportEntries_.append(exp))
                return false;
        }
    }

    RootedArrayObject requestedModules(cx_, createArray<JSAtom*>(requestedModules_));
    if (!requestedModules)
        return false;

    RootedArrayObject importEntries(cx_, createArray<ImportEntryObject*>(importEntries_));
    if (!importEntries)
        return false;

    RootedArrayObject localExportEntries(cx_, createArray<ExportEntryObject*>(localExportEntries_));
    if (!localExportEntries)
        return false;

    RootedArrayObject indirectExportEntries(cx_);
    indirectExportEntries = createArray<ExportEntryObject*>(indirectExportEntries_);
    if (!indirectExportEntries)
        return false;

    RootedArrayObject starExportEntries(cx_, createArray<ExportEntryObject*>(starExportEntries_));
    if (!starExportEntries)
        return false;

    module->initImportExportData(requestedModules,
                                 importEntries,
                                 localExportEntries,
                                 indirectExportEntries,
                                 starExportEntries);

    return true;
}