mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:41:41 +00:00
[js_util] Generalize dartify, and make it more robust.
Change-Id: I3608bcbdf320f6d4da2a52119d48fb02df2ad0e5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/252565 Reviewed-by: Srujan Gaddam <srujzs@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
4f9002286f
commit
fe78a23e9a
|
@ -374,14 +374,55 @@ Object? get objectPrototype => JS('', 'Object.prototype');
|
|||
@patch
|
||||
List<Object?> objectKeys(Object? object) => JS('', 'Object.keys(#)', object);
|
||||
|
||||
// TODO(joshualitt): Move these `is` checks to a helper library to help
|
||||
// declutter this patch file.
|
||||
bool _isJavaScriptDate(value) => JS('bool', '# instanceof Date', value);
|
||||
bool _isJavaScriptRegExp(value) => JS('bool', '# instanceof RegExp', value);
|
||||
|
||||
@patch
|
||||
bool isJavaScriptArray(value) => JS('bool', '# instanceof Array', value);
|
||||
|
||||
// Although it may be tempting to try and rewrite [isJavaScriptSimpleObject]
|
||||
// using `js_util` calls, it turns out this can be fragile on some browsers
|
||||
// under some situations.
|
||||
@patch
|
||||
bool isJavaScriptSimpleObject(value) {
|
||||
var proto = JS('', 'Object.getPrototypeOf(#)', value);
|
||||
return JS('bool', '# === Object.prototype', proto) ||
|
||||
JS('bool', '# === null', proto);
|
||||
}
|
||||
|
||||
bool _isJavaScriptPromise(value) =>
|
||||
JS('bool', r'typeof Promise != "undefined" && # instanceof Promise', value);
|
||||
|
||||
DateTime _dateToDateTime(date) {
|
||||
int millisSinceEpoch = JS('int', '#.getTime()', date);
|
||||
return new DateTime.fromMillisecondsSinceEpoch(millisSinceEpoch, isUtc: true);
|
||||
}
|
||||
|
||||
@patch
|
||||
Object? dartify(Object? o) {
|
||||
var _convertedObjects = HashMap.identity();
|
||||
Object? convert() {
|
||||
Object? convert(Object? o) {
|
||||
if (_convertedObjects.containsKey(o)) {
|
||||
return _convertedObjects[o];
|
||||
}
|
||||
if (o == null || o is bool || o is num || o is String) return o;
|
||||
|
||||
if (_isJavaScriptDate(o)) {
|
||||
return _dateToDateTime(o);
|
||||
}
|
||||
|
||||
if (_isJavaScriptRegExp(o)) {
|
||||
// TODO(joshualitt): Consider investigating if there is a way to convert
|
||||
// from `JSRegExp` to `RegExp`.
|
||||
throw new ArgumentError('structured clone of RegExp');
|
||||
}
|
||||
|
||||
if (_isJavaScriptPromise(o)) {
|
||||
return promiseToFuture(o);
|
||||
}
|
||||
|
||||
if (isJavaScriptSimpleObject(o)) {
|
||||
Map<Object?, Object?> dartObject = {};
|
||||
_convertedObjects[o] = dartObject;
|
||||
|
@ -394,23 +435,27 @@ Object? dartify(Object? o) {
|
|||
Object? jsKey = originalKeys[i];
|
||||
Object? dartKey = dartKeys[i];
|
||||
if (jsKey != null) {
|
||||
dartObject[dartKey] = dartify(getProperty(o, jsKey));
|
||||
dartObject[dartKey] = convert(getProperty(o, jsKey));
|
||||
}
|
||||
}
|
||||
return dartObject;
|
||||
}
|
||||
|
||||
if (isJavaScriptArray(o)) {
|
||||
var l = JS<List>('returns:List;creates:;', '#', o);
|
||||
List<Object?> dartObject = [];
|
||||
_convertedObjects[o] = dartObject;
|
||||
int length = getProperty(o, 'length');
|
||||
for (int i = 0; i < length; i++) {
|
||||
dartObject.add(dartify(getProperty(o, i)));
|
||||
dartObject.add(convert(l[i]));
|
||||
}
|
||||
return dartObject;
|
||||
}
|
||||
throw ArgumentError(
|
||||
"JavaScriptObject $o must be a primitive, simple object, or array");
|
||||
|
||||
// Assume anything else is already a valid Dart object, either by having
|
||||
// already been processed, or e.g. a cloneable native class.
|
||||
return o;
|
||||
}
|
||||
|
||||
return convert();
|
||||
return convert(o);
|
||||
}
|
||||
|
|
|
@ -150,13 +150,10 @@ external Object? get objectPrototype;
|
|||
external List<Object?> objectKeys(Object? object);
|
||||
|
||||
/// Returns `true` if a given object is a JavaScript array.
|
||||
bool isJavaScriptArray(value) => instanceOfString(value, 'Array');
|
||||
external bool isJavaScriptArray(value);
|
||||
|
||||
/// Returns `true` if a given object is a simple JavaScript object.
|
||||
bool isJavaScriptSimpleObject(value) {
|
||||
final Object? proto = objectGetPrototypeOf(value);
|
||||
return proto == null || proto == objectPrototype;
|
||||
}
|
||||
external bool isJavaScriptSimpleObject(value);
|
||||
|
||||
/// Effectively the inverse of [jsify], [dartify] Takes a JavaScript object, and
|
||||
/// converts it to a Dart based object. Only JS primitives, arrays, or 'map'
|
||||
|
|
|
@ -30,7 +30,15 @@ main() {
|
|||
};
|
||||
globalThis.recObjectData = {};
|
||||
globalThis.recObjectData = {'foo': globalThis.recObjectData}
|
||||
globalThis.throwData = function() {};
|
||||
globalThis.throwData = new RegExp();
|
||||
globalThis.complexData = {
|
||||
'a': new Date(0),
|
||||
'b': new Promise((resolve, reject) => {}),
|
||||
};
|
||||
globalThis.complexList = [
|
||||
new Date(0),
|
||||
new Promise((resolve, reject) => {}),
|
||||
];
|
||||
""");
|
||||
|
||||
test('convert an array', () {
|
||||
|
@ -79,7 +87,22 @@ main() {
|
|||
Expect.deepEquals(expectedValues, dartObject);
|
||||
});
|
||||
|
||||
test('throws if object is not an object literal or array', () {
|
||||
test('complex types convert in an Object', () {
|
||||
Object? jsObject = js_util.getProperty(js_util.globalThis, 'complexData');
|
||||
Map<Object?, Object?> dartObject =
|
||||
js_util.dartify(jsObject) as Map<Object?, Object?>;
|
||||
Expect.isTrue(dartObject['a']! is DateTime);
|
||||
Expect.isTrue(dartObject['b']! is Future);
|
||||
});
|
||||
|
||||
test('complex types convert in a List', () {
|
||||
Object? jsArray = js_util.getProperty(js_util.globalThis, 'complexList');
|
||||
List<Object?> dartList = js_util.dartify(jsArray) as List<Object?>;
|
||||
Expect.isTrue(dartList[0] is DateTime);
|
||||
Expect.isTrue(dartList[1] is Future);
|
||||
});
|
||||
|
||||
test('throws if object is a regexp', () {
|
||||
expect(
|
||||
() => js_util
|
||||
.dartify(js_util.getProperty(js_util.globalThis, 'throwData')),
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// 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.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
// Tests the dartify functionality of the js_util library.
|
||||
|
||||
@JS()
|
||||
|
@ -30,13 +32,21 @@ main() {
|
|||
};
|
||||
globalThis.recObjectData = {};
|
||||
globalThis.recObjectData = {'foo': globalThis.recObjectData}
|
||||
globalThis.throwData = function() {};
|
||||
globalThis.throwData = new RegExp();
|
||||
globalThis.complexData = {
|
||||
'a': new Date(0),
|
||||
'b': new Promise((resolve, reject) => {}),
|
||||
};
|
||||
globalThis.complexList = [
|
||||
new Date(0),
|
||||
new Promise((resolve, reject) => {}),
|
||||
];
|
||||
""");
|
||||
|
||||
test('convert an array', () {
|
||||
Object? jsArray = js_util.getProperty(js_util.globalThis, 'arrayData');
|
||||
Object? dartArray = js_util.dartify(jsArray);
|
||||
List<Object?> expectedValues = [
|
||||
Object jsArray = js_util.getProperty(js_util.globalThis, 'arrayData');
|
||||
Object dartArray = js_util.dartify(jsArray);
|
||||
List<Object> expectedValues = [
|
||||
1,
|
||||
2,
|
||||
false,
|
||||
|
@ -50,16 +60,16 @@ main() {
|
|||
});
|
||||
|
||||
test('convert a recursive array', () {
|
||||
Object? jsArray = js_util.getProperty(js_util.globalThis, 'recArrayData');
|
||||
Object? dartArray = js_util.dartify(jsArray);
|
||||
List<Object?> expectedValues = [[]];
|
||||
Object jsArray = js_util.getProperty(js_util.globalThis, 'recArrayData');
|
||||
Object dartArray = js_util.dartify(jsArray);
|
||||
List<Object> expectedValues = [[]];
|
||||
Expect.deepEquals(expectedValues, dartArray);
|
||||
});
|
||||
|
||||
test('convert an object literal', () {
|
||||
Object? jsObject = js_util.getProperty(js_util.globalThis, 'objectData');
|
||||
Object? dartObject = js_util.dartify(jsObject);
|
||||
Map<Object?, Object?> expectedValues = {
|
||||
Object jsObject = js_util.getProperty(js_util.globalThis, 'objectData');
|
||||
Object dartObject = js_util.dartify(jsObject);
|
||||
Map<Object, Object> expectedValues = {
|
||||
'a': 1,
|
||||
'b': [1, 2, 3],
|
||||
'c': {
|
||||
|
@ -71,15 +81,31 @@ main() {
|
|||
});
|
||||
|
||||
test('convert a recursive object literal', () {
|
||||
Object? jsObject = js_util.getProperty(js_util.globalThis, 'recObjectData');
|
||||
Object? dartObject = js_util.dartify(jsObject);
|
||||
Map<Object?, Object?> expectedValues = {
|
||||
Object jsObject = js_util.getProperty(js_util.globalThis, 'recObjectData');
|
||||
Object dartObject = js_util.dartify(jsObject);
|
||||
Map<Object, Object> expectedValues = {
|
||||
'foo': {},
|
||||
};
|
||||
Expect.deepEquals(expectedValues, dartObject);
|
||||
});
|
||||
|
||||
test('throws if object is not an object literal or array', () {
|
||||
test('complex types convert in an Object', () {
|
||||
Object jsObject = js_util.getProperty(js_util.globalThis, 'complexData');
|
||||
Map<Object, Object> dartObject =
|
||||
js_util.dartify(jsObject) as Map<Object, Object>;
|
||||
print(dartObject);
|
||||
Expect.isTrue(dartObject['a'] is DateTime);
|
||||
Expect.isTrue(dartObject['b'] is Future);
|
||||
});
|
||||
|
||||
test('complex types convert in a List', () {
|
||||
Object jsArray = js_util.getProperty(js_util.globalThis, 'complexList');
|
||||
List<Object> dartList = js_util.dartify(jsArray) as List<Object>;
|
||||
Expect.isTrue(dartList[0] is DateTime);
|
||||
Expect.isTrue(dartList[1] is Future);
|
||||
});
|
||||
|
||||
test('throws if object is a regexp', () {
|
||||
expect(
|
||||
() => js_util
|
||||
.dartify(js_util.getProperty(js_util.globalThis, 'throwData')),
|
||||
|
|
Loading…
Reference in a new issue