Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library dart.js; | |
| 6 | |
| 7 import 'dart:async' show runAsync; | |
| 8 import 'dart:html' show Element, document; | |
|
vsm
2013/06/06 17:06:41
Can you try killing the two imports above and test
| |
| 9 import 'dart:_foreign_helper' show JS; | |
| 10 import 'dart:_js_helper' show convertDartClosureToJS; | |
| 11 | |
| 12 JsObject get context { | |
| 13 _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
| |
| 14 return new JsObject._fromJs(JS('Object', 'window')); | |
| 15 } | |
| 16 | |
| 17 int _scopeDepth = 0; | |
| 18 | |
| 19 int _dartObjectsTotal = 0; | |
| 20 final Map<int, List> _dartObjects = new Map<int, List>(); | |
| 21 | |
| 22 int _jsObjectsTotal = 0; | |
| 23 final List<JsObject> _jsObjects = new List<JsObject>(); | |
| 24 | |
| 25 void _enterScopeIfNeeded() { | |
| 26 if (_scopeDepth == 0) { | |
| 27 final depth = _enterScope(); | |
| 28 runAsync(() => _exitScope(depth)); | |
| 29 } | |
| 30 } | |
| 31 | |
| 32 scoped(f) { | |
| 33 final depth = _enterScope(); | |
| 34 try { | |
| 35 return f(); | |
| 36 } finally { | |
| 37 _exitScope(depth); | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 int _enterScope() => ++_scopeDepth; | |
| 42 | |
| 43 void _exitScope(int depth) { | |
| 44 assert(_scopeDepth == depth); | |
| 45 _dartObjects.remove(_scopeDepth); | |
| 46 _jsObjects | |
| 47 ..where((JsObject e) => e._scope == depth).forEach((JsObject e) { | |
| 48 e._scope = null; | |
| 49 }) | |
| 50 ..removeWhere((JsObject e) => e._scope == depth); | |
| 51 _scopeDepth--; | |
| 52 } | |
| 53 | |
| 54 int $experimentalEnterScope() { | |
|
vsm
2013/06/06 17:06:41
These can go.
| |
| 55 return _enterScope(); | |
| 56 } | |
| 57 | |
| 58 void $experimentalExitScope(int depth) { | |
| 59 _exitScope(depth); | |
| 60 } | |
| 61 | |
| 62 final _globalDartObjects = []; | |
| 63 final _globalJsObjects = new List<JsObject>(); | |
| 64 | |
| 65 dynamic retain(Serializable<JsObject> object) { | |
| 66 final JsObject jsObject = object.toJs(); | |
| 67 jsObject._scope = 0; | |
| 68 _globalJsObjects.add(jsObject); | |
| 69 _jsObjects.removeWhere((e) => identical(e, jsObject)); | |
| 70 return object; | |
| 71 } | |
| 72 void release(Serializable<JsObject> object) { | |
| 73 final JsObject jsObject = object.toJs(); | |
| 74 jsObject._scope = null; | |
| 75 _removeLast(_globalJsObjects, jsObject); | |
| 76 } | |
| 77 | |
| 78 void _removeLast(List l, object) { | |
| 79 for (int i = l.length - 1; i >= 0; i--) { | |
| 80 var element = l[i]; | |
| 81 if (identical(element, object)) { | |
| 82 l.removeAt(i); | |
| 83 return; | |
| 84 } | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data); | |
| 89 | |
| 90 class Callback implements Serializable<JsFunction> { | |
| 91 final bool _manualDispose; | |
|
vsm
2013/06/06 17:06:41
We can try getting rid of _manualDispose and relat
| |
| 92 final Function _f; // here to allow capture in closure | |
| 93 final bool _withThis; // here to allow capture in closure | |
| 94 Object _jsFunction; | |
|
vsm
2013/06/06 17:06:41
This should probably be typed dynamic. That is wh
| |
| 95 | |
| 96 Callback._internal(this._manualDispose, this._f, this._withThis) { | |
| 97 _globalDartObjects.add(_f); | |
| 98 _jsFunction = JS('Object', r''' | |
| 99 (function(){ | |
| 100 var f = #; | |
| 101 return function(){ | |
| 102 return f(this, Array.prototype.slice.apply(arguments)); | |
| 103 }; | |
| 104 }).apply(this)''', convertDartClosureToJS(_call, 2)); | |
| 105 } | |
| 106 | |
| 107 _call(thisArg, List args) { | |
| 108 if (!_globalDartObjects.any((e) => identical(e, _f)) && | |
| 109 !_dartObjects.values.expand((e) => e).any((e) => identical(e, _f))) { | |
| 110 throw 'function has already been disposed'; | |
| 111 } | |
| 112 | |
| 113 final arguments = new List.from(args); | |
| 114 if (_withThis) arguments.insert(0, thisArg); | |
| 115 final deserializedArgs = arguments.map(_deserialize).toList(); | |
| 116 if (_manualDispose) { | |
| 117 return _serialize(Function.apply(_f, deserializedArgs)); | |
| 118 } else { | |
| 119 try { | |
| 120 return _serialize(Function.apply(_f, deserializedArgs)); | |
| 121 } finally { | |
| 122 _dispose(); | |
| 123 } | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 _dispose() { | |
| 128 _removeLast(_globalDartObjects, _f); | |
| 129 } | |
| 130 | |
| 131 JsFunction toJs() => new JsFunction._fromJs(_jsFunction); | |
| 132 | |
| 133 dispose() { | |
| 134 assert(_manualDispose); | |
| 135 _dispose(); | |
| 136 } | |
| 137 | |
| 138 factory Callback.once(Function f, {bool withThis: false}) => | |
| 139 new Callback._internal(false, f, withThis); | |
| 140 | |
| 141 factory Callback.many(Function f, {bool withThis: false}) => | |
| 142 new Callback._internal(true, f, withThis); | |
| 143 } | |
| 144 | |
| 145 class JsObject implements Serializable<JsObject> { | |
| 146 final Object _jsObject; | |
| 147 int _scope = _scopeDepth; | |
| 148 | |
| 149 JsObject._fromJs(this._jsObject) { | |
| 150 if (_scopeDepth == 0) throw 'Cannot allocate a proxy outside of a scope.'; | |
| 151 _jsObjectsTotal++; | |
| 152 _jsObjects.add(this); | |
| 153 } | |
| 154 | |
| 155 factory JsObject(Serializable<JsFunction> constructor, [List arguments]) { | |
| 156 _enterScopeIfNeeded(); | |
| 157 final constr = _serialize(constructor); | |
| 158 if (arguments == null) { | |
| 159 return new JsObject._fromJs(JS('Object', 'new #()', constr)); | |
| 160 } | |
| 161 final args = arguments.map(_serialize).toList(); | |
| 162 switch (args.length) { | |
| 163 case 0: | |
| 164 return new JsObject._fromJs(JS('Object', 'new #()', constr)); | |
| 165 case 1: | |
| 166 return new JsObject._fromJs(JS('Object', 'new #(#)', constr, args[0])); | |
| 167 case 2: | |
| 168 return new JsObject._fromJs(JS('Object', 'new #(#,#)', constr, args[0], | |
| 169 args[1])); | |
| 170 case 3: | |
| 171 return new JsObject._fromJs(JS('Object', 'new #(#,#,#)', constr, | |
| 172 args[0], args[1], args[2])); | |
| 173 case 4: | |
| 174 return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#)', constr, | |
| 175 args[0], args[1], args[2], args[3])); | |
| 176 case 5: | |
| 177 return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#)', constr, | |
| 178 args[0], args[1], args[2], args[3], args[4])); | |
| 179 case 6: | |
| 180 return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#)', constr, | |
| 181 args[0], args[1], args[2], args[3], args[4], args[5])); | |
| 182 case 7: | |
| 183 return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#)', constr, | |
| 184 args[0], args[1], args[2], args[3], args[4], args[5], args[6])); | |
| 185 case 8: | |
| 186 return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#,#)', | |
| 187 constr, args[0], args[1], args[2], args[3], args[4], args[5], | |
| 188 args[6], args[7])); | |
| 189 case 9: | |
| 190 return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#,#,#)', | |
| 191 constr, args[0], args[1], args[2], args[3], args[4], args[5], | |
| 192 args[6], args[7], args[8])); | |
| 193 case 10: | |
| 194 return new JsObject._fromJs(JS('Object', 'new #(#,#,#,#,#,#,#,#,#,#)', | |
| 195 constr, args[0], args[1], args[2], args[3], args[4], args[5], | |
| 196 args[6], args[7], args[8], args[9])); | |
| 197 } | |
| 198 return new JsObject._fromJs(JS('Object', r'''(function(){ | |
| 199 var Type = function(){}; | |
| 200 Type.prototype = #.prototype; | |
| 201 var instance = new Type(); | |
| 202 ret = constructor.apply(instance, #); | |
| 203 ret = Object(ret) === ret ? ret : instance; | |
| 204 })()''', constr, args)); | |
| 205 } | |
| 206 | |
| 207 factory JsObject._json(data) { | |
| 208 _enterScopeIfNeeded(); | |
| 209 return new JsObject._fromJs(_serializeDataTree(data)); | |
| 210 } | |
| 211 | |
| 212 static _serializeDataTree(data) { | |
| 213 if (data is Map) { | |
| 214 final serializedData = JS('Object', '{}'); | |
| 215 for (var key in data.keys) { | |
| 216 JS('Object', '#[#]=#', serializedData, key, | |
| 217 _serializeDataTree(data[key])); | |
| 218 } | |
| 219 return serializedData; | |
| 220 } else if (data is Iterable) { | |
| 221 return data.map(_serializeDataTree).toList(); | |
| 222 } else { | |
| 223 return _serialize(data); | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 JsObject toJs() => this; | |
| 228 | |
| 229 operator[](key) => _deserialize(JS('var', '#[#]', _serialize(this), key)); | |
| 230 operator[]=(key, value) => JS('void', '#[#]=#', _serialize(this), key, | |
| 231 _serialize(value)); | |
| 232 | |
| 233 operator==(other) => identical(this, other) || | |
| 234 (other is JsObject && JS('bool', '# == #', _serialize(this), | |
| 235 _serialize(other))); | |
| 236 | |
| 237 bool hasProperty(String property) => JS('bool', '# in #', property, | |
| 238 _serialize(this)); | |
| 239 | |
| 240 void deleteProperty(String name) { | |
| 241 JS('void', 'delete #[#]', _serialize(this), name); | |
| 242 } | |
| 243 | |
| 244 bool instanceof(Serializable<JsFunction> type) => | |
| 245 JS('bool', '# instanceof #', _serialize(this), _serialize(type)); | |
| 246 | |
| 247 String toString() { | |
| 248 try { | |
| 249 return JS('String', '#.toString()', _serialize(this)); | |
| 250 } catch(e) { | |
| 251 return super.toString(); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 callMethod(String name, [List args]) => | |
| 256 _deserialize(JS('var', '#[#].apply(#, #)', _serialize(this), name, | |
| 257 _serialize(this), | |
| 258 args == null ? null : args.map(_serialize).toList())); | |
| 259 } | |
| 260 | |
| 261 class JsFunction extends JsObject implements Serializable<JsFunction> { | |
| 262 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); | |
| 263 apply(thisArg, [List args]) => | |
| 264 _deserialize(JS('var', '#.apply(#, #)', _serialize(this), | |
| 265 _serialize(thisArg), | |
| 266 args == null ? null : args.map(_serialize).toList())); | |
| 267 } | |
| 268 | |
| 269 abstract class Serializable<T> { | |
| 270 T toJs(); | |
| 271 } | |
| 272 | |
| 273 dynamic _serialize(dynamic o) { | |
|
vsm
2013/06/06 17:06:41
Is this really serializing? Perhaps call _convert
| |
| 274 if (o == null) { | |
| 275 return null; | |
| 276 } else if (o is String || o is num || o is bool) { | |
| 277 return o; | |
| 278 } 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
| |
| 279 return o; | |
| 280 } else if (o is JsObject) { | |
| 281 if (o._scope == null){ | |
| 282 throw 'Proxy $o has been invalidated'; | |
| 283 } | |
| 284 return o._jsObject; | |
| 285 } else if (o is Serializable) { | |
| 286 return _serialize(o.toJs()); | |
| 287 } else { | |
| 288 _enterScopeIfNeeded(); | |
| 289 _dartObjectsTotal++; | |
| 290 _dartObjects.putIfAbsent(_scopeDepth, () => []).add(o); | |
| 291 return o; | |
|
vsm
2013/06/06 17:06:41
Does this mean we're passing a raw Dart object to
| |
| 292 } | |
| 293 } | |
| 294 dynamic _deserialize(dynamic o) { | |
|
vsm
2013/06/06 17:06:42
_convertToDart?
| |
| 295 if (o == null) { | |
| 296 return null; | |
| 297 } else if (o is num || o is String || o is bool) { | |
| 298 return o; | |
| 299 } else if (JS('bool', '# instanceof Element && ' | |
| 300 '(#.ownerDocument == null || #.ownerDocument === document)', o, o, o)) { | |
| 301 return JS('Element', '#', o); | |
| 302 } else if (JS('bool', '# instanceof Function', o)) { | |
| 303 return new JsFunction._fromJs(JS('Function', '#', o)); | |
| 304 } else if (_globalDartObjects.any((e) => identical(e, o)) || | |
| 305 _dartObjects.values.expand((e) => e).any((e) => identical(e, o))) { | |
| 306 return o; | |
| 307 } else { | |
| 308 return new JsObject._fromJs(JS('Object', '#', o)); | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 int proxyCount({all: false, dartOnly: false, jsOnly: false}) { | |
| 313 final js = !dartOnly; | |
|
vsm
2013/06/06 17:06:42
Note, if you're getting rid of scopes, you can try
| |
| 314 final dart = !jsOnly; | |
| 315 var sum = 0; | |
| 316 if (!all) { | |
| 317 if (js) | |
| 318 sum += _globalJsObjects.length; | |
| 319 if (dart) | |
| 320 sum += _globalDartObjects.length; | |
| 321 } else { | |
| 322 if (js) | |
| 323 sum += _jsObjectsTotal; | |
| 324 if (dart) | |
| 325 sum += _dartObjectsTotal; | |
| 326 } | |
| 327 return sum; | |
| 328 } | |
| OLD | NEW |