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

Side by Side Diff: pkg/browser/lib/interop.js

Issue 15782009: RFC: introduce dart:js (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: fix comments on patch 9 Created 7 years, 5 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 | « no previous file | sdk/lib/_internal/compiler/implementation/native_handler.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 // Support for JS interoperability 6 // Support for JS interoperability
7 // --------------------------------------------------------------------------- 7 // ---------------------------------------------------------------------------
8 function SendPortSync() { 8 function SendPortSync() {
9 } 9 }
10 10
11 function ReceivePortSync() { 11 function ReceivePortSync() {
12 this.id = ReceivePortSync.id++; 12 this.id = ReceivePortSync.id++;
13 ReceivePortSync.map[this.id] = this; 13 ReceivePortSync.map[this.id] = this;
14 } 14 }
15 15
16 // Type for remote proxies to Dart objects with dart2js.
17 function DartProxy(o) {
18 this.o = o;
19 }
20
16 (function() { 21 (function() {
17 // Serialize the following types as follows: 22 // Serialize the following types as follows:
18 // - primitives / null: unchanged 23 // - primitives / null: unchanged
19 // - lists: [ 'list', internal id, list of recursively serialized elements ] 24 // - lists: [ 'list', internal id, list of recursively serialized elements ]
20 // - maps: [ 'map', internal id, map of keys and recursively serialized value s ] 25 // - maps: [ 'map', internal id, map of keys and recursively serialized value s ]
21 // - send ports: [ 'sendport', type, isolate id, port id ] 26 // - send ports: [ 'sendport', type, isolate id, port id ]
22 // 27 //
23 // Note, internal id's are for cycle detection. 28 // Note, internal id's are for cycle detection.
24 function serialize(message) { 29 function serialize(message) {
25 var visited = []; 30 var visited = [];
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 var result = null; 213 var result = null;
209 var listener = function (e) { 214 var listener = function (e) {
210 result = JSON.parse(getPortSyncEventData(e)); 215 result = JSON.parse(getPortSyncEventData(e));
211 }; 216 };
212 window.addEventListener(source, listener, false); 217 window.addEventListener(source, listener, false);
213 dispatchEvent(target, [source, serialized]); 218 dispatchEvent(target, [source, serialized]);
214 window.removeEventListener(source, listener, false); 219 window.removeEventListener(source, listener, false);
215 return deserialize(result); 220 return deserialize(result);
216 } 221 }
217 })(); 222 })();
223
224 (function() {
225 // Proxy support for js.dart.
226
227 var globalContext = window;
228
229 // Table for local objects and functions that are proxied.
230 function ProxiedObjectTable() {
231 // Name for debugging.
232 this.name = 'js-ref';
233
234 // Table from IDs to JS objects.
235 this.map = {};
236
237 // Generator for new IDs.
238 this._nextId = 0;
239
240 // Ports for managing communication to proxies.
241 this.port = new ReceivePortSync();
242 this.sendPort = this.port.toSendPort();
243 }
244
245 // Number of valid IDs. This is the number of objects (global and local)
246 // kept alive by this table.
247 ProxiedObjectTable.prototype.count = function () {
248 return Object.keys(this.map).length;
249 }
250
251 // Adds an object to the table and return an ID for serialization.
252 ProxiedObjectTable.prototype.add = function (obj) {
253 for (var ref in this.map) {
254 var o = this.map[ref];
255 if (o === obj) {
256 return ref;
257 }
258 }
259 var ref = this.name + '-' + this._nextId++;
260 this.map[ref] = obj;
261 return ref;
262 }
263
264 // Gets the object or function corresponding to this ID.
265 ProxiedObjectTable.prototype.get = function (id) {
266 if (!this.map.hasOwnProperty(id)) {
267 throw 'Proxy ' + id + ' has been invalidated.'
268 }
269 return this.map[id];
270 }
271
272 ProxiedObjectTable.prototype._initialize = function () {
273 // Configure this table's port to forward methods, getters, and setters
274 // from the remote proxy to the local object.
275 var table = this;
276
277 this.port.receive(function (message) {
278 // TODO(vsm): Support a mechanism to register a handler here.
279 try {
280 var receiver = table.get(message[0]);
281 var member = message[1];
282 var kind = message[2];
283 var args = message[3].map(deserialize);
284 if (kind == 'get') {
285 // Getter.
286 var field = member;
287 if (field in receiver && args.length == 0) {
288 return [ 'return', serialize(receiver[field]) ];
289 }
290 } else if (kind == 'set') {
291 // Setter.
292 var field = member;
293 if (args.length == 1) {
294 return [ 'return', serialize(receiver[field] = args[0]) ];
295 }
296 } else if (kind == 'hasProperty') {
297 var field = member;
298 return [ 'return', field in receiver ];
299 } else if (kind == 'apply') {
300 // Direct function invocation.
301 return [ 'return',
302 serialize(receiver.apply(args[0], args.slice(1))) ];
303 } else if (member == '[]' && args.length == 1) {
304 // Index getter.
305 return [ 'return', serialize(receiver[args[0]]) ];
306 } else if (member == '[]=' && args.length == 2) {
307 // Index setter.
308 return [ 'return', serialize(receiver[args[0]] = args[1]) ];
309 } else {
310 // Member function invocation.
311 var f = receiver[member];
312 if (f) {
313 var result = f.apply(receiver, args);
314 return [ 'return', serialize(result) ];
315 }
316 }
317 return [ 'none' ];
318 } catch (e) {
319 return [ 'throws', e.toString() ];
320 }
321 });
322 }
323
324 // Singleton for local proxied objects.
325 var proxiedObjectTable = new ProxiedObjectTable();
326 proxiedObjectTable._initialize()
327
328 // Type for remote proxies to Dart objects.
329 function DartProxy(id, sendPort) {
330 this.id = id;
331 this.port = sendPort;
332 }
333
334 // Serializes JS types to SendPortSync format:
335 // - primitives -> primitives
336 // - sendport -> sendport
337 // - Function -> [ 'funcref', function-id, sendport ]
338 // - Object -> [ 'objref', object-id, sendport ]
339 function serialize(message) {
340 if (message == null) {
341 return null; // Convert undefined to null.
342 } else if (typeof(message) == 'string' ||
343 typeof(message) == 'number' ||
344 typeof(message) == 'boolean') {
345 // Primitives are passed directly through.
346 return message;
347 } else if (message instanceof SendPortSync) {
348 // Non-proxied objects are serialized.
349 return message;
350 } else if (typeof(message) == 'function') {
351 if ('_dart_id' in message) {
352 // Remote function proxy.
353 var remoteId = message._dart_id;
354 var remoteSendPort = message._dart_port;
355 return [ 'funcref', remoteId, remoteSendPort ];
356 } else {
357 // Local function proxy.
358 return [ 'funcref',
359 proxiedObjectTable.add(message),
360 proxiedObjectTable.sendPort ];
361 }
362 } else if (message instanceof DartProxy) {
363 // Remote object proxy.
364 return [ 'objref', message.id, message.port ];
365 } else {
366 // Local object proxy.
367 return [ 'objref',
368 proxiedObjectTable.add(message),
369 proxiedObjectTable.sendPort ];
370 }
371 }
372
373 function deserialize(message) {
374 if (message == null) {
375 return null; // Convert undefined to null.
376 } else if (typeof(message) == 'string' ||
377 typeof(message) == 'number' ||
378 typeof(message) == 'boolean') {
379 // Primitives are passed directly through.
380 return message;
381 } else if (message instanceof SendPortSync) {
382 // Serialized type.
383 return message;
384 }
385 var tag = message[0];
386 switch (tag) {
387 case 'funcref': return deserializeFunction(message);
388 case 'objref': return deserializeObject(message);
389 }
390 throw 'Unsupported serialized data: ' + message;
391 }
392
393 // Create a local function that forwards to the remote function.
394 function deserializeFunction(message) {
395 var id = message[1];
396 var port = message[2];
397 // TODO(vsm): Add a more robust check for a local SendPortSync.
398 if ("receivePort" in port) {
399 // Local function.
400 return proxiedObjectTable.get(id);
401 } else {
402 // Remote function. Forward to its port.
403 var f = function () {
404 var args = Array.prototype.slice.apply(arguments);
405 args.splice(0, 0, this);
406 args = args.map(serialize);
407 var result = port.callSync([id, '#call', args]);
408 if (result[0] == 'throws') throw deserialize(result[1]);
409 return deserialize(result[1]);
410 };
411 // Cache the remote id and port.
412 f._dart_id = id;
413 f._dart_port = port;
414 return f;
415 }
416 }
417
418 // Creates a DartProxy to forwards to the remote object.
419 function deserializeObject(message) {
420 var id = message[1];
421 var port = message[2];
422 // TODO(vsm): Add a more robust check for a local SendPortSync.
423 if ("receivePort" in port) {
424 // Local object.
425 return proxiedObjectTable.get(id);
426 } else {
427 // Remote object.
428 return new DartProxy(id, port);
429 }
430 }
431
432 // Remote handler to construct a new JavaScript object given its
433 // serialized constructor and arguments.
434 function construct(args) {
435 args = args.map(deserialize);
436 var constructor = args[0];
437 args = Array.prototype.slice.call(args, 1);
438
439 // Until 10 args, the 'new' operator is used. With more arguments we use a
440 // generic way that may not work, particularly when the constructor does not
441 // have an "apply" method.
442 var ret = null;
443 if (args.length === 0) {
444 ret = new constructor();
445 } else if (args.length === 1) {
446 ret = new constructor(args[0]);
447 } else if (args.length === 2) {
448 ret = new constructor(args[0], args[1]);
449 } else if (args.length === 3) {
450 ret = new constructor(args[0], args[1], args[2]);
451 } else if (args.length === 4) {
452 ret = new constructor(args[0], args[1], args[2], args[3]);
453 } else if (args.length === 5) {
454 ret = new constructor(args[0], args[1], args[2], args[3], args[4]);
455 } else if (args.length === 6) {
456 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
457 args[5]);
458 } else if (args.length === 7) {
459 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
460 args[5], args[6]);
461 } else if (args.length === 8) {
462 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
463 args[5], args[6], args[7]);
464 } else if (args.length === 9) {
465 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
466 args[5], args[6], args[7], args[8]);
467 } else if (args.length === 10) {
468 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
469 args[5], args[6], args[7], args[8], args[9]);
470 } else {
471 // Dummy Type with correct constructor.
472 var Type = function(){};
473 Type.prototype = constructor.prototype;
474
475 // Create a new instance
476 var instance = new Type();
477
478 // Call the original constructor.
479 ret = constructor.apply(instance, args);
480 ret = Object(ret) === ret ? ret : instance;
481 }
482 return serialize(ret);
483 }
484
485 // Remote handler to return the top-level JavaScript context.
486 function context(data) {
487 return serialize(globalContext);
488 }
489
490 // Return true if a JavaScript proxy is instance of a given type (instanceof).
491 function proxyInstanceof(args) {
492 var obj = deserialize(args[0]);
493 var type = deserialize(args[1]);
494 return obj instanceof type;
495 }
496
497 // Return true if a JavaScript proxy is instance of a given type (instanceof).
498 function proxyDeleteProperty(args) {
499 var obj = deserialize(args[0]);
500 var member = deserialize(args[1]);
501 delete obj[member];
502 }
503
504 function proxyConvert(args) {
505 return serialize(deserializeDataTree(args));
506 }
507
508 function deserializeDataTree(data) {
509 var type = data[0];
510 var value = data[1];
511 if (type === 'map') {
512 var obj = {};
513 for (var i = 0; i < value.length; i++) {
514 obj[value[i][0]] = deserializeDataTree(value[i][1]);
515 }
516 return obj;
517 } else if (type === 'list') {
518 var list = [];
519 for (var i = 0; i < value.length; i++) {
520 list.push(deserializeDataTree(value[i]));
521 }
522 return list;
523 } else /* 'simple' */ {
524 return deserialize(value);
525 }
526 }
527
528 function makeGlobalPort(name, f) {
529 var port = new ReceivePortSync();
530 port.receive(f);
531 window.registerPort(name, port.toSendPort());
532 }
533
534 makeGlobalPort('dart-js-context', context);
535 makeGlobalPort('dart-js-create', construct);
536 makeGlobalPort('dart-js-instanceof', proxyInstanceof);
537 makeGlobalPort('dart-js-delete-property', proxyDeleteProperty);
538 makeGlobalPort('dart-js-convert', proxyConvert);
539 })();
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/native_handler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698