Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Unified Diff: tool/input_sdk/lib/js/dart2js/js_dart2js.dart

Issue 1200233004: fixes #168, dart:js implementation with a test (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: fix error (window not defined) Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « test/codegen/sunflower/dom.dart ('k') | tool/input_sdk/patch/async_patch.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
}
« no previous file with comments | « test/codegen/sunflower/dom.dart ('k') | tool/input_sdk/patch/async_patch.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698