Add dart:js_util library and tests to ddc. This library is already added to dart2js and dartium.

BUG=
R=jmesserly@google.com

Review URL: https://codereview.chromium.org/2269963005 .
This commit is contained in:
Jacob Richman 2016-08-24 09:13:06 -07:00
parent 9d8ae169de
commit 87577db2b2
6 changed files with 587 additions and 0 deletions

View file

@ -20,6 +20,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
const core = Object.create(null);
const isolate = Object.create(null);
const js = Object.create(null);
const js_util = Object.create(null);
const math = Object.create(null);
const mirrors = Object.create(null);
const typed_data = Object.create(null);
@ -629,6 +630,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
let dynamicAnddynamicAndFnToObject = () => (dynamicAnddynamicAndFnToObject = dart.constFn(dart.definiteFunctionType(core.Object, [dart.dynamic, dart.dynamic, dynamicTodynamic()])))();
let FToF = () => (FToF = dart.constFn(dart.definiteFunctionType(F => [F, [F]])))();
let FunctionToFunction = () => (FunctionToFunction = dart.constFn(dart.definiteFunctionType(core.Function, [core.Function])))();
let FunctionAndListTodynamic = () => (FunctionAndListTodynamic = dart.constFn(dart.definiteFunctionType(dart.dynamic, [core.Function, core.List])))();
let TAndTToT = () => (TAndTToT = dart.constFn(dart.definiteFunctionType(T => [T, [T, T]])))();
let TAndTToT$ = () => (TAndTToT$ = dart.constFn(dart.definiteFunctionType(T => [T, [T, T]])))();
let numAndnumTodouble = () => (numAndnumTodouble = dart.constFn(dart.definiteFunctionType(core.double, [core.num, core.num])))();
@ -33773,6 +33775,109 @@ dart_library.library('dart_sdk', null, /* Imports */[
return ret;
};
dart.fn(js.allowInteropCaptureThis, FunctionToFunction());
js_util.jsify = function(object) {
if (!core.Map.is(object) && !core.Iterable.is(object)) {
dart.throw(new core.ArgumentError("object must be a Map or Iterable"));
}
return js_util._convertDataTree(object);
};
dart.fn(js_util.jsify, dynamicTodynamic$());
js_util._convertDataTree = function(data) {
let _convertedObjects = collection.HashMap.identity();
function _convert(o) {
if (dart.test(_convertedObjects.containsKey(o))) {
return _convertedObjects.get(o);
}
if (core.Map.is(o)) {
let convertedMap = {};
_convertedObjects.set(o, convertedMap);
for (let key of o[dartx.keys]) {
convertedMap[key] = _convert(o[dartx.get](key));
}
return convertedMap;
} else if (core.Iterable.is(o)) {
let convertedList = [];
_convertedObjects.set(o, convertedList);
convertedList[dartx.addAll](o[dartx.map](dart.dynamic)(_convert));
return convertedList;
} else {
return o;
}
}
dart.fn(_convert, dynamicTodynamic$());
return _convert(data);
};
dart.fn(js_util._convertDataTree, dynamicTodynamic$());
js_util.newObject = function() {
return {};
};
dart.fn(js_util.newObject, VoidTodynamic$());
js_util.hasProperty = function(o, name) {
return name in o;
};
dart.fn(js_util.hasProperty, dynamicAnddynamicTodynamic$());
js_util.getProperty = function(o, name) {
return o[name];
};
dart.fn(js_util.getProperty, dynamicAnddynamicTodynamic$());
js_util.setProperty = function(o, name, value) {
return o[name] = value;
};
dart.fn(js_util.setProperty, dynamicAnddynamicAnddynamicTodynamic());
js_util.callMethod = function(o, method, args) {
return o[method].apply(o, args);
};
dart.fn(js_util.callMethod, dynamicAndStringAndListTodynamic());
js_util.instanceof = function(o, type) {
return o instanceof type;
};
dart.fn(js_util.instanceof, dynamicAndFunctionTodynamic());
js_util.callConstructor = function(constr, arguments$) {
if (arguments$ == null) {
return new constr();
}
if (arguments$ instanceof Array) {
let argumentCount = arguments$.length;
switch (argumentCount) {
case 0:
{
return new constr();
}
case 1:
{
let arg0 = arguments$[0];
return new constr(arg0);
}
case 2:
{
let arg0 = arguments$[0];
let arg1 = arguments$[1];
return new constr(arg0, arg1);
}
case 3:
{
let arg0 = arguments$[0];
let arg1 = arguments$[1];
let arg2 = arguments$[2];
return new constr(arg0, arg1, arg2);
}
case 4:
{
let arg0 = arguments$[0];
let arg1 = arguments$[1];
let arg2 = arguments$[2];
let arg3 = arguments$[3];
return new constr(arg0, arg1, arg2, arg3);
}
}
}
let args = [null];
args[dartx.addAll](arguments$);
let factoryFunction = constr.bind.apply(constr, args);
String(factoryFunction);
return new factoryFunction();
};
dart.fn(js_util.callConstructor, FunctionAndListTodynamic());
math.E = 2.718281828459045;
math.LN10 = 2.302585092994046;
math.LN2 = 0.6931471805599453;
@ -84098,6 +84203,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
exports.core = core;
exports.isolate = isolate;
exports.js = js;
exports.js_util = js_util;
exports.math = math;
exports.mirrors = mirrors;
exports.typed_data = typed_data;

View file

@ -2629,6 +2629,7 @@
'js_function_getter_trust_types_test': 'unittest',
'js_interop_1_test': 'unittest',
'js_test': 'unittest',
'js_util_test': 'unittest',
'js_typed_interop_anonymous2_exp_test': 'unittest',
'js_typed_interop_anonymous2_test': 'unittest',
'js_typed_interop_anonymous_exp_test': 'unittest',

View file

@ -0,0 +1,347 @@
// Copyright (c) 2013, 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.
@JS()
library js_native_test;
import 'dart:async';
import 'dart:html';
import 'dart:typed_data' show ByteBuffer, Int32List;
import 'dart:indexed_db' show IdbFactory, KeyRange;
import 'package:js/js.dart';
import 'package:js/js_util.dart' as js_util;
import 'package:unittest/unittest.dart';
import 'package:unittest/html_individual_config.dart';
_injectJs() {
final script = new ScriptElement();
script.type = 'text/javascript';
script.innerHtml = r"""
var x = 42;
var _x = 123;
var myArray = ["value1"];
function returnThis() {
return this;
}
function getTypeOf(o) {
return typeof(o);
}
function Foo(a) {
this.a = a;
}
Foo.b = 38;
Foo.prototype.bar = function() {
return this.a;
}
Foo.prototype.toString = function() {
return "I'm a Foo a=" + this.a;
}
var container = new Object();
container.Foo = Foo;
function checkMap(m, key, value) {
if (m.hasOwnProperty(key))
return m[key] == value;
else
return false;
}
""";
document.body.append(script);
}
@JS()
external bool checkMap(m, String, value);
@JS('JSON.stringify')
external String stringify(o);
@JS('Node')
external get JSNodeType;
@JS('Element')
external get JSElementType;
@JS('Text')
external get JSTextType;
@JS('HTMLCanvasElement')
external get JSHtmlCanvasElementType;
@JS()
class Foo {
external Foo(num a);
external num get a;
external num bar();
}
@JS('Foo')
external get JSFooType;
@JS()
@anonymous
class ExampleTypedLiteral {
external factory ExampleTypedLiteral({a, b, JS$_c, JS$class});
external get a;
external get b;
external get JS$_c;
external set JS$_c(v);
// Identical to JS$_c but only accessible within the library.
external get _c;
external get JS$class;
external set JS$class(v);
}
@JS("Object.prototype.hasOwnProperty")
external get _hasOwnProperty;
bool hasOwnProperty(o, String name) {
return js_util.callMethod(_hasOwnProperty, 'call', [o, name]);
}
main() {
_injectJs();
useHtmlIndividualConfiguration();
group('js_util.jsify()', () {
test('convert a List', () {
final list = [1, 2, 3, 4, 5, 6, 7, 8];
var array = js_util.jsify(list);
expect(array is List, isTrue);
expect(identical(array, list), isFalse);
expect(array.length, equals(list.length));
for (var i = 0; i < list.length; i++) {
expect(array[i], equals(list[i]));
}
});
test('convert an Iterable', () {
final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
var array = js_util.jsify(set);
expect(array is List, isTrue);
expect(array.length, equals(set.length));
for (var i = 0; i < array.length; i++) {
expect(set.contains(array[i]), isTrue);
}
});
test('convert a Map', () {
var map = {'a': 1, 'b': 2, 'c': 3};
var jsMap = js_util.jsify(map);
expect(jsMap is! List, isTrue);
for (var key in map.keys) {
expect(checkMap(jsMap, key, map[key]), isTrue);
}
});
test('deep convert a complex object', () {
final object = {
'a': [
1,
[2, 3]
],
'b': {'c': 3, 'd': new Foo(42)},
'e': null
};
var jsObject = js_util.jsify(object);
expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
expect(
js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
expect(
js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'c'),
equals(object['b']['c']));
expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
equals(object['b']['d']));
expect(
js_util.callMethod(
js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
'bar', []),
equals(42));
expect(js_util.getProperty(jsObject, 'e'), isNull);
});
test('throws if object is not a Map or Iterable', () {
expect(() => js_util.jsify('a'),
throwsA(new isInstanceOf<ArgumentError>()));
});
});
group('js_util.newObject', () {
test('create', () {
expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
});
test('callMethod', () {
var o = js_util.newObject();
expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
expect(stringify(o), equals('{}'));
});
test('properties', () {
var o = js_util.newObject();
expect(js_util.hasProperty(o, 'foo bar'), isFalse);
expect(js_util.hasProperty(o, 'toString'), isTrue);
expect(hasOwnProperty(o, 'toString'), isFalse);
expect(hasOwnProperty(o, 'foo bar'), isFalse);
js_util.setProperty(o, 'foo bar', 42);
expect(hasOwnProperty(o, 'foo bar'), isTrue);
expect(js_util.getProperty(o, 'foo bar'), equals(42));
expect(js_util.hasProperty(o, 'foo bar'), isTrue);
expect(stringify(o), equals('{"foo bar":42}'));
});
});
group('hasProperty', () {
test('typed object', () {
var f = new Foo(42);
expect(js_util.hasProperty(f, 'a'), isTrue);
expect(js_util.hasProperty(f, 'toString'), isTrue);
js_util.setProperty(f, '__proto__', null);
expect(js_util.hasProperty(f, 'toString'), isFalse);
});
test('typed literal', () {
var l =
new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: null, JS$class: true);
expect(js_util.hasProperty(l, 'a'), isTrue);
expect(js_util.hasProperty(l, 'b'), isTrue);
expect(js_util.hasProperty(l, '_c'), isTrue);
expect(l.JS$_c, isNull);
expect(js_util.hasProperty(l, 'class'), isTrue);
// JS$_c escapes to _c so the property JS$_c will not exist on the object.
expect(js_util.hasProperty(l, r'JS$_c'), isFalse);
expect(js_util.hasProperty(l, r'JS$class'), isFalse);
expect(l.JS$class, isTrue);
l = new ExampleTypedLiteral(a: null);
expect(js_util.hasProperty(l, 'a'), isTrue);
expect(js_util.hasProperty(l, 'b'), isFalse);
expect(js_util.hasProperty(l, '_c'), isFalse);
expect(js_util.hasProperty(l, 'class'), isFalse);
l = new ExampleTypedLiteral(JS$_c: 74);
expect(js_util.hasProperty(l, '_c'), isTrue);
expect(l.JS$_c, equals(74));
});
});
group('getProperty', () {
test('typed object', () {
var f = new Foo(42);
expect(js_util.getProperty(f, 'a'), equals(42));
expect(js_util.getProperty(f, 'toString') is Function, isTrue);
js_util.setProperty(f, '__proto__', null);
expect(js_util.getProperty(f, 'toString'), isNull);
});
test('typed literal', () {
var l = new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: 7, JS$class: true);
expect(js_util.getProperty(l, 'a'), equals('x'));
expect(js_util.getProperty(l, 'b'), equals(42));
expect(js_util.getProperty(l, '_c'), equals(7));
expect(l.JS$_c, equals(7));
expect(js_util.getProperty(l, 'class'), isTrue);
expect(js_util.getProperty(l, r'JS$_c'), isNull);
expect(js_util.getProperty(l, r'JS$class'), isNull);
});
});
group('setProperty', () {
test('typed object', () {
var f = new Foo(42);
expect(js_util.getProperty(f, 'a'), equals(42));
js_util.setProperty(f, 'a', 100);
expect(f.a, equals(100));
expect(js_util.getProperty(f, 'a'), equals(100));
});
test('typed literal', () {
var l = new ExampleTypedLiteral();
js_util.setProperty(l, 'a', 'foo');
expect(js_util.getProperty(l, 'a'), equals('foo'));
expect(l.a, equals('foo'));
js_util.setProperty(l, 'a', l);
expect(identical(l.a, l), isTrue);
var list = ['arr'];
js_util.setProperty(l, 'a', list);
expect(identical(l.a, list), isTrue);
l.JS$class = 42;
expect(l.JS$class, equals(42));
js_util.setProperty(l, 'class', 100);
expect(l.JS$class, equals(100));
});
});
group('callMethod', () {
test('html object', () {
var canvas = new Element.tag('canvas');
expect(
identical(canvas.getContext('2d'),
js_util.callMethod(canvas, 'getContext', ['2d'])),
isTrue);
});
test('typed object', () {
var f = new Foo(42);
expect(js_util.callMethod(f, 'bar', []), equals(42));
});
});
group('instanceof', () {
test('html object', () {
var canvas = new Element.tag('canvas');
expect(js_util.instanceof(canvas, JSNodeType), isTrue);
expect(js_util.instanceof(canvas, JSTextType), isFalse);
expect(js_util.instanceof(canvas, JSElementType), isTrue);
expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
var div = new Element.tag('div');
expect(js_util.instanceof(div, JSNodeType), isTrue);
expect(js_util.instanceof(div, JSTextType), isFalse);
expect(js_util.instanceof(div, JSElementType), isTrue);
expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
var text = new Text('foo');
expect(js_util.instanceof(text, JSNodeType), isTrue);
expect(js_util.instanceof(text, JSTextType), isTrue);
expect(js_util.instanceof(text, JSElementType), isFalse);
});
test('typed object', () {
var f = new Foo(42);
expect(js_util.instanceof(f, JSFooType), isTrue);
expect(js_util.instanceof(f, JSNodeType), isFalse);
});
test('typed literal', () {
var l = new ExampleTypedLiteral();
expect(js_util.instanceof(l, JSFooType), isFalse);
});
});
group('callConstructor', () {
test('html object', () {
var textNode = js_util.callConstructor(JSTextType, ['foo']);
expect(js_util.instanceof(textNode, JSTextType), isTrue);
expect(textNode is Text, isTrue);
expect(textNode.text, equals('foo'));
});
test('typed object', () {
Foo f = js_util.callConstructor(JSFooType, [42]);
expect(f.a, equals(42));
});
});
}

