Files
electron/shell/common/node_natives_code_cache_stub.cc
Samuel Attard d8571176d3 perf: build-time V8 code cache for the electron/js2c framework bundles (#51697)
* build: dedupe get-intrinsic to shrink the sandboxed renderer bundle

The hoisted get-intrinsic resolved to 1.2.1 (satisfying the older
^1.0.2..^1.2.0 consumers), so side-channel-weakmap, side-channel-map,
call-bound and es-set-tostringtag — which need ^1.2.5/^1.3.0 — each
pulled a nested get-intrinsic@1.3.0 copy. webpack bundled three of
those nested copies into sandbox_bundle.js (~16.5 KB each).

1.3.0 satisfies every range in the tree (all ^1.x), so pin it via
resolutions to collapse to a single copy. Drops sandbox_bundle.js
from 364,762 to 323,116 bytes (-41,646, -11.4%) — that bundle is
parsed/preparsed in every sandboxed renderer before the HTML parser,
so this is a direct per-renderer startup win on top of the IPC and
code-cache changes.

* perf: build-time V8 code cache for the electron/js2c framework bundles

Generate a V8 code cache for Electron's embedded electron/js2c/* bundles
at build time and embed it in the binary, so each process deserializes
its framework bundle (kConsumeCodeCache) instead of parsing and compiling
it from source at startup.

V8's code-cache key includes FlagList::Hash() over non-default flags and
the snapshot's read-only checksum, both of which differ per process type,
so one cache variant is generated per flavor and each process consumes
the matching one:

  sandbox  : sandboxed renderer, no Node env  (+SAB)
  renderer : normal renderer, has Node env    (+SAB +rehash +no-freeze)
  browser  : main process                     (+rehash +no-freeze)
  utility  : utility process                  (+rehash +no-freeze)
  worker   : node/web worker                  (+rehash +no-freeze)

All flavors create their isolate from the v8 context snapshot. The flag
deltas are deterministic; --no-freeze-flags-after-init (present iff the
process has a Node Environment) lets V8 recompute the flag-hash after
init, so the consume-time hash of every node-env process includes
--rehash-snapshot. A sandboxed renderer has no Node env (flags stay
frozen) so it is a distinct flavor, discriminated at runtime by
node::Environment::GetCurrent(context).

The generator compiles each bundle with kEagerCompile so the cache
covers the inner functions, not just the top-level wrapper -- the
framework bundles run in full at startup, so this front-loads the lazy
compiles to build time. Cross-arch is supported (the generator is built
in v8_snapshot_toolchain so it links V8 with V8_TARGET_ARCH set to the
target and reads the target's snapshot blob); cross-OS is untested and
gated off.

- electron_natives_codecache_main.cc: per-flavor build-time generator
  compiling each bundle through the same BuiltinLoader path its runtime
  consumer uses.
- shell/common/js2c_bundle_ids.h: shared bundle ids and wrapper-function
  param names used by the generator and the runtime CompileAndCall call
  sites, so the source/params hash matches by construction.
- node_util.cc feeds the standalone loader; FeedEnvironmentCodeCache
  feeds the per-Environment loader before LoadEnvironment so the *_init
  bundles are consumed.
- Test-only (DCHECK builds, compiled out of production): a per-process
  id->accepted status map exposed via v8_util.getJs2cCodeCacheStatus, a
  sandboxed-preload shim, and spec/api-js2c-code-cache-spec.ts which
  spawns a clean app and asserts every bundle is consumed in
  browser/sandbox/renderer/utility.

DidClearWindowObject (sandboxed renderer, pre-HTML-parser): ~9.8 ms
(cache rejected/absent) -> ~6.4 ms (eager cache consumed), a ~3.4 ms /
~35% cold-start win on every launch. No V8 patch.

* chore: export node patch for the js2c build-time code cache
2026-05-20 19:51:30 +00:00

38 lines
1.1 KiB
C++

// Copyright (c) 2026 Anthropic, PBC.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// Used when electron_generate_js2c_code_cache=false; all flavors return an
// empty set so the BuiltinLoader compiles from source, as before.
#include "shell/common/node_natives_code_cache_internal.h"
#include "base/no_destructor.h"
#include "shell/common/node_includes.h"
namespace electron::internal {
static const std::vector<node::builtins::CodeCacheInfo>& Empty() {
static const base::NoDestructor<std::vector<node::builtins::CodeCacheInfo>>
kEmpty;
return *kEmpty;
}
const std::vector<node::builtins::CodeCacheInfo>& Js2cCacheSandbox() {
return Empty();
}
const std::vector<node::builtins::CodeCacheInfo>& Js2cCacheRenderer() {
return Empty();
}
const std::vector<node::builtins::CodeCacheInfo>& Js2cCacheBrowser() {
return Empty();
}
const std::vector<node::builtins::CodeCacheInfo>& Js2cCacheUtility() {
return Empty();
}
const std::vector<node::builtins::CodeCacheInfo>& Js2cCacheWorker() {
return Empty();
}
} // namespace electron::internal