commit 442c00c57481e2175d7b5cedcab280624739b2c8
parent 0ab5fc76883d63d41db23ae49916382fe81f1313
Author: Cornelius Emase <corneliuslochipi@gmail.com>
Date: Wed, 19 Nov 2025 13:53:01 +0000
Bug 1999787 - Support `bytes` modules in parseModule r=bthrall,arai
Add support for `bytes` modules in the JS shell. We're now also checking
the ArrayBuffer and wrap with CreateDefaultExportSyntheticModule.
Module type now uses JS::ModuleType.
Differential Revision: https://phabricator.services.mozilla.com/D272694
Diffstat:
2 files changed, 86 insertions(+), 26 deletions(-)
diff --git a/js/src/jit-test/tests/modules/bytes-module.js b/js/src/jit-test/tests/modules/bytes-module.js
@@ -0,0 +1,25 @@
+let buf = new ArrayBuffer(4);
+let view = new Uint8Array(buf);
+view[0] = 0x41;
+view[1] = 0x42;
+view[2] = 0x43;
+view[3] = 0x44;
+
+let m = parseModule(buf, "bytes-module.js", "bytes");
+let a = registerModule("bytes-module", m);
+
+let importer = parseModule(`
+ import buf from 'bytes-module' with { type: 'bytes' };
+ globalThis.importedBuf = buf;
+`);
+
+let b = registerModule("importer", importer);
+
+moduleLink(b);
+moduleEvaluate(b);
+
+let importedView = new Uint8Array(globalThis.importedBuf);
+
+for (let i = 0; i < view.length; i++) {
+ assertEq(importedView[i], view[i]);
+}
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
@@ -5712,14 +5712,6 @@ static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
- if (!args[0].isString()) {
- const char* typeName = InformalValueTypeName(args[0]);
- JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
- return false;
- }
-
- JSString* scriptContents = args[0].toString();
-
UniqueChars filename;
CompileOptions options(cx);
JS::ModuleType moduleType = JS::ModuleType::JavaScript;
@@ -5738,7 +5730,8 @@ static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
options.setFileAndLine(filename.get(), 1);
- // The 2nd argument is the module type string. "js" or "json" is expected.
+ // The 2nd argument is the module type string. "js", "json" or "bytes" is
+ // expected.
if (args.length() == 3) {
if (!args[2].isString()) {
const char* typeName = InformalValueTypeName(args[2]);
@@ -5753,8 +5746,11 @@ static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
}
if (JS_LinearStringEqualsLiteral(linearStr, "json")) {
moduleType = JS::ModuleType::JSON;
+ } else if (JS_LinearStringEqualsLiteral(linearStr, "bytes")) {
+ moduleType = JS::ModuleType::Bytes;
} else if (!JS_LinearStringEqualsLiteral(linearStr, "js")) {
- JS_ReportErrorASCII(cx, "moduleType string ('js' or 'json') expected");
+ JS_ReportErrorASCII(
+ cx, "moduleType string ('js' or 'json' or 'bytes') expected");
return false;
}
}
@@ -5762,23 +5758,62 @@ static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
options.setFileAndLine("<string>", 1);
}
- AutoStableStringChars linearChars(cx);
- if (!linearChars.initTwoByte(cx, scriptContents)) {
- return false;
- }
+ RootedObject module(cx);
+ switch (moduleType) {
+ case JS::ModuleType::JavaScript:
+ case JS::ModuleType::JSON: {
+ if (!args[0].isString()) {
+ const char* typeName = InformalValueTypeName(args[0]);
+ JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
+ return false;
+ }
- JS::SourceText<char16_t> srcBuf;
- if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
- return false;
- }
+ JSString* scriptContents = args[0].toString();
- RootedObject module(cx);
- if (moduleType == JS::ModuleType::JSON) {
- module = JS::CompileJsonModule(cx, options, srcBuf);
- } else {
- options.setModule();
- module = JS::CompileModule(cx, options, srcBuf);
+ AutoStableStringChars linearChars(cx);
+ if (!linearChars.initTwoByte(cx, scriptContents)) {
+ return false;
+ }
+
+ JS::SourceText<char16_t> srcBuf;
+ if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
+ return false;
+ }
+
+ if (moduleType == JS::ModuleType::JSON) {
+ module = JS::CompileJsonModule(cx, options, srcBuf);
+ } else {
+ options.setModule();
+ module = JS::CompileModule(cx, options, srcBuf);
+ }
+
+ break;
+ }
+
+ case JS::ModuleType::Bytes: {
+ if (!args[0].isObject() ||
+ !JS::IsArrayBufferObject(&args[0].toObject())) {
+ const char* typeName = InformalValueTypeName(args[0]);
+ JS_ReportErrorASCII(cx, "expected ArrayBuffer for bytes module, got %s",
+ typeName);
+ return false;
+ }
+
+ /*
+ * NOTE: The spec requires checking that the ArrayBuffer is immutable.
+ * Immutable ArrayBuffers (see bug 1952253) are still only a Stage 2.7
+ * proposal. This check will be added in a future update.
+ */
+ module = JS::CreateDefaultExportSyntheticModule(cx, args[0]);
+ break;
+ }
+
+ case JS::ModuleType::CSS:
+ case JS::ModuleType::Unknown:
+ JS_ReportErrorASCII(cx, "Unsupported module type in parseModule");
+ return false;
}
+
if (!module) {
return false;
}
@@ -10200,9 +10235,9 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
" Sleep for dt seconds."),
JS_FN_HELP("parseModule", ParseModule, 3, 0,
-"parseModule(code, 'filename', 'js' | 'json')",
+"parseModule(code, 'filename', 'js' | 'json' | 'bytes')",
" Parses source text as a JS module ('js', this is the default) or a JSON"
-" module ('json') and returns a ModuleObject wrapper object."),
+" module ('json') or bytes module ('bytes') and returns a ModuleObject wrapper object."),
JS_FN_HELP("instantiateModuleStencil", InstantiateModuleStencil, 1, 0,
"instantiateModuleStencil(stencil, [options])",