[ddc] Add support for new native types

Use the "extension type" class as the interceptor object for the
dart:_rti library. This applies to the new type system for values
that are represented as a native JavaScript type. For example:
Number, bool, String, Array, etc.

Issue: https://github.com/dart-lang/sdk/issues/48585
Change-Id: Ie6214aa897d3ae8abb4f9619cd2be984eeb9c4ea
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/266544
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
Nicholas Shahan 2022-12-10 00:07:45 +00:00 committed by Commit Queue
parent 1e4a974a5d
commit c010e4ffbd
8 changed files with 64 additions and 24 deletions

View file

@ -813,6 +813,11 @@ defaultNoSuchMethod(obj, Invocation i) {
throw NoSuchMethodError.withInvocation(obj, i); throw NoSuchMethodError.withInvocation(obj, i);
} }
// TODO(nshahan) Replace with rti.getRuntimeType() when classes representing
// native types don't have to "pretend" to be Dart classes. Ex:
// JSNumber -> int or double
// JSArray<E> -> List<E>
// NativeFloat32List -> Float32List
runtimeType(obj) { runtimeType(obj) {
return obj == null ? Null : JS('', '#[dartx.runtimeType]', obj); return obj == null ? Null : JS('', '#[dartx.runtimeType]', obj);
} }

View file

@ -110,6 +110,34 @@ RecordType getRecordType(_RecordImpl obj) {
return type; return type;
} }
/// Returns the interceptor for [obj] as needed by the dart:rti library.
@notNull
Object getInterceptorForRti(obj) {
var classRef;
if (obj == null) {
classRef = JS_CLASS_REF(Null);
} else {
switch (JS<String>('!', 'typeof #', obj)) {
case 'number':
classRef = JS('', 'Math.floor(#) == # ? # : #', obj, obj,
JS_CLASS_REF(JSInt), JS_CLASS_REF(JSNumNotInt));
break;
case 'function':
var signature =
JS('', '#[#]', obj, JS_GET_NAME(JsGetName.SIGNATURE_NAME));
if (signature != null) classRef = JS_CLASS_REF(Function);
break;
default:
// The interceptors for native JavaScript types like bool, string, etc.
// (excluding number and function, see above) are stored as a symbolized
// property and can be accessed from the native value itself.
classRef = JS('', '#[#]', obj, _extensionType);
}
}
if (classRef == null) throw 'Unknown interceptor for object: ($obj)';
return JS<Object>('!', '#.prototype', classRef);
}
/// Returns the runtime representation of the type of obj. /// Returns the runtime representation of the type of obj.
/// ///
/// The resulting object is used internally for runtime type checking. This is /// The resulting object is used internally for runtime type checking. This is

View file

@ -21,12 +21,15 @@ import 'dart:_foreign_helper'
spread; spread;
import 'dart:_interceptors' import 'dart:_interceptors'
show show
JSArray,
jsNull,
JSFunction,
NativeError,
JavaScriptObject, JavaScriptObject,
LegacyJavaScriptObject; JSArray,
JSInt,
jsNull,
JSNumNotInt,
JSFunction,
LegacyJavaScriptObject,
NativeError;
import 'dart:_internal' as internal show LateError, Symbol; import 'dart:_internal' as internal show LateError, Symbol;
import 'dart:_js_helper' import 'dart:_js_helper'
show show

View file

@ -89,6 +89,9 @@ void nativeNonNullAsserts(bool enable) {
_nativeNonNullAsserts = enable; _nativeNonNullAsserts = enable;
} }
/// A JavaScript Symbol used to store the Rti object on a native array.
final arrayRti = JS('', r'Symbol("$ti")');
final metadata = JS('', 'Symbol("metadata")'); final metadata = JS('', 'Symbol("metadata")');
/// A javascript Symbol used to store a canonical version of T? on T. /// A javascript Symbol used to store a canonical version of T? on T.

View file

