| Index: tool/input_sdk/lib/js/dart2js/js_dart2js.dart
|
| diff --git a/tool/input_sdk/lib/js/dart2js/js_dart2js.dart b/tool/input_sdk/lib/js/dart2js/js_dart2js.dart
|
| index 6de50320ae1f5770d1a63cd21d13b48a083f50aa..91bac6adfbde6d31a6881df93286e6e110c91db5 100644
|
| --- a/tool/input_sdk/lib/js/dart2js/js_dart2js.dart
|
| +++ b/tool/input_sdk/lib/js/dart2js/js_dart2js.dart
|
| @@ -1,4 +1,4 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| +// Copyright (c) 2015, 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.
|
|
|
| @@ -87,35 +87,13 @@
|
| */
|
| library dart.js;
|
|
|
| -import 'dart:html' show Blob, Event, ImageData, Node, Window;
|
| import 'dart:collection' show HashMap, ListMixin;
|
| -import 'dart:indexed_db' show KeyRange;
|
| -import 'dart:typed_data' show TypedData;
|
| -
|
| -import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
|
| -import 'dart:_interceptors' show JavaScriptObject, UnknownJavaScriptObject;
|
| -import 'dart:_js_helper' show Primitives, convertDartClosureToJS,
|
| - getIsolateAffinityTag;
|
| -
|
| -final JsObject context = _wrapToDart(JS('', 'self'));
|
| -
|
| -_convertDartFunction(Function f, {bool captureThis: false}) {
|
| - return JS('',
|
| - 'function(_call, f, captureThis) {'
|
| - 'return function() {'
|
| - 'return _call(f, captureThis, this, '
|
| - 'Array.prototype.slice.apply(arguments));'
|
| - '}'
|
| - '}(#, #, #)', DART_CLOSURE_TO_JS(_callDartFunction), f, captureThis);
|
| -}
|
|
|
| -_callDartFunction(callback, bool captureThis, self, List arguments) {
|
| - if (captureThis) {
|
| - arguments = [self]..addAll(arguments);
|
| - }
|
| - var dartArgs = new List.from(arguments.map(_convertToDart));
|
| - return _convertToJS(Function.apply(callback, dartArgs));
|
| -}
|
| +import 'dart:_interceptors' as _interceptors show JSArray;
|
| +import 'dart:_js_helper' show JsName, Primitives;
|
| +import 'dart:_foreign_helper' show JS;
|
| +
|
| +final JsObject context = _wrapToDart(JS('', 'dart.global'));
|
|
|
| /**
|
| * Proxies a JavaScript object to Dart.
|
| @@ -127,7 +105,7 @@ class JsObject {
|
| // The wrapped JS object.
|
| final dynamic _jsObject;
|
|
|
| - // This shoud only be called from _wrapToDart
|
| + // This should only be called from _wrapToDart
|
| JsObject._fromJs(this._jsObject) {
|
| assert(_jsObject != null);
|
| }
|
| @@ -137,26 +115,11 @@ class JsObject {
|
| * to it.
|
| */
|
| factory JsObject(JsFunction constructor, [List arguments]) {
|
| - var constr = _convertToJS(constructor);
|
| + var ctor = constructor._jsObject;
|
| if (arguments == null) {
|
| - return _wrapToDart(JS('', 'new #()', constr));
|
| + return _wrapToDart(JS('', 'new #()', ctor));
|
| }
|
| - // 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 'this', 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.map(_convertToJS));
|
| - 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
|
| - var jsObj = JS('JavaScriptObject', 'new #()', factoryFunction);
|
| -
|
| - return _wrapToDart(jsObj);
|
| + return _wrapToDart(JS('', 'new #(...#)', ctor, arguments));
|
| }
|
|
|
| /**
|
| @@ -172,8 +135,7 @@ class JsObject {
|
| */
|
| factory JsObject.fromBrowserObject(object) {
|
| if (object is num || object is String || object is bool || object == null) {
|
| - throw new ArgumentError(
|
| - "object cannot be a num, string, bool, or null");
|
| + throw new ArgumentError("object cannot be a num, string, bool, or null");
|
| }
|
| return _wrapToDart(_convertToJS(object));
|
| }
|
| @@ -184,7 +146,7 @@ class JsObject {
|
| *
|
| * [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
|
| + * Primitives and other transferable values are directly converted to their
|
| * JavaScript type, and all other objects are proxied.
|
| */
|
| factory JsObject.jsify(object) {
|
| @@ -202,10 +164,10 @@ class JsObject {
|
| return _convertedObjects[o];
|
| }
|
| if (o is Map) {
|
| - final convertedMap = JS('=Object', '{}');
|
| + final convertedMap = JS('', '{}');
|
| _convertedObjects[o] = convertedMap;
|
| for (var key in o.keys) {
|
| - JS('=Object', '#[#]=#', convertedMap, key, _convert(o[key]));
|
| + JS('', '#[#] = #', convertedMap, key, _convert(o[key]));
|
| }
|
| return convertedMap;
|
| } else if (o is Iterable) {
|
| @@ -227,7 +189,7 @@ class JsObject {
|
| *
|
| * The type of [property] must be either [String] or [num].
|
| */
|
| - dynamic operator[](property) {
|
| + dynamic operator [](property) {
|
| if (property is! String && property is! num) {
|
| throw new ArgumentError("property is not a String or num");
|
| }
|
| @@ -240,17 +202,17 @@ class JsObject {
|
| *
|
| * The type of [property] must be either [String] or [num].
|
| */
|
| - operator[]=(property, value) {
|
| + operator []=(property, value) {
|
| if (property is! String && property is! num) {
|
| throw new ArgumentError("property is not a String or num");
|
| }
|
| - JS('', '#[#]=#', _jsObject, property, _convertToJS(value));
|
| + JS('', '#[#] = #', _jsObject, property, _convertToJS(value));
|
| }
|
|
|
| int get hashCode => 0;
|
|
|
| - bool operator==(other) => other is JsObject &&
|
| - JS('bool', '# === #', _jsObject, other._jsObject);
|
| + bool operator ==(other) =>
|
| + other is JsObject && JS('bool', '# === #', _jsObject, other._jsObject);
|
|
|
| /**
|
| * Returns `true` if the JavaScript object contains the specified property
|
| @@ -292,7 +254,7 @@ class JsObject {
|
| String toString() {
|
| try {
|
| return JS('String', 'String(#)', _jsObject);
|
| - } catch(e) {
|
| + } catch (e) {
|
| return super.toString();
|
| }
|
| }
|
| @@ -307,9 +269,12 @@ class JsObject {
|
| if (method is! String && method is! num) {
|
| throw new ArgumentError("method is not a String or num");
|
| }
|
| - return _convertToDart(JS('', '#[#].apply(#, #)', _jsObject, method,
|
| - _jsObject,
|
| - args == null ? null : new List.from(args.map(_convertToJS))));
|
| + if (args != null) args = new List.from(args.map(_convertToJS));
|
| + var fn = JS('', '#[#]', _jsObject, method);
|
| + if (!JS('bool', '# instanceof Function', fn)) {
|
| + throw new NoSuchMethodError(_jsObject, new Symbol(method), args, {});
|
| + }
|
| + return _convertToDart(JS('', '#.apply(#, #)', fn, _jsObject, args));
|
| }
|
| }
|
|
|
| @@ -323,8 +288,13 @@ class JsFunction extends JsObject {
|
| * with the value of this passed as the first argument.
|
| */
|
| factory JsFunction.withThis(Function f) {
|
| - var jsFunc = _convertDartFunction(f, captureThis: true);
|
| - return new JsFunction._fromJs(jsFunc);
|
| + return new JsFunction._fromJs(JS('', 'function(/*...arguments*/) {'
|
| + ' let args = [#(this)];'
|
| + ' for (let arg of arguments) {'
|
| + ' args.push(#(arg));'
|
| + ' }'
|
| + ' return #(#(...args));'
|
| + '}', _convertToDart, _convertToDart, _convertToJS, f));
|
| }
|
|
|
| JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
|
| @@ -333,15 +303,13 @@ class JsFunction extends JsObject {
|
| * Invokes the JavaScript function with arguments [args]. If [thisArg] is
|
| * supplied it is the value of `this` for the invocation.
|
| */
|
| - dynamic apply(List args, { thisArg }) =>
|
| - _convertToDart(JS('', '#.apply(#, #)', _jsObject,
|
| - _convertToJS(thisArg),
|
| - args == null ? null : new List.from(args.map(_convertToJS))));
|
| + dynamic apply(List args, {thisArg}) => _convertToDart(JS('', '#.apply(#, #)',
|
| + _jsObject, _convertToJS(thisArg),
|
| + args == null ? null : new List.from(args.map(_convertToJS))));
|
| }
|
|
|
| -/**
|
| - * A [List] that proxies a JavaScript array.
|
| - */
|
| +// TODO(jmesserly): this is totally unnecessary in dev_compiler.
|
| +/** A [List] that proxies a JavaScript array. */
|
| class JsArray<E> extends JsObject with ListMixin<E> {
|
|
|
| /**
|
| @@ -409,8 +377,9 @@ class JsArray<E> extends JsObject with ListMixin<E> {
|
| throw new StateError('Bad JsArray length');
|
| }
|
|
|
| - void set length(int length) { super['length'] = length; }
|
| -
|
| + void set length(int length) {
|
| + super['length'] = length;
|
| + }
|
|
|
| // Methods overriden for better performance
|
|
|
| @@ -446,7 +415,7 @@ class JsArray<E> extends JsObject with ListMixin<E> {
|
| }
|
|
|
| void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
|
| - _checkRange(start, end, length);
|
| + _checkRange(start, end, this.length);
|
| int length = end - start;
|
| if (length == 0) return;
|
| if (skipCount < 0) throw new ArgumentError(skipCount);
|
| @@ -460,84 +429,47 @@ class JsArray<E> extends JsObject with ListMixin<E> {
|
| }
|
| }
|
|
|
| -// property added to a Dart object referencing its JS-side DartObject proxy
|
| -final String _DART_OBJECT_PROPERTY_NAME =
|
| - getIsolateAffinityTag(r'_$dart_dartObject');
|
| -final String _DART_CLOSURE_PROPERTY_NAME =
|
| - getIsolateAffinityTag(r'_$dart_dartClosure');
|
| -
|
| -// property added to a JS object referencing its Dart-side JsObject proxy
|
| -const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject';
|
| -const _JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction';
|
| -
|
| -bool _defineProperty(o, String name, value) {
|
| - if (_isExtensible(o) &&
|
| - // TODO(ahe): Calling _hasOwnProperty to work around
|
| - // https://code.google.com/p/dart/issues/detail?id=21331.
|
| - !_hasOwnProperty(o, name)) {
|
| - try {
|
| - JS('void', 'Object.defineProperty(#, #, { value: #})', o, name, value);
|
| - return true;
|
| - } catch (e) {
|
| - // object is native and lies about being extensible
|
| - // see https://bugzilla.mozilla.org/show_bug.cgi?id=775185
|
| - }
|
| - }
|
| - return false;
|
| +bool _isBrowserType(o) => JS('bool',
|
| + '# instanceof Blob || '
|
| + '# instanceof Event || '
|
| + '(window.KeyRange && # instanceof KeyRange) || '
|
| + '# instanceof ImageData || '
|
| + '# instanceof Node || '
|
| + '(window.TypedData && # instanceof TypedData) || '
|
| + '# instanceof Window', o, o, o, o, o, o, o);
|
| +
|
| +class _DartObject {
|
| + final _dartObj;
|
| + _DartObject(this._dartObj);
|
| }
|
|
|
| -bool _hasOwnProperty(o, String name) {
|
| - return JS('bool', 'Object.prototype.hasOwnProperty.call(#, #)', o, name);
|
| -}
|
| -
|
| -bool _isExtensible(o) => JS('bool', 'Object.isExtensible(#)', o);
|
| -
|
| -Object _getOwnProperty(o, String name) {
|
| - if (_hasOwnProperty(o, name)) {
|
| - return JS('', '#[#]', o, name);
|
| - }
|
| - return null;
|
| -}
|
| -
|
| -bool _isLocalObject(o) => JS('bool', '# instanceof Object', o);
|
| -
|
| -// The shared constructor function for proxies to Dart objects in JavaScript.
|
| -final _dartProxyCtor = JS('', 'function DartObject(o) { this.o = o; }');
|
| -
|
| dynamic _convertToJS(dynamic o) {
|
| - // Note: we don't write `if (o == null) return null;` to make sure dart2js
|
| - // doesn't convert `return null;` into `return;` (which would make `null` be
|
| - // `undefined` in Javascprit). See dartbug.com/20305 for details.
|
| - if (o == null || o is String || o is num || o is bool) {
|
| - return o;
|
| - } else if (o is Blob || o is Event || o is KeyRange || o is ImageData
|
| - || o is Node || o is TypedData || o is Window) {
|
| + if (o == null ||
|
| + o is String ||
|
| + o is num ||
|
| + o is bool ||
|
| + _isBrowserType(o)) {
|
| return o;
|
| } else if (o is DateTime) {
|
| return Primitives.lazyAsJsDate(o);
|
| } else if (o is JsObject) {
|
| return o._jsObject;
|
| } else if (o is Function) {
|
| - return _getJsProxy(o, _JS_FUNCTION_PROPERTY_NAME, (o) {
|
| - var jsFunction = _convertDartFunction(o);
|
| - // set a property on the JS closure referencing the Dart closure
|
| - _defineProperty(jsFunction, _DART_CLOSURE_PROPERTY_NAME, o);
|
| - return jsFunction;
|
| - });
|
| + return _putIfAbsent(_jsProxies, o, _wrapDartFunction);
|
| } else {
|
| - var ctor = _dartProxyCtor;
|
| - return _getJsProxy(o, _JS_OBJECT_PROPERTY_NAME,
|
| - (o) => JS('', 'new #(#)', ctor, o));
|
| + // TODO(jmesserly): for now, we wrap other objects, to keep compatibility
|
| + // with the original dart:js behavior.
|
| + return _putIfAbsent(_jsProxies, o, (o) => new _DartObject(o));
|
| }
|
| }
|
|
|
| -Object _getJsProxy(o, String propertyName, createProxy(o)) {
|
| - var jsProxy = _getOwnProperty(o, propertyName);
|
| - if (jsProxy == null) {
|
| - jsProxy = createProxy(o);
|
| - _defineProperty(o, propertyName, jsProxy);
|
| - }
|
| - return jsProxy;
|
| +dynamic _wrapDartFunction(f) {
|
| + var wrapper = JS('', 'function(/*...arguments*/) {'
|
| + ' let args = Array.prototype.map.call(arguments, #);'
|
| + ' return #(#(...args));'
|
| + '}', _convertToDart, _convertToJS, f);
|
| + _dartProxies[wrapper] = f;
|
| + return wrapper;
|
| }
|
|
|
| // converts a Dart object to a reference to a native JS object
|
| @@ -546,48 +478,37 @@ Object _convertToDart(o) {
|
| if (JS('bool', '# == null', o) ||
|
| JS('bool', 'typeof # == "string"', o) ||
|
| JS('bool', 'typeof # == "number"', o) ||
|
| - JS('bool', 'typeof # == "boolean"', o)) {
|
| + JS('bool', 'typeof # == "boolean"', o) ||
|
| + _isBrowserType(o)) {
|
| return o;
|
| - } else if (_isLocalObject(o)
|
| - && (o is Blob || o is Event || o is KeyRange || o is ImageData
|
| - || o is Node || o is TypedData || o is Window)) {
|
| - // long line: dart2js doesn't allow string concatenation in the JS() form
|
| - return JS('Blob|Event|KeyRange|ImageData|Node|TypedData|Window', '#', o);
|
| } else if (JS('bool', '# instanceof Date', o)) {
|
| var ms = JS('num', '#.getTime()', o);
|
| return new DateTime.fromMillisecondsSinceEpoch(ms);
|
| - } else if (JS('bool', '#.constructor === #', o, _dartProxyCtor)) {
|
| - return JS('', '#.o', o);
|
| + } else if (o is _DartObject) {
|
| + return o._dartObj;
|
| } else {
|
| - return _wrapToDart(o);
|
| + return _putIfAbsent(_dartProxies, o, _wrapToDart);
|
| }
|
| }
|
|
|
| -JsObject _wrapToDart(o) {
|
| +_wrapToDart(o) {
|
| if (JS('bool', 'typeof # == "function"', o)) {
|
| - return _getDartProxy(o, _DART_CLOSURE_PROPERTY_NAME,
|
| - (o) => new JsFunction._fromJs(o));
|
| - } else if (JS('bool', '# instanceof Array', o)) {
|
| - return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME,
|
| - (o) => new JsArray._fromJs(o));
|
| - } else {
|
| - return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME,
|
| - (o) => new JsObject._fromJs(o));
|
| + return new JsFunction._fromJs(o);
|
| }
|
| + if (JS('bool', '# instanceof Array', o)) {
|
| + return new JsArray._fromJs(o);
|
| + }
|
| + return new JsObject._fromJs(o);
|
| }
|
|
|
| -Object _getDartProxy(o, String propertyName, createProxy(o)) {
|
| - var dartProxy = _getOwnProperty(o, propertyName);
|
| - // Temporary fix for dartbug.com/15193
|
| - // In some cases it's possible to see a JavaScript object that
|
| - // came from a different context and was previously proxied to
|
| - // Dart in that context. The JS object will have a cached proxy
|
| - // but it won't be a valid Dart object in this context.
|
| - // For now we throw away the cached proxy, but we should be able
|
| - // to cache proxies from multiple JS contexts and Dart isolates.
|
| - if (dartProxy == null || !_isLocalObject(o)) {
|
| - dartProxy = createProxy(o);
|
| - _defineProperty(o, propertyName, dartProxy);
|
| - }
|
| - return dartProxy;
|
| +final _dartProxies = JS('', 'new WeakMap()');
|
| +final _jsProxies = JS('', 'new WeakMap()');
|
| +
|
| +Object _putIfAbsent(weakMap, o, getValue(o)) {
|
| + var value = JS('', '#.get(#)', weakMap, o);
|
| + if (value == null) {
|
| + value = getValue(o);
|
| + JS('', '#.set(#, #)', weakMap, o, value);
|
| + }
|
| + return value;
|
| }
|
|
|