mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:35:05 +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 =
|
||||
_findInterceptorsClass('UnknownJavaScriptObject');
|
||||
|
||||
late final ClassEntity jsJavaScriptBigIntClass =
|
||||
_findInterceptorsClass('JavaScriptBigInt');
|
||||
|
||||
late final ClassEntity jsJavaScriptFunctionClass =
|
||||
_findInterceptorsClass('JavaScriptFunction');
|
||||
|
||||
late final ClassEntity jsJavaScriptSymbolClass =
|
||||
_findInterceptorsClass('JavaScriptSymbol');
|
||||
|
||||
InterfaceType get jsJavaScriptFunctionType =>
|
||||
_getRawType(jsJavaScriptFunctionClass);
|
||||
|
||||
|
|
|
@ -395,7 +395,9 @@ class BackendImpacts {
|
|||
_commonElements.jsJavaScriptObjectClass,
|
||||
_commonElements.jsLegacyJavaScriptObjectClass,
|
||||
_commonElements.jsPlainJavaScriptObjectClass,
|
||||
_commonElements.jsJavaScriptFunctionClass
|
||||
_commonElements.jsJavaScriptBigIntClass,
|
||||
_commonElements.jsJavaScriptFunctionClass,
|
||||
_commonElements.jsJavaScriptSymbolClass
|
||||
],
|
||||
features: EnumSet<BackendFeature>.fromValues([
|
||||
BackendFeature.needToInitializeDispatchProperty,
|
||||
|
@ -460,7 +462,9 @@ class BackendImpacts {
|
|||
_commonElements.jsJavaScriptObjectClass,
|
||||
_commonElements.jsLegacyJavaScriptObjectClass,
|
||||
_commonElements.jsPlainJavaScriptObjectClass,
|
||||
_commonElements.jsJavaScriptFunctionClass
|
||||
_commonElements.jsJavaScriptBigIntClass,
|
||||
_commonElements.jsJavaScriptFunctionClass,
|
||||
_commonElements.jsJavaScriptSymbolClass
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -308,8 +308,12 @@ class CodegenEnqueuerListener extends EnqueuerListener {
|
|||
registerInstantiation(_commonElements.jsPlainJavaScriptObjectClass);
|
||||
} else if (cls == _commonElements.jsUnknownJavaScriptObjectClass) {
|
||||
registerInstantiation(_commonElements.jsUnknownJavaScriptObjectClass);
|
||||
} else if (cls == _commonElements.jsJavaScriptBigIntClass) {
|
||||
registerInstantiation(_commonElements.jsJavaScriptBigIntClass);
|
||||
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
||||
registerInstantiation(_commonElements.jsJavaScriptFunctionClass);
|
||||
} else if (cls == _commonElements.jsJavaScriptSymbolClass) {
|
||||
registerInstantiation(_commonElements.jsJavaScriptSymbolClass);
|
||||
} else if (cls == _commonElements.jsIndexingBehaviorInterface) {
|
||||
_impacts.jsIndexingBehavior
|
||||
.registerImpact(impactBuilder, _elementEnvironment);
|
||||
|
|
|
@ -400,9 +400,13 @@ class ResolutionEnqueuerListener extends EnqueuerListener {
|
|||
} else if (cls == _commonElements.jsUnknownJavaScriptObjectClass) {
|
||||
_addInterceptors(
|
||||
_commonElements.jsUnknownJavaScriptObjectClass, impactBuilder);
|
||||
} else if (cls == _commonElements.jsJavaScriptBigIntClass) {
|
||||
_addInterceptors(_commonElements.jsJavaScriptBigIntClass, impactBuilder);
|
||||
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
||||
_addInterceptors(
|
||||
_commonElements.jsJavaScriptFunctionClass, impactBuilder);
|
||||
} else if (cls == _commonElements.jsJavaScriptSymbolClass) {
|
||||
_addInterceptors(_commonElements.jsJavaScriptSymbolClass, impactBuilder);
|
||||
} else if (_nativeData.isNativeOrExtendsNative(cls)) {
|
||||
_interceptorData.addInterceptorsForNativeClassMembers(cls);
|
||||
} else if (cls == _commonElements.jsIndexingBehaviorInterface) {
|
||||
|
|
|
@ -74,8 +74,12 @@ class InterceptorStubGenerator {
|
|||
condition = js('(typeof receiver) == "string"');
|
||||
} else if (cls == _commonElements.jsNullClass) {
|
||||
condition = js('receiver == null');
|
||||
} else if (cls == _commonElements.jsJavaScriptBigIntClass) {
|
||||
condition = js('(typeof receiver) == "bigint"');
|
||||
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
|
||||
condition = js('(typeof receiver) == "function"');
|
||||
} else if (cls == _commonElements.jsJavaScriptSymbolClass) {
|
||||
condition = js('(typeof receiver) == "symbol"');
|
||||
} else {
|
||||
throw 'internal error';
|
||||
}
|
||||
|
@ -92,7 +96,9 @@ class InterceptorStubGenerator {
|
|||
bool hasNative = false;
|
||||
bool anyNativeClasses = _nativeCodegenEnqueuer.hasInstantiatedNativeClasses;
|
||||
bool hasJavaScriptFunction = false;
|
||||
bool hasJavaScriptBigInt = false;
|
||||
bool hasJavaScriptObject = false;
|
||||
bool hasJavaScriptSymbol = false;
|
||||
|
||||
for (ClassEntity cls in classes) {
|
||||
if (cls == _commonElements.jsArrayClass ||
|
||||
|
@ -112,8 +118,12 @@ class InterceptorStubGenerator {
|
|||
hasNumber = true;
|
||||
else if (cls == _commonElements.jsStringClass)
|
||||
hasString = true;
|
||||
else if (cls == _commonElements.jsJavaScriptBigIntClass)
|
||||
hasJavaScriptBigInt = true;
|
||||
else if (cls == _commonElements.jsJavaScriptFunctionClass)
|
||||
hasJavaScriptFunction = true;
|
||||
else if (cls == _commonElements.jsJavaScriptSymbolClass)
|
||||
hasJavaScriptSymbol = true;
|
||||
else if (cls == _commonElements.jsJavaScriptObjectClass)
|
||||
hasJavaScriptObject = true;
|
||||
else {
|
||||
|
@ -190,10 +200,20 @@ class InterceptorStubGenerator {
|
|||
|
||||
// If a program `hasNative` then we will insert a check for
|
||||
// `JavaScriptFunction` in the `hasNative` block of the interceptor logic.
|
||||
// Otherwise, we have to insert a specific check for `JavScriptFunction.
|
||||
if (hasJavaScriptFunction && !hasNative) {
|
||||
statements.add(
|
||||
buildInterceptorCheck(_commonElements.jsJavaScriptFunctionClass));
|
||||
// Otherwise, we have to insert a specific check for `JavaScriptFunction.
|
||||
if (!hasNative) {
|
||||
if (hasJavaScriptFunction) {
|
||||
statements.add(
|
||||
buildInterceptorCheck(_commonElements.jsJavaScriptFunctionClass));
|
||||
}
|
||||
if (hasJavaScriptSymbol) {
|
||||
statements.add(
|
||||
buildInterceptorCheck(_commonElements.jsJavaScriptSymbolClass));
|
||||
}
|
||||
if (hasJavaScriptBigInt) {
|
||||
statements.add(
|
||||
buildInterceptorCheck(_commonElements.jsJavaScriptBigIntClass));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasJavaScriptObject && !hasNative) {
|
||||
|
@ -215,12 +235,16 @@ class InterceptorStubGenerator {
|
|||
statements.add(js.statement(r'''{
|
||||
if (typeof receiver != "object") {
|
||||
if (typeof receiver == "function" ) return #;
|
||||
if (typeof receiver == "symbol" ) return #;
|
||||
if (typeof receiver == "bigint" ) return #;
|
||||
return receiver;
|
||||
}
|
||||
if (receiver instanceof #) return receiver;
|
||||
return #(receiver);
|
||||
}''', [
|
||||
interceptorFor(_commonElements.jsJavaScriptFunctionClass),
|
||||
interceptorFor(_commonElements.jsJavaScriptSymbolClass),
|
||||
interceptorFor(_commonElements.jsJavaScriptBigIntClass),
|
||||
_emitter.constructorAccess(_commonElements.objectClass),
|
||||
_emitter
|
||||
.staticFunctionAccess(_commonElements.getNativeInterceptorMethod)
|
||||
|
|
|
@ -92,7 +92,8 @@ abstract class NativeEnqueuer {
|
|||
type == _commonElements.stringType ||
|
||||
type == _commonElements.nullType ||
|
||||
type == _commonElements.boolType ||
|
||||
type == _commonElements.jsJavaScriptFunctionType ||
|
||||
type.element == _commonElements.jsJavaScriptBigIntClass ||
|
||||
type.element == _commonElements.jsJavaScriptSymbolClass ||
|
||||
type == _commonElements.jsJavaScriptObjectType ||
|
||||
_dartTypes.isSubtype(type,
|
||||
_elementEnvironment.getRawType(_commonElements.jsArrayClass))) {
|
||||
|
|
|
@ -464,3 +464,41 @@ final class JavaScriptFunction extends LegacyJavaScriptObject
|
|||
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