@ -7,6 +7,7 @@ library dart._foreign_helper;
import 'dart:_interceptors' show JSArray; import 'dart:_interceptors' show JSArray;
import 'dart:_js_helper' show notNull; import 'dart:_js_helper' show notNull;
import 'dart:_js_shared_embedded_names' show JsBuiltin, JsGetName; import 'dart:_js_shared_embedded_names' show JsBuiltin, JsGetName;
import 'dart:_runtime' as dart show getInterceptorForRti;
import 'dart:_rti' show Rti; import 'dart:_rti' show Rti;
/** /**
@ -299,18 +300,7 @@ external JS_BUILTIN(String typeDescription, JsBuiltin builtin,
/// Returns the interceptor for [object]. /// Returns the interceptor for [object].
/// ///
// TODO(nshahan) Replace calls at compile time? // TODO(nshahan) Replace calls at compile time?
Object getInterceptor(obj) { Object getInterceptor(obj) => dart.getInterceptorForRti(obj);
var classRef;
if (obj == null) {
classRef = JS_CLASS_REF(Null);
} else if (JS<String>('!', 'typeof #', obj) == 'function') {
var signature = JS('', '#[#]', obj, JS_GET_NAME(JsGetName.SIGNATURE_NAME));
// Dart functions are always tagged with a signature.
if (signature != null) classRef = JS_CLASS_REF(Function);
}
if (classRef == null) throw 'Unknown interceptor for object: ($obj)';
return JS<Object>('!', '#.prototype', classRef);
}
/// Returns the Rti object for the type for JavaScript arrays via JS-interop. /// Returns the Rti object for the type for JavaScript arrays via JS-interop.
/// ///

View file

@ -7,10 +7,12 @@ library dart._interceptors;
import 'dart:collection'; import 'dart:collection';
import 'dart:_internal' hide Symbol; import 'dart:_internal' hide Symbol;
import 'dart:_js_helper'; import 'dart:_js_helper';
import 'dart:_foreign_helper' show JS, JS_GET_FLAG, JSExportName; import 'dart:_foreign_helper'
show JS, JS_EMBEDDED_GLOBAL, JS_GET_FLAG, JSExportName;
import 'dart:math' show Random, ln2; import 'dart:math' show Random, ln2;
import 'dart:_rti' as rti show createRuntimeType, Rti; import 'dart:_rti' as rti show createRuntimeType, Rti;
import 'dart:_runtime' as dart; import 'dart:_runtime' as dart;
import 'dart:_js_shared_embedded_names' show ARRAY_RTI_PROPERTY;
part 'js_array.dart'; part 'js_array.dart';
part 'js_number.dart'; part 'js_number.dart';
@ -115,6 +117,7 @@ class UnknownJavaScriptObject extends LegacyJavaScriptObject {
const UnknownJavaScriptObject(); const UnknownJavaScriptObject();
} }
@JsPeerInterface(name: 'Error')
class NativeError extends Interceptor { class NativeError extends Interceptor {
String dartStack() => JS<String>('!', '#.stack', this); String dartStack() => JS<String>('!', '#.stack', this);
} }

View file

@ -24,6 +24,9 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
// TODO(jmesserly): this uses special compiler magic to close over the // TODO(jmesserly): this uses special compiler magic to close over the
// parameterized ES6 'JSArray' class. // parameterized ES6 'JSArray' class.
JS('', '#.__proto__ = JSArray.prototype', list); JS('', '#.__proto__ = JSArray.prototype', list);
if (JS_GET_FLAG('NEW_RUNTIME_TYPES'))
JS('', '#.# = #', list, JS_EMBEDDED_GLOBAL('', ARRAY_RTI_PROPERTY),
JSArray<E>);
return JS('-dynamic', '#', list); return JS('-dynamic', '#', list);
} }
@ -31,6 +34,9 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
factory JSArray.fixed(list) { factory JSArray.fixed(list) {
JS('', '#.__proto__ = JSArray.prototype', list); JS('', '#.__proto__ = JSArray.prototype', list);
JS('', r'#.fixed$length = Array', list); JS('', r'#.fixed$length = Array', list);
if (JS_GET_FLAG('NEW_RUNTIME_TYPES'))
JS('', '#.# = #', list, JS_EMBEDDED_GLOBAL('', ARRAY_RTI_PROPERTY),
JSArray<E>);
return JS('-dynamic', '#', list); return JS('-dynamic', '#', list);
} }
@ -38,6 +44,9 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
JS('', '#.__proto__ = JSArray.prototype', list); JS('', '#.__proto__ = JSArray.prototype', list);
JS('', r'#.fixed$length = Array', list); JS('', r'#.fixed$length = Array', list);
JS('', r'#.immutable$list = Array', list); JS('', r'#.immutable$list = Array', list);
if (JS_GET_FLAG('NEW_RUNTIME_TYPES'))
JS('', '#.# = #', list, JS_EMBEDDED_GLOBAL('', ARRAY_RTI_PROPERTY),
JSArray<E>);
return JS('-dynamic', '#', list); return JS('-dynamic', '#', list);
} }
@ -597,8 +606,7 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
return ListMapView<E>(this); return ListMapView<E>(this);
} }
Type get runtimeType => Type get runtimeType => List<E>;
dart.wrapType(JS('', '#(#)', dart.getGenericClassStatic<List>(), E));
Iterable<E> followedBy(Iterable<E> other) => Iterable<E> followedBy(Iterable<E> other) =>
FollowedByIterable<E>.firstEfficient(this, other); FollowedByIterable<E>.firstEfficient(this, other);

View file

@ -16,7 +16,7 @@ class JSNumNotInt extends JSNumber implements double {}
/// ///
/// These are made available as extension methods on `Number` in JS. /// These are made available as extension methods on `Number` in JS.
@JsPeerInterface(name: 'Number') @JsPeerInterface(name: 'Number')
class JSNumber extends Interceptor implements int, double { class JSNumber extends Interceptor implements double {
const JSNumber(); const JSNumber();
@notNull @notNull
@ -482,7 +482,7 @@ class JSNumber extends Interceptor implements int, double {
return BigInt.from(this).modPow(BigInt.from(e), BigInt.from(m)).toInt(); return BigInt.from(this).modPow(BigInt.from(e), BigInt.from(m)).toInt();
} }
int b = this; int b = JS<int>('!', '#', this);
if (b < 0 || b > m) { if (b < 0 || b > m) {
b %= m; b %= m;
} }
@ -574,7 +574,7 @@ class JSNumber extends Interceptor implements int, double {
if (this is! int) throwArgumentErrorValue(this); if (this is! int) throwArgumentErrorValue(this);
if (m <= 0) throw RangeError.range(m, 1, null, "modulus"); if (m <= 0) throw RangeError.range(m, 1, null, "modulus");
if (m == 1) return 0; if (m == 1) return 0;
int t = this; int t = JS<int>('!', '#', this);
if ((t < 0) || (t >= m)) t %= m; if ((t < 0) || (t >= m)) t %= m;
if (t == 1) return 1; if (t == 1) return 1;
if ((t == 0) || (t.isEven && m.isEven)) { if ((t == 0) || (t.isEven && m.isEven)) {
@ -587,7 +587,7 @@ class JSNumber extends Interceptor implements int, double {
@notNull @notNull
int gcd(@nullCheck int other) { int gcd(@nullCheck int other) {
if (this is! int) throwArgumentErrorValue(this); if (this is! int) throwArgumentErrorValue(this);
int x = this.abs(); int x = JS<int>('!', '#', this).abs();
int y = other.abs(); int y = other.abs();
if (x == 0) return y; if (x == 0) return y;
if (y == 0) return x; if (y == 0) return x;