Index: sdk/lib/js/dartium/js_dartium.dart |
diff --git a/sdk/lib/js/dartium/js_dartium.dart b/sdk/lib/js/dartium/js_dartium.dart |
index a0c48ea0d93ed32372e0262d46effebb8c054aef..234a4bf3bff3f352481c7667dd5b22612853a160 100644 |
--- a/sdk/lib/js/dartium/js_dartium.dart |
+++ b/sdk/lib/js/dartium/js_dartium.dart |
@@ -66,16 +66,60 @@ |
library dart.js; |
+import 'dart:collection' show HashMap; |
import 'dart:html'; |
import 'dart:isolate'; |
// Global ports to manage communication from Dart to JS. |
+ |
SendPortSync _jsPortSync = window.lookupPort('dart-js-context'); |
SendPortSync _jsPortCreate = window.lookupPort('dart-js-create'); |
SendPortSync _jsPortInstanceof = window.lookupPort('dart-js-instanceof'); |
SendPortSync _jsPortDeleteProperty = window.lookupPort('dart-js-delete-property'); |
SendPortSync _jsPortConvert = window.lookupPort('dart-js-convert'); |
+final String _objectIdPrefix = 'dart-obj-ref'; |
+final String _functionIdPrefix = 'dart-fun-ref'; |
+final _objectTable = new _ObjectTable(); |
+final _functionTable = new _ObjectTable.forFunctions(); |
+ |
+// Port to handle and forward requests to the underlying Dart objects. |
+// A remote proxy is uniquely identified by an ID and SendPortSync. |
+ReceivePortSync _port = new ReceivePortSync() |
+ ..receive((msg) { |
+ try { |
+ var id = msg[0]; |
+ var method = msg[1]; |
+ if (method == '#call') { |
+ var receiver = _getObjectTable(id).get(id); |
+ var result; |
+ if (receiver is Function) { |
+ // remove the first argument, which is 'this', but never |
+ // used for a raw function |
+ var args = msg[2].sublist(1).map(_deserialize).toList(); |
+ result = Function.apply(receiver, args); |
+ } else if (receiver is Callback) { |
+ var args = msg[2].map(_deserialize).toList(); |
+ result = receiver._call(args); |
+ } else { |
+ throw new StateError('bad function type: $receiver'); |
+ } |
+ return ['return', _serialize(result)]; |
+ } else { |
+ // TODO(vsm): Support a mechanism to register a handler here. |
+ throw 'Invocation unsupported on non-function Dart proxies'; |
+ } |
+ } catch (e) { |
+ // TODO(vsm): callSync should just handle exceptions itself. |
+ return ['throws', '$e']; |
+ } |
+ }); |
+ |
+_ObjectTable _getObjectTable(String id) { |
+ if (id.startsWith(_functionIdPrefix)) return _functionTable; |
+ if (id.startsWith(_objectIdPrefix)) return _objectTable; |
+ throw new ArgumentError('internal error: invalid object id: $id'); |
+} |
JsObject _context; |
@@ -104,21 +148,24 @@ JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data); |
* JavaScript. |
*/ |
class Callback implements Serializable<JsFunction> { |
- JsFunction _f; |
+ final bool _withThis; |
+ final Function _function; |
+ JsFunction _jsFunction; |
- Callback._(Function f, bool withThis) { |
- final id = _proxiedObjectTable.add((List args) { |
- final arguments = new List.from(args); |
- if (!withThis) arguments.removeAt(0); |
- return Function.apply(f, arguments); |
- }); |
- _f = new JsFunction._internal(_proxiedObjectTable.sendPort, id); |
+ Callback._(this._function, this._withThis) { |
+ var id = _functionTable.add(this); |
+ _jsFunction = new JsFunction._internal(_port.toSendPort(), id); |
} |
factory Callback(Function f) => new Callback._(f, false); |
factory Callback.withThis(Function f) => new Callback._(f, true); |
- JsFunction toJs() => _f; |
+ dynamic _call(List args) { |
+ var arguments = (_withThis) ? args : args.sublist(1); |
+ return Function.apply(_function, arguments); |
+ } |
+ |
+ JsFunction toJs() => _jsFunction; |
} |
/** |
@@ -242,70 +289,46 @@ abstract class Serializable<T> { |
T toJs(); |
} |
-// A table to managed local Dart objects that are proxied in JavaScript. |
-class _ProxiedObjectTable { |
- // Debugging name. |
- final String _name; |
- |
- // Generator for unique IDs. |
- int _nextId; |
- |
- // Table of IDs to Dart objects. |
- final Map<String, Object> _registry; |
- |
- // Port to handle and forward requests to the underlying Dart objects. |
- // A remote proxy is uniquely identified by an ID and SendPortSync. |
- final ReceivePortSync _port; |
- |
- _ProxiedObjectTable() : |
- _name = 'dart-ref', |
- _nextId = 0, |
- _registry = {}, |
- _port = new ReceivePortSync() { |
- _port.receive((msg) { |
- try { |
- final receiver = _registry[msg[0]]; |
- final method = msg[1]; |
- final args = msg[2].map(_deserialize).toList(); |
- if (method == '#call') { |
- final func = receiver as Function; |
- var result = _serialize(func(args)); |
- return ['return', result]; |
- } else { |
- // TODO(vsm): Support a mechanism to register a handler here. |
- throw 'Invocation unsupported on non-function Dart proxies'; |
- } |
- } catch (e) { |
- // TODO(vsm): callSync should just handle exceptions itself. |
- return ['throws', '$e']; |
- } |
- }); |
- } |
- |
- // Adds a new object to the table and return a new ID for it. |
- String add(x) { |
+class _ObjectTable { |
+ final String name; |
+ final Map<String, Object> objects; |
+ final Map<Object, String> ids; |
+ int nextId = 0; |
+ |
+ // Creates a table that uses an identity Map to store IDs |
+ _ObjectTable() |
+ : name = _objectIdPrefix, |
+ objects = new HashMap<String, Object>(), |
+ ids = new HashMap<Object, String>.identity(); |
+ |
+ // Creates a table that uses an equality-based Map to store IDs, since |
+ // closurized methods may be equal, but not identical |
+ _ObjectTable.forFunctions() |
+ : name = _functionIdPrefix, |
+ objects = new HashMap<String, Object>(), |
+ ids = new HashMap<Object, String>(); |
+ |
+ // Adds a new object to the table. If [id] is not given, a new unique ID is |
+ // generated. Returns the ID. |
+ String add(Object o, {String id}) { |
// TODO(vsm): Cache x and reuse id. |
- final id = '$_name-${_nextId++}'; |
- _registry[id] = x; |
+ if (id == null) id = ids[o]; |
+ if (id == null) id = '$name-${nextId++}'; |
+ ids[o] = id; |
+ objects[id] = o; |
return id; |
} |
// Gets an object by ID. |
- Object get(String id) { |
- return _registry[id]; |
- } |
+ Object get(String id) => objects[id]; |
- // Gets the current number of objects kept alive by this table. |
- get count => _registry.length; |
- |
- // Gets a send port for this table. |
- get sendPort => _port.toSendPort(); |
-} |
+ bool contains(String id) => objects.containsKey(id); |
-// The singleton to manage proxied Dart objects. |
-_ProxiedObjectTable _proxiedObjectTable = new _ProxiedObjectTable(); |
+ String getId(Object o) => ids[o]; |
-/// End of proxy implementation. |
+ // Gets the current number of objects kept alive by this table. |
+ get count => objects.length; |
+} |
// Dart serialization support. |
@@ -322,20 +345,23 @@ _serialize(var message) { |
return message; |
} else if (message is JsFunction) { |
// Remote function proxy. |
- return [ 'funcref', message._id, message._port ]; |
+ return ['funcref', message._id, message._port]; |
} else if (message is JsObject) { |
// Remote object proxy. |
- return [ 'objref', message._id, message._port ]; |
+ return ['objref', message._id, message._port]; |
} else if (message is Serializable) { |
// use of result of toJs() |
return _serialize(message.toJs()); |
} else if (message is Function) { |
- return _serialize(new Callback(message)); |
+ var id = _functionTable.getId(message); |
+ if (id != null) { |
+ return ['funcref', id, _port.toSendPort()]; |
+ } |
+ id = _functionTable.add(message); |
+ return ['funcref', id, _port.toSendPort()]; |
} else { |
// Local object proxy. |
- return [ 'objref', |
- _proxiedObjectTable.add(message), |
- _proxiedObjectTable.sendPort ]; |
+ return ['objref', _objectTable.add(message), _port.toSendPort()]; |
} |
} |
@@ -343,24 +369,34 @@ _deserialize(var message) { |
deserializeFunction(message) { |
var id = message[1]; |
var port = message[2]; |
- if (port == _proxiedObjectTable.sendPort) { |
+ if (port == _port.toSendPort()) { |
// Local function. |
- return _proxiedObjectTable.get(id); |
+ return _functionTable.get(id); |
} else { |
- // Remote function. Forward to its port. |
- return new JsFunction._internal(port, id); |
+ // Remote function. |
+ var jsFunction = _functionTable.get(id); |
+ if (jsFunction == null) { |
+ jsFunction = new JsFunction._internal(port, id); |
+ _functionTable.add(jsFunction, id: id); |
+ } |
+ return jsFunction; |
} |
} |
deserializeObject(message) { |
var id = message[1]; |
var port = message[2]; |
- if (port == _proxiedObjectTable.sendPort) { |
+ if (port == _port.toSendPort()) { |
// Local object. |
- return _proxiedObjectTable.get(id); |
+ return _objectTable.get(id); |
} else { |
// Remote object. |
- return new JsObject._internal(port, id); |
+ var jsObject = _objectTable.get(id); |
+ if (jsObject == null) { |
+ jsObject = new JsObject._internal(port, id); |
+ _objectTable.add(jsObject, id: id); |
+ } |
+ return jsObject; |
} |
} |