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

Unified Diff: sdk/lib/js/dart2js/js.dart

Issue 15782009: RFC: introduce dart:js (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 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
Index: sdk/lib/js/dart2js/js.dart
diff --git a/sdk/lib/js/dart2js/js.dart b/sdk/lib/js/dart2js/js.dart
new file mode 100644
index 0000000000000000000000000000000000000000..0af96797f0e7986710fee0d796235148c9c0ffc9
--- /dev/null
+++ b/sdk/lib/js/dart2js/js.dart
@@ -0,0 +1,328 @@
+// 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.
+
+library dart.js;
+
+import 'dart:async' show runAsync;
+import 'dart:html' show Element, document;
vsm 2013/06/06 17:06:41 Can you try killing the two imports above and test
+import 'dart:_foreign_helper' show JS;
+import 'dart:_js_helper' show convertDartClosureToJS;
+
+JsObject get context {
+ _enterScopeIfNeeded();
vsm 2013/05/29 04:55:39 Shall we make scopes no-ops here and get rid of th
alexandre.ardhuin 2013/05/31 19:11:13 I just made some tests. On js_tests.dart we loose
vsm 2013/06/06 17:06:41 Did you eliminate dart:async above as well? On 20
+ return new JsObject._fromJs(JS('Object', 'window'));
+}
+
+int _scopeDepth = 0;
+
+int _dartObjectsTotal = 0;
+final Map<int, List> _dartObjects = new Map<int, List>();
+
+int _jsObjectsTotal = 0;
+final List<JsObject> _jsObjects = new List<JsObject>();
+
+void _enterScopeIfNeeded() {
+ if (_scopeDepth == 0) {
+ final depth = _enterScope();
+ runAsync(() => _exitScope(depth));
+ }
+}
+
+scoped(f) {
+ final depth = _enterScope();
+ try {
+ return f();
+ } finally {
+ _exitScope(depth);
+ }
+}
+
+int _enterScope() => ++_scopeDepth;
+
+void _exitScope(int depth) {
+ assert(_scopeDepth == depth);
+ _dartObjects.remove(_scopeDepth);
+ _jsObjects
+ ..where((JsObject e) => e._scope == depth).forEach((JsObject e) {
+ e._scope = null;
+ })
+ ..removeWhere((JsObject e) => e._scope == depth);
+ _scopeDepth--;
+}
+
+int $experimentalEnterScope() {
vsm 2013/06/06 17:06:41 These can go.
+ return _enterScope();
+}
+
+void $experimentalExitScope(int depth) {
+ _exitScope(depth);
+}
+
+final _globalDartObjects = [];
+final _globalJsObjects = new List<JsObject>();
+
+dynamic retain(Serializable<JsObject> object) {
+ final JsObject jsObject = object.toJs();
+ jsObject._scope = 0;
+ _globalJsObjects.add(jsObject);
+ _jsObjects.removeWhere((e) => identical(e, jsObject));
+ return object;
+}
+void release(Serializable<JsObject> object) {
+ final JsObject jsObject = object.toJs();
+ jsObject._scope = null;
+ _removeLast(_globalJsObjects, jsObject);
+}
+
+void _removeLast(List l, object) {
+ for (int i = l.length - 1; i >= 0; i--) {
+ var element = l[i];
+ if (identical(element, object)) {
+ l.removeAt(i);
+ return;
+ }
+ }
+}
+
+JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data);
+
+class Callback implements Serializable<JsFunction> {
+ final bool _manualDispose;
vsm 2013/06/06 17:06:41 We can try getting rid of _manualDispose and relat
+ final Function _f; // here to allow capture in closure
+ final bool _withThis; // here to allow capture in closure
+ Object _jsFunction;
vsm 2013/06/06 17:06:41 This should probably be typed dynamic. That is wh
+
+ Callback._internal(this._manualDispose, this._f, this._withThis) {
+ _globalDartObjects.add(_f);
+ _jsFunction = JS('Object', r'''
+(function(){
+ var f = #;
+ return function(){
+ return f(this, Array.prototype.slice.apply(arguments));
+ };
+}).apply(this)''', convertDartClosureToJS(_call, 2));
+ }
+
+ _call(thisArg, List args) {
+ if (!_globalDartObjects.any((e) => identical(e, _f)) &&
+ !_dartObjects.values.expand((e) => e).any((e) => identical(e, _f))) {
+ throw 'function has already been disposed';
+ }
+
+ final arguments = new List.from(args);
+ if (_withThis) arguments.insert(0, thisArg);
+ final deserializedArgs = arguments.map(_deserialize).toList();
+ if (_manualDispose) {
+ return _serialize(Function.apply(_f, deserializedArgs));
+ } else {
+ try {
+ return _serialize(Function.apply(_f, deserializedArgs));
+ } finally {
+ _dispose();
+ }
+ }
+ }
+
+ _dispose() {
+ _removeLast(_globalDartObjects, _f);
+ }
+
+ JsFunction toJs() => new JsFunction._fromJs(_jsFunction);
+
+ dispose() {
+ assert(_manualDispose);
+ _dispose();
+ }
+
+ factory Callback.once(Function f, {bool withThis: false}) =>
+ new Callback._internal(false, f, withThis);
+
+ factory Callback.many(Function f, {bool withThis: false}) =>
+ new Callback._internal(true, f, withThis);
+}
+
+class JsObject implements Serializable<JsObject> {
+ final Object _jsObject;
+ int _scope = _scopeDepth;
+
+ JsObject._fromJs(this._jsObject) {
+ if (_scopeDepth == 0) throw 'Cannot allocate a proxy outside of a scope.';
+ _jsObjectsTotal++;
+ _jsObjects.add(this);
+ }
+
+ factory JsObject(Serializable<JsFunction> constructor, [List arguments]) {
+ _enterScopeIfNeeded();
+ final constr = _serialize(constructor);
+ if (arguments == null) {
+ return new JsObject._fromJs(JS('Object', 'new #()', constr));
+ }
+ final args = arguments.map(_serialize).toList();
+ switch (args.length) {
+ case 0:
+ return new JsObject._fromJs(JS('Object', 'new #()', constr));
+ case 1:
+ return new JsObject._fromJs(JS('Object', 'new #(#)', constr, args[0]));
+ case 2:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#)', constr, args[0],
+ args[1]));
+ case 3:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#)', constr,
+ args[0], args[1], args[2]));
+ case 4:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#)', constr,
+ args[0], args[1], args[2], args[3]));
+ case 5:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#)', constr,
+ args[0], args[1], args[2], args[3], args[4]));
+ case 6:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#)', constr,
+ args[0], args[1], args[2], args[3], args[4], args[5]));
+ case 7:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#)', constr,
+ args[0], args[1], args[2], args[3], args[4], args[5], args[6]));
+ case 8:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#,#)',
+ constr, args[0], args[1], args[2], args[3], args[4], args[5],
+ args[6], args[7]));
+ case 9:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#,#,#)',
+ constr, args[0], args[1], args[2], args[3], args[4], args[5],
+ args[6], args[7], args[8]));
+ case 10:
+ return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#,#,#,#)',
+ constr, args[0], args[1], args[2], args[3], args[4], args[5],
+ args[6], args[7], args[8], args[9]));
+ }
+ return new JsObject._fromJs(JS('Object', r'''(function(){
+var Type = function(){};
+Type.prototype = #.prototype;
+var instance = new Type();
+ret = constructor.apply(instance, #);
+ret = Object(ret) === ret ? ret : instance;
+})()''', constr, args));
+ }
+
+ factory JsObject._json(data) {
+ _enterScopeIfNeeded();
+ return new JsObject._fromJs(_serializeDataTree(data));
+ }
+
+ static _serializeDataTree(data) {
+ if (data is Map) {
+ final serializedData = JS('Object', '{}');
+ for (var key in data.keys) {
+ JS('Object', '#[#]=#', serializedData, key,
+ _serializeDataTree(data[key]));
+ }
+ return serializedData;
+ } else if (data is Iterable) {
+ return data.map(_serializeDataTree).toList();
+ } else {
+ return _serialize(data);
+ }
+ }
+
+ JsObject toJs() => this;
+
+ operator[](key) => _deserialize(JS('var', '#[#]', _serialize(this), key));
+ operator[]=(key, value) => JS('void', '#[#]=#', _serialize(this), key,
+ _serialize(value));
+
+ operator==(other) => identical(this, other) ||
+ (other is JsObject && JS('bool', '# == #', _serialize(this),
+ _serialize(other)));
+
+ bool hasProperty(String property) => JS('bool', '# in #', property,
+ _serialize(this));
+
+ void deleteProperty(String name) {
+ JS('void', 'delete #[#]', _serialize(this), name);
+ }
+
+ bool instanceof(Serializable<JsFunction> type) =>
+ JS('bool', '# instanceof #', _serialize(this), _serialize(type));
+
+ String toString() {
+ try {
+ return JS('String', '#.toString()', _serialize(this));
+ } catch(e) {
+ return super.toString();
+ }
+ }
+
+ callMethod(String name, [List args]) =>
+ _deserialize(JS('var', '#[#].apply(#, #)', _serialize(this), name,
+ _serialize(this),
+ args == null ? null : args.map(_serialize).toList()));
+}
+
+class JsFunction extends JsObject implements Serializable<JsFunction> {
+ JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
+ apply(thisArg, [List args]) =>
+ _deserialize(JS('var', '#.apply(#, #)', _serialize(this),
+ _serialize(thisArg),
+ args == null ? null : args.map(_serialize).toList()));
+}
+
+abstract class Serializable<T> {
+ T toJs();
+}
+
+dynamic _serialize(dynamic o) {
vsm 2013/06/06 17:06:41 Is this really serializing? Perhaps call _convert
+ if (o == null) {
+ return null;
+ } else if (o is String || o is num || o is bool) {
+ return o;
+ } else if (o is Element && (o.document == null || o.document == document)) {
vsm 2013/06/06 17:06:41 Let's try removing the dependence on dart:html. C
+ return o;
+ } else if (o is JsObject) {
+ if (o._scope == null){
+ throw 'Proxy $o has been invalidated';
+ }
+ return o._jsObject;
+ } else if (o is Serializable) {
+ return _serialize(o.toJs());
+ } else {
+ _enterScopeIfNeeded();
+ _dartObjectsTotal++;
+ _dartObjects.putIfAbsent(_scopeDepth, () => []).add(o);
+ return o;
vsm 2013/06/06 17:06:41 Does this mean we're passing a raw Dart object to
+ }
+}
+dynamic _deserialize(dynamic o) {
vsm 2013/06/06 17:06:42 _convertToDart?
+ if (o == null) {
+ return null;
+ } else if (o is num || o is String || o is bool) {
+ return o;
+ } else if (JS('bool', '# instanceof Element && '
+ '(#.ownerDocument == null || #.ownerDocument === document)', o, o, o)) {
+ return JS('Element', '#', o);
+ } else if (JS('bool', '# instanceof Function', o)) {
+ return new JsFunction._fromJs(JS('Function', '#', o));
+ } else if (_globalDartObjects.any((e) => identical(e, o)) ||
+ _dartObjects.values.expand((e) => e).any((e) => identical(e, o))) {
+ return o;
+ } else {
+ return new JsObject._fromJs(JS('Object', '#', o));
+ }
+}
+
+int proxyCount({all: false, dartOnly: false, jsOnly: false}) {
+ final js = !dartOnly;
vsm 2013/06/06 17:06:42 Note, if you're getting rid of scopes, you can try
+ final dart = !jsOnly;
+ var sum = 0;
+ if (!all) {
+ if (js)
+ sum += _globalJsObjects.length;
+ if (dart)
+ sum += _globalDartObjects.length;
+ } else {
+ if (js)
+ sum += _jsObjectsTotal;
+ if (dart)
+ sum += _dartObjectsTotal;
+ }
+ return sum;
+}

Powered by Google App Engine
This is Rietveld 408576698