[dart2js] Use symbols for isolate tags

All supported browsers have JavaScript Symbols so use Symbols.
Avoiding string property names should fix a bug where separate
programs running in separate iframes arrive at using the same
property.

Issue: #53154
Change-Id: I470dc47de3ad381aeab670cf62d62e53f2e72873
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/319865
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
Stephen Adams 2023-08-10 23:42:16 +00:00 committed by Commit Queue
parent ab7f49166e
commit 58f4b3a7a6
8 changed files with 33 additions and 122 deletions

View file

@ -11,6 +11,5 @@ export 'interceptor_stub_generator.dart';
export 'main_call_stub_generator.dart'; export 'main_call_stub_generator.dart';
export 'metadata_collector.dart'; export 'metadata_collector.dart';
export 'native_emitter.dart'; export 'native_emitter.dart';
export 'native_generator.dart';
export 'parameter_stub_generator.dart'; export 'parameter_stub_generator.dart';
export 'runtime_type_generator.dart'; export 'runtime_type_generator.dart';

View file

@ -1,77 +0,0 @@
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.js_emitter.native_generator;
import 'package:js_runtime/synced/embedded_names.dart' as embeddedNames;
import '../js/js.dart' as jsAst;
import '../js/js.dart' show js;
import '../js_backend/backend_usage.dart' show BackendUsage;
class NativeGenerator {
static bool needsIsolateAffinityTagInitialization(BackendUsage backendUsage) {
return backendUsage.needToInitializeIsolateAffinityTag;
}
/// Generates the code for isolate affinity tags.
///
/// Independently Dart programs on the same page must not interfere and
/// this code sets up the variables needed to guarantee that behavior.
static jsAst.Statement generateIsolateAffinityTagInitialization(
BackendUsage backendUsage,
jsAst.Expression generateEmbeddedGlobalAccess(String global),
jsAst.Expression internStringFunction) {
assert(backendUsage.needToInitializeIsolateAffinityTag);
jsAst.Expression getIsolateTagAccess =
generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG);
jsAst.Expression isolateTagAccess =
generateEmbeddedGlobalAccess(embeddedNames.ISOLATE_TAG);
jsAst.Expression dispatchPropertyNameAccess =
generateEmbeddedGlobalAccess(embeddedNames.DISPATCH_PROPERTY_NAME);
return js.statement('''
!function() {
var intern = #internStringFunction;
#getIsolateTag = function(name) {
return intern("___dart_" + name + #isolateTag);
};
// To ensure that different programs loaded into the same context (page)
// use distinct dispatch properties, we place an object on `Object` to
// contain the names already in use.
var tableProperty = "___dart_isolate_tags_";
var usedProperties = Object[tableProperty] ||
(Object[tableProperty] = Object.create(null));
var rootProperty = "_${generateIsolateTagRoot()}";
for (var i = 0; ; i++) {
var property = intern(rootProperty + "_" + i + "_");
if (!(property in usedProperties)) {
usedProperties[property] = 1;
#isolateTag = property;
break;
}
}
if (#initializeDispatchProperty) {
#dispatchPropertyName = #getIsolateTag("dispatch_record");
}
}();
''', {
'initializeDispatchProperty':
backendUsage.needToInitializeDispatchProperty,
'internStringFunction': internStringFunction,
'getIsolateTag': getIsolateTagAccess,
'isolateTag': isolateTagAccess,
'dispatchPropertyName': dispatchPropertyNameAccess
});
}
static String generateIsolateTagRoot() {
// TODO(sra): MD5 of contributing source code or URIs?
return 'ZxYxX';
}
}

View file

