Files
palemoon27/js/src/builtin/ModuleObject.cpp
T
roytam1 5d360f5c45 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1198572 - Add telemetry for how often HSTS would fix mixed content problems r=smaug r=tanvi (1abeb434a4)
- Bug 1181683 - Mark ping and beacon as blockable mixed content instead of optionally blockable. r=smaug (ab4b612439)
- Bug 1148732 - When checking a document's scheme, check the innermost uri. r=dveditz, smaug (f03eef9b3a)
- put back source RegExp (112cbc1797)
- Bug 1026520 - CSP: Inline report sending into allows - csp changes (r=dveditz) (f835967ad3)
- Bug 1026520 - CSP: Inline report sending into allows - callsite updates (r=dveditz) (39b200f027)
- Bug 1026520 - CSP: Inline report sending into allows - test updates (r=dveditz) (6a9dd4d859)
- Bug 1201822 - Update web-platform-tests expected data to revision d0571e01e1a2e4b8c5f696af2f81cbc1be9a5842, a=testonly (851485e4f9)
- Bug 1026520 - CSP: Inline report sending into allows - web platform test updates (r=deveditz) (0fbb9ce2c7)
- Bug 1612470 - Remove Document.hasScriptsBlockedBySandbox and Document.inlineScriptAllowedByCSP. r=bzbarsky (840ec6ebfb)
- Bug 1192333 - Use channel->ascynOpen2 in dom/xslt/xslt/txMozillaStyleheetCompiler.cpp (r=sicking) (73989dc9d2)
- Bug 1186843 - Don't recreate message manager. r=smaug (d4e2e28136)
- Bug 1209361 - Add missing includes to dom/base. r=mccr8 (702a9b0c21)
- Bug 1209621 - Add a way to get the TabParent for the content-primary tab, r=mconley (7d8a7e501f)
- Bug 1209001 - Fix OOM handling when creating ModuleObject r=terrence (e5b9fc8db3)
- Bug 1183289 - Run fewer CGC tests r=sfink (d529983340)
- Bug 1204692 - Add arm64-sim variant to the autospider.sh script. r=sstangl (8f46c42105)
- Bug 1183289 - Partially revert previous patch so jstests get run with default jitflags only r=me (97d1721e43)
- Bug 1210924 - Do not fire read barriers when using ReadBarriered in a boolean context; r=sfink (697885508c)
- code style and reshuffle (1b55c0f721)
- Bug 1207821 - Change the initialized length of an unboxed array in some places without triggering pre barriers, r=jandem. (92452a2db0)
- Bug 1074935 - Add SPS pseudo frames for JSRope flattening; r=jandem (5589064dc1)
- Bug 1184423 - Properly report OOM when initializing the RematerizedFrameTable fails; r=shu (60d964e1ca)
- Bug 1201575 - Give copied FrameIter::Data the cx of the current frame. (r=jimb) (af5e59d95a)
- Bug 1204725 - IonMonkey: Check result when copying frame iter data, r=nbp (7de175e301)
- Bug 1184423 - Properly report OOM when adding a rematerialized frame to the current JitActivation fails; r=shu (a00c310833)
- Bug 1210391 - Module scopes are currently not cacheable r=jandem (b42adeb292)
- pointer style (30f967d1ee)
- Bug 1199221 - Implement JS::ubi::Node::size for js::ObjectGroup referents; r=sfink (364cbff290)
- Bug 1199219 - Implement JS::ubi::Node::size for js::Shape referents; r=sfink (683d11d589)
- Bug 1199220 - Implement JS::ubi::Node::size for js::BaseShape referents; r=sfink (a9e1c02cbc)
- Bug 1200482: Make Debugger respect 'enabled' flag when setting allocation tracking hook on debuggee compartments, and check that it does. r=fitzgen (cf203e12e0)
- Bug 1177508 - Truncate the stack more aggressively in adoptAsyncStack. r=fitzgen (c335f6dcd5)
- Bug 1209989 - Implicitly attach a PRThread to native threads when first used as a PRThread in the PosixNSPR implementation. r=terrence (c9d771fb47)
- Bug 1206640: Fix an implicit constructor in PosixNSPR.cpp; r=nbp (178312c0f2)
- Bug 1204863 - Ignore frames from self-hosted scripts; r=shu (4b8cd11832)
- Bug 1180047: Debugger.prototype.findObjects should not return objects that must not be exposed to JS. r=fitzgen (634f19195b)
- Bug 1208908 - Fix a conditional statement in BytecodeRangeWithPosition::updatePosition(); r=ejpbruel (e58c656255)
- some profiler and crash import (d5b8b4f798)
- Bug 1185532: Turn on the NPAPI process sandbox for Windows 64-bit flash by default. r=bsmedberg (5aa8fa8a6e)
- Bug 1194488: Whitelist specific plugins for async init; r=jimm (7c78723689)
- Bug 1200698: Rename async plugin init pref; r=bsmedberg (df37e5833e)
- Bug 1184068: Ensure that mShutdown is not incorrectly set from true to false if plugin crashes during CallNP_Shutdown; r=jimm Bug 1202024: Initialize plugin details and quirks in parent on first run for async init. r=aklotz (934d877d8c)
- missing bit of Bug 1119878 Part 2 (3a0dd8afc1)
- Bug 1209351 (part 6) - Optimize nsTHashTable::RemoveEntry() usage in toolkit/. r=froydnj. (810fbbab89)
- Bug 1209351 (part 1) - Add an overloading of nsTHashTable::RemoveEntry() that takes an already-found entry. r=froydnj. (45d6181461)
- Bug 1209351 (part 2) - Optimize nsTHashTable::RemoveEntry() usage in dom/. r=bz. (bdb19ed49f)
- Bug 1209351 (part 3) - Optimize nsTHashTable::RemoveEntry() usage in gfx/. r=jrmuizel. (dc29a2b97f)
- Bug 1209351 (part 4) - Optimize nsTHashTable::RemoveEntry() usage in netwerk/. r=michal.novotny. (3be1f09b5c)
- Bug 1209351 (part 5) - Optimize nsTHashTable::RemoveEntry() usage in security/. r=keeler. (47999463fe)
2022-09-09 10:02:21 +08:00