View file

@ -37,6 +37,7 @@ main(List<String> arguments) {
'dart:core',
'dart:isolate',
'dart:js',
'dart:js_util',
'dart:math',
'dart:mirrors',
'dart:typed_data',

View file

@ -76,6 +76,12 @@ const Map<String, LibraryInfo> LIBRARIES = const {
maturity: Maturity.STABLE,
dart2jsPath: "js/dart2js/js_dart2js.dart"),
"js_util": const LibraryInfo(
"js_util/dartium/js_util_dartium.dart",
categories: "Client",
maturity: Maturity.STABLE,
dart2jsPath: "js_util/dart2js/js_util_dart2js.dart"),
"math": const LibraryInfo(
"math/math.dart",
maturity: Maturity.STABLE,

View file

@ -0,0 +1,126 @@
// Copyright (c) 2016, 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.
/// Utility methods to efficiently manipulate typed JSInterop objects in cases
/// where the name to call is not known at runtime. You should only use these
/// methods when the same effect cannot be achieved with @JS annotations.
/// These methods would be extension methods on JSObject if Dart supported
/// extension methods.
library dart.js_util;
import 'dart:_foreign_helper' show JS;
import 'dart:collection' show HashMap;
/// WARNING: performance of this method is much worse than other uitil
/// methods in this library. Only use this method as a last resort.
///
/// Recursively converts a JSON-like collection of Dart objects to a
/// collection of JavaScript objects and returns a [JsObject] proxy to it.
///
/// [object] must be a [Map] or [Iterable], the contents of which are also
/// converted. Maps and Iterables are copied to a new JavaScript object.
/// Primitives and other transferrable values are directly converted to their
/// JavaScript type, and all other objects are proxied.
jsify(object) {
if ((object is! Map) && (object is! Iterable)) {
throw new ArgumentError("object must be a Map or Iterable");
}
return _convertDataTree(object);
}
_convertDataTree(data) {
var _convertedObjects = new HashMap.identity();
_convert(o) {
if (_convertedObjects.containsKey(o)) {
return _convertedObjects[o];
}
if (o is Map) {
final convertedMap = JS('=Object', '{}');
_convertedObjects[o] = convertedMap;
for (var key in o.keys) {
JS('=Object', '#[#]=#', convertedMap, key, _convert(o[key]));
}
return convertedMap;
} else if (o is Iterable) {
var convertedList = [];
_convertedObjects[o] = convertedList;
convertedList.addAll(o.map(_convert));
return convertedList;
} else {
return o;
}
}
return _convert(data);
}
newObject() => JS('=Object', '{}');
hasProperty(o, name) => JS('bool', '# in #', name, o);
getProperty(o, name) => JS('Object', '#[#]', o, name);
setProperty(o, name, value) => JS('', '#[#]=#', o, name, value);
callMethod(o, String method, List args) =>
JS('Object', '#[#].apply(#, #)', o, method, o, args);
instanceof(o, Function type) => JS('bool', '# instanceof #', o, type);
callConstructor(Function constr, List arguments) {
if (arguments == null) {
return JS('Object', 'new #()', constr);
}
if (JS('bool', '# instanceof Array', arguments)) {
int argumentCount = JS('int', '#.length', arguments);
switch (argumentCount) {
case 0:
return JS('Object', 'new #()', constr);
case 1:
var arg0 = JS('', '#[0]', arguments);
return JS('Object', 'new #(#)', constr, arg0);
case 2:
var arg0 = JS('', '#[0]', arguments);
var arg1 = JS('', '#[1]', arguments);
return JS('Object', 'new #(#, #)', constr, arg0, arg1);
case 3:
var arg0 = JS('', '#[0]', arguments);
var arg1 = JS('', '#[1]', arguments);
var arg2 = JS('', '#[2]', arguments);
return JS('Object', 'new #(#, #, #)', constr, arg0, arg1, arg2);
case 4:
var arg0 = JS('', '#[0]', arguments);
var arg1 = JS('', '#[1]', arguments);
var arg2 = JS('', '#[2]', arguments);
var arg3 = JS('', '#[3]', arguments);
return JS(
'Object', 'new #(#, #, #, #)', constr, arg0, arg1, arg2, arg3);
}
}
// The following code solves the problem of invoking a JavaScript
// constructor with an unknown number arguments.
// First bind the constructor to the argument list using bind.apply().
// The first argument to bind() is the binding of 't', so add 'null' to
// the arguments list passed to apply().
// After that, use the JavaScript 'new' operator which overrides any binding
// of 'this' with the new instance.
var args = [null]..addAll(arguments);
var factoryFunction = JS('', '#.bind.apply(#, #)', constr, constr, args);
// Without this line, calling factoryFunction as a constructor throws
JS('String', 'String(#)', factoryFunction);
// This could return an UnknownJavaScriptObject, or a native
// object for which there is an interceptor
return JS('Object', 'new #()', factoryFunction);
// TODO(sra): Investigate:
//
// var jsObj = JS('', 'Object.create(#.prototype)', constr);
// JS('', '#.apply(#, #)', constr, jsObj,
// []..addAll(arguments.map(_convertToJS)));
// return _wrapToDart(jsObj);
}