@ -1900,6 +1900,11 @@ class FragmentEmitter {
js.string(RECORD_TYPE_TEST_COMBINATORS_PROPERTY), recordStubs)); js.string(RECORD_TYPE_TEST_COMBINATORS_PROPERTY), recordStubs));
} }
if (_closedWorld.backendUsage.needToInitializeDispatchProperty) {
globals.add(js.Property(js.string(DISPATCH_PROPERTY_NAME),
js.js('Symbol("dispatch_property")')));
}
js.ObjectInitializer globalsObject = js.ObjectInitializer globalsObject =
js.ObjectInitializer(globals, isOneLiner: false); js.ObjectInitializer(globals, isOneLiner: false);
@ -2060,22 +2065,6 @@ class FragmentEmitter {
js.Statement emitNativeSupport(Fragment fragment) { js.Statement emitNativeSupport(Fragment fragment) {
List<js.Statement> statements = []; List<js.Statement> statements = [];
// The isolate-affinity tag must only be initialized once per program.
if (fragment.isMainFragment &&
NativeGenerator.needsIsolateAffinityTagInitialization(
_closedWorld.backendUsage)) {
statements.add(NativeGenerator.generateIsolateAffinityTagInitialization(
_closedWorld.backendUsage, generateEmbeddedGlobalAccess, js.js("""
// On V8, the 'intern' function converts a string to a symbol, which
// makes property access much faster.
// TODO(sra): Use Symbol on non-IE11 browsers.
function (s) {
var o = {};
o[s] = 1;
return Object.keys(hunkHelpers.convertToFastObject(o))[0];
}""", [])));
}
Map<String, js.Expression> interceptorsByTag = {}; Map<String, js.Expression> interceptorsByTag = {};
Map<String, js.Expression> leafTags = {}; Map<String, js.Expression> leafTags = {};
List<js.Statement> subclassAssignments = []; List<js.Statement> subclassAssignments = [];

View file

@ -12,6 +12,7 @@ import 'package:js_runtime/synced/embedded_names.dart'
DEFERRED_LIBRARY_PARTS, DEFERRED_LIBRARY_PARTS,
DEFERRED_PART_URIS, DEFERRED_PART_URIS,
DEFERRED_PART_HASHES, DEFERRED_PART_HASHES,
DISPATCH_PROPERTY_NAME,
INITIALIZATION_EVENT_LOG, INITIALIZATION_EVENT_LOG,
INITIALIZE_LOADED_HUNK, INITIALIZE_LOADED_HUNK,
INTERCEPTORS_BY_TAG, INTERCEPTORS_BY_TAG,

View file

@ -66,17 +66,17 @@ part 'js_array.dart';
part 'js_number.dart'; part 'js_number.dart';
part 'js_string.dart'; part 'js_string.dart';
final String DART_CLOSURE_PROPERTY_NAME = final JavaScriptSymbol DART_CLOSURE_PROPERTY_NAME =
getIsolateAffinityTag(r'_$dart_dartClosure'); getIsolateAffinityTag(r'_$dart_dartClosure');
getDispatchProperty(object) { getDispatchProperty(object) {
return JS( return JS('', '#[#]', object,
'', '#[#]', object, JS_EMBEDDED_GLOBAL('String', DISPATCH_PROPERTY_NAME)); JS_EMBEDDED_GLOBAL('JavaScriptSymbol', DISPATCH_PROPERTY_NAME));
} }
setDispatchProperty(object, value) { setDispatchProperty(object, value) {
defineProperty( defineProperty(object,
object, JS_EMBEDDED_GLOBAL('String', DISPATCH_PROPERTY_NAME), value); JS_EMBEDDED_GLOBAL('JavaScriptSymbol', DISPATCH_PROPERTY_NAME), value);
} }
// Avoid inlining this method because inlining gives us multiple allocation // Avoid inlining this method because inlining gives us multiple allocation
@ -191,11 +191,8 @@ getNativeInterceptor(object) {
return JS_INTERCEPTOR_CONSTANT(UnknownJavaScriptObject); return JS_INTERCEPTOR_CONSTANT(UnknownJavaScriptObject);
} }
// A JS String or Symbol. final JavaScriptSymbol JS_INTEROP_INTERCEPTOR_TAG =
dynamic _JS_INTEROP_INTERCEPTOR_TAG = null; getIsolateAffinityTag(r'_$dart_js');
get JS_INTEROP_INTERCEPTOR_TAG {
return _JS_INTEROP_INTERCEPTOR_TAG ??= getIsolateAffinityTag(r'_$dart_js');
}
lookupInterceptorByConstructor(constructor) { lookupInterceptorByConstructor(constructor) {
return constructor == null return constructor == null

View file

@ -2808,15 +2808,12 @@ String jsonEncodeNative(String string) {
return JS('String', 'JSON.stringify(#)', string); return JS('String', 'JSON.stringify(#)', string);
} }
/// Returns a property name for placing data on JavaScript objects shared /// Returns a property symbol for this isolate to place private data on
/// between DOM isolates. This happens when multiple programs are loaded in the /// JavaScript objects where we need to avoid other isolates from seeing the
/// same JavaScript context (i.e. page). The name is based on [name] but with /// data. This happens when multiple programs are loaded in the same JavaScript
/// an additional part that is unique for each isolate. /// context (i.e. page). The property is based on [name].
/// JavaScriptSymbol getIsolateAffinityTag(String name) {
/// The form of the name is '___dart_$name_$id'. return JS('JavaScriptSymbol', 'Symbol(#)', name);
String getIsolateAffinityTag(String name) {
var isolateTagGetter = JS_EMBEDDED_GLOBAL('', GET_ISOLATE_TAG);
return JS('String', '#(#)', isolateTagGetter, name);
} }
final Map<String, Completer<Null>?> _loadingLibraries = {}; final Map<String, Completer<Null>?> _loadingLibraries = {};

View file

@ -7,7 +7,7 @@ import 'dart:collection' show HashMap, ListMixin;
import 'dart:typed_data' show TypedData; import 'dart:typed_data' show TypedData;
import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS; import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
import 'dart:_interceptors' show DART_CLOSURE_PROPERTY_NAME; import 'dart:_interceptors' show DART_CLOSURE_PROPERTY_NAME, JavaScriptSymbol;
import 'dart:_internal' show patch; import 'dart:_internal' show patch;
import 'dart:_js_helper' import 'dart:_js_helper'
show show
@ -17,6 +17,10 @@ import 'dart:_js_helper'
JS_FUNCTION_PROPERTY_NAME; JS_FUNCTION_PROPERTY_NAME;
import 'dart:_js' show isBrowserObject, convertFromBrowserObject; import 'dart:_js' show isBrowserObject, convertFromBrowserObject;
// TODO(sra): Replace with an extension type when they become available.
typedef _JavaScriptProperty = Object; // String | JavaScriptSymbol
@patch @patch
JsObject get context => _context; JsObject get context => _context;
@ -371,7 +375,7 @@ class JsArray<E> /*extends JsObject with ListMixin<E>*/ {
} }
// property added to a Dart object referencing its JS-side DartObject proxy // property added to a Dart object referencing its JS-side DartObject proxy
final String _DART_OBJECT_PROPERTY_NAME = final JavaScriptSymbol _DART_OBJECT_PROPERTY_NAME =
getIsolateAffinityTag(r'_$dart_dartObject'); getIsolateAffinityTag(r'_$dart_dartObject');
// property added to a JS object referencing its Dart-side JsObject proxy // property added to a JS object referencing its Dart-side JsObject proxy
@ -380,7 +384,7 @@ const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject';
@pragma('dart2js:tryInline') @pragma('dart2js:tryInline')
JsObject _castToJsObject(o) => JS<JsObject>('', '#', o); JsObject _castToJsObject(o) => JS<JsObject>('', '#', o);
bool _defineProperty(o, String name, value) { bool _defineProperty(o, _JavaScriptProperty name, value) {
try { try {
if (_isExtensible(o) && if (_isExtensible(o) &&
// TODO(ahe): Calling _hasOwnProperty to work around // TODO(ahe): Calling _hasOwnProperty to work around
@ -397,13 +401,13 @@ bool _defineProperty(o, String name, value) {
return false; return false;
} }
bool _hasOwnProperty(o, String name) { bool _hasOwnProperty(o, _JavaScriptProperty name) {
return JS('bool', 'Object.prototype.hasOwnProperty.call(#, #)', o, name); return JS('bool', 'Object.prototype.hasOwnProperty.call(#, #)', o, name);
} }
bool _isExtensible(o) => JS('bool', 'Object.isExtensible(#)', o); bool _isExtensible(o) => JS('bool', 'Object.isExtensible(#)', o);
Object? _getOwnProperty(o, String name) { Object? _getOwnProperty(o, _JavaScriptProperty name) {
if (_hasOwnProperty(o, name)) { if (_hasOwnProperty(o, name)) {
return JS('', '#[#]', o, name); return JS('', '#[#]', o, name);
} }
@ -491,8 +495,8 @@ Object _wrapToDart(o) {
o, _DART_OBJECT_PROPERTY_NAME, (o) => JsObject._fromJs(o)); o, _DART_OBJECT_PROPERTY_NAME, (o) => JsObject._fromJs(o));
} }
Object _getDartProxy(o, String propertyName, JsObject createProxy(o)) { Object _getDartProxy(o, _JavaScriptProperty property, JsObject createProxy(o)) {
var dartProxy = _getOwnProperty(o, propertyName); var dartProxy = _getOwnProperty(o, property);
// Temporary fix for dartbug.com/15193 // Temporary fix for dartbug.com/15193
// In some cases it's possible to see a JavaScript object that // In some cases it's possible to see a JavaScript object that
// came from a different context and was previously proxied to // came from a different context and was previously proxied to
@ -502,7 +506,7 @@ Object _getDartProxy(o, String propertyName, JsObject createProxy(o)) {
// to cache proxies from multiple JS contexts and Dart isolates. // to cache proxies from multiple JS contexts and Dart isolates.
if (dartProxy == null || !_isLocalObject(o)) { if (dartProxy == null || !_isLocalObject(o)) {
dartProxy = createProxy(o); dartProxy = createProxy(o);
_defineProperty(o, propertyName, dartProxy); _defineProperty(o, property, dartProxy);
} }
return dartProxy; return dartProxy;
} }

View file

@ -68,7 +68,8 @@ String toStringForNativeObject(var obj) {
int hashCodeForNativeObject(object) => Primitives.objectHashCode(object); int hashCodeForNativeObject(object) => Primitives.objectHashCode(object);
/// Sets a JavaScript property on an object. /// Sets a JavaScript property on an object.
void defineProperty(var obj, String property, var value) { void defineProperty(
var obj, Object /*String | JavaScriptSymbol*/ property, var value) {
JS( JS(
'void', 'void',
'Object.defineProperty(#, #, ' 'Object.defineProperty(#, #, '