diff --git a/client/dart.js b/client/dart.js index 15ac8a40b48..ab06336df90 100644 --- a/client/dart.js +++ b/client/dart.js @@ -86,8 +86,13 @@ function ReceivePortSync() { } else if (message instanceof DartSendPortSync) { return [ 'sendport', 'dart', message.isolateId, message.portId ]; } else if (message instanceof Function) { - return [ 'funcref', functionRefTable.makeRef(message), - doSerialize(functionRefTable.sendPort) ]; + // In case we are reserializing a previously serialized + // function, use a cached value. + if (message._dart_serialized) return message._dart_serialized; + message._dart_serialized = [ 'funcref', + functionRefTable.makeRef(message), + doSerialize(functionRefTable.sendPort) ]; + return message._dart_serialized; } else if (message instanceof HTMLElement) { var id = elementId(message); // Verify that the element is connected to the document. @@ -180,7 +185,10 @@ function ReceivePortSync() { var sendPort = deserializeSendPort(message[2]); // Number of arguments is not used as of now // we cannot find it out for Dart function in pure Dart. - return _makeFunctionFromRef(ref, sendPort); + var result = _makeFunctionFromRef(ref, sendPort); + // Cache the serialized form in case we resend this. + result._dart_serialized = message; + return result; } function deserializeProxy(message) { @@ -324,7 +332,7 @@ function ReceivePortSync() { FunctionRefTable.prototype = new RefTable('func-ref'); FunctionRefTable.prototype.initialize = function () { - map = this.map; + var map = this.map; this.port.receive(function (message) { var id = message[0]; var args = message[1]; @@ -341,7 +349,7 @@ function ReceivePortSync() { JSRefTable.prototype = new RefTable('js-ref'); JSRefTable.prototype.initialize = function () { - map = this.map; + var map = this.map; this.port.receive(function (message) { // TODO(vsm): Support a mechanism to register a handler here. var receiver = map[message[0]]; @@ -384,14 +392,20 @@ function ReceivePortSync() { // Leaking implementation. // TODO(vsm): provide proper, backend-specific implementation. function _makeFunctionFromRef(ref, sendPort) { + // If the sendPort is local, just return the underlying function. + // Otherwise, create a new function that forwards to the remote + // port. + if (sendPort instanceof LocalSendPortSync) { + return functionRefTable.map[ref]; + } return function() { return sendPort.callSync([ref, Array.prototype.slice.call(arguments)]); - } + }; } var localNextElementId = 0; var _DART_ID = 'data-dart_id'; - + function elementId(e) { if (e.hasAttribute(_DART_ID)) return e.getAttribute(_DART_ID); var id = (localNextElementId++).toString(); @@ -406,6 +420,6 @@ function ReceivePortSync() { if (list.length == 0) { throw 'Element must be attached to the document: ' + id; } - return list[0]; + return list[0]; } })(); diff --git a/lib/html/dart2js/html_dart2js.dart b/lib/html/dart2js/html_dart2js.dart index 39be8b77b63..122d62cd23a 100644 --- a/lib/html/dart2js/html_dart2js.dart +++ b/lib/html/dart2js/html_dart2js.dart @@ -37737,6 +37737,11 @@ class _JsSerializer extends _Serializer { } visitFunction(Function func) { + // Look for a cached serialization first. The cached version + // should point to the original port. + var serialized = _deserializedFunctionTable.find(func); + if (serialized != null) return serialized; + // Create a new serialization forwarding to this port. return [ 'funcref', _functionRegistry._add(func), visitSendPortSync(_functionRegistry._sendPort), null ]; @@ -37833,6 +37838,34 @@ _deserialize(var message) { return new _JsDeserializer().deserialize(message); } +// TODO(vsm): Replace this with a hash map once functions are +// hashable. +class _DeserializedFunctionTable { + List data; + _DeserializedFunctionTable() { + data = []; + } + + find(Function f) { + for (var item in data) { + if (f == item[0]) return item[1]; + } + return null; + } + + add(Function f, x) { + data.add([f, x]); + } +} + +_DeserializedFunctionTable __deserializedFunctionTable = null; +get _deserializedFunctionTable { + if (__deserializedFunctionTable == null) { + __deserializedFunctionTable = new _DeserializedFunctionTable(); + } + return __deserializedFunctionTable; +} + class _JsDeserializer extends _Deserializer { static const _UNSPECIFIED = const Object(); @@ -37864,9 +37897,15 @@ class _JsDeserializer extends _Deserializer { deserializeFunction(List x) { var id = x[1]; + // If the sendPort is local, just return the underlying function. + // Otherwise, create a new function that forwards to the remote + // port. SendPortSync port = deserializeSendPort(x[2]); + if (port is _LocalSendPortSync) { + return _functionRegistry._get(id); + } // TODO: Support varargs when there is support in the language. - return ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, + var f = ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) { var args = [arg0, arg1, arg2, arg3]; var last = args.indexOf(_UNSPECIFIED); @@ -37874,6 +37913,8 @@ class _JsDeserializer extends _Deserializer { var message = [id, args]; return port.callSync(message); }; + _deserializedFunctionTable.add(f, x); + return f; } deserializeProxy(x) { diff --git a/lib/html/dartium/html_dartium.dart b/lib/html/dartium/html_dartium.dart index 424f0d25c6c..6801b0c97f2 100644 --- a/lib/html/dartium/html_dartium.dart +++ b/lib/html/dartium/html_dartium.dart @@ -40842,6 +40842,11 @@ class _JsSerializer extends _Serializer { } visitFunction(Function func) { + // Look for a cached serialization first. The cached version + // should point to the original port. + var serialized = _deserializedFunctionTable.find(func); + if (serialized != null) return serialized; + // Create a new serialization forwarding to this port. return [ 'funcref', _functionRegistry._add(func), visitSendPortSync(_functionRegistry._sendPort), null ]; @@ -40938,6 +40943,34 @@ _deserialize(var message) { return new _JsDeserializer().deserialize(message); } +// TODO(vsm): Replace this with a hash map once functions are +// hashable. +class _DeserializedFunctionTable { + List data; + _DeserializedFunctionTable() { + data = []; + } + + find(Function f) { + for (var item in data) { + if (f == item[0]) return item[1]; + } + return null; + } + + add(Function f, x) { + data.add([f, x]); + } +} + +_DeserializedFunctionTable __deserializedFunctionTable = null; +get _deserializedFunctionTable { + if (__deserializedFunctionTable == null) { + __deserializedFunctionTable = new _DeserializedFunctionTable(); + } + return __deserializedFunctionTable; +} + class _JsDeserializer extends _Deserializer { static const _UNSPECIFIED = const Object(); @@ -40969,9 +41002,15 @@ class _JsDeserializer extends _Deserializer { deserializeFunction(List x) { var id = x[1]; + // If the sendPort is local, just return the underlying function. + // Otherwise, create a new function that forwards to the remote + // port. SendPortSync port = deserializeSendPort(x[2]); + if (port is _LocalSendPortSync) { + return _functionRegistry._get(id); + } // TODO: Support varargs when there is support in the language. - return ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, + var f = ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) { var args = [arg0, arg1, arg2, arg3]; var last = args.indexOf(_UNSPECIFIED); @@ -40979,6 +41018,8 @@ class _JsDeserializer extends _Deserializer { var message = [id, args]; return port.callSync(message); }; + _deserializedFunctionTable.add(f, x); + return f; } deserializeProxy(x) { diff --git a/lib/html/src/Isolates.dart b/lib/html/src/Isolates.dart index d4b30c4bb6d..690600548c1 100644 --- a/lib/html/src/Isolates.dart +++ b/lib/html/src/Isolates.dart @@ -78,6 +78,11 @@ class _JsSerializer extends _Serializer { } visitFunction(Function func) { + // Look for a cached serialization first. The cached version + // should point to the original port. + var serialized = _deserializedFunctionTable.find(func); + if (serialized != null) return serialized; + // Create a new serialization forwarding to this port. return [ 'funcref', _functionRegistry._add(func), visitSendPortSync(_functionRegistry._sendPort), null ]; @@ -174,6 +179,34 @@ _deserialize(var message) { return new _JsDeserializer().deserialize(message); } +// TODO(vsm): Replace this with a hash map once functions are +// hashable. +class _DeserializedFunctionTable { + List data; + _DeserializedFunctionTable() { + data = []; + } + + find(Function f) { + for (var item in data) { + if (f == item[0]) return item[1]; + } + return null; + } + + add(Function f, x) { + data.add([f, x]); + } +} + +_DeserializedFunctionTable __deserializedFunctionTable = null; +get _deserializedFunctionTable { + if (__deserializedFunctionTable == null) { + __deserializedFunctionTable = new _DeserializedFunctionTable(); + } + return __deserializedFunctionTable; +} + class _JsDeserializer extends _Deserializer { static const _UNSPECIFIED = const Object(); @@ -205,9 +238,15 @@ class _JsDeserializer extends _Deserializer { deserializeFunction(List x) { var id = x[1]; + // If the sendPort is local, just return the underlying function. + // Otherwise, create a new function that forwards to the remote + // port. SendPortSync port = deserializeSendPort(x[2]); + if (port is _LocalSendPortSync) { + return _functionRegistry._get(id); + } // TODO: Support varargs when there is support in the language. - return ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, + var f = ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) { var args = [arg0, arg1, arg2, arg3]; var last = args.indexOf(_UNSPECIFIED); @@ -215,6 +254,8 @@ class _JsDeserializer extends _Deserializer { var message = [id, args]; return port.callSync(message); }; + _deserializedFunctionTable.add(f, x); + return f; } deserializeProxy(x) {