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

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

Powered by Google App Engine
This is Rietveld 408576698