mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
[dart2js] Add interceptors for JavaScript Symbol and BigInt
The interceptors provide a Dart `toString` method that uses the JavaScript `toString` method. Issue: #53106 Change-Id: I1cf1df9e24fb4fd2d79679f1f014f39f083be7e9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/319563 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
parent
aba5663d0a
commit
7f08f8e494
|
@ -568,9 +568,15 @@ abstract class CommonElements {
|
||||||
late final ClassEntity jsUnknownJavaScriptObjectClass =
|
late final ClassEntity jsUnknownJavaScriptObjectClass =
|
||||||
_findInterceptorsClass('UnknownJavaScriptObject');
|
_findInterceptorsClass('UnknownJavaScriptObject');
|
||||||
|
|
||||||
|
late final ClassEntity jsJavaScriptBigIntClass =
|
||||||
|
_findInterceptorsClass('JavaScriptBigInt');
|
||||||
|
|
||||||
late final ClassEntity jsJavaScriptFunctionClass =
|
late final ClassEntity jsJavaScriptFunctionClass =
|
||||||
_findInterceptorsClass('JavaScriptFunction');
|
_findInterceptorsClass('JavaScriptFunction');
|
||||||
|
|
||||||
|
late final ClassEntity jsJavaScriptSymbolClass =
|
||||||
|
_findInterceptorsClass('JavaScriptSymbol');
|
||||||
|
|
||||||
InterfaceType get jsJavaScriptFunctionType =>
|
InterfaceType get jsJavaScriptFunctionType =>
|
||||||
_getRawType(jsJavaScriptFunctionClass);
|
_getRawType(jsJavaScriptFunctionClass);
|
||||||
|
|
||||||
|
|
|
@ -395,7 +395,9 @@ class BackendImpacts {
|
||||||
_commonElements.jsJavaScriptObjectClass,
|
_commonElements.jsJavaScriptObjectClass,
|
||||||
_commonElements.jsLegacyJavaScriptObjectClass,
|
_commonElements.jsLegacyJavaScriptObjectClass,
|
||||||
_commonElements.jsPlainJavaScriptObjectClass,
|
_commonElements.jsPlainJavaScriptObjectClass,
|
||||||
_commonElements.jsJavaScriptFunctionClass
|
_commonElements.jsJavaScriptBigIntClass,
|
||||||
|
_commonElements.jsJavaScriptFunctionClass,
|
||||||
|
_commonElements.jsJavaScriptSymbolClass
|
||||||
],
|
],
|
||||||
features: EnumSet<BackendFeature>.fromValues([
|
features: EnumSet<BackendFeature>.fromValues([
|
||||||
BackendFeature.needToInitializeDispatchProperty,
|
BackendFeature.needToInitializeDispatchProperty,
|
||||||
|
@ -460,7 +462,9 @@ class BackendImpacts {
|
||||||
_commonElements.jsJavaScriptObjectClass,
|
_commonElements.jsJavaScriptObjectClass,
|
||||||
_commonElements.jsLegacyJavaScriptObjectClass,
|
_commonElements.jsLegacyJavaScriptObjectClass,
|
||||||
_commonElements.jsPlainJavaScriptObjectClass,
|
_commonElements.jsPlainJavaScriptObjectClass,
|
||||||
_commonElements.jsJavaScriptFunctionClass
|
_commonElements.jsJavaScriptBigIntClass,
|
||||||
|
_commonElements.jsJavaScriptFunctionClass,
|
||||||
|
_commonElements.jsJavaScriptSymbolClass
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -308,8 +308,12 @@ class CodegenEnqueuerListener extends EnqueuerListener {
|
||||||
registerInstantiation(_commonElements.jsPlainJavaScriptObjectClass);
|
registerInstantiation(_commonElements.jsPlainJavaScriptObjectClass);
|
||||||
} else if (cls == _commonElements.jsUnknownJavaScriptObjectClass) {
|
} else if (cls == _commonElements.jsUnknownJavaScriptObjectClass) {
|
||||||
registerInstantiation(_commonElements.jsUnknownJavaScriptObjectClass);
|
registerInstantiation(_commonElements.jsUnknownJavaScriptObjectClass);
|
||||||
|
} else if (cls == _commonElements.jsJavaScriptBigIntClass) {
|
||||||
|
registerInstantiation(_commonElements.jsJavaScriptBigIntClass);
|
||||||
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
||||||
registerInstantiation(_commonElements.jsJavaScriptFunctionClass);
|
registerInstantiation(_commonElements.jsJavaScriptFunctionClass);
|
||||||
|
} else if (cls == _commonElements.jsJavaScriptSymbolClass) {
|
||||||
|
registerInstantiation(_commonElements.jsJavaScriptSymbolClass);
|
||||||
} else if (cls == _commonElements.jsIndexingBehaviorInterface) {
|
} else if (cls == _commonElements.jsIndexingBehaviorInterface) {
|
||||||
_impacts.jsIndexingBehavior
|
_impacts.jsIndexingBehavior
|
||||||
.registerImpact(impactBuilder, _elementEnvironment);
|
.registerImpact(impactBuilder, _elementEnvironment);
|
||||||
|
|
|
@ -400,9 +400,13 @@ class ResolutionEnqueuerListener extends EnqueuerListener {
|
||||||
} else if (cls == _commonElements.jsUnknownJavaScriptObjectClass) {
|
} else if (cls == _commonElements.jsUnknownJavaScriptObjectClass) {
|
||||||
_addInterceptors(
|
_addInterceptors(
|
||||||
_commonElements.jsUnknownJavaScriptObjectClass, impactBuilder);
|
_commonElements.jsUnknownJavaScriptObjectClass, impactBuilder);
|
||||||
|
} else if (cls == _commonElements.jsJavaScriptBigIntClass) {
|
||||||
|
_addInterceptors(_commonElements.jsJavaScriptBigIntClass, impactBuilder);
|
||||||
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
||||||
_addInterceptors(
|
_addInterceptors(
|
||||||
_commonElements.jsJavaScriptFunctionClass, impactBuilder);
|
_commonElements.jsJavaScriptFunctionClass, impactBuilder);
|
||||||
|
} else if (cls == _commonElements.jsJavaScriptSymbolClass) {
|
||||||
|
_addInterceptors(_commonElements.jsJavaScriptSymbolClass, impactBuilder);
|
||||||
} else if (_nativeData.isNativeOrExtendsNative(cls)) {
|
} else if (_nativeData.isNativeOrExtendsNative(cls)) {
|
||||||
_interceptorData.addInterceptorsForNativeClassMembers(cls);
|
_interceptorData.addInterceptorsForNativeClassMembers(cls);
|
||||||
} else if (cls == _commonElements.jsIndexingBehaviorInterface) {
|
} else if (cls == _commonElements.jsIndexingBehaviorInterface) {
|
||||||
|
|
|
@ -74,8 +74,12 @@ class InterceptorStubGenerator {
|
||||||
condition = js('(typeof receiver) == "string"');
|
condition = js('(typeof receiver) == "string"');
|
||||||
} else if (cls == _commonElements.jsNullClass) {
|
} else if (cls == _commonElements.jsNullClass) {
|
||||||
condition = js('receiver == null');
|
condition = js('receiver == null');
|
||||||
|
} else if (cls == _commonElements.jsJavaScriptBigIntClass) {
|
||||||
|
condition = js('(typeof receiver) == "bigint"');
|
||||||
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
||||||
condition = js('(typeof receiver) == "function"');
|
condition = js('(typeof receiver) == "function"');
|
||||||
|
} else if (cls == _commonElements.jsJavaScriptSymbolClass) {
|
||||||
|
condition = js('(typeof receiver) == "symbol"');
|
||||||
} else {
|
} else {
|
||||||
throw 'internal error';
|
throw 'internal error';
|
||||||
}
|
}
|
||||||
|
@ -92,7 +96,9 @@ class InterceptorStubGenerator {
|
||||||
bool hasNative = false;
|
bool hasNative = false;
|
||||||
bool anyNativeClasses = _nativeCodegenEnqueuer.hasInstantiatedNativeClasses;
|
bool anyNativeClasses = _nativeCodegenEnqueuer.hasInstantiatedNativeClasses;
|
||||||
bool hasJavaScriptFunction = false;
|
bool hasJavaScriptFunction = false;
|
||||||
|
bool hasJavaScriptBigInt = false;
|
||||||
bool hasJavaScriptObject = false;
|
bool hasJavaScriptObject = false;
|
||||||
|
bool hasJavaScriptSymbol = false;
|
||||||
|
|
||||||
for (ClassEntity cls in classes) {
|
for (ClassEntity cls in classes) {
|
||||||
if (cls == _commonElements.jsArrayClass ||
|
if (cls == _commonElements.jsArrayClass ||
|
||||||
|
@ -112,8 +118,12 @@ class InterceptorStubGenerator {
|
||||||
hasNumber = true;
|
hasNumber = true;
|
||||||
else if (cls == _commonElements.jsStringClass)
|
else if (cls == _commonElements.jsStringClass)
|
||||||
hasString = true;
|
hasString = true;
|
||||||
|
else if (cls == _commonElements.jsJavaScriptBigIntClass)
|
||||||
|
hasJavaScriptBigInt = true;
|
||||||
else if (cls == _commonElements.jsJavaScriptFunctionClass)
|
else if (cls == _commonElements.jsJavaScriptFunctionClass)
|
||||||
hasJavaScriptFunction = true;
|
hasJavaScriptFunction = true;
|
||||||
|
else if (cls == _commonElements.jsJavaScriptSymbolClass)
|
||||||
|
hasJavaScriptSymbol = true;
|
||||||
else if (cls == _commonElements.jsJavaScriptObjectClass)
|
else if (cls == _commonElements.jsJavaScriptObjectClass)
|
||||||
hasJavaScriptObject = true;
|
hasJavaScriptObject = true;
|
||||||
else {
|
else {
|
||||||
|
@ -190,11 +200,21 @@ class InterceptorStubGenerator {
|
||||||
|
|
||||||
// If a program `hasNative` then we will insert a check for
|
// If a program `hasNative` then we will insert a check for
|
||||||
// `JavaScriptFunction` in the `hasNative` block of the interceptor logic.
|
// `JavaScriptFunction` in the `hasNative` block of the interceptor logic.
|
||||||
// Otherwise, we have to insert a specific check for `JavScriptFunction.
|
// Otherwise, we have to insert a specific check for `JavaScriptFunction.
|
||||||
if (hasJavaScriptFunction && !hasNative) {
|
if (!hasNative) {
|
||||||
|
if (hasJavaScriptFunction) {
|
||||||
statements.add(
|
statements.add(
|
||||||
buildInterceptorCheck(_commonElements.jsJavaScriptFunctionClass));
|
buildInterceptorCheck(_commonElements.jsJavaScriptFunctionClass));
|
||||||
}
|
}
|
||||||
|
if (hasJavaScriptSymbol) {
|
||||||
|
statements.add(
|
||||||
|
buildInterceptorCheck(_commonElements.jsJavaScriptSymbolClass));
|
||||||
|
}
|
||||||
|
if (hasJavaScriptBigInt) {
|
||||||
|
statements.add(
|
||||||
|
buildInterceptorCheck(_commonElements.jsJavaScriptBigIntClass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasJavaScriptObject && !hasNative) {
|
if (hasJavaScriptObject && !hasNative) {
|
||||||
statements.add(js.statement(r'''
|
statements.add(js.statement(r'''
|
||||||
|
@ -215,12 +235,16 @@ class InterceptorStubGenerator {
|
||||||
statements.add(js.statement(r'''{
|
statements.add(js.statement(r'''{
|
||||||
if (typeof receiver != "object") {
|
if (typeof receiver != "object") {
|
||||||
if (typeof receiver == "function" ) return #;
|
if (typeof receiver == "function" ) return #;
|
||||||
|
if (typeof receiver == "symbol" ) return #;
|
||||||
|
if (typeof receiver == "bigint" ) return #;
|
||||||
return receiver;
|
return receiver;
|
||||||
}
|
}
|
||||||
if (receiver instanceof #) return receiver;
|
if (receiver instanceof #) return receiver;
|
||||||
return #(receiver);
|
return #(receiver);
|
||||||
}''', [
|
}''', [
|
||||||
interceptorFor(_commonElements.jsJavaScriptFunctionClass),
|
interceptorFor(_commonElements.jsJavaScriptFunctionClass),
|
||||||
|
interceptorFor(_commonElements.jsJavaScriptSymbolClass),
|
||||||
|
interceptorFor(_commonElements.jsJavaScriptBigIntClass),
|
||||||
_emitter.constructorAccess(_commonElements.objectClass),
|
_emitter.constructorAccess(_commonElements.objectClass),
|
||||||
_emitter
|
_emitter
|
||||||
.staticFunctionAccess(_commonElements.getNativeInterceptorMethod)
|
.staticFunctionAccess(_commonElements.getNativeInterceptorMethod)
|
||||||
|
|
|
@ -92,7 +92,8 @@ abstract class NativeEnqueuer {
|
||||||
type == _commonElements.stringType ||
|
type == _commonElements.stringType ||
|
||||||
type == _commonElements.nullType ||
|
type == _commonElements.nullType ||
|
||||||
type == _commonElements.boolType ||
|
type == _commonElements.boolType ||
|
||||||
type == _commonElements.jsJavaScriptFunctionType ||
|
type.element == _commonElements.jsJavaScriptBigIntClass ||
|
||||||
|
type.element == _commonElements.jsJavaScriptSymbolClass ||
|
||||||
type == _commonElements.jsJavaScriptObjectType ||
|
type == _commonElements.jsJavaScriptObjectType ||
|
||||||
_dartTypes.isSubtype(type,
|
_dartTypes.isSubtype(type,
|
||||||
_elementEnvironment.getRawType(_commonElements.jsArrayClass))) {
|
_elementEnvironment.getRawType(_commonElements.jsArrayClass))) {
|
||||||
|
|
|
@ -464,3 +464,41 @@ final class JavaScriptFunction extends LegacyJavaScriptObject
|
||||||
return 'JavaScript function for ${dartClosure.toString()}';
|
return 'JavaScript function for ${dartClosure.toString()}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interceptor for JavaScript BigInt primitive values, i.e. values `x` for
|
||||||
|
/// which `typeof x == "bigint"`.
|
||||||
|
final class JavaScriptBigInt extends Interceptor {
|
||||||
|
const JavaScriptBigInt();
|
||||||
|
|
||||||
|
// JavaScript BigInt objects don't have any operations that look efficient
|
||||||
|
// enough to generate a good hash code.
|
||||||
|
//
|
||||||
|
// TODO(https://dartbug.com/53162): "bigint" primitive values might be used as
|
||||||
|
// keys without using the `hashCode`, but that will not help for compound hash
|
||||||
|
// values, e.g. produced by `Object.hash`.
|
||||||
|
int get hashCode => 0;
|
||||||
|
|
||||||
|
/// Returns the result of the JavaScript BigInt's `toString` method.
|
||||||
|
String toString() => JS('String', 'String(#)', this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interceptor for JavaScript Symbol primitive values, i.e. values `x` for
|
||||||
|
/// which `typeof x == "symbol"`.
|
||||||
|
final class JavaScriptSymbol extends Interceptor {
|
||||||
|
const JavaScriptSymbol();
|
||||||
|
|
||||||
|
// It is not clear what to do for a Symbol's hashCode. Registered Symbols
|
||||||
|
// [can't be keys of WeakMaps][1]. Using a property won't work because the
|
||||||
|
// property will be added to the ephmeral Object wrapper, either being an
|
||||||
|
// incorrect operation or an error in strict mode.
|
||||||
|
//
|
||||||
|
// TODO(https://dartbug.com/53162): "symbol" primitive values might be used as
|
||||||
|
// keys without using the `hashCode`, but that will not help for compound hash
|
||||||
|
// values, e.g. produced by `Object.hash`.
|
||||||
|
//
|
||||||
|
// [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#shared_symbols_in_the_global_symbol_registry
|
||||||
|
int get hashCode => 0;
|
||||||
|
|
||||||
|
/// Returns the result of the JavaScript Symbol's `toString` method.
|
||||||
|
String toString() => JS('String', 'String(#)', this);
|
||||||
|
}
|
||||||
|
|
32
tests/web/native/javascript_bigint_test.dart
Normal file
32
tests/web/native/javascript_bigint_test.dart
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright (c) 2023, 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.
|
||||||
|
|
||||||
|
import 'native_testing.dart';
|
||||||
|
|
||||||
|
import 'dart:_interceptors';
|
||||||
|
|
||||||
|
dynamic makeBigIntDynamic(String name) native;
|
||||||
|
JavaScriptBigInt makeBigInt(String name) native;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
JS('', r"""
|
||||||
|
(function(){
|
||||||
|
self.makeBigInt = function(name){return BigInt(name)};
|
||||||
|
self.makeBigIntDynamic = function(name){return BigInt(name)};
|
||||||
|
})()""");
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
nativeTesting();
|
||||||
|
setup();
|
||||||
|
const s = '9876543210000000000000123456789';
|
||||||
|
|
||||||
|
Expect.notEquals(s, makeBigInt(s));
|
||||||
|
Expect.notEquals(s, makeBigIntDynamic(s));
|
||||||
|
|
||||||
|
Expect.equals(s, makeBigInt(s).toString());
|
||||||
|
Expect.equals(s, makeBigIntDynamic(s).toString());
|
||||||
|
Expect.equals(s, '${makeBigInt(s)}');
|
||||||
|
Expect.equals(s, '${makeBigIntDynamic(s)}');
|
||||||
|
}
|
31
tests/web/native/javascript_symbol_test.dart
Normal file
31
tests/web/native/javascript_symbol_test.dart
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) 2023, 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.
|
||||||
|
|
||||||
|
import 'native_testing.dart';
|
||||||
|
|
||||||
|
import 'dart:_interceptors';
|
||||||
|
|
||||||
|
dynamic makeSymbolDynamic(String name) native;
|
||||||
|
JavaScriptSymbol makeSymbol(String name) native;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
JS('', r"""
|
||||||
|
(function(){
|
||||||
|
self.makeSymbol = function(name){return Symbol(name)};
|
||||||
|
self.makeSymbolDynamic = function(name){return Symbol(name)};
|
||||||
|
})()""");
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
nativeTesting();
|
||||||
|
setup();
|
||||||
|
|
||||||
|
Expect.notEquals('Symbol(foo)', makeSymbol('foo'));
|
||||||
|
Expect.notEquals('Symbol(foo)', makeSymbolDynamic('foo'));
|
||||||
|
|
||||||
|
Expect.equals('Symbol(foo)', makeSymbol('foo').toString());
|
||||||
|
Expect.equals('Symbol(foo)', makeSymbolDynamic('foo').toString());
|
||||||
|
Expect.equals('Symbol(foo)', '${makeSymbol('foo')}');
|
||||||
|
Expect.equals('Symbol(foo)', '${makeSymbolDynamic('foo')}');
|
||||||
|
}
|
Loading…
Reference in a new issue