fix(modules): bind synthetic text sources to their owning records

Resolve JSON and CSS evaluation data by exact module identity in the compiling realm, with weak context entries owned by compiled records. Remove the main-Document source lookup that broke child imports.

Cover static and deferred dynamic child imports, realm-local caches, parse errors, identity collisions and record disposal. Mark both JSON/CSS cross-origin WPT cases passing after CLI/CDP verification.
This commit is contained in:
ldm0
2026-09-27 07:59:16 +08:00
parent 53fba3fc3c
commit 9fe4534412
12 changed files with 371 additions and 118 deletions
@@ -3147,12 +3147,10 @@ html/semantics/popovers/popover-shadow-dom.html
html/semantics/popovers/popover-stacking.html
html/semantics/popovers/popover-types-with-hints.html
html/semantics/scripting-1/the-script-element/css-module/content-type-checking.html
html/semantics/scripting-1/the-script-element/css-module/cors-crossorigin-requests.html
html/semantics/scripting-1/the-script-element/execution-timing/083.html
html/semantics/scripting-1/the-script-element/execution-timing/085.html
html/semantics/scripting-1/the-script-element/execution-timing/128.html
html/semantics/scripting-1/the-script-element/execution-timing/137.html
html/semantics/scripting-1/the-script-element/json-module/cors-crossorigin-requests.html
html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.html
html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror-module.html
html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror.html
@@ -6612,6 +6612,7 @@ html/semantics/scripting-1/the-script-element/async_011.htm
html/semantics/scripting-1/the-script-element/css-module/charset-2.html
html/semantics/scripting-1/the-script-element/css-module/charset-bom.html
html/semantics/scripting-1/the-script-element/css-module/charset.html
html/semantics/scripting-1/the-script-element/css-module/cors-crossorigin-requests.html
html/semantics/scripting-1/the-script-element/css-module/css-module-parse-error.html
html/semantics/scripting-1/the-script-element/css-module/import-css-module-basic.html
html/semantics/scripting-1/the-script-element/css-module/import-css-module-dynamic.html
@@ -6778,6 +6779,7 @@ html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-att
html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.html
html/semantics/scripting-1/the-script-element/json-module/charset-2.html
html/semantics/scripting-1/the-script-element/json-module/charset.html
html/semantics/scripting-1/the-script-element/json-module/cors-crossorigin-requests.html
html/semantics/scripting-1/the-script-element/json-module/integrity.html
html/semantics/scripting-1/the-script-element/json-module/module.html
html/semantics/scripting-1/the-script-element/json-module/parse-error.html
+3
View File
@@ -13,6 +13,9 @@ use moli_core::{
use support::FixtureServer;
use tokio::time::Duration;
#[path = "scripts/child_synthetic_modules.rs"]
mod child_synthetic_modules;
fn diagnostic_global<'a>(
page: &'a moli_core::page::Page,
name: &str,
@@ -0,0 +1,153 @@
use super::*;
async fn evaluate_child_synthetic_expression(expression: &str) -> Result<serde_json::Value> {
let server = FixtureServer::spawn().await?;
let browser = Browser::new(AppConfig::default())?;
let mut page = browser.fetch(&server.url("/static")).await?;
let result = page
.evaluate_runtime_expression_with_await_async(expression, true)
.await?;
server.shutdown().await;
Ok(result)
}
async fn child_synthetic_probe(source: &str) -> Result<serde_json::Value> {
let html = format!(
r#"<!doctype html><script>
window.result = 'not evaluated';
window.onerror = (message, url, line, column, error) => {{
window.result = error.name + ': ' + error.message;
}};
</script><script type="module">{source}</script>"#
);
let expression = format!(
r#"new Promise(resolve => {{
const frame = document.createElement('iframe');
frame.onload = () => resolve(frame.contentWindow.result);
frame.srcdoc = {};
document.body.appendChild(frame);
}})"#,
serde_json::to_string(&html)?
);
evaluate_child_synthetic_expression(&expression).await
}
#[tokio::test(flavor = "multi_thread")]
async fn child_synthetic_json_module_uses_its_own_document_source() -> Result<()> {
let result = child_synthetic_probe(
r#"import value from 'data:application/json,{"answer":42}' with { type: 'json' };
window.result = value.answer + ':' + (Object.getPrototypeOf(value) === Object.prototype);"#,
)
.await?;
assert_eq!(result["value"], "42:true", "{result:?}");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn child_synthetic_css_module_uses_its_own_document_source() -> Result<()> {
let result = child_synthetic_probe(
r#"import sheet from 'data:text/css,body%20%7B%20color%3A%20red%3B%20%7D' with { type: 'css' };
window.result = (sheet instanceof CSSStyleSheet) + ':' + sheet.cssRules.length;"#,
)
.await?;
assert_eq!(result["value"], "true:1", "{result:?}");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn child_synthetic_json_parse_error_is_not_a_missing_source_error() -> Result<()> {
let result = child_synthetic_probe(
r#"import value from 'data:application/json,not-json' with { type: 'json' };
window.result = 'must not execute';"#,
)
.await?;
assert!(
result["value"]
.as_str()
.is_some_and(|value| value.starts_with("SyntaxError: ")),
"{result:?}"
);
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn child_synthetic_dynamic_import_after_load_uses_its_realm() -> Result<()> {
let result = evaluate_child_synthetic_expression(
r#"(async () => {
const frame = document.createElement('iframe');
const loaded = new Promise(resolve => frame.onload = resolve);
frame.srcdoc = `<script>
window.probe = async () => {
await new Promise(resolve => setTimeout(resolve, 0));
const jsonUrl = 'data:application/json,{"answer":42}';
const cssUrl = 'data:text/css,body%7Bcolor%3Ared%7D';
const json = await import(jsonUrl, {with: {type: 'json'}});
const css = await import(cssUrl, {with: {type: 'css'}});
const again = await import(jsonUrl, {with: {type: 'json'}});
let failure;
try { await import('data:application/json,invalid', {with: {type: 'json'}}); }
catch (error) { failure = error; }
return [json.default.answer === 42,
Object.getPrototypeOf(json.default) === Object.prototype,
css.default instanceof CSSStyleSheet, css.default.cssRules.length === 1,
json === again, failure instanceof SyntaxError].join(':');
};
</script>`;
document.body.appendChild(frame);
await loaded;
return await frame.contentWindow.probe();
})()"#,
)
.await?;
assert_eq!(
result["value"], "true:true:true:true:true:true",
"{result:?}"
);
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn child_synthetic_module_values_are_cached_per_realm_not_per_url() -> Result<()> {
let result = evaluate_child_synthetic_expression(
r#"(async () => {
const jsonUrl = 'data:application/json,{"answer":42}';
const cssUrl = 'data:text/css,body%7Bcolor%3Ared%7D';
const rootJson = (await import(jsonUrl, {with: {type: 'json'}})).default;
const rootSheet = (await import(cssUrl, {with: {type: 'css'}})).default;
const makeFrame = async () => {
const frame = document.createElement('iframe');
const loaded = new Promise(resolve => frame.onload = resolve);
frame.srcdoc = `<script type="module">
import value from '${jsonUrl}' with {type: 'json'};
import sheet from '${cssUrl}' with {type: 'css'};
window.value = value;
window.sheet = sheet;
window.readAgain = async () => {
const json = await import('${jsonUrl}', {with: {type: 'json'}});
const css = await import('${cssUrl}', {with: {type: 'css'}});
return json.default === value && css.default === sheet;
};
</script>`;
document.body.appendChild(frame);
await loaded;
return frame.contentWindow;
};
const [left, right] = await Promise.all([makeFrame(), makeFrame()]);
left.value.answer = 7;
left.sheet.insertRule('p { color: blue; }', 1);
return [rootJson.answer === 42, right.value.answer === 42,
left.value.answer === 7, rootSheet.cssRules.length === 1,
right.sheet.cssRules.length === 1, left.sheet.cssRules.length === 2,
left.value !== right.value, left.value !== rootJson,
left.sheet !== right.sheet, left.sheet !== rootSheet,
Object.getPrototypeOf(left.value) === left.Object.prototype,
Object.getPrototypeOf(right.value) === right.Object.prototype,
left.sheet instanceof left.CSSStyleSheet,
right.sheet instanceof right.CSSStyleSheet,
await left.readAgain(), await right.readAgain()].join(':');
})()"#,
)
.await?;
assert_eq!(result["value"], ["true"; 16].join(":"), "{result:?}");
Ok(())
}
@@ -198,15 +198,6 @@ impl DocumentRuntime {
.native_module_source(entry_id)
}
pub(crate) fn native_module_source_for(
&self,
module: v8::Local<'_, v8::Module>,
) -> Option<(ModuleMapKey, ModuleSource)> {
self.script_lifecycle
.scripts()
.native_module_source_for(module)
}
pub(crate) fn native_module_wasm_record_for(
&self,
module: v8::Local<'_, v8::Module>,
-7
View File
@@ -601,13 +601,6 @@ impl HostScriptScheduler {
self.module_owner.native_module_source(entry_id)
}
pub(crate) fn native_module_source_for(
&self,
module: v8::Local<'_, v8::Module>,
) -> Option<(ModuleMapKey, ModuleSource)> {
self.module_owner.native_module_source_for(module)
}
pub(crate) fn native_module_wasm_record_for(
&self,
module: v8::Local<'_, v8::Module>,
+2 -7
View File
@@ -12,6 +12,7 @@ mod record;
mod record_resolver;
mod resolver;
mod single_module_fetch;
mod synthetic_text;
#[cfg(test)]
mod tests;
mod tree_adapter;
@@ -51,6 +52,7 @@ pub(crate) use self::resolver::{
ResolverScopeGuard, resolve_static_module_callback, resolve_static_source_callback,
};
pub(crate) use self::single_module_fetch::NativeModuleSingleFetchRequest;
pub(crate) use self::synthetic_text::SyntheticTextModuleSource;
pub(crate) use self::tree_owner::{
NativeModuleTreeDocumentOwnerAdapter, NativeModuleTreeFrameDocumentOwner,
};
@@ -486,13 +488,6 @@ impl ModuleOwnerState {
self.document_modulator.entry(entry_id).source().cloned()
}
pub(crate) fn native_module_source_for(
&self,
module: v8::Local<'_, v8::Module>,
) -> Option<(ModuleMapKey, ModuleSource)> {
self.document_modulator.module_source_for(module)
}
pub(crate) fn native_module_wasm_record_for(
&self,
module: v8::Local<'_, v8::Module>,
@@ -655,14 +655,6 @@ impl NativeDocumentModulator {
.set_record_state_for_entry(self.core.entry(entry_id), ModuleRecordState::Evaluated);
}
pub(crate) fn module_source_for(
&self,
module: v8::Local<'_, v8::Module>,
) -> Option<(ModuleMapKey, ModuleSource)> {
let entry = self.entry(self.module_entry_id_for(module)?);
Some((entry.effective_key().clone(), entry.source()?.clone()))
}
pub(crate) fn module_wasm_record_for(
&self,
module: v8::Local<'_, v8::Module>,
@@ -19,6 +19,8 @@ pub(crate) struct ModuleRecordEntry {
compiled_module: v8::Global<v8::Module>,
requests: Vec<ModuleRequestRecord>,
wasm_module: Option<WasmModuleRecord>,
// Own the evaluation data; the Context's lookup only keeps a weak reference.
synthetic_text_module_source: Option<Rc<super::SyntheticTextModuleSource>>,
state: ModuleRecordState,
}
@@ -33,6 +35,7 @@ impl ModuleRecordEntry {
compiled_module,
requests,
wasm_module: None,
synthetic_text_module_source: None,
state: ModuleRecordState::Compiled,
}
}
@@ -48,6 +51,7 @@ impl ModuleRecordEntry {
compiled_module,
requests,
wasm_module: Some(wasm_module),
synthetic_text_module_source: None,
state: ModuleRecordState::Compiled,
}
}
@@ -56,6 +60,14 @@ impl ModuleRecordEntry {
&self.key
}
pub(crate) fn with_synthetic_text_module_source(
mut self,
source: Rc<super::SyntheticTextModuleSource>,
) -> Self {
self.synthetic_text_module_source = Some(source);
self
}
pub(crate) fn compiled_module(&self) -> &v8::Global<v8::Module> {
&self.compiled_module
}
@@ -0,0 +1,153 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
use super::{ModuleIdentityHash, ModuleMapKey, module_identity_hash_from_v8_module};
/// Evaluation data belongs to the compiled module record, not the page's
/// default Document. The Context only holds weak references, so this lookup
/// does not keep a disposed realm or its compiled modules alive.
#[derive(Debug)]
pub(crate) struct SyntheticTextModuleSource {
module: v8::Global<v8::Module>,
key: ModuleMapKey,
source: String,
}
#[derive(Default)]
struct SyntheticTextModuleSources {
entries: RefCell<HashMap<ModuleIdentityHash, Vec<Weak<SyntheticTextModuleSource>>>>,
}
impl SyntheticTextModuleSource {
pub(crate) fn register(
scope: &mut v8::PinScope<'_, '_>,
module: v8::Local<'_, v8::Module>,
key: ModuleMapKey,
source: &str,
) -> Rc<Self> {
let context = scope.get_current_context();
let sources = context
.get_slot::<SyntheticTextModuleSources>()
.unwrap_or_else(|| {
let sources = Rc::new(SyntheticTextModuleSources::default());
context.set_slot(sources.clone());
sources
});
let record = Rc::new(Self {
module: v8::Global::new(scope, module),
key,
source: source.to_owned(),
});
let mut entries = sources.entries.borrow_mut();
let candidates = entries
.entry(module_identity_hash_from_v8_module(module))
.or_default();
candidates.retain(|candidate| candidate.strong_count() != 0);
candidates.push(Rc::downgrade(&record));
record
}
pub(crate) fn for_module(
context: v8::Local<'_, v8::Context>,
module: v8::Local<'_, v8::Module>,
) -> Option<Rc<Self>> {
let sources = context.get_slot::<SyntheticTextModuleSources>()?;
let entries = sources.entries.borrow();
entries
.get(&module_identity_hash_from_v8_module(module))?
.iter()
.filter_map(Weak::upgrade)
// V8 identity hashes are not unique, even within one Context.
.find(|candidate| module == candidate.module)
}
pub(crate) fn key(&self) -> &ModuleMapKey {
&self.key
}
pub(crate) fn source(&self) -> &str {
&self.source
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::module_runtime::{ModuleAttributesKey, ModuleRecordEntry};
use url::Url;
fn unused_evaluation_steps<'s>(
context: v8::Local<'s, v8::Context>,
_module: v8::Local<'s, v8::Module>,
) -> Option<v8::Local<'s, v8::Value>> {
v8::callback_scope!(unsafe scope, context);
Some(v8::undefined(scope).into())
}
#[test]
fn synthetic_text_module_lookup_checks_context_and_exact_identity() {
crate::ensure_v8_for_test();
let mut isolate = v8::Isolate::new(Default::default());
let scope = std::pin::pin!(v8::HandleScope::new(&mut isolate));
let scope = &mut scope.init();
let context = v8::Context::new(scope, Default::default());
let scope = &mut v8::ContextScope::new(scope, context);
let name = v8::String::new(scope, "https://example.test/shared.json").unwrap();
let first = v8::Module::create_synthetic_module(scope, name, &[], unused_evaluation_steps);
let second = v8::Module::create_synthetic_module(scope, name, &[], unused_evaluation_steps);
let key = ModuleMapKey::json_with_attributes(
Url::parse("https://example.test/shared.json").unwrap(),
ModuleAttributesKey::empty(),
);
let first_source = SyntheticTextModuleSource::register(scope, first, key.clone(), "1");
let second_source = SyntheticTextModuleSource::register(scope, second, key, "2");
// Inject a conflicting candidate deterministically instead of waiting
// for a random V8 identity-hash collision.
let sources = context.get_slot::<SyntheticTextModuleSources>().unwrap();
sources
.entries
.borrow_mut()
.get_mut(&module_identity_hash_from_v8_module(second))
.unwrap()
.insert(0, Rc::downgrade(&first_source));
assert!(Rc::ptr_eq(
&SyntheticTextModuleSource::for_module(context, second).unwrap(),
&second_source,
));
let foreign = v8::Context::new(scope, Default::default());
assert!(SyntheticTextModuleSource::for_module(foreign, first).is_none());
}
#[test]
fn synthetic_text_module_lookup_does_not_keep_disposed_records_alive() {
crate::ensure_v8_for_test();
let mut isolate = v8::Isolate::new(Default::default());
let scope = std::pin::pin!(v8::HandleScope::new(&mut isolate));
let scope = &mut scope.init();
let context = v8::Context::new(scope, Default::default());
let scope = &mut v8::ContextScope::new(scope, context);
let name = v8::String::new(scope, "https://example.test/value.json").unwrap();
let module = v8::Module::create_synthetic_module(scope, name, &[], unused_evaluation_steps);
let key = ModuleMapKey::json_with_attributes(
Url::parse("https://example.test/value.json").unwrap(),
ModuleAttributesKey::empty(),
);
let source = SyntheticTextModuleSource::register(scope, module, key.clone(), "42");
let weak = Rc::downgrade(&source);
let record = ModuleRecordEntry::new(key, v8::Global::new(scope, module), Vec::new())
.with_synthetic_text_module_source(source);
let clone = record.clone();
drop(record);
assert_eq!(
SyntheticTextModuleSource::for_module(context, module)
.unwrap()
.source(),
"42"
);
drop(clone);
assert!(weak.upgrade().is_none());
assert!(SyntheticTextModuleSource::for_module(context, module).is_none());
}
}
@@ -2,10 +2,7 @@ use super::{JsContextHost, OwnerDispatchScope};
use crate::network::loads::{ResourceLoadDisposition, ResourceLoadKind, ResourceLoadLease};
use crate::types::DedicatedWorkerId;
use crate::{
module_runtime::{
ModuleAttributesKey, ModuleMapKey, ModuleSource, PendingDynamicModuleImport,
WasmModuleRecord,
},
module_runtime::{ModuleAttributesKey, PendingDynamicModuleImport, WasmModuleRecord},
page_task_queue::RendererResourceCompletionSender,
renderer_resource_scheduler::RendererResourceScheduler,
types::{
@@ -86,14 +83,6 @@ impl JsContextHost {
unsafe { &mut *self.runtime }.resolve_module_specifier(specifier, base_url)
}
pub(crate) fn native_module_source_for(
&self,
module: v8::Local<'_, v8::Module>,
) -> Option<(ModuleMapKey, ModuleSource)> {
// SAFETY: JsContextHost is owned by the ScriptVm that owns this DocumentRuntime.
unsafe { &*self.runtime }.native_module_source_for(module)
}
pub(crate) fn native_module_wasm_record_for(
&self,
module: v8::Local<'_, v8::Module>,
+45 -73
View File
@@ -3433,22 +3433,6 @@ impl ScriptVm {
})
}
fn compile_synthetic_module_record_in_context(
&mut self,
context_ptr: *const v8::Global<v8::Context>,
key: ModuleMapKey,
_source: &str,
source_url: &Url,
) -> std::result::Result<(ModuleRecordEntry, ModuleIdentityHash), ModuleLoadError> {
self.compile_synthetic_module_record_with_exports_in_context(
context_ptr,
key,
source_url,
&["default"],
None,
)
}
fn compile_wasm_module_record_in_context(
&mut self,
context_ptr: *const v8::Global<v8::Context>,
@@ -3499,7 +3483,7 @@ impl ScriptVm {
&scope,
module_name,
&export_names,
synthetic_module_evaluation_steps,
wasm_synthetic_module_evaluation_steps,
);
let identity = module_identity_hash_from_v8_module(module);
let compiled_module = v8::Global::new(scope.as_ref(), module);
@@ -3519,13 +3503,12 @@ impl ScriptVm {
})
}
fn compile_synthetic_module_record_with_exports_in_context(
fn compile_synthetic_module_record_in_context(
&mut self,
context_ptr: *const v8::Global<v8::Context>,
key: ModuleMapKey,
source: &str,
source_url: &Url,
export_names: &[&str],
wasm_record: Option<(Vec<ModuleRequestRecord>, WasmModuleRecord)>,
) -> std::result::Result<(ModuleRecordEntry, ModuleIdentityHash), ModuleLoadError> {
self.renderer_document_isolate
.with_entered_renderer_document_isolate(|isolate| {
@@ -3534,36 +3517,29 @@ impl ScriptVm {
let context = unsafe { v8::Local::new(scope, &*context_ptr) };
let scope = &mut v8::ContextScope::new(scope, context);
let try_catch = pin!(v8::TryCatch::new(scope));
let scope = try_catch.init();
let mut scope = try_catch.init();
let module_name = v8_string(&scope, source_url.as_str()).ok_or_else(|| {
anyhow::anyhow!("failed to allocate v8 synthetic module name")
})?;
let export_names = export_names
.iter()
.map(|name| {
v8_string(&scope, name).ok_or_else(|| {
anyhow::anyhow!("failed to allocate synthetic export name")
})
})
.collect::<Result<Vec<_>>>()?;
let default_export = v8_string(&scope, "default")
.ok_or_else(|| anyhow::anyhow!("failed to allocate synthetic export name"))?;
let module = v8::Module::create_synthetic_module(
&scope,
module_name,
&export_names,
synthetic_module_evaluation_steps,
&[default_export],
synthetic_text_module_evaluation_steps,
);
let identity = module_identity_hash_from_v8_module(module);
let evaluation_source = crate::module_runtime::SyntheticTextModuleSource::register(
&mut scope,
module,
key.clone(),
source,
);
let compiled_module = v8::Global::new(scope.as_ref(), module);
let entry = match wasm_record {
Some((requests, wasm_module)) => ModuleRecordEntry::new_with_wasm_module(
key,
compiled_module,
requests,
wasm_module,
),
None => ModuleRecordEntry::new(key, compiled_module, Vec::new()),
};
let entry = ModuleRecordEntry::new(key, compiled_module, Vec::new())
.with_synthetic_text_module_source(evaluation_source);
Ok((entry, identity))
})
.map_err(|error| ModuleLoadError::new(ModuleLoadStage::Compile, error.to_string()))
@@ -4639,7 +4615,27 @@ fn get_i64_reaction_data_slot<'s>(
lossless.then_some(value)
}
fn synthetic_module_evaluation_steps<'s>(
fn synthetic_text_module_evaluation_steps<'s>(
context: v8::Local<'s, v8::Context>,
module: v8::Local<'s, v8::Module>,
) -> Option<v8::Local<'s, v8::Value>> {
v8::callback_scope!(unsafe scope, context);
let Some(record) =
crate::module_runtime::SyntheticTextModuleSource::for_module(context, module)
else {
return throw_synthetic_module_error(scope, "synthetic module source is not available");
};
let source = record.source();
match record.key().kind() {
ModuleKind::Json => evaluate_json_synthetic_module(scope, module, source),
ModuleKind::Css => {
evaluate_css_synthetic_module(scope, module, record.key().url().as_str(), source)
}
_ => throw_synthetic_module_error(scope, "unexpected synthetic text module kind"),
}
}
fn wasm_synthetic_module_evaluation_steps<'s>(
context: v8::Local<'s, v8::Context>,
module: v8::Local<'s, v8::Module>,
) -> Option<v8::Local<'s, v8::Value>> {
@@ -4647,39 +4643,15 @@ fn synthetic_module_evaluation_steps<'s>(
let Some(host_ptr) = context_host_ptr_from_global_bridge(scope) else {
return throw_synthetic_module_error(scope, "synthetic module host is not available");
};
let Some((key, source)) = (unsafe { &*host_ptr }).native_module_source_for(module) else {
return throw_synthetic_module_error(scope, "synthetic module source is not available");
};
match key.kind() {
ModuleKind::Json => {
let Some(source) = source.text_source() else {
return throw_synthetic_module_error(scope, "JSON module source is not text");
};
evaluate_json_synthetic_module(scope, module, source)
}
ModuleKind::Css => {
let Some(source) = source.text_source() else {
return throw_synthetic_module_error(scope, "CSS module source is not text");
};
evaluate_css_synthetic_module(scope, module, key.url().as_str(), source)
}
ModuleKind::WebAssembly => {
let Some(wasm_record) = (unsafe { &*host_ptr }).native_module_wasm_record_for(module)
else {
return throw_synthetic_module_error(
scope,
"WebAssembly synthetic module record is not available",
);
};
evaluate_wasm_synthetic_module(scope, module, &wasm_record, |scope, import| {
wasm_import_value(scope, module, import)
})
}
ModuleKind::JavaScript | ModuleKind::ModulePreloadText => throw_synthetic_module_error(
let Some(wasm_record) = (unsafe { &*host_ptr }).native_module_wasm_record_for(module) else {
return throw_synthetic_module_error(
scope,
"non-synthetic module reached synthetic module evaluation",
),
}
"WebAssembly synthetic module record is not available",
);
};
evaluate_wasm_synthetic_module(scope, module, &wasm_record, |scope, import| {
wasm_import_value(scope, module, import)
})
}
fn evaluate_json_synthetic_module<'s>(