From 2d21ca796ad693d39faecbc469f084ed75dcf174 Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Thu, 27 Mar 2025 15:33:43 -0700 Subject: [PATCH] [dart2js] Cache globalThis Feature-test for `self` and `globalThis`, and cache the result. The cached access has smaller minified code size than either `self` or `globalThis`. All older browsers that we have supported that don't have `globalThis` do have `self`. We prioritize `self` over `globalThis`. They are usually the same but it is possible that there are programs which run in an environment that has replaced `self`, for example, a test might do that. We don't want to break existing tests. Bug: #59982 Change-Id: I11d19183d85a73e9552b66a2f49ac99be10c3c83 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417912 Reviewed-by: Srujan Gaddam Commit-Queue: Stephen Adams --- .../js_emitter/startup_emitter/fragment_emitter.dart | 6 ++++++ .../js_emitter/startup_emitter/model_emitter.dart | 1 + pkg/compiler/lib/src/native/behavior.dart | 7 ++++++- pkg/compiler/lib/src/ssa/nodes.dart | 9 +++++++-- pkg/js_runtime/lib/synced/embedded_names.dart | 4 ++++ sdk/lib/_internal/js_runtime/lib/js_helper.dart | 12 +++++++++++- .../js_runtime/lib/synced/embedded_names.dart | 4 ++++ 7 files changed, 39 insertions(+), 4 deletions(-) diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart index 6e477a1309e..80a822d9c19 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart @@ -2011,6 +2011,12 @@ class FragmentEmitter { ) { List globals = []; + globals.add( + js.Property( + js.string(CACHED_GLOBAL_THIS), + js.js(r'typeof self != "undefined" ? self : globalThis'), + ), + ); if (fragmentsToLoad.isNotEmpty) { globals.addAll( emitEmbeddedGlobalsForDeferredLoading( diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart index 8fa1daced12..ecad0870b1b 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart @@ -10,6 +10,7 @@ import 'package:js_runtime/synced/array_flags.dart' show ArrayFlags; import 'package:js_runtime/synced/embedded_names.dart' show + CACHED_GLOBAL_THIS, DEFERRED_INITIALIZED, DEFERRED_LIBRARY_PARTS, DEFERRED_PART_URIS, diff --git a/pkg/compiler/lib/src/native/behavior.dart b/pkg/compiler/lib/src/native/behavior.dart index df7386917f6..8d051497ca4 100644 --- a/pkg/compiler/lib/src/native/behavior.dart +++ b/pkg/compiler/lib/src/native/behavior.dart @@ -654,6 +654,10 @@ class NativeBehavior { behavior.sideEffects.setTo(newEffects); } + void setUseGvn(bool useGvn) { + behavior.useGvn = useGvn; + } + processSpecString( commonElements.dartTypes, reporter, @@ -662,6 +666,7 @@ class NativeBehavior { validTags: validTags, lookupType: lookupType, setSideEffects: setSideEffects, + setUseGvn: setUseGvn, typesReturned: behavior.typesReturned, typesInstantiated: behavior.typesInstantiated, objectType: commonElements.objectType, @@ -709,7 +714,7 @@ class NativeBehavior { lookupType, reporter, commonElements, - validTags: ['returns', 'creates'], + validTags: ['returns', 'creates', 'depends', 'effects', 'gvn'], ); return behavior; } diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index 8f9b2da92c2..9fd434a0dda 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -2723,8 +2723,13 @@ class HForeignCode extends HForeign { bool typeEquals(other) => other is HForeignCode; @override bool dataEquals(HForeignCode other) { - return codeTemplate.source != null && - codeTemplate.source == other.codeTemplate.source; + if (codeTemplate.source == null) { + return other.codeTemplate.source == null && + // The ASTs will be equal if identical, and in some limited other + // cases, like a ModularExpression. + codeTemplate.ast == other.codeTemplate.ast; + } + return codeTemplate.source == other.codeTemplate.source; } @override diff --git a/pkg/js_runtime/lib/synced/embedded_names.dart b/pkg/js_runtime/lib/synced/embedded_names.dart index 4dcba62529f..b8f7e11fae0 100644 --- a/pkg/js_runtime/lib/synced/embedded_names.dart +++ b/pkg/js_runtime/lib/synced/embedded_names.dart @@ -183,6 +183,10 @@ const STARTUP_METRICS = 'sm'; // TODO(51016): This might be moved to improve deferred loading. const RECORD_TYPE_TEST_COMBINATORS_PROPERTY = 'rttc'; +/// An embedded global that contains the value of `globalThis`. Unlike many +/// embedded globals, this one has many references, so we choose short name. +const CACHED_GLOBAL_THIS = 'G'; + /// Names of fields of collected tear-off parameters object. /// /// Tear-off getters are created before the Dart classes are initialized, so a diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index 04b070ca452..bfc1eb740f9 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -6,6 +6,7 @@ library _js_helper; import 'dart:_js_embedded_names' show + CACHED_GLOBAL_THIS, CURRENT_SCRIPT, DEFERRED_LIBRARY_PARTS, DEFERRED_PART_URIS, @@ -3953,8 +3954,17 @@ abstract class TrustedGetRuntimeType {} /// `pkg/_js_interop_checks/lib/src/js_util_optimizer.dart`. /// /// This should match the global context that non-static interop members use. +@pragma('dart2js:prefer-inline') +// Unlike `JS(...)`, `JS_EMBEDDED_GLOBAL` is not generic. So we need to cast +// the dynamic result to the desired type. We know the result is some kind of +// non-null, non-Dart object. +@pragma('dart2js:as:trust') Object get staticInteropGlobalContext => - JS('creates:;returns:Object;depends:none;effects:none;gvn:true', 'self'); + JS_EMBEDDED_GLOBAL( + 'creates:;returns:JSObject;depends:none;effects:none;gvn:true', + CACHED_GLOBAL_THIS, + ) + as Object; /// Return a fresh object literal. T createObjectLiteral() => JS('PlainJavaScriptObject', '{}'); diff --git a/sdk/lib/_internal/js_runtime/lib/synced/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/synced/embedded_names.dart index 4dcba62529f..b8f7e11fae0 100644 --- a/sdk/lib/_internal/js_runtime/lib/synced/embedded_names.dart +++ b/sdk/lib/_internal/js_runtime/lib/synced/embedded_names.dart @@ -183,6 +183,10 @@ const STARTUP_METRICS = 'sm'; // TODO(51016): This might be moved to improve deferred loading. const RECORD_TYPE_TEST_COMBINATORS_PROPERTY = 'rttc'; +/// An embedded global that contains the value of `globalThis`. Unlike many +/// embedded globals, this one has many references, so we choose short name. +const CACHED_GLOBAL_THIS = 'G'; + /// Names of fields of collected tear-off parameters object. /// /// Tear-off getters are created before the Dart classes are initialized, so a