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

Side by Side Diff: sdk/lib/js/dartium/js_dartium.dart

Issue 26742008: Revert "Maintain referential integrity of proxy instances in dart:js" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/js/dart2js/js_dart2js.dart ('k') | tests/html/js_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * The js.dart library provides simple JavaScript invocation from Dart that 6 * The js.dart library provides simple JavaScript invocation from Dart that
7 * works on both Dartium and on other modern browsers via Dart2JS. 7 * works on both Dartium and on other modern browsers via Dart2JS.
8 * 8 *
9 * It provides a model based on scoped [JsObject] objects. Proxies give Dart 9 * It provides a model based on scoped [JsObject] objects. Proxies give Dart
10 * code access to JavaScript objects, fields, and functions as well as the 10 * code access to JavaScript objects, fields, and functions as well as the
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 * var foo = new JsObject(context['Foo'], [42]); 59 * var foo = new JsObject(context['Foo'], [42]);
60 * var foo2 = foo.callMethod('add', [foo]); 60 * var foo2 = foo.callMethod('add', [foo]);
61 * print(foo2['x']); 61 * print(foo2['x']);
62 * 62 *
63 * will construct a JavaScript Foo object with the parameter 42, invoke its 63 * will construct a JavaScript Foo object with the parameter 42, invoke its
64 * add method, and return a [JsObject] to a new Foo object whose x field is 84. 64 * add method, and return a [JsObject] to a new Foo object whose x field is 84.
65 */ 65 */
66 66
67 library dart.js; 67 library dart.js;
68 68
69 import 'dart:collection' show HashMap;
70 import 'dart:html'; 69 import 'dart:html';
71 import 'dart:isolate'; 70 import 'dart:isolate';
72 71
73 // Global ports to manage communication from Dart to JS. 72 // Global ports to manage communication from Dart to JS.
74
75 SendPortSync _jsPortSync = window.lookupPort('dart-js-context'); 73 SendPortSync _jsPortSync = window.lookupPort('dart-js-context');
76 SendPortSync _jsPortCreate = window.lookupPort('dart-js-create'); 74 SendPortSync _jsPortCreate = window.lookupPort('dart-js-create');
77 SendPortSync _jsPortInstanceof = window.lookupPort('dart-js-instanceof'); 75 SendPortSync _jsPortInstanceof = window.lookupPort('dart-js-instanceof');
78 SendPortSync _jsPortDeleteProperty = window.lookupPort('dart-js-delete-property' ); 76 SendPortSync _jsPortDeleteProperty = window.lookupPort('dart-js-delete-property' );
79 SendPortSync _jsPortConvert = window.lookupPort('dart-js-convert'); 77 SendPortSync _jsPortConvert = window.lookupPort('dart-js-convert');
80 78
81 final String _objectIdPrefix = 'dart-obj-ref';
82 final String _functionIdPrefix = 'dart-fun-ref';
83 final _objectTable = new _ObjectTable();
84 final _functionTable = new _ObjectTable.forFunctions();
85
86 // Port to handle and forward requests to the underlying Dart objects.
87 // A remote proxy is uniquely identified by an ID and SendPortSync.
88 ReceivePortSync _port = new ReceivePortSync()
89 ..receive((msg) {
90 try {
91 var id = msg[0];
92 var method = msg[1];
93 if (method == '#call') {
94 var receiver = _getObjectTable(id).get(id);
95 var result;
96 if (receiver is Function) {
97 // remove the first argument, which is 'this', but never
98 // used for a raw function
99 var args = msg[2].sublist(1).map(_deserialize).toList();
100 result = Function.apply(receiver, args);
101 } else if (receiver is Callback) {
102 var args = msg[2].map(_deserialize).toList();
103 result = receiver._call(args);
104 } else {
105 throw new StateError('bad function type: $receiver');
106 }
107 return ['return', _serialize(result)];
108 } else {
109 // TODO(vsm): Support a mechanism to register a handler here.
110 throw 'Invocation unsupported on non-function Dart proxies';
111 }
112 } catch (e) {
113 // TODO(vsm): callSync should just handle exceptions itself.
114 return ['throws', '$e'];
115 }
116 });
117
118 _ObjectTable _getObjectTable(String id) {
119 if (id.startsWith(_functionIdPrefix)) return _functionTable;
120 if (id.startsWith(_objectIdPrefix)) return _objectTable;
121 throw new ArgumentError('internal error: invalid object id: $id');
122 }
123 79
124 JsObject _context; 80 JsObject _context;
125 81
126 /** 82 /**
127 * Returns a proxy to the global JavaScript context for this page. 83 * Returns a proxy to the global JavaScript context for this page.
128 */ 84 */
129 JsObject get context { 85 JsObject get context {
130 if (_context == null) { 86 if (_context == null) {
131 var port = _jsPortSync; 87 var port = _jsPortSync;
132 if (port == null) { 88 if (port == null) {
133 return null; 89 return null;
134 } 90 }
135 _context = _deserialize(_jsPortSync.callSync([])); 91 _context = _deserialize(_jsPortSync.callSync([]));
136 } 92 }
137 return _context; 93 return _context;
138 } 94 }
139 95
140 /** 96 /**
141 * Converts a json-like [data] to a JavaScript map or array and return a 97 * Converts a json-like [data] to a JavaScript map or array and return a
142 * [JsObject] to it. 98 * [JsObject] to it.
143 */ 99 */
144 JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data); 100 JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data);
145 101
146 /** 102 /**
147 * Converts a local Dart function to a callback that can be passed to 103 * Converts a local Dart function to a callback that can be passed to
148 * JavaScript. 104 * JavaScript.
149 */ 105 */
150 class Callback implements Serializable<JsFunction> { 106 class Callback implements Serializable<JsFunction> {
151 final bool _withThis; 107 JsFunction _f;
152 final Function _function;
153 JsFunction _jsFunction;
154 108
155 Callback._(this._function, this._withThis) { 109 Callback._(Function f, bool withThis) {
156 var id = _functionTable.add(this); 110 final id = _proxiedObjectTable.add((List args) {
157 _jsFunction = new JsFunction._internal(_port.toSendPort(), id); 111 final arguments = new List.from(args);
112 if (!withThis) arguments.removeAt(0);
113 return Function.apply(f, arguments);
114 });
115 _f = new JsFunction._internal(_proxiedObjectTable.sendPort, id);
158 } 116 }
159 117
160 factory Callback(Function f) => new Callback._(f, false); 118 factory Callback(Function f) => new Callback._(f, false);
161 factory Callback.withThis(Function f) => new Callback._(f, true); 119 factory Callback.withThis(Function f) => new Callback._(f, true);
162 120
163 dynamic _call(List args) { 121 JsFunction toJs() => _f;
164 var arguments = (_withThis) ? args : args.sublist(1);
165 return Function.apply(_function, arguments);
166 }
167
168 JsFunction toJs() => _jsFunction;
169 } 122 }
170 123
171 /** 124 /**
172 * Proxies to JavaScript objects. 125 * Proxies to JavaScript objects.
173 */ 126 */
174 class JsObject implements Serializable<JsObject> { 127 class JsObject implements Serializable<JsObject> {
175 final SendPortSync _port; 128 final SendPortSync _port;
176 final String _id; 129 final String _id;
177 130
178 /** 131 /**
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 } 235 }
283 } 236 }
284 237
285 /// Marker class used to indicate it is serializable to js. If a class is a 238 /// Marker class used to indicate it is serializable to js. If a class is a
286 /// [Serializable] the "toJs" method will be called and the result will be used 239 /// [Serializable] the "toJs" method will be called and the result will be used
287 /// as value. 240 /// as value.
288 abstract class Serializable<T> { 241 abstract class Serializable<T> {
289 T toJs(); 242 T toJs();
290 } 243 }
291 244
292 class _ObjectTable { 245 // A table to managed local Dart objects that are proxied in JavaScript.
293 final String name; 246 class _ProxiedObjectTable {
294 final Map<String, Object> objects; 247 // Debugging name.
295 final Map<Object, String> ids; 248 final String _name;
296 int nextId = 0;
297 249
298 // Creates a table that uses an identity Map to store IDs 250 // Generator for unique IDs.
299 _ObjectTable() 251 int _nextId;
300 : name = _objectIdPrefix,
301 objects = new HashMap<String, Object>(),
302 ids = new HashMap<Object, String>.identity();
303 252
304 // Creates a table that uses an equality-based Map to store IDs, since 253 // Table of IDs to Dart objects.
305 // closurized methods may be equal, but not identical 254 final Map<String, Object> _registry;
306 _ObjectTable.forFunctions()
307 : name = _functionIdPrefix,
308 objects = new HashMap<String, Object>(),
309 ids = new HashMap<Object, String>();
310 255
311 // Adds a new object to the table. If [id] is not given, a new unique ID is 256 // Port to handle and forward requests to the underlying Dart objects.
312 // generated. Returns the ID. 257 // A remote proxy is uniquely identified by an ID and SendPortSync.
313 String add(Object o, {String id}) { 258 final ReceivePortSync _port;
259
260 _ProxiedObjectTable() :
261 _name = 'dart-ref',
262 _nextId = 0,
263 _registry = {},
264 _port = new ReceivePortSync() {
265 _port.receive((msg) {
266 try {
267 final receiver = _registry[msg[0]];
268 final method = msg[1];
269 final args = msg[2].map(_deserialize).toList();
270 if (method == '#call') {
271 final func = receiver as Function;
272 var result = _serialize(func(args));
273 return ['return', result];
274 } else {
275 // TODO(vsm): Support a mechanism to register a handler here.
276 throw 'Invocation unsupported on non-function Dart proxies';
277 }
278 } catch (e) {
279 // TODO(vsm): callSync should just handle exceptions itself.
280 return ['throws', '$e'];
281 }
282 });
283 }
284
285 // Adds a new object to the table and return a new ID for it.
286 String add(x) {
314 // TODO(vsm): Cache x and reuse id. 287 // TODO(vsm): Cache x and reuse id.
315 if (id == null) id = ids[o]; 288 final id = '$_name-${_nextId++}';
316 if (id == null) id = '$name-${nextId++}'; 289 _registry[id] = x;
317 ids[o] = id;
318 objects[id] = o;
319 return id; 290 return id;
320 } 291 }
321 292
322 // Gets an object by ID. 293 // Gets an object by ID.
323 Object get(String id) => objects[id]; 294 Object get(String id) {
324 295 return _registry[id];
325 bool contains(String id) => objects.containsKey(id); 296 }
326
327 String getId(Object o) => ids[o];
328 297
329 // Gets the current number of objects kept alive by this table. 298 // Gets the current number of objects kept alive by this table.
330 get count => objects.length; 299 get count => _registry.length;
300
301 // Gets a send port for this table.
302 get sendPort => _port.toSendPort();
331 } 303 }
332 304
305 // The singleton to manage proxied Dart objects.
306 _ProxiedObjectTable _proxiedObjectTable = new _ProxiedObjectTable();
307
308 /// End of proxy implementation.
309
333 // Dart serialization support. 310 // Dart serialization support.
334 311
335 _serialize(var message) { 312 _serialize(var message) {
336 if (message == null) { 313 if (message == null) {
337 return null; // Convert undefined to null. 314 return null; // Convert undefined to null.
338 } else if (message is String || 315 } else if (message is String ||
339 message is num || 316 message is num ||
340 message is bool) { 317 message is bool) {
341 // Primitives are passed directly through. 318 // Primitives are passed directly through.
342 return message; 319 return message;
343 } else if (message is SendPortSync) { 320 } else if (message is SendPortSync) {
344 // Non-proxied objects are serialized. 321 // Non-proxied objects are serialized.
345 return message; 322 return message;
346 } else if (message is JsFunction) { 323 } else if (message is JsFunction) {
347 // Remote function proxy. 324 // Remote function proxy.
348 return ['funcref', message._id, message._port]; 325 return [ 'funcref', message._id, message._port ];
349 } else if (message is JsObject) { 326 } else if (message is JsObject) {
350 // Remote object proxy. 327 // Remote object proxy.
351 return ['objref', message._id, message._port]; 328 return [ 'objref', message._id, message._port ];
352 } else if (message is Serializable) { 329 } else if (message is Serializable) {
353 // use of result of toJs() 330 // use of result of toJs()
354 return _serialize(message.toJs()); 331 return _serialize(message.toJs());
355 } else if (message is Function) { 332 } else if (message is Function) {
356 var id = _functionTable.getId(message); 333 return _serialize(new Callback(message));
357 if (id != null) {
358 return ['funcref', id, _port.toSendPort()];
359 }
360 id = _functionTable.add(message);
361 return ['funcref', id, _port.toSendPort()];
362 } else { 334 } else {
363 // Local object proxy. 335 // Local object proxy.
364 return ['objref', _objectTable.add(message), _port.toSendPort()]; 336 return [ 'objref',
337 _proxiedObjectTable.add(message),
338 _proxiedObjectTable.sendPort ];
365 } 339 }
366 } 340 }
367 341
368 _deserialize(var message) { 342 _deserialize(var message) {
369 deserializeFunction(message) { 343 deserializeFunction(message) {
370 var id = message[1]; 344 var id = message[1];
371 var port = message[2]; 345 var port = message[2];
372 if (port == _port.toSendPort()) { 346 if (port == _proxiedObjectTable.sendPort) {
373 // Local function. 347 // Local function.
374 return _functionTable.get(id); 348 return _proxiedObjectTable.get(id);
375 } else { 349 } else {
376 // Remote function. 350 // Remote function. Forward to its port.
377 var jsFunction = _functionTable.get(id); 351 return new JsFunction._internal(port, id);
378 if (jsFunction == null) {
379 jsFunction = new JsFunction._internal(port, id);
380 _functionTable.add(jsFunction, id: id);
381 }
382 return jsFunction;
383 } 352 }
384 } 353 }
385 354
386 deserializeObject(message) { 355 deserializeObject(message) {
387 var id = message[1]; 356 var id = message[1];
388 var port = message[2]; 357 var port = message[2];
389 if (port == _port.toSendPort()) { 358 if (port == _proxiedObjectTable.sendPort) {
390 // Local object. 359 // Local object.
391 return _objectTable.get(id); 360 return _proxiedObjectTable.get(id);
392 } else { 361 } else {
393 // Remote object. 362 // Remote object.
394 var jsObject = _objectTable.get(id); 363 return new JsObject._internal(port, id);
395 if (jsObject == null) {
396 jsObject = new JsObject._internal(port, id);
397 _objectTable.add(jsObject, id: id);
398 }
399 return jsObject;
400 } 364 }
401 } 365 }
402 366
403 if (message == null) { 367 if (message == null) {
404 return null; // Convert undefined to null. 368 return null; // Convert undefined to null.
405 } else if (message is String || 369 } else if (message is String ||
406 message is num || 370 message is num ||
407 message is bool) { 371 message is bool) {
408 // Primitives are passed directly through. 372 // Primitives are passed directly through.
409 return message; 373 return message;
410 } else if (message is SendPortSync) { 374 } else if (message is SendPortSync) {
411 // Serialized type. 375 // Serialized type.
412 return message; 376 return message;
413 } 377 }
414 var tag = message[0]; 378 var tag = message[0];
415 switch (tag) { 379 switch (tag) {
416 case 'funcref': return deserializeFunction(message); 380 case 'funcref': return deserializeFunction(message);
417 case 'objref': return deserializeObject(message); 381 case 'objref': return deserializeObject(message);
418 } 382 }
419 throw 'Unsupported serialized data: $message'; 383 throw 'Unsupported serialized data: $message';
420 } 384 }
OLDNEW
« no previous file with comments | « sdk/lib/js/dart2js/js_dart2js.dart ('k') | tests/html/js_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698