761 lines
26 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/ModuleObject.h"
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
#include "gc/Tracer.h"
#include "jsobjinlines.h"
using namespace js;
using namespace js::frontend;
typedef JS::Rooted<ImportEntryObject*> RootedImportEntry;
typedef JS::Rooted<ExportEntryObject*> RootedExportEntry;
template<typename T, Value ValueGetter(T* obj)>
static bool
ModuleValueGetterImpl(JSContext* cx, const CallArgs& args)
{
args.rval().set(ValueGetter(&args.thisv().toObject().as<T>()));
return true;
}
template<typename T, Value ValueGetter(T* obj)>
static bool
ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<T::isInstance, ModuleValueGetterImpl<T, ValueGetter>>(cx, args);
}
#define DEFINE_GETTER_FUNCTIONS(cls, name, slot) \
static Value \
cls##_##name##Value(cls* obj) { \
return obj->getFixedSlot(cls::slot); \
} \
\
static bool \
cls##_##name##Getter(JSContext* cx, unsigned argc, Value* vp) \
{ \
return ModuleValueGetter<cls, cls##_##name##Value>(cx, argc, vp); \
}
#define DEFINE_ATOM_ACCESSOR_METHOD(cls, name) \
JSAtom* \
cls::name() \
{ \
Value value = cls##_##name##Value(this); \
return &value.toString()->asAtom(); \
}
#define DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(cls, name) \
JSAtom* \
cls::name() \
{ \
Value value = cls##_##name##Value(this); \
if (value.isNull()) \
return nullptr; \
return &value.toString()->asAtom(); \
}
///////////////////////////////////////////////////////////////////////////
// ImportEntryObject
/* static */ const Class
ImportEntryObject::class_ = {
"ImportEntry",
JSCLASS_HAS_RESERVED_SLOTS(ImportEntryObject::SlotCount) |
JSCLASS_HAS_CACHED_PROTO(JSProto_ImportEntry) |
JSCLASS_IS_ANONYMOUS
};
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest, ModuleRequestSlot)
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, importName, ImportNameSlot)
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, localName, LocalNameSlot)
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, moduleRequest)
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, importName)
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, localName)
/* static */ bool
ImportEntryObject::isInstance(HandleValue value)
{
return value.isObject() && value.toObject().is<ImportEntryObject>();
}
/* static */ JSObject*
ImportEntryObject::initClass(JSContext* cx, HandleObject obj)
{
static const JSPropertySpec protoAccessors[] = {
JS_PSG("moduleRequest", ImportEntryObject_moduleRequestGetter, 0),
JS_PSG("importName", ImportEntryObject_importNameGetter, 0),
JS_PSG("localName", ImportEntryObject_localNameGetter, 0),
JS_PS_END
};
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
if (!proto)
return nullptr;
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
return nullptr;
global->setPrototype(JSProto_ImportEntry, ObjectValue(*proto));
return proto;
}
JSObject*
js::InitImportEntryClass(JSContext* cx, HandleObject obj)
{
return ImportEntryObject::initClass(cx, obj);
}
/* static */ ImportEntryObject*
ImportEntryObject::create(JSContext* cx,
HandleAtom moduleRequest,
HandleAtom importName,
HandleAtom localName)
{
RootedImportEntry self(cx, NewBuiltinClassInstance<ImportEntryObject>(cx));
if (!self)
return nullptr;
self->initReservedSlot(ModuleRequestSlot, StringValue(moduleRequest));
self->initReservedSlot(ImportNameSlot, StringValue(importName));
self->initReservedSlot(LocalNameSlot, StringValue(localName));
return self;
}
///////////////////////////////////////////////////////////////////////////
// ExportEntryObject
/* static */ const Class
ExportEntryObject::class_ = {
"ExportEntry",
JSCLASS_HAS_RESERVED_SLOTS(ExportEntryObject::SlotCount) |
JSCLASS_HAS_CACHED_PROTO(JSProto_ExportEntry) |
JSCLASS_IS_ANONYMOUS
};
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, exportName, ExportNameSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, moduleRequest, ModuleRequestSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, importName, ImportNameSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, localName, LocalNameSlot)
DEFINE_ATOM_ACCESSOR_METHOD(ExportEntryObject, exportName)
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, moduleRequest)
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, importName)
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, localName)
/* static */ bool
ExportEntryObject::isInstance(HandleValue value)
{
return value.isObject() && value.toObject().is<ExportEntryObject>();
}
/* static */ JSObject*
ExportEntryObject::initClass(JSContext* cx, HandleObject obj)
{
static const JSPropertySpec protoAccessors[] = {
JS_PSG("exportName", ExportEntryObject_exportNameGetter, 0),
JS_PSG("moduleRequest", ExportEntryObject_moduleRequestGetter, 0),
JS_PSG("importName", ExportEntryObject_importNameGetter, 0),
JS_PSG("localName", ExportEntryObject_localNameGetter, 0),
JS_PS_END
};
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
if (!proto)
return nullptr;
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
return nullptr;
global->setPrototype(JSProto_ExportEntry, ObjectValue(*proto));
return proto;
}
JSObject*
js::InitExportEntryClass(JSContext* cx, HandleObject obj)
{
return ExportEntryObject::initClass(cx, obj);
}
static Value
StringOrNullValue(JSString* maybeString)
{
return maybeString ? StringValue(maybeString) : NullValue();
}
/* static */ ExportEntryObject*
ExportEntryObject::create(JSContext* cx,
HandleAtom maybeExportName,
HandleAtom maybeModuleRequest,
HandleAtom maybeImportName,
HandleAtom maybeLocalName)
{
RootedExportEntry self(cx, NewBuiltinClassInstance<ExportEntryObject>(cx));
if (!self)
return nullptr;
self->initReservedSlot(ExportNameSlot, StringOrNullValue(maybeExportName));
self->initReservedSlot(ModuleRequestSlot, StringOrNullValue(maybeModuleRequest));
self->initReservedSlot(ImportNameSlot, StringOrNullValue(maybeImportName));
self->initReservedSlot(LocalNameSlot, StringOrNullValue(maybeLocalName));
return self;
}
///////////////////////////////////////////////////////////////////////////
// IndirectBinding
IndirectBinding::IndirectBinding(Handle<ModuleEnvironmentObject*> environment, HandleId localName)
: environment(environment), localName(localName)
{}
///////////////////////////////////////////////////////////////////////////
// ModuleObject
/* static */ const Class
ModuleObject::class_ = {
"Module",
JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Module) |
JSCLASS_IS_ANONYMOUS,
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
nullptr, /* finalize */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
ModuleObject::trace
};
#define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot) \
ArrayObject& \
cls::name() const \
{ \
return getFixedSlot(cls::slot).toObject().as<ArrayObject>(); \
}
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, requestedModules, RequestedModulesSlot)
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, importEntries, ImportEntriesSlot)
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, localExportEntries, LocalExportEntriesSlot)
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot)
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, starExportEntries, StarExportEntriesSlot)
/* static */ bool
ModuleObject::isInstance(HandleValue value)
{
return value.isObject() && value.toObject().is<ModuleObject>();
}
/* static */ ModuleObject*
ModuleObject::create(ExclusiveContext* cx)
{
Rooted<ModuleObject*> self(cx, NewBuiltinClassInstance<ModuleObject>(cx, TenuredObject));
if (!self)
return nullptr;
IndirectBindingMap* bindings = cx->new_<IndirectBindingMap>();
if (!bindings || !bindings->init()) {
ReportOutOfMemory(cx);
return nullptr;
}
self->setReservedSlot(ImportBindingsSlot, PrivateValue(bindings));
return self;
}
/* static */ void
ModuleObject::finalize(js::FreeOp* fop, JSObject* obj)
{
ModuleObject* self = &obj->as<ModuleObject>();
if (!self->getReservedSlot(ImportBindingsSlot).isUndefined())
fop->delete_(&self->importBindings());
}
ModuleEnvironmentObject*
ModuleObject::environment() const
{
Value value = getReservedSlot(EnvironmentSlot);
if (value.isUndefined())
return nullptr;
return &value.toObject().as<ModuleEnvironmentObject>();
}
IndirectBindingMap&
ModuleObject::importBindings()
{
return *static_cast<IndirectBindingMap*>(getReservedSlot(ImportBindingsSlot).toPrivate());
}
void
ModuleObject::init(HandleScript script)
{
MOZ_ASSERT(!script->enclosingStaticScope());
initReservedSlot(ScriptSlot, PrivateValue(script));
initReservedSlot(EvaluatedSlot, BooleanValue(false));
}
void
ModuleObject::setInitialEnvironment(HandleModuleEnvironmentObject initialEnvironment)
{
initReservedSlot(InitialEnvironmentSlot, ObjectValue(*initialEnvironment));
}
void
ModuleObject::initImportExportData(HandleArrayObject requestedModules,
HandleArrayObject importEntries,
HandleArrayObject localExportEntries,
HandleArrayObject indirectExportEntries,
HandleArrayObject starExportEntries)
{
initReservedSlot(RequestedModulesSlot, ObjectValue(*requestedModules));
initReservedSlot(ImportEntriesSlot, ObjectValue(*importEntries));
initReservedSlot(LocalExportEntriesSlot, ObjectValue(*localExportEntries));
initReservedSlot(IndirectExportEntriesSlot, ObjectValue(*indirectExportEntries));
initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries));
}
bool
ModuleObject::hasScript() const
{
// When modules are parsed via the Reflect.parse() API, the module object
// doesn't have a script.
return !getReservedSlot(ScriptSlot).isUndefined();
}
JSScript*
ModuleObject::script() const
{
return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
}
bool
ModuleObject::evaluated() const
{
return getReservedSlot(EvaluatedSlot).toBoolean();
}
ModuleEnvironmentObject&
ModuleObject::initialEnvironment() const
{
return getReservedSlot(InitialEnvironmentSlot).toObject().as<ModuleEnvironmentObject>();
}
JSObject*
ModuleObject::enclosingStaticScope() const
{
// A ModuleObject is always the last thing on the scope chain before the global.
// TODO: This may no longer be true when we get top-level lexical scopes.
MOZ_ASSERT_IF(hasScript(), !script()->enclosingStaticScope());
return nullptr;
}
/* static */ void
ModuleObject::trace(JSTracer* trc, JSObject* obj)
{
ModuleObject& module = obj->as<ModuleObject>();
if (module.hasScript()) {
JSScript* script = module.script();
TraceManuallyBarrieredEdge(trc, &script, "Module script");
module.setReservedSlot(ScriptSlot, PrivateValue(script));
}
IndirectBindingMap& bindings = module.importBindings();
for (IndirectBindingMap::Enum e(bindings); !e.empty(); e.popFront()) {
IndirectBinding& b = e.front().value();
TraceEdge(trc, &b.environment, "module import environment");
TraceEdge(trc, &b.localName, "module import local name");
jsid bindingName = e.front().key();
TraceManuallyBarrieredEdge(trc, &bindingName, "module import binding name");
MOZ_ASSERT(bindingName == e.front().key());
}
}
void
ModuleObject::createEnvironment()
{
// The environment has already been created, we just neet to set it in the
// right slot.
MOZ_ASSERT(!getReservedSlot(InitialEnvironmentSlot).isUndefined());
MOZ_ASSERT(getReservedSlot(EnvironmentSlot).isUndefined());
setReservedSlot(EnvironmentSlot, getReservedSlot(InitialEnvironmentSlot));
}
void
ModuleObject::setEvaluated()
{
MOZ_ASSERT(!evaluated());
setReservedSlot(EvaluatedSlot, TrueHandleValue);
}
bool
ModuleObject::evaluate(JSContext* cx, MutableHandleValue rval)
{
RootedScript script(cx, this->script());
return JS_ExecuteScript(cx, script, rval);
}
DEFINE_GETTER_FUNCTIONS(ModuleObject, initialEnvironment, InitialEnvironmentSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, environment, EnvironmentSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluated, EvaluatedSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot)
JSObject*
js::InitModuleClass(JSContext* cx, HandleObject obj)
{
static const JSPropertySpec protoAccessors[] = {
JS_PSG("initialEnvironment", ModuleObject_initialEnvironmentGetter, 0),
JS_PSG("environment", ModuleObject_environmentGetter, 0),
JS_PSG("evaluated", ModuleObject_evaluatedGetter, 0),
JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),
JS_PSG("indirectExportEntries", ModuleObject_indirectExportEntriesGetter, 0),
JS_PSG("starExportEntries", ModuleObject_starExportEntriesGetter, 0),
JS_PS_END
};
static const JSFunctionSpec protoFunctions[] = {
JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 3, 0),
JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleDeclarationInstantiation", 0, 0),
JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluation", 0, 0),
JS_FS_END
};
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
if (!proto)
return nullptr;
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, protoFunctions))
return nullptr;
global->setPrototype(JSProto_Module, ObjectValue(*proto));
return proto;
}
#undef DEFINE_GETTER_FUNCTIONS
#undef DEFINE_STRING_ACCESSOR_METHOD
#undef DEFINE_ARRAY_SLOT_ACCESSOR
///////////////////////////////////////////////////////////////////////////
// ModuleBuilder
ModuleBuilder::ModuleBuilder(JSContext* cx)
: cx_(cx),
requestedModules_(cx, AtomVector(cx)),
importedBoundNames_(cx, AtomVector(cx)),
importEntries_(cx, ImportEntryVector(cx)),
exportEntries_(cx, ExportEntryVector(cx)),
localExportEntries_(cx, ExportEntryVector(cx)),
indirectExportEntries_(cx, ExportEntryVector(cx)),
starExportEntries_(cx, ExportEntryVector(cx))
{}
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;
}
bool
ModuleBuilder::processImport(frontend::ParseNode* pn)
{
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST));
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
RootedAtom module(cx_, pn->pn_right->pn_atom);
if (!maybeAppendRequestedModule(module))
return false;
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
MOZ_ASSERT(spec->isKind(PNK_IMPORT_SPEC));
MOZ_ASSERT(spec->pn_left->isArity(PN_NAME));
MOZ_ASSERT(spec->pn_right->isArity(PN_NAME));
RootedAtom importName(cx_, spec->pn_left->pn_atom);
RootedAtom localName(cx_, spec->pn_right->pn_atom);
if (!importedBoundNames_.append(localName))
return false;
RootedImportEntry importEntry(cx_);
importEntry = ImportEntryObject::create(cx_, module, importName, localName);
if (!importEntry || !importEntries_.append(importEntry))
return false;
}
return true;
}
bool
ModuleBuilder::processExport(frontend::ParseNode* pn)
{
MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY);
bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT;
ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid;
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_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;
}
bool
ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
{
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_left->isKind(PNK_EXPORT_SPEC_LIST));
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
RootedAtom module(cx_, pn->pn_right->pn_atom);
if (!maybeAppendRequestedModule(module))
return false;
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
if (spec->isKind(PNK_EXPORT_SPEC)) {
RootedAtom bindingName(cx_, spec->pn_left->pn_atom);
RootedAtom exportName(cx_, spec->pn_right->pn_atom);
if (!appendIndirectExportEntry(exportName, module, bindingName))
return false;
} else {
MOZ_ASSERT(spec->isKind(PNK_EXPORT_BATCH_SPEC));
RootedAtom importName(cx_, cx_->names().star);
if (!appendIndirectExportEntry(nullptr, module, importName))
return false;
}
}
return true;
}
ImportEntryObject*
ModuleBuilder::importEntryFor(JSAtom* localName)
{
for (auto import : importEntries_) {
if (import->localName() == localName)
return import;
}
return nullptr;
}
bool
ModuleBuilder::appendLocalExportEntry(HandleAtom exportName, HandleAtom localName)
{
Rooted<ExportEntryObject*> exportEntry(cx_);
exportEntry = ExportEntryObject::create(cx_, exportName, nullptr, nullptr, localName);
return exportEntry && exportEntries_.append(exportEntry);
}
bool
ModuleBuilder::appendIndirectExportEntry(HandleAtom exportName, HandleAtom moduleRequest,
HandleAtom importName)
{
Rooted<ExportEntryObject*> exportEntry(cx_);
exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest, importName, nullptr);
return exportEntry && exportEntries_.append(exportEntry);
}
bool
ModuleBuilder::maybeAppendRequestedModule(HandleAtom module)
{
for (auto m : requestedModules_) {
if (m == module)
return true;
}
return requestedModules_.append(module);
}
static Value
MakeElementValue(JSString *string)
{
return StringValue(string);
}
static Value
MakeElementValue(JSObject *object)
{
return ObjectValue(*object);
}
template <typename T>
ArrayObject* ModuleBuilder::createArray(const TraceableVector<T>& vector)
{
uint32_t length = vector.length();
RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length));
if (!array)
return nullptr;
array->setDenseInitializedLength(length);
for (uint32_t i = 0; i < length; i++)
array->initDenseElement(i, MakeElementValue(vector[i]));
if (!JS_FreezeObject(cx_, array))
return nullptr;
return array;
}