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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/native_handler.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/browser/lib/interop.js
diff --git a/pkg/browser/lib/interop.js b/pkg/browser/lib/interop.js
index 52cc966cdb200a57402da7387b8e4aa5012f5be8..40e1700ad76d53fe7d69ec4819c10387678d1789 100644
--- a/pkg/browser/lib/interop.js
+++ b/pkg/browser/lib/interop.js
@@ -13,6 +13,11 @@ function ReceivePortSync() {
ReceivePortSync.map[this.id] = this;
}
+// Type for remote proxies to Dart objects with dart2js.
+function DartProxy(o) {
+ this.o = o;
+}
+
(function() {
// Serialize the following types as follows:
// - primitives / null: unchanged
@@ -215,3 +220,320 @@ function ReceivePortSync() {
return deserialize(result);
}
})();
+
+(function() {
+ // Proxy support for js.dart.
+
+ var globalContext = window;
+
+ // Table for local objects and functions that are proxied.
+ function ProxiedObjectTable() {
+ // Name for debugging.
+ this.name = 'js-ref';
+
+ // Table from IDs to JS objects.
+ this.map = {};
+
+ // Generator for new IDs.
+ this._nextId = 0;
+
+ // Ports for managing communication to proxies.
+ this.port = new ReceivePortSync();
+ this.sendPort = this.port.toSendPort();
+ }
+
+ // Number of valid IDs. This is the number of objects (global and local)
+ // kept alive by this table.
+ ProxiedObjectTable.prototype.count = function () {
+ return Object.keys(this.map).length;
+ }
+
+ // Adds an object to the table and return an ID for serialization.
+ ProxiedObjectTable.prototype.add = function (obj) {
+ for (var ref in this.map) {
+ var o = this.map[ref];
+ if (o === obj) {
+ return ref;
+ }
+ }
+ var ref = this.name + '-' + this._nextId++;
+ this.map[ref] = obj;
+ return ref;
+ }
+
+ // Gets the object or function corresponding to this ID.
+ ProxiedObjectTable.prototype.get = function (id) {
+ if (!this.map.hasOwnProperty(id)) {
+ throw 'Proxy ' + id + ' has been invalidated.'
+ }
+ return this.map[id];
+ }
+
+ ProxiedObjectTable.prototype._initialize = function () {
+ // Configure this table's port to forward methods, getters, and setters
+ // from the remote proxy to the local object.
+ var table = this;
+
+ this.port.receive(function (message) {
+ // TODO(vsm): Support a mechanism to register a handler here.
+ try {
+ var receiver = table.get(message[0]);
+ var member = message[1];
+ var kind = message[2];
+ var args = message[3].map(deserialize);
+ if (kind == 'get') {
+ // Getter.
+ var field = member;
+ if (field in receiver && args.length == 0) {
+ return [ 'return', serialize(receiver[field]) ];
+ }
+ } else if (kind == 'set') {
+ // Setter.
+ var field = member;
+ if (args.length == 1) {
+ return [ 'return', serialize(receiver[field] = args[0]) ];
+ }
+ } else if (kind == 'hasProperty') {
+ var field = member;
+ return [ 'return', field in receiver ];
+ } else if (kind == 'apply') {
+ // Direct function invocation.
+ return [ 'return',
+ serialize(receiver.apply(args[0], args.slice(1))) ];
+ } else if (member == '[]' && args.length == 1) {
+ // Index getter.
+ return [ 'return', serialize(receiver[args[0]]) ];
+ } else if (member == '[]=' && args.length == 2) {
+ // Index setter.
+ return [ 'return', serialize(receiver[args[0]] = args[1]) ];
+ } else {
+ // Member function invocation.
+ var f = receiver[member];
+ if (f) {
+ var result = f.apply(receiver, args);
+ return [ 'return', serialize(result) ];
+ }
+ }
+ return [ 'none' ];
+ } catch (e) {
+ return [ 'throws', e.toString() ];
+ }
+ });
+ }
+
+ // Singleton for local proxied objects.
+ var proxiedObjectTable = new ProxiedObjectTable();
+ proxiedObjectTable._initialize()
+
+ // Type for remote proxies to Dart objects.
+ function DartProxy(id, sendPort) {
+ this.id = id;
+ this.port = sendPort;
+ }
+
+ // Serializes JS types to SendPortSync format:
+ // - primitives -> primitives
+ // - sendport -> sendport
+ // - Function -> [ 'funcref', function-id, sendport ]
+ // - Object -> [ 'objref', object-id, sendport ]
+ function serialize(message) {
+ if (message == null) {
+ return null; // Convert undefined to null.
+ } else if (typeof(message) == 'string' ||
+ typeof(message) == 'number' ||
+ typeof(message) == 'boolean') {
+ // Primitives are passed directly through.
+ return message;
+ } else if (message instanceof SendPortSync) {
+ // Non-proxied objects are serialized.
+ return message;
+ } else if (typeof(message) == 'function') {
+ if ('_dart_id' in message) {
+ // Remote function proxy.
+ var remoteId = message._dart_id;
+ var remoteSendPort = message._dart_port;
+ return [ 'funcref', remoteId, remoteSendPort ];
+ } else {
+ // Local function proxy.
+ return [ 'funcref',
+ proxiedObjectTable.add(message),
+ proxiedObjectTable.sendPort ];
+ }
+ } else if (message instanceof DartProxy) {
+ // Remote object proxy.
+ return [ 'objref', message.id, message.port ];
+ } else {
+ // Local object proxy.
+ return [ 'objref',
+ proxiedObjectTable.add(message),
+ proxiedObjectTable.sendPort ];
+ }
+ }
+
+ function deserialize(message) {
+ if (message == null) {
+ return null; // Convert undefined to null.
+ } else if (typeof(message) == 'string' ||
+ typeof(message) == 'number' ||
+ typeof(message) == 'boolean') {
+ // Primitives are passed directly through.
+ return message;
+ } else if (message instanceof SendPortSync) {
+ // Serialized type.
+ return message;
+ }
+ var tag = message[0];
+ switch (tag) {
+ case 'funcref': return deserializeFunction(message);
+ case 'objref': return deserializeObject(message);
+ }
+ throw 'Unsupported serialized data: ' + message;
+ }
+
+ // Create a local function that forwards to the remote function.
+ function deserializeFunction(message) {
+ var id = message[1];
+ var port = message[2];
+ // TODO(vsm): Add a more robust check for a local SendPortSync.
+ if ("receivePort" in port) {
+ // Local function.
+ return proxiedObjectTable.get(id);
+ } else {
+ // Remote function. Forward to its port.
+ var f = function () {
+ var args = Array.prototype.slice.apply(arguments);
+ args.splice(0, 0, this);
+ args = args.map(serialize);
+ var result = port.callSync([id, '#call', args]);
+ if (result[0] == 'throws') throw deserialize(result[1]);
+ return deserialize(result[1]);
+ };
+ // Cache the remote id and port.
+ f._dart_id = id;
+ f._dart_port = port;
+ return f;
+ }
+ }
+
+ // Creates a DartProxy to forwards to the remote object.
+ function deserializeObject(message) {
+ var id = message[1];
+ var port = message[2];
+ // TODO(vsm): Add a more robust check for a local SendPortSync.
+ if ("receivePort" in port) {
+ // Local object.
+ return proxiedObjectTable.get(id);
+ } else {
+ // Remote object.
+ return new DartProxy(id, port);
+ }
+ }
+
+ // Remote handler to construct a new JavaScript object given its
+ // serialized constructor and arguments.
+ function construct(args) {
+ args = args.map(deserialize);
+ var constructor = args[0];
+ args = Array.prototype.slice.call(args, 1);
+
+ // Until 10 args, the 'new' operator is used. With more arguments we use a
+ // generic way that may not work, particularly when the constructor does not
+ // have an "apply" method.
+ var ret = null;
+ if (args.length === 0) {
+ ret = new constructor();
+ } else if (args.length === 1) {
+ ret = new constructor(args[0]);
+ } else if (args.length === 2) {
+ ret = new constructor(args[0], args[1]);
+ } else if (args.length === 3) {
+ ret = new constructor(args[0], args[1], args[2]);
+ } else if (args.length === 4) {
+ ret = new constructor(args[0], args[1], args[2], args[3]);
+ } else if (args.length === 5) {
+ ret = new constructor(args[0], args[1], args[2], args[3], args[4]);
+ } else if (args.length === 6) {
+ ret = new constructor(args[0], args[1], args[2], args[3], args[4],
+ args[5]);
+ } else if (args.length === 7) {
+ ret = new constructor(args[0], args[1], args[2], args[3], args[4],
+ args[5], args[6]);
+ } else if (args.length === 8) {
+ ret = new constructor(args[0], args[1], args[2], args[3], args[4],
+ args[5], args[6], args[7]);
+ } else if (args.length === 9) {
+ ret = new constructor(args[0], args[1], args[2], args[3], args[4],
+ args[5], args[6], args[7], args[8]);
+ } else if (args.length === 10) {
+ ret = new constructor(args[0], args[1], args[2], args[3], args[4],
+ args[5], args[6], args[7], args[8], args[9]);
+ } else {
+ // Dummy Type with correct constructor.
+ var Type = function(){};
+ Type.prototype = constructor.prototype;
+
+ // Create a new instance
+ var instance = new Type();
+
+ // Call the original constructor.
+ ret = constructor.apply(instance, args);
+ ret = Object(ret) === ret ? ret : instance;
+ }
+ return serialize(ret);
+ }
+
+ // Remote handler to return the top-level JavaScript context.
+ function context(data) {
+ return serialize(globalContext);
+ }
+
+ // Return true if a JavaScript proxy is instance of a given type (instanceof).
+ function proxyInstanceof(args) {
+ var obj = deserialize(args[0]);
+ var type = deserialize(args[1]);
+ return obj instanceof type;
+ }
+
+ // Return true if a JavaScript proxy is instance of a given type (instanceof).
+ function proxyDeleteProperty(args) {
+ var obj = deserialize(args[0]);
+ var member = deserialize(args[1]);
+ delete obj[member];
+ }
+
+ function proxyConvert(args) {
+ return serialize(deserializeDataTree(args));
+ }
+
+ function deserializeDataTree(data) {
+ var type = data[0];
+ var value = data[1];
+ if (type === 'map') {
+ var obj = {};
+ for (var i = 0; i < value.length; i++) {
+ obj[value[i][0]] = deserializeDataTree(value[i][1]);
+ }
+ return obj;
+ } else if (type === 'list') {
+ var list = [];
+ for (var i = 0; i < value.length; i++) {
+ list.push(deserializeDataTree(value[i]));
+ }
+ return list;
+ } else /* 'simple' */ {
+ return deserialize(value);
+ }
+ }
+
+ function makeGlobalPort(name, f) {
+ var port = new ReceivePortSync();
+ port.receive(f);
+ window.registerPort(name, port.toSendPort());
+ }
+
+ makeGlobalPort('dart-js-context', context);
+ makeGlobalPort('dart-js-create', construct);
+ makeGlobalPort('dart-js-instanceof', proxyInstanceof);
+ makeGlobalPort('dart-js-delete-property', proxyDeleteProperty);
+ makeGlobalPort('dart-js-convert', proxyConvert);
+})();
« 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