[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);
}
// 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) {
return obj == null ? Null : JS('', '#[dartx.runtimeType]', obj);
}

View file

@ -110,6 +110,34 @@ RecordType getRecordType(_RecordImpl obj) {
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.
///
/// The resulting object is used internally for runtime type checking. This is

View file

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

View file

@ -89,6 +89,9 @@ void nativeNonNullAsserts(bool 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")');
/// 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:_js_helper' show notNull;
import 'dart:_js_shared_embedded_names' show JsBuiltin, JsGetName;
import 'dart:_runtime' as dart show getInterceptorForRti;
import 'dart:_rti' show Rti;
/**
@ -299,18 +300,7 @@ external JS_BUILTIN(String typeDescription, JsBuiltin builtin,
/// Returns the interceptor for [object].
///
// TODO(nshahan) Replace calls at compile time?
Object getInterceptor(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);
}
Object getInterceptor(obj) => dart.getInterceptorForRti(obj);
/// 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:_internal' hide Symbol;
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:_rti' as rti show createRuntimeType, Rti;
import 'dart:_runtime' as dart;
import 'dart:_js_shared_embedded_names' show ARRAY_RTI_PROPERTY;
part 'js_array.dart';
part 'js_number.dart';
@ -115,6 +117,7 @@ class UnknownJavaScriptObject extends LegacyJavaScriptObject {
const UnknownJavaScriptObject();
}
@JsPeerInterface(name: 'Error')
class NativeError extends Interceptor {
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
// parameterized ES6 'JSArray' class.
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);
}
@ -31,6 +34,9 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
factory JSArray.fixed(list) {
JS('', '#.__proto__ = JSArray.prototype', 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);
}
@ -38,6 +44,9 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
JS('', '#.__proto__ = JSArray.prototype', list);
JS('', r'#.fixed$length = 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);
}
@ -597,8 +606,7 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
return ListMapView<E>(this);
}
Type get runtimeType =>
dart.wrapType(JS('', '#(#)', dart.getGenericClassStatic<List>(), E));
Type get runtimeType => List<E>;
Iterable<E> followedBy(Iterable<E> 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.
@JsPeerInterface(name: 'Number')
class JSNumber extends Interceptor implements int, double {
class JSNumber extends Interceptor implements double {
const JSNumber();
@notNull
@ -482,7 +482,7 @@ class JSNumber extends Interceptor implements int, double {
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) {
b %= m;
}
@ -574,7 +574,7 @@ class JSNumber extends Interceptor implements int, double {
if (this is! int) throwArgumentErrorValue(this);
if (m <= 0) throw RangeError.range(m, 1, null, "modulus");
if (m == 1) return 0;
int t = this;
int t = JS<int>('!', '#', this);
if ((t < 0) || (t >= m)) t %= m;
if (t == 1) return 1;
if ((t == 0) || (t.isEven && m.isEven)) {
@ -587,7 +587,7 @@ class JSNumber extends Interceptor implements int, double {
@notNull
int gcd(@nullCheck int other) {
if (this is! int) throwArgumentErrorValue(this);
int x = this.abs();
int x = JS<int>('!', '#', this).abs();
int y = other.abs();
if (x == 0) return y;
if (y == 0) return x;