1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! `bun.jsc.RuntimeTranspilerCache` (src/jsc/RuntimeTranspilerCache.zig).
//!
//! Single canonical struct, lowered to `bun_ast` so both the parser
//! (`Features.runtime_transpiler_cache`) and the printer
//! (`Options.runtime_transpiler_cache`) can name it without a `bun_js_parser`
//! edge. `bun_bundler::cache` re-exports it and adds disk-I/O / `js_printer`
//! dispatch via an extension trait (those need `bun_js_printer` / `bun_sys`
//! which sit a tier above). The parser writes `input_hash` / `features_hash` /
//! `exports_kind` and calls `get()` through the vtable; the bundler/jsc tier
//! owns `entry` and the on-disk encode/decode (`Metadata` / `Entry` live in
//! `bun_bundler::cache` and are stored here type-erased as `*mut ()`).
use crate::{ExportsKind, Source};
use core::ptr::NonNull;
pub struct RuntimeTranspilerCache {
pub input_hash: Option<u64>,
pub input_byte_length: Option<u64>,
pub features_hash: Option<u64>,
pub exports_kind: ExportsKind,
/// Set by `put()` / `get()` when a cache hit returns transpiled output.
/// Zig: `?bun.String` — bundler/parser only store/read the bytes; T6 owns
/// the `bun.String` wrapper when surfacing to JS.
pub output_code: Option<Box<[u8]>>,
/// Opaque storage for `bun_bundler::cache::RuntimeTranspilerCacheEntry` —
/// the concrete type lives a tier up and is round-tripped via cast.
pub entry: Option<*mut ()>,
/// Dispatch slot — `bun_jsc` sets `Some(TranspilerCacheImplKind::Jsc)` at
/// init. `None` ⇒ caching disabled (e.g. wasm builds, `--no-transpiler-cache`).
pub r#impl: Option<TranspilerCacheImplKind>,
}
impl Default for RuntimeTranspilerCache {
fn default() -> Self {
Self {
input_hash: None,
input_byte_length: None,
features_hash: None,
exports_kind: ExportsKind::None,
output_code: None,
entry: None,
r#impl: None,
}
}
}
// Low tier (`bun_ast`) names no `bun_jsc` types; `bun_jsc` provides the
// `Jsc` arm. Cold path: at most twice per parse; callee does file I/O.
// `parser_options` is `*const ()` because `parser::Options` would be a forward
// edge here; the jsc impl casts it back.
bun_dispatch::link_interface! {
pub TranspilerCacheImpl[Jsc] {
fn get(source: &Source, parser_options: NonNull<()>, used_jsx: bool) -> bool;
fn put(output_code: &[u8], sourcemap: &[u8], esm_record: &[u8]);
fn is_disabled() -> bool;
}
}
impl RuntimeTranspilerCache {
/// Build the dispatch handle for the set-once `r#impl` slot.
///
/// Centralises the raw-pointer obligation so the three public entry
/// points below stay safe.
/// `this` is always derived from a live `&self` / `&mut self` in those
/// callers and the returned `Copy` handle is consumed immediately, so
/// the `link_interface!` liveness contract (owner valid for every
/// dispatch through the handle) is upheld.
#[inline]
fn handle(kind: TranspilerCacheImplKind, this: *mut Self) -> TranspilerCacheImpl {
// SAFETY: `this` is non-null, aligned, and live for the immediate
// dispatch at every call site (`get`/`put`: `&mut self`-derived with
// write provenance; `is_disabled`: `&self`-derived, impl ignores
// `this`). See `link_interface!` `new()` contract.
unsafe { TranspilerCacheImpl::new(kind, this) }
}
#[inline]
pub fn get(&mut self, source: &Source, parser_options: NonNull<()>, used_jsx: bool) -> bool {
match self.r#impl {
Some(k) => Self::handle(k, self).get(source, parser_options, used_jsx),
None => false,
}
}
#[inline]
pub fn put(&mut self, output_code: &[u8], sourcemap: &[u8], esm_record: &[u8]) {
match self.r#impl {
Some(k) => Self::handle(k, self).put(output_code, sourcemap, esm_record),
None => {
if self.input_hash.is_none() {
return;
}
debug_assert!(self.entry.is_none());
self.output_code = Some(Box::<[u8]>::from(output_code));
}
}
}
#[inline]
pub fn is_disabled(&self) -> bool {
match self.r#impl {
Some(k) => Self::handle(k, core::ptr::from_ref(self).cast_mut()).is_disabled(),
None => true,
}
}
}