Chromium Code Reviews| 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)); |
|
vsm
2015/06/24 23:16:15
Well, that's a lot simpler... :-)
|
| } |
| /** |
| @@ -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])); |
|
vsm
2015/06/24 23:16:15
Should we preserve the '=Object'? We won't use it
Jennifer Messerly
2015/06/25 20:11:27
hmm, I hope not :)
Not sure it ever makes sense t
|
| } |
| 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; |
| } |