Skip to content

Commit

Permalink
[ddc] Add support for new native types
Browse files Browse the repository at this point in the history
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: #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>
  • Loading branch information
nshahan authored and Commit Queue committed Dec 10, 2022
1 parent 1e4a974 commit c010e4f
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 22 deletions.
Expand Up @@ -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);
}
Expand Down
28 changes: 28 additions & 0 deletions sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/rtti.dart
Expand Up @@ -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
Expand Down
Expand Up @@ -21,12 +21,15 @@ import 'dart:_foreign_helper'
spread;
import 'dart:_interceptors'
show
JavaScriptObject,
JSArray,
JSInt,
jsNull,
JSNumNotInt,
JSFunction,
NativeError,
JavaScriptObject,
LegacyJavaScriptObject;
LegacyJavaScriptObject,
NativeError;

import 'dart:_internal' as internal show LateError, Symbol;
import 'dart:_js_helper'
show
Expand Down
Expand Up @@ -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.
Expand Down
14 changes: 2 additions & 12 deletions sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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.
///
Expand Down
5 changes: 4 additions & 1 deletion sdk/lib/_internal/js_dev_runtime/private/interceptors.dart
Expand Up @@ -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';
Expand Down Expand Up @@ -115,6 +117,7 @@ class UnknownJavaScriptObject extends LegacyJavaScriptObject {
const UnknownJavaScriptObject();
}

@JsPeerInterface(name: 'Error')
class NativeError extends Interceptor {
String dartStack() => JS<String>('!', '#.stack', this);
}
Expand Down
12 changes: 10 additions & 2 deletions sdk/lib/_internal/js_dev_runtime/private/js_array.dart
Expand Up @@ -24,20 +24,29 @@ 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);
}

// TODO(jmesserly): consider a fixed array subclass instead.
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);
}

factory JSArray.unmodifiable(list) {
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);
}

Expand Down Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions sdk/lib/_internal/js_dev_runtime/private/js_number.dart
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
Expand Down

0 comments on commit c010e4f

Please sign in to comment.