summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/ModuleObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/builtin/ModuleObject.cpp338
1 files changed, 176 insertions, 162 deletions
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
index 0365f744a6..251e8b46ff 100644
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -177,6 +177,17 @@ ResolvedBindingObject* ResolvedBindingObject::create(
}
///////////////////////////////////////////////////////////////////////////
+// ImportAttribute
+
+ImportAttribute::ImportAttribute(Handle<JSAtom*> key, Handle<JSString*> value)
+ : key_(key), value_(value) {}
+
+void ImportAttribute::trace(JSTracer* trc) {
+ TraceNullableEdge(trc, &key_, "ImportAttribute::key_");
+ TraceNullableEdge(trc, &value_, "ImportAttribute::value_");
+}
+
+///////////////////////////////////////////////////////////////////////////
// ModuleRequestObject
/* static */ const JSClass ModuleRequestObject::class_ = {
"ModuleRequest",
@@ -185,13 +196,15 @@ ResolvedBindingObject* ResolvedBindingObject::create(
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ModuleRequestObject, specifier,
SpecifierSlot)
-ArrayObject* ModuleRequestObject::attributes() const {
- JSObject* obj = getReservedSlot(AttributesSlot).toObjectOrNull();
- if (!obj) {
- return nullptr;
+Span<const ImportAttribute> ModuleRequestObject::attributes() const {
+ Value value = getReservedSlot(AttributesSlot);
+ if (value.isNullOrUndefined()) {
+ return Span<const ImportAttribute>();
}
-
- return &obj->as<ArrayObject>();
+ void* ptr = value.toPrivate();
+ MOZ_ASSERT(ptr);
+ auto* vector = static_cast<ImportAttributeVector*>(ptr);
+ return *vector;
}
bool ModuleRequestObject::hasAttributes() const {
@@ -208,32 +221,22 @@ bool ModuleRequestObject::getModuleType(
return true;
}
- Rooted<ArrayObject*> attributesArray(cx, moduleRequest->attributes());
- RootedObject attributeObject(cx);
- RootedId typeId(cx, NameToId(cx->names().type));
- RootedValue value(cx);
-
- uint32_t numberOfAttributes = attributesArray->length();
- for (uint32_t i = 0; i < numberOfAttributes; i++) {
- attributeObject = &attributesArray->getDenseElement(i).toObject();
-
- if (!GetProperty(cx, attributeObject, attributeObject, typeId, &value)) {
- continue;
- }
+ for (const ImportAttribute& importAttribute : moduleRequest->attributes()) {
+ if (importAttribute.key() == cx->names().type) {
+ int32_t isJsonString;
+ if (!js::CompareStrings(cx, cx->names().json, importAttribute.value(),
+ &isJsonString)) {
+ return false;
+ }
- int32_t isJsonString;
- if (!js::CompareStrings(cx, cx->names().json, value.toString(),
- &isJsonString)) {
- return false;
- }
+ if (isJsonString == 0) {
+ moduleType = JS::ModuleType::JSON;
+ return true;
+ }
- if (isJsonString == 0) {
- moduleType = JS::ModuleType::JSON;
+ moduleType = JS::ModuleType::Unknown;
return true;
}
-
- moduleType = JS::ModuleType::Unknown;
- return true;
}
moduleType = JS::ModuleType::JavaScript;
@@ -248,7 +251,7 @@ bool ModuleRequestObject::isInstance(HandleValue value) {
/* static */
ModuleRequestObject* ModuleRequestObject::create(
JSContext* cx, Handle<JSAtom*> specifier,
- Handle<ArrayObject*> maybeAttributes) {
+ MutableHandle<UniquePtr<ImportAttributeVector>> maybeAttributes) {
ModuleRequestObject* self =
NewObjectWithGivenProto<ModuleRequestObject>(cx, nullptr);
if (!self) {
@@ -256,7 +259,12 @@ ModuleRequestObject* ModuleRequestObject::create(
}
self->initReservedSlot(SpecifierSlot, StringOrNullValue(specifier));
- self->initReservedSlot(AttributesSlot, ObjectOrNullValue(maybeAttributes));
+
+ if (maybeAttributes) {
+ InitReservedSlot(self, AttributesSlot, maybeAttributes.get().release(),
+ MemoryUse::ModuleImportAttributes);
+ }
+
return self;
}
@@ -1589,39 +1597,28 @@ bool frontend::StencilModuleMetadata::createModuleRequestObjects(
ModuleRequestObject* frontend::StencilModuleMetadata::createModuleRequestObject(
JSContext* cx, CompilationAtomCache& atomCache,
const StencilModuleRequest& request) const {
- Rooted<ArrayObject*> assertionArray(cx);
- uint32_t numberOfAssertions = request.assertions.length();
- if (numberOfAssertions > 0) {
- assertionArray = NewDenseFullyAllocatedArray(cx, numberOfAssertions);
- if (!assertionArray) {
+ uint32_t numberOfAttributes = request.attributes.length();
+
+ Rooted<UniquePtr<ImportAttributeVector>> attributes(cx);
+ if (numberOfAttributes > 0) {
+ attributes = cx->make_unique<ImportAttributeVector>();
+ if (!attributes) {
+ ReportOutOfMemory(cx);
return nullptr;
}
- assertionArray->ensureDenseInitializedLength(0, numberOfAssertions);
-
- Rooted<PlainObject*> assertionObject(cx);
- RootedId assertionKey(cx);
- RootedValue assertionValue(cx);
- for (uint32_t j = 0; j < numberOfAssertions; ++j) {
- assertionObject = NewPlainObject(cx);
- if (!assertionObject) {
- return nullptr;
- }
- JSAtom* jsatom =
- atomCache.getExistingAtomAt(cx, request.assertions[j].key);
- MOZ_ASSERT(jsatom);
- assertionKey = AtomToId(jsatom);
-
- jsatom = atomCache.getExistingAtomAt(cx, request.assertions[j].value);
- MOZ_ASSERT(jsatom);
- assertionValue = StringValue(jsatom);
-
- if (!DefineDataProperty(cx, assertionObject, assertionKey, assertionValue,
- JSPROP_ENUMERATE)) {
- return nullptr;
- }
+ if (!attributes->reserve(numberOfAttributes)) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
- assertionArray->initDenseElement(j, ObjectValue(*assertionObject));
+ Rooted<JSAtom*> attributeKey(cx);
+ Rooted<JSAtom*> attributeValue(cx);
+ for (uint32_t j = 0; j < numberOfAttributes; ++j) {
+ attributeKey = atomCache.getExistingAtomAt(cx, request.attributes[j].key);
+ attributeValue =
+ atomCache.getExistingAtomAt(cx, request.attributes[j].value);
+ attributes->infallibleEmplaceBack(attributeKey, attributeValue);
}
}
@@ -1629,7 +1626,7 @@ ModuleRequestObject* frontend::StencilModuleMetadata::createModuleRequestObject(
atomCache.getExistingAtomAt(cx, request.specifier));
MOZ_ASSERT(specifier);
- return ModuleRequestObject::create(cx, specifier, assertionArray);
+ return ModuleRequestObject::create(cx, specifier, &attributes);
}
bool frontend::StencilModuleMetadata::createImportEntries(
@@ -1796,34 +1793,24 @@ bool frontend::StencilModuleMetadata::initModule(
return true;
}
-bool ModuleBuilder::isAssertionSupported(frontend::TaggedParserAtomIndex key) {
- if (!key.isWellKnownAtomId()) {
- return false;
- }
-
- return key.toWellKnownAtomId() == WellKnownAtomId::type;
-}
-
-bool ModuleBuilder::processAssertions(frontend::StencilModuleRequest& request,
- frontend::ListNode* assertionList) {
+bool ModuleBuilder::processAttributes(frontend::StencilModuleRequest& request,
+ frontend::ListNode* attributeList) {
using namespace js::frontend;
- for (ParseNode* assertionItem : assertionList->contents()) {
- BinaryNode* assertion = &assertionItem->as<BinaryNode>();
- MOZ_ASSERT(assertion->isKind(ParseNodeKind::ImportAttribute));
+ for (ParseNode* attributeItem : attributeList->contents()) {
+ BinaryNode* attribute = &attributeItem->as<BinaryNode>();
+ MOZ_ASSERT(attribute->isKind(ParseNodeKind::ImportAttribute));
- auto key = assertion->left()->as<NameNode>().atom();
- auto value = assertion->right()->as<NameNode>().atom();
+ auto key = attribute->left()->as<NameNode>().atom();
+ auto value = attribute->right()->as<NameNode>().atom();
- if (isAssertionSupported(key)) {
- markUsedByStencil(key);
- markUsedByStencil(value);
+ markUsedByStencil(key);
+ markUsedByStencil(value);
- StencilModuleAssertion assertionStencil(key, value);
- if (!request.assertions.append(assertionStencil)) {
- js::ReportOutOfMemory(fc_);
- return false;
- }
+ StencilModuleImportAttribute attributeStencil(key, value);
+ if (!request.attributes.append(attributeStencil)) {
+ js::ReportOutOfMemory(fc_);
+ return false;
}
}
@@ -1844,12 +1831,12 @@ bool ModuleBuilder::processImport(frontend::BinaryNode* importNode) {
auto* moduleSpec = &moduleRequest->left()->as<NameNode>();
MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::StringExpr));
- auto* assertionList = &moduleRequest->right()->as<ListNode>();
- MOZ_ASSERT(assertionList->isKind(ParseNodeKind::ImportAttributeList));
+ auto* attributeList = &moduleRequest->right()->as<ListNode>();
+ MOZ_ASSERT(attributeList->isKind(ParseNodeKind::ImportAttributeList));
auto specifier = moduleSpec->atom();
MaybeModuleRequestIndex moduleRequestIndex =
- appendModuleRequest(specifier, assertionList);
+ appendModuleRequest(specifier, attributeList);
if (!moduleRequestIndex.isSome()) {
return false;
}
@@ -2089,12 +2076,12 @@ bool ModuleBuilder::processExportFrom(frontend::BinaryNode* exportNode) {
auto* moduleSpec = &moduleRequest->left()->as<NameNode>();
MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::StringExpr));
- auto* assertionList = &moduleRequest->right()->as<ListNode>();
- MOZ_ASSERT(assertionList->isKind(ParseNodeKind::ImportAttributeList));
+ auto* attributeList = &moduleRequest->right()->as<ListNode>();
+ MOZ_ASSERT(attributeList->isKind(ParseNodeKind::ImportAttributeList));
auto specifier = moduleSpec->atom();
MaybeModuleRequestIndex moduleRequestIndex =
- appendModuleRequest(specifier, assertionList);
+ appendModuleRequest(specifier, attributeList);
if (!moduleRequestIndex.isSome()) {
return false;
}
@@ -2192,11 +2179,11 @@ bool ModuleBuilder::appendExportEntry(
frontend::MaybeModuleRequestIndex ModuleBuilder::appendModuleRequest(
frontend::TaggedParserAtomIndex specifier,
- frontend::ListNode* assertionList) {
+ frontend::ListNode* attributeList) {
markUsedByStencil(specifier);
auto request = frontend::StencilModuleRequest(specifier);
- if (!processAssertions(request, assertionList)) {
+ if (!processAttributes(request, attributeList)) {
return MaybeModuleRequestIndex();
}
@@ -2304,17 +2291,17 @@ bool ModuleObject::topLevelCapabilityReject(JSContext* cx,
return AsyncFunctionThrown(cx, promise, error);
}
-// https://tc39.es/proposal-import-assertions/#sec-evaluate-import-call
+// https://tc39.es/proposal-import-attributes/#sec-evaluate-import-call
// NOTE: The caller needs to handle the promise.
static bool EvaluateDynamicImportOptions(
JSContext* cx, HandleValue optionsArg,
- MutableHandle<ArrayObject*> assertionArrayArg) {
- // Step 10. If options is not undefined, then.
+ MutableHandle<ImportAttributeVector> attributesArrayArg) {
+ // Step 11. If options is not undefined, then
if (optionsArg.isUndefined()) {
return true;
}
- // Step 10.a. If Type(options) is not Object,
+ // Step 11.a. If options is not an Object, then
if (!optionsArg.isObject()) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "import",
@@ -2322,114 +2309,124 @@ static bool EvaluateDynamicImportOptions(
return false;
}
- RootedObject assertWrapperObject(cx, &optionsArg.toObject());
- RootedValue assertValue(cx);
+ RootedObject attributesWrapperObject(cx, &optionsArg.toObject());
+ RootedValue attributesValue(cx);
- // Step 10.b. Let attributesObj be Completion(Get(options, "with")).
+ // Step 11.b. Let attributesObj be Completion(Get(options, "with")).
RootedId withId(cx, NameToId(cx->names().with));
- if (!GetProperty(cx, assertWrapperObject, assertWrapperObject, withId,
- &assertValue)) {
+ if (!GetProperty(cx, attributesWrapperObject, attributesWrapperObject, withId,
+ &attributesValue)) {
return false;
}
- if (assertValue.isUndefined() &&
+ // Step 11.d. If the host supports the deprecated assert keyword for import
+ // attributes and attributesObj is undefined, then
+ if (attributesValue.isUndefined() &&
cx->options().importAttributesAssertSyntax()) {
- // Step 10.b. Let assertionsObj be Get(options, "assert").
+ // Step 11.d.i. Set attributesObj to Completion(Get(options, "assert")).
RootedId assertId(cx, NameToId(cx->names().assert_));
- if (!GetProperty(cx, assertWrapperObject, assertWrapperObject, assertId,
- &assertValue)) {
+ if (!GetProperty(cx, attributesWrapperObject, attributesWrapperObject,
+ assertId, &attributesValue)) {
return false;
}
}
- // Step 10.d. If assertionsObj is not undefined.
- if (assertValue.isUndefined()) {
+ // Step 11.e. If attributesObj is not undefined, then
+ if (attributesValue.isUndefined()) {
return true;
}
- // Step 10.d.i. If Type(assertionsObj) is not Object.
- if (!assertValue.isObject()) {
+ // Step 11.e.i. If attributesObj is not an Object, then
+ if (!attributesValue.isObject()) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "import",
- "object or undefined", InformalValueTypeName(assertValue));
+ "object or undefined", InformalValueTypeName(attributesValue));
return false;
}
- // Step 10.d.i. Let keys be EnumerableOwnPropertyNames(assertionsObj, key).
- RootedObject assertObject(cx, &assertValue.toObject());
- RootedIdVector assertions(cx);
- if (!GetPropertyKeys(cx, assertObject, JSITER_OWNONLY, &assertions)) {
+ // Step 11.e.ii. Let entries be
+ // Completion(EnumerableOwnProperties(attributesObj, key+value)).
+ RootedObject attributesObject(cx, &attributesValue.toObject());
+ RootedIdVector attributes(cx);
+ if (!GetPropertyKeys(cx, attributesObject, JSITER_OWNONLY, &attributes)) {
return false;
}
- uint32_t numberOfAssertions = assertions.length();
- if (numberOfAssertions == 0) {
+ uint32_t numberOfAttributes = attributes.length();
+ if (numberOfAttributes == 0) {
return true;
}
- // Step 9 (reordered). Let assertions be a new empty List.
- Rooted<ArrayObject*> assertionArray(
- cx, NewDenseFullyAllocatedArray(cx, numberOfAssertions));
- if (!assertionArray) {
+ // Step 10 (reordered). Let attributes be a new empty List.
+ if (!attributesArrayArg.reserve(numberOfAttributes)) {
+ ReportOutOfMemory(cx);
return false;
}
- assertionArray->ensureDenseInitializedLength(0, numberOfAssertions);
- // Step 10.d.iv. Let supportedAssertions be
- // !HostGetSupportedImportAssertions().
- // Note: This should be driven by a host hook, howver the infrastructure of
- // said host hook is deeply unclear, and so right now embedders will
- // not have the ability to alter or extend the set of supported
- // assertion types.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1840723.
- size_t numberOfValidAssertions = 0;
+ size_t numberOfValidAttributes = 0;
- // Step 10.d.v. For each String key of keys,
+ // Step 11.e.iv. For each element entry of entries, do
RootedId key(cx);
- for (size_t i = 0; i < numberOfAssertions; i++) {
- key = assertions[i];
-
- // Step 10.d.v.1. Let value be Get(assertionsObj, key).
- RootedValue value(cx);
- if (!GetProperty(cx, assertObject, assertObject, key, &value)) {
- return false;
- }
-
- // Step 10.d.v.3. If Type(value) is not String, then.
- if (!value.isString()) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_NOT_EXPECTED_TYPE, "import", "string",
- InformalValueTypeName(value));
+ RootedValue value(cx);
+ Rooted<JSAtom*> keyAtom(cx);
+ Rooted<JSString*> valueString(cx);
+ for (size_t i = 0; i < numberOfAttributes; i++) {
+ // Step 11.e.ii.iv.1. Let key be ! Get(entry, "0").
+ key = attributes[i];
+
+ // Step 11.e.ii.iv.2. Let value be ! Get(entry, "1").
+ if (!GetProperty(cx, attributesObject, attributesObject, key, &value)) {
return false;
}
- // Step 10.d.v.4. If supportedAssertions contains key, then Append {
- // [[Key]]: key, [[Value]]: value } to assertions.
- // Note: We only currently support the "type" assertion; this will need
- // extension
- bool supported = key.isAtom() ? key.toAtom() == cx->names().type : false;
- if (supported) {
- Rooted<PlainObject*> assertionObj(cx, NewPlainObject(cx));
- if (!assertionObj) {
+ // Step 11.e.ii.iv.3. If key is a String, then
+ if (key.isString()) {
+ // Step 11.f (reordered). If AllImportAttributesSupported(attributes) is
+ // false, then
+ //
+ // Note: This should be driven by a host hook
+ // (HostGetSupportedImportAttributes), however the infrastructure of said
+ // host hook is deeply unclear, and so right now embedders will not have
+ // the ability to alter or extend the set of supported attributes.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1840723.
+ bool supported = key.isAtom(cx->names().type);
+ if (!supported) {
+ UniqueChars printableKey = AtomToPrintableString(cx, key.toAtom());
+ if (!printableKey) {
+ return false;
+ }
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_IMPORT_ATTRIBUTES_UNSUPPORTED_ATTRIBUTE,
+ printableKey.get());
return false;
}
- if (!DefineDataProperty(cx, assertionObj, key, value, JSPROP_ENUMERATE)) {
+ // Step 10.d.v.3.a. If value is not a String, then
+ if (!value.isString()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_NOT_EXPECTED_TYPE, "import", "string",
+ InformalValueTypeName(value));
return false;
}
- assertionArray->initDenseElement(numberOfValidAssertions,
- ObjectValue(*assertionObj));
- ++numberOfValidAssertions;
+ // Step 10.d.v.3.b. Append the ImportAttribute Record { [[Key]]: key,
+ // [[Value]]: value } to attributes.
+ keyAtom = key.toAtom();
+ valueString = value.toString();
+ attributesArrayArg.infallibleEmplaceBack(keyAtom, valueString);
+ ++numberOfValidAttributes;
}
}
- if (numberOfValidAssertions == 0) {
+ if (numberOfValidAttributes == 0) {
return true;
}
- assertionArray->setLength(numberOfValidAssertions);
- assertionArrayArg.set(assertionArray);
+ // Step 10.g (skipped). Sort attributes according to the lexicographic order
+ // of their [[Key]] fields, treating the value of each such field as a
+ // sequence of UTF-16 code unit values.
+ //
+ // We only support "type", so we can ignore this.
return true;
}
@@ -2482,16 +2479,32 @@ JSObject* js::StartDynamicModuleImport(JSContext* cx, HandleScript script,
return promise;
}
- Rooted<ArrayObject*> assertionArray(cx);
- if (!EvaluateDynamicImportOptions(cx, optionsArg, &assertionArray)) {
+ Rooted<ImportAttributeVector> tempAttributes(cx);
+ if (!EvaluateDynamicImportOptions(cx, optionsArg, &tempAttributes)) {
if (!RejectPromiseWithPendingError(cx, promise)) {
return nullptr;
}
return promise;
}
+ Rooted<UniquePtr<ImportAttributeVector>> attributes(cx);
+ if (!tempAttributes.empty()) {
+ attributes = cx->make_unique<ImportAttributeVector>();
+ if (!attributes) {
+ return nullptr;
+ }
+ if (!attributes->reserve(tempAttributes.length())) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ if (!attributes->appendAll(tempAttributes)) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ }
+
RootedObject moduleRequest(
- cx, ModuleRequestObject::create(cx, specifierAtom, assertionArray));
+ cx, ModuleRequestObject::create(cx, specifierAtom, &attributes));
if (!moduleRequest) {
if (!RejectPromiseWithPendingError(cx, promise)) {
return nullptr;
@@ -2669,8 +2682,9 @@ static bool OnResolvedDynamicModule(JSContext* cx, unsigned argc, Value* vp) {
}
Rooted<PromiseObject*> promise(cx, TargetFromHandler<PromiseObject>(args));
+ Rooted<UniquePtr<ImportAttributeVector>> attributes(cx);
RootedObject moduleRequest(
- cx, ModuleRequestObject::create(cx, specifier, nullptr));
+ cx, ModuleRequestObject::create(cx, specifier, &attributes));
if (!moduleRequest) {
return RejectPromiseWithPendingError(cx, promise);
}