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 |