OLD | NEW |
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. | 16 // Type for remote proxies to Dart objects with dart2js. |
17 function DartProxy(o) { | 17 function DartObject(o) { |
18 this.o = o; | 18 this.o = o; |
19 } | 19 } |
20 | 20 |
21 (function() { | 21 (function() { |
22 // Serialize the following types as follows: | 22 // Serialize the following types as follows: |
23 // - primitives / null: unchanged | 23 // - primitives / null: unchanged |
24 // - lists: [ 'list', internal id, list of recursively serialized elements ] | 24 // - lists: [ 'list', internal id, list of recursively serialized elements ] |
25 // - 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 ] |
26 // - send ports: [ 'sendport', type, isolate id, port id ] | 26 // - send ports: [ 'sendport', type, isolate id, port id ] |
27 // | 27 // |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 this.port = new ReceivePortSync(); | 243 this.port = new ReceivePortSync(); |
244 this.sendPort = this.port.toSendPort(); | 244 this.sendPort = this.port.toSendPort(); |
245 } | 245 } |
246 | 246 |
247 // Number of valid IDs. This is the number of objects (global and local) | 247 // Number of valid IDs. This is the number of objects (global and local) |
248 // kept alive by this table. | 248 // kept alive by this table. |
249 ProxiedObjectTable.prototype.count = function () { | 249 ProxiedObjectTable.prototype.count = function () { |
250 return Object.keys(this.map).length; | 250 return Object.keys(this.map).length; |
251 } | 251 } |
252 | 252 |
253 // Adds an object to the table and return an ID for serialization. | 253 var _dartRefPropertyName = "_$dart_ref"; |
254 ProxiedObjectTable.prototype.add = function (obj) { | 254 |
255 for (var ref in this.map) { | 255 // attempts to add an unenumerable property to o. If that is not allowed |
256 var o = this.map[ref]; | 256 // it silently fails. |
257 if (o === obj) { | 257 function _defineProperty(o, name, value) { |
258 return ref; | 258 if (Object.isExtensible(o)) { |
| 259 try { |
| 260 Object.defineProperty(o, name, { 'value': value }); |
| 261 } catch (e) { |
| 262 // object is native and lies about being extensible |
| 263 // see https://bugzilla.mozilla.org/show_bug.cgi?id=775185 |
259 } | 264 } |
260 } | 265 } |
261 var ref = this.name + '-' + this._nextId++; | 266 } |
262 this.map[ref] = obj; | 267 |
263 return ref; | 268 // Adds an object to the table and return an ID for serialization. |
| 269 ProxiedObjectTable.prototype.add = function (obj, id) { |
| 270 if (id != null) { |
| 271 this.map[id] = obj; |
| 272 return id; |
| 273 } else { |
| 274 var ref = obj[_dartRefPropertyName]; |
| 275 if (ref == null) { |
| 276 ref = this.name + '-' + this._nextId++; |
| 277 this.map[ref] = obj; |
| 278 _defineProperty(obj, _dartRefPropertyName, ref); |
| 279 } |
| 280 return ref; |
| 281 } |
264 } | 282 } |
265 | 283 |
266 // Gets the object or function corresponding to this ID. | 284 // Gets the object or function corresponding to this ID. |
| 285 ProxiedObjectTable.prototype.contains = function (id) { |
| 286 return this.map.hasOwnProperty(id); |
| 287 } |
| 288 |
| 289 // Gets the object or function corresponding to this ID. |
267 ProxiedObjectTable.prototype.get = function (id) { | 290 ProxiedObjectTable.prototype.get = function (id) { |
268 if (!this.map.hasOwnProperty(id)) { | 291 if (!this.map.hasOwnProperty(id)) { |
269 throw 'Proxy ' + id + ' has been invalidated.' | 292 throw 'Proxy ' + id + ' has been invalidated.' |
270 } | 293 } |
271 return this.map[id]; | 294 return this.map[id]; |
272 } | 295 } |
273 | 296 |
274 ProxiedObjectTable.prototype._initialize = function () { | 297 ProxiedObjectTable.prototype._initialize = function () { |
275 // Configure this table's port to forward methods, getters, and setters | 298 // Configure this table's port to forward methods, getters, and setters |
276 // from the remote proxy to the local object. | 299 // from the remote proxy to the local object. |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 return [ 'throws', e.toString() ]; | 344 return [ 'throws', e.toString() ]; |
322 } | 345 } |
323 }); | 346 }); |
324 } | 347 } |
325 | 348 |
326 // Singleton for local proxied objects. | 349 // Singleton for local proxied objects. |
327 var proxiedObjectTable = new ProxiedObjectTable(); | 350 var proxiedObjectTable = new ProxiedObjectTable(); |
328 proxiedObjectTable._initialize() | 351 proxiedObjectTable._initialize() |
329 | 352 |
330 // Type for remote proxies to Dart objects. | 353 // Type for remote proxies to Dart objects. |
331 function DartProxy(id, sendPort) { | 354 function DartObject(id, sendPort) { |
332 this.id = id; | 355 this.id = id; |
333 this.port = sendPort; | 356 this.port = sendPort; |
334 } | 357 } |
335 | 358 |
336 // Serializes JS types to SendPortSync format: | 359 // Serializes JS types to SendPortSync format: |
337 // - primitives -> primitives | 360 // - primitives -> primitives |
338 // - sendport -> sendport | 361 // - sendport -> sendport |
339 // - Function -> [ 'funcref', function-id, sendport ] | 362 // - Function -> [ 'funcref', function-id, sendport ] |
340 // - Object -> [ 'objref', object-id, sendport ] | 363 // - Object -> [ 'objref', object-id, sendport ] |
341 function serialize(message) { | 364 function serialize(message) { |
(...skipping 12 matching lines...) Expand all Loading... |
354 // Remote function proxy. | 377 // Remote function proxy. |
355 var remoteId = message._dart_id; | 378 var remoteId = message._dart_id; |
356 var remoteSendPort = message._dart_port; | 379 var remoteSendPort = message._dart_port; |
357 return [ 'funcref', remoteId, remoteSendPort ]; | 380 return [ 'funcref', remoteId, remoteSendPort ]; |
358 } else { | 381 } else { |
359 // Local function proxy. | 382 // Local function proxy. |
360 return [ 'funcref', | 383 return [ 'funcref', |
361 proxiedObjectTable.add(message), | 384 proxiedObjectTable.add(message), |
362 proxiedObjectTable.sendPort ]; | 385 proxiedObjectTable.sendPort ]; |
363 } | 386 } |
364 } else if (message instanceof DartProxy) { | 387 } else if (message instanceof DartObject) { |
365 // Remote object proxy. | 388 // Remote object proxy. |
366 return [ 'objref', message.id, message.port ]; | 389 return [ 'objref', message.id, message.port ]; |
367 } else { | 390 } else { |
368 // Local object proxy. | 391 // Local object proxy. |
369 return [ 'objref', | 392 return [ 'objref', |
370 proxiedObjectTable.add(message), | 393 proxiedObjectTable.add(message), |
371 proxiedObjectTable.sendPort ]; | 394 proxiedObjectTable.sendPort ]; |
372 } | 395 } |
373 } | 396 } |
374 | 397 |
(...skipping 20 matching lines...) Expand all Loading... |
395 // Create a local function that forwards to the remote function. | 418 // Create a local function that forwards to the remote function. |
396 function deserializeFunction(message) { | 419 function deserializeFunction(message) { |
397 var id = message[1]; | 420 var id = message[1]; |
398 var port = message[2]; | 421 var port = message[2]; |
399 // TODO(vsm): Add a more robust check for a local SendPortSync. | 422 // TODO(vsm): Add a more robust check for a local SendPortSync. |
400 if ("receivePort" in port) { | 423 if ("receivePort" in port) { |
401 // Local function. | 424 // Local function. |
402 return proxiedObjectTable.get(id); | 425 return proxiedObjectTable.get(id); |
403 } else { | 426 } else { |
404 // Remote function. Forward to its port. | 427 // Remote function. Forward to its port. |
| 428 if (proxiedObjectTable.contains(id)) { |
| 429 return proxiedObjectTable.get(id); |
| 430 } |
405 var f = function () { | 431 var f = function () { |
406 var args = Array.prototype.slice.apply(arguments); | 432 var args = Array.prototype.slice.apply(arguments); |
407 args.splice(0, 0, this); | 433 args.splice(0, 0, this); |
408 args = args.map(serialize); | 434 args = args.map(serialize); |
409 var result = port.callSync([id, '#call', args]); | 435 var result = port.callSync([id, '#call', args]); |
410 if (result[0] == 'throws') throw deserialize(result[1]); | 436 if (result[0] == 'throws') throw deserialize(result[1]); |
411 return deserialize(result[1]); | 437 return deserialize(result[1]); |
412 }; | 438 }; |
413 // Cache the remote id and port. | 439 // Cache the remote id and port. |
414 f._dart_id = id; | 440 f._dart_id = id; |
415 f._dart_port = port; | 441 f._dart_port = port; |
| 442 proxiedObjectTable.add(f, id); |
416 return f; | 443 return f; |
417 } | 444 } |
418 } | 445 } |
419 | 446 |
420 // Creates a DartProxy to forwards to the remote object. | 447 // Creates a DartObject to forwards to the remote object. |
421 function deserializeObject(message) { | 448 function deserializeObject(message) { |
422 var id = message[1]; | 449 var id = message[1]; |
423 var port = message[2]; | 450 var port = message[2]; |
424 // TODO(vsm): Add a more robust check for a local SendPortSync. | 451 // TODO(vsm): Add a more robust check for a local SendPortSync. |
425 if ("receivePort" in port) { | 452 if ("receivePort" in port) { |
426 // Local object. | 453 // Local object. |
427 return proxiedObjectTable.get(id); | 454 return proxiedObjectTable.get(id); |
428 } else { | 455 } else { |
429 // Remote object. | 456 // Remote object. |
430 return new DartProxy(id, port); | 457 if (proxiedObjectTable.contains(id)) { |
| 458 return proxiedObjectTable.get(id); |
| 459 } |
| 460 proxy = new DartObject(id, port); |
| 461 proxiedObjectTable.add(proxy, id); |
| 462 return proxy; |
431 } | 463 } |
432 } | 464 } |
433 | 465 |
434 // Remote handler to construct a new JavaScript object given its | 466 // Remote handler to construct a new JavaScript object given its |
435 // serialized constructor and arguments. | 467 // serialized constructor and arguments. |
436 function construct(args) { | 468 function construct(args) { |
437 args = args.map(deserialize); | 469 args = args.map(deserialize); |
438 var constructor = args[0]; | 470 var constructor = args[0]; |
439 args = Array.prototype.slice.call(args, 1); | |
440 | 471 |
441 // Until 10 args, the 'new' operator is used. With more arguments we use a | 472 // The following code solves the problem of invoking a JavaScript |
442 // generic way that may not work, particularly when the constructor does not | 473 // constructor with an unknown number arguments. |
443 // have an "apply" method. | 474 // First bind the constructor to the argument list using bind.apply(). |
444 var ret = null; | 475 // The first argument to bind() is the binding of 'this', make it 'null' |
445 if (args.length === 0) { | 476 // After that, use the JavaScript 'new' operator which overrides any binding |
446 ret = new constructor(); | 477 // of 'this' with the new instance. |
447 } else if (args.length === 1) { | 478 args[0] = null; |
448 ret = new constructor(args[0]); | 479 var factoryFunction = constructor.bind.apply(constructor, args); |
449 } else if (args.length === 2) { | 480 return serialize(new factoryFunction()); |
450 ret = new constructor(args[0], args[1]); | |
451 } else if (args.length === 3) { | |
452 ret = new constructor(args[0], args[1], args[2]); | |
453 } else if (args.length === 4) { | |
454 ret = new constructor(args[0], args[1], args[2], args[3]); | |
455 } else if (args.length === 5) { | |
456 ret = new constructor(args[0], args[1], args[2], args[3], args[4]); | |
457 } else if (args.length === 6) { | |
458 ret = new constructor(args[0], args[1], args[2], args[3], args[4], | |
459 args[5]); | |
460 } else if (args.length === 7) { | |
461 ret = new constructor(args[0], args[1], args[2], args[3], args[4], | |
462 args[5], args[6]); | |
463 } else if (args.length === 8) { | |
464 ret = new constructor(args[0], args[1], args[2], args[3], args[4], | |
465 args[5], args[6], args[7]); | |
466 } else if (args.length === 9) { | |
467 ret = new constructor(args[0], args[1], args[2], args[3], args[4], | |
468 args[5], args[6], args[7], args[8]); | |
469 } else if (args.length === 10) { | |
470 ret = new constructor(args[0], args[1], args[2], args[3], args[4], | |
471 args[5], args[6], args[7], args[8], args[9]); | |
472 } else { | |
473 // Dummy Type with correct constructor. | |
474 var Type = function(){}; | |
475 Type.prototype = constructor.prototype; | |
476 | |
477 // Create a new instance | |
478 var instance = new Type(); | |
479 | |
480 // Call the original constructor. | |
481 ret = constructor.apply(instance, args); | |
482 ret = Object(ret) === ret ? ret : instance; | |
483 } | |
484 return serialize(ret); | |
485 } | 481 } |
486 | 482 |
487 // Remote handler to return the top-level JavaScript context. | 483 // Remote handler to return the top-level JavaScript context. |
488 function context(data) { | 484 function context(data) { |
489 return serialize(globalContext); | 485 return serialize(globalContext); |
490 } | 486 } |
491 | 487 |
492 // Return true if a JavaScript proxy is instance of a given type (instanceof). | 488 // Return true if a JavaScript proxy is instance of a given type (instanceof). |
493 function proxyInstanceof(args) { | 489 function proxyInstanceof(args) { |
494 var obj = deserialize(args[0]); | 490 var obj = deserialize(args[0]); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
532 port.receive(f); | 528 port.receive(f); |
533 window.registerPort(name, port.toSendPort()); | 529 window.registerPort(name, port.toSendPort()); |
534 } | 530 } |
535 | 531 |
536 makeGlobalPort('dart-js-context', context); | 532 makeGlobalPort('dart-js-context', context); |
537 makeGlobalPort('dart-js-create', construct); | 533 makeGlobalPort('dart-js-create', construct); |
538 makeGlobalPort('dart-js-instanceof', proxyInstanceof); | 534 makeGlobalPort('dart-js-instanceof', proxyInstanceof); |
539 makeGlobalPort('dart-js-delete-property', proxyDeleteProperty); | 535 makeGlobalPort('dart-js-delete-property', proxyDeleteProperty); |
540 makeGlobalPort('dart-js-convert', proxyConvert); | 536 makeGlobalPort('dart-js-convert', proxyConvert); |
541 })(); | 537 })(); |
OLD | NEW |