Index: pkg/browser/lib/interop.js |
diff --git a/pkg/browser/lib/interop.js b/pkg/browser/lib/interop.js |
index 8577e4f28b8c30aa67270ff78727da5789d0bea0..71af132522e77c5ef3f49b7b27e40535760acc3d 100644 |
--- a/pkg/browser/lib/interop.js |
+++ b/pkg/browser/lib/interop.js |
@@ -14,7 +14,7 @@ function ReceivePortSync() { |
} |
// Type for remote proxies to Dart objects with dart2js. |
-function DartProxy(o) { |
+function DartObject(o) { |
this.o = o; |
} |
@@ -250,17 +250,40 @@ function DartProxy(o) { |
return Object.keys(this.map).length; |
} |
+ var _dartRefPropertyName = "_$dart_ref"; |
+ |
+ // attempts to add an unenumerable property to o. If that is not allowed |
+ // it silently fails. |
+ function _defineProperty(o, name, value) { |
+ if (Object.isExtensible(o)) { |
+ try { |
+ Object.defineProperty(o, name, { 'value': value }); |
+ } catch (e) { |
+ // object is native and lies about being extensible |
+ // see https://bugzilla.mozilla.org/show_bug.cgi?id=775185 |
+ } |
+ } |
+ } |
+ |
// 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; |
+ ProxiedObjectTable.prototype.add = function (obj, id) { |
+ if (id != null) { |
+ this.map[id] = obj; |
+ return id; |
+ } else { |
+ var ref = obj[_dartRefPropertyName]; |
+ if (ref == null) { |
+ ref = this.name + '-' + this._nextId++; |
+ this.map[ref] = obj; |
+ _defineProperty(obj, _dartRefPropertyName, ref); |
} |
+ 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.contains = function (id) { |
+ return this.map.hasOwnProperty(id); |
} |
// Gets the object or function corresponding to this ID. |
@@ -328,7 +351,7 @@ function DartProxy(o) { |
proxiedObjectTable._initialize() |
// Type for remote proxies to Dart objects. |
- function DartProxy(id, sendPort) { |
+ function DartObject(id, sendPort) { |
this.id = id; |
this.port = sendPort; |
} |
@@ -361,7 +384,7 @@ function DartProxy(o) { |
proxiedObjectTable.add(message), |
proxiedObjectTable.sendPort ]; |
} |
- } else if (message instanceof DartProxy) { |
+ } else if (message instanceof DartObject) { |
// Remote object proxy. |
return [ 'objref', message.id, message.port ]; |
} else { |
@@ -402,6 +425,9 @@ function DartProxy(o) { |
return proxiedObjectTable.get(id); |
} else { |
// Remote function. Forward to its port. |
+ if (proxiedObjectTable.contains(id)) { |
+ return proxiedObjectTable.get(id); |
+ } |
var f = function () { |
var args = Array.prototype.slice.apply(arguments); |
args.splice(0, 0, this); |
@@ -413,11 +439,12 @@ function DartProxy(o) { |
// Cache the remote id and port. |
f._dart_id = id; |
f._dart_port = port; |
+ proxiedObjectTable.add(f, id); |
return f; |
} |
} |
- // Creates a DartProxy to forwards to the remote object. |
+ // Creates a DartObject to forwards to the remote object. |
function deserializeObject(message) { |
var id = message[1]; |
var port = message[2]; |
@@ -427,7 +454,12 @@ function DartProxy(o) { |
return proxiedObjectTable.get(id); |
} else { |
// Remote object. |
- return new DartProxy(id, port); |
+ if (proxiedObjectTable.contains(id)) { |
+ return proxiedObjectTable.get(id); |
+ } |
+ proxy = new DartObject(id, port); |
+ proxiedObjectTable.add(proxy, id); |
+ return proxy; |
} |
} |
@@ -436,52 +468,16 @@ function DartProxy(o) { |
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); |
+ // The following code solves the problem of invoking a JavaScript |
+ // constructor with an unknown number arguments. |
+ // First bind the constructor to the argument list using bind.apply(). |
+ // The first argument to bind() is the binding of 'this', make it 'null' |
+ // After that, use the JavaScript 'new' operator which overrides any binding |
+ // of 'this' with the new instance. |
+ args[0] = null; |
+ var factoryFunction = constructor.bind.apply(constructor, args); |
+ return serialize(new factoryFunction()); |
} |
// Remote handler to return the top-level JavaScript context. |