diff --git a/src/runtime/bake/BakeGlobalObject.cpp b/src/runtime/bake/BakeGlobalObject.cpp index 766b100614fe..d962472c9a77 100644 --- a/src/runtime/bake/BakeGlobalObject.cpp +++ b/src/runtime/bake/BakeGlobalObject.cpp @@ -8,6 +8,7 @@ #include "JavaScriptCore/Completion.h" #include "JavaScriptCore/JSSourceCode.h" +// These (and BakeProdLoad below) return an owned reference: consume it with transferToWTFString(). extern "C" BunString BakeProdResolve(JSC::JSGlobalObject*, BunString a, BunString b); extern "C" BunString BakeToWindowsPath(BunString a); @@ -45,7 +46,7 @@ bakeModuleLoaderImportModule(JSC::JSGlobalObject* global, BunString result = BakeProdResolve(global, Bun::toString(refererString), Bun::toString(keyString)); RETURN_IF_EXCEPTION(scope, nullptr); - return JSC::importModule(global, JSC::Identifier::fromString(vm, result.toWTFString()), + return JSC::importModule(global, JSC::Identifier::fromString(vm, result.transferToWTFString()), JSC::Identifier(), WTF::move(parameters), nullptr); } @@ -71,7 +72,7 @@ JSC::Identifier bakeModuleLoaderResolve(JSC::JSGlobalObject* jsGlobal, BunString result = BakeProdResolve(global, Bun::toString(referrer.getString(global)), Bun::toString(keyString)); RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier); - return JSC::Identifier::fromString(vm, result.toWTFString(BunString::ZeroCopy)); + return JSC::Identifier::fromString(vm, result.transferToWTFString()); } } @@ -137,7 +138,7 @@ JSC::JSPromise* bakeModuleLoaderFetch(JSC::JSGlobalObject* globalObject, JSC::SourceOrigin origin = JSC::SourceOrigin(WTF::URL(moduleKey)); JSC::SourceCode sourceCode = JSC::SourceCode(Bake::SourceProvider::create( globalObject, - source.toWTFString(), + source.transferToWTFString(), origin, WTF::move(moduleKey), WTF::TextPosition(), @@ -159,7 +160,7 @@ JSC::JSPromise* bakeModuleLoaderFetch(JSC::JSGlobalObject* globalObject, // it, because `moduleLoaderFetch(...)` may read the path from disk // and so we need to give a Windows path to it. auto temp = BakeToWindowsPath(Bun::toString(bakePrefixRemoved)); - bakePrefixRemoved = temp.toWTFString(); + bakePrefixRemoved = temp.transferToWTFString(); #endif JSString* bakePrefixRemovedString = jsNontrivialString(vm, bakePrefixRemoved); JSValue bakePrefixRemovedJsvalue = bakePrefixRemovedString; diff --git a/src/runtime/bake/BakeSourceProvider.cpp b/src/runtime/bake/BakeSourceProvider.cpp index b337c9f3f218..a951e855aaef 100644 --- a/src/runtime/bake/BakeSourceProvider.cpp +++ b/src/runtime/bake/BakeSourceProvider.cpp @@ -58,6 +58,7 @@ extern "C" JSC::JSPromise* BakeLoadModuleByKey(GlobalObject* global, JSC::JSStri return JSC::loadAndEvaluateModule(global, key->getString(global), nullptr, nullptr); } +// Both HMR patch entry points take ownership of `source` (DevServer.rs passes an OwnedString). extern "C" JSC::EncodedJSValue BakeLoadServerHmrPatch(GlobalObject* global, BunString source) { JSC::VM&vm = global->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -66,7 +67,7 @@ extern "C" JSC::EncodedJSValue BakeLoadServerHmrPatch(GlobalObject* global, BunS JSC::SourceOrigin origin = JSC::SourceOrigin(WTF::URL(string)); JSC::SourceCode sourceCode = JSC::SourceCode(SourceProvider::create( global, - source.toWTFString(), + source.transferToWTFString(), origin, WTF::move(string), WTF::TextPosition(), @@ -90,7 +91,7 @@ extern "C" JSC::EncodedJSValue BakeLoadServerHmrPatchWithSourceMap(GlobalObject* // Use DevServerSourceProvider with the source map JSON auto provider = DevServerSourceProvider::create( global, - source.toWTFString(), + source.transferToWTFString(), sourceMapJSONPtr, sourceMapJSONLength, origin, diff --git a/src/runtime/bake/DevServer.rs b/src/runtime/bake/DevServer.rs index e8f6e34786c5..ad48b6daad6e 100644 --- a/src/runtime/bake/DevServer.rs +++ b/src/runtime/bake/DevServer.rs @@ -4254,7 +4254,7 @@ pub(super) fn finalize_bundle( match c::bake_load_server_hmr_patch_with_source_map( global, - BunString::clone_utf8(&server_bundle), + OwnedString::new(BunString::clone_utf8(&server_bundle)), json.as_ptr(), json.len(), ) { @@ -4272,7 +4272,10 @@ pub(super) fn finalize_bundle( } } } else { - match c::bake_load_server_hmr_patch(global, BunString::clone_latin1(&server_bundle)) { + match c::bake_load_server_hmr_patch( + global, + OwnedString::new(BunString::clone_latin1(&server_bundle)), + ) { Ok(v) => v, Err(err) => { // SAFETY: vm is JSC_BORROW — valid for DevServer lifetime; @@ -5560,19 +5563,21 @@ impl DevServer { mod c { use super::*; + /// The C++ side releases `code` (transferToWTFString). pub(super) fn bake_load_server_hmr_patch( global: &JSGlobalObject, - code: BunString, + code: OwnedString, ) -> JsResult { unsafe extern "C" { safe fn BakeLoadServerHmrPatch(global: &JSGlobalObject, code: BunString) -> JSValue; } - jsc::from_js_host_call(global, || BakeLoadServerHmrPatch(global, code)) + jsc::from_js_host_call(global, || BakeLoadServerHmrPatch(global, code.into_inner())) } + /// The C++ side releases `code` (transferToWTFString). pub(super) fn bake_load_server_hmr_patch_with_source_map( global: &JSGlobalObject, - code: BunString, + code: OwnedString, source_map_json_ptr: *const u8, source_map_json_len: usize, ) -> JsResult { @@ -5595,7 +5600,7 @@ mod c { jsc::from_js_host_call(global, || unsafe { BakeLoadServerHmrPatchWithSourceMap( global, - code, + code.into_inner(), source_map_json_ptr, source_map_json_len, ) diff --git a/src/runtime/bake/production.rs b/src/runtime/bake/production.rs index 81c32d956928..05957c95f5cf 100644 --- a/src/runtime/bake/production.rs +++ b/src/runtime/bake/production.rs @@ -20,8 +20,8 @@ use bun_bundler::options::{self as bundler_options, OutputFile, SourceMapOption} use bun_bundler::output_file::Index as OutputFileIndex; use bun_collections::{AutoBitSet, StringArrayHashMap}; -use bun_core::String as BunString; use bun_core::{Global, Output}; +use bun_core::{OwnedString, String as BunString}; use bun_dotenv as dotenv; use bun_jsc::js_promise::{UnwrapMode, Unwrapped}; use bun_jsc::virtual_machine::VirtualMachine; @@ -332,8 +332,9 @@ fn build_with_vm(ctx: Context, cwd: &[u8], pt: &mut PerThread) -> crate::Result< } }; - let config_entry_point_string = - BunString::clone_utf8(config_entry_point.path_const().unwrap().text); + let config_entry_point_string = OwnedString::new(BunString::clone_utf8( + config_entry_point.path_const().unwrap().text, + )); let Some(config_promise) = JSModuleLoader::load_and_evaluate_module_ptr(vm.global, Some(&config_entry_point_string)) @@ -657,7 +658,9 @@ fn build_with_vm(ctx: Context, cwd: &[u8], pt: &mut PerThread) -> crate::Result< // Client files go to disk. // Server files get loaded in memory. // Populate indexes in `entry_points` to be looked up during prerendering - let mut module_keys: Vec = vec![BunString::dead(); entry_points.files.count()]; + let mut module_keys: Vec = (0..entry_points.files.count()) + .map(|_| OwnedString::new(BunString::dead())) + .collect(); let mut output_module_map: StringArrayHashMap = StringArrayHashMap::default(); let mut source_maps: StringArrayHashMap = StringArrayHashMap::default(); { @@ -748,7 +751,7 @@ fn build_with_vm(ctx: Context, cwd: &[u8], pt: &mut PerThread) -> crate::Result< BStr::new(without_prefix) )); str.to_thread_safe(); - module_keys[entry_point_index as usize] = str; + module_keys[entry_point_index as usize] = OwnedString::new(str); } } @@ -825,15 +828,14 @@ fn build_with_vm(ctx: Context, cwd: &[u8], pt: &mut PerThread) -> crate::Result< for (i, router_type) in router.types.iter().enumerate() { if let Some(client_file) = router_type.client_file { - let str = BunString::create_format(format_args!( + let mut url = BunString::create_format(format_args!( "{}{}", BStr::new(public_path), BStr::new(&pt.output_file(client_file).dest_path), - )) - .to_js(global) - .map_err(js_err)?; + )); + let url = jsc::bun_string_jsc::transfer_to_js(&mut url, global).map_err(js_err)?; client_entry_urls - .put_index(global, u32::try_from(i).expect("int cast"), str) + .put_index(global, u32::try_from(i).expect("int cast"), url) .map_err(js_err)?; } else { client_entry_urls @@ -928,15 +930,15 @@ fn build_with_vm(ctx: Context, cwd: &[u8], pt: &mut PerThread) -> crate::Result< debug_assert!(output_file.dest_path[0] != b'.'); // CSS chunks must be in contiguous order!! debug_assert!(output_file.loader.is_css()); + let mut url = BunString::create_format(format_args!( + "{}{}", + BStr::new(public_path), + BStr::new(&output_file.dest_path), + )); css_chunk_js_strings.push( - BunString::create_format(format_args!( - "{}{}", - BStr::new(public_path), - BStr::new(&output_file.dest_path), - )) - .to_js(global) - .map_err(js_err)? - .protected(), + jsc::bun_string_jsc::transfer_to_js(&mut url, global) + .map_err(js_err)? + .protected(), ); } @@ -1104,12 +1106,12 @@ fn build_with_vm(ctx: Context, cwd: &[u8], pt: &mut PerThread) -> crate::Result< } // Init the items - let pattern_string = BunString::clone_utf8(pattern.slice()); + let mut pattern_string = BunString::clone_utf8(pattern.slice()); route_patterns .put_index( global, u32::try_from(nav_index).expect("int cast"), - pattern_string.to_js(global).map_err(js_err)?, + jsc::bun_string_jsc::transfer_to_js(&mut pattern_string, global).map_err(js_err)?, ) .map_err(js_err)?; @@ -1448,7 +1450,7 @@ pub struct PerThread { pub(crate) entry_points: EntryPointMap, pub(crate) bundled_outputs: Vec, /// Indexed by entry point index (OpaqueFileId) - pub(crate) module_keys: Vec, + pub(crate) module_keys: Vec, /// Unordered pub(crate) module_map: StringArrayHashMap, pub(crate) source_maps: StringArrayHashMap, @@ -1513,7 +1515,7 @@ impl PerThread { vm: *mut VirtualMachine, entry_points: EntryPointMap, bundled_outputs: Vec, - module_keys: Vec, + module_keys: Vec, module_map: StringArrayHashMap, source_maps: StringArrayHashMap, ) -> crate::Result { diff --git a/test/bake/dev/production.test.ts b/test/bake/dev/production.test.ts index 1335e3244c00..cc98f61e9865 100644 --- a/test/bake/dev/production.test.ts +++ b/test/bake/dev/production.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { existsSync } from "fs"; -import { bunEnv, bunExe } from "harness"; +import { bunEnv, bunExe, isASAN, isWindows } from "harness"; import path from "path"; import { tempDirWithBakeDeps } from "../bake-harness"; @@ -594,4 +594,134 @@ export default function IndexPage() { // Verify NO JavaScript imports are included in the HTML expect(htmlContent).not.toContain('