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

Unified Diff: lib/js.dart

Issue 12457030: [js-interop] Fix function binding and avoid noSuchMethod when using map notation (Closed) Base URL: https://github.com/dart-lang/js-interop.git@master
Patch Set: Fix for constructors plus cleanup Created 7 years, 8 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 | « lib/dart_interop.js ('k') | test/js/browser_tests.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/js.dart
diff --git a/lib/js.dart b/lib/js.dart
index 215b849f9d2fd660f9e21b76f355bbe7af10faf9..afe9e672f958c76a6e1094eaf66ed32661e9e02b 100644
--- a/lib/js.dart
+++ b/lib/js.dart
@@ -89,6 +89,36 @@ final _JS_BOOTSTRAP = r"""
var globalContext = window;
+ // Support for binding the receiver (this) in proxied functions.
+ function bindIfFunction(f, _this) {
+ if (typeof(f) != "function") {
+ return f;
+ } else {
+ return new BoundFunction(_this, f);
+ }
+ }
+
+ function unbind(obj) {
+ if (obj instanceof BoundFunction) {
+ return obj.object;
+ } else {
+ return obj;
+ }
+ }
+
+ function getBoundThis(obj) {
+ if (obj instanceof BoundFunction) {
+ return obj._this;
+ } else {
+ return globalContext;
+ }
+ }
+
+ function BoundFunction(_this, object) {
+ this._this = _this;
+ this.object = object;
+ }
+
// Table for local objects and functions that are proxied.
function ProxiedObjectTable() {
// Name for debugging.
@@ -204,7 +234,8 @@ final _JS_BOOTSTRAP = r"""
this.port.receive(function (message) {
// TODO(vsm): Support a mechanism to register a handler here.
try {
- var receiver = table.get(message[0]);
+ var object = table.get(message[0]);
+ var receiver = unbind(object);
var member = message[1];
var kind = message[2];
var args = message[3].map(deserialize);
@@ -212,7 +243,8 @@ final _JS_BOOTSTRAP = r"""
// Getter.
var field = member;
if (field in receiver && args.length == 0) {
- return [ 'return', serialize(receiver[field]) ];
+ var result = bindIfFunction(receiver[field], receiver);
+ return [ 'return', serialize(result) ];
}
} else if (kind == 'set') {
// Setter.
@@ -222,15 +254,17 @@ final _JS_BOOTSTRAP = r"""
}
} else if (kind == 'apply') {
// Direct function invocation.
- // TODO(vsm): Should we capture _this_ automatically?
- return [ 'return', serialize(receiver.apply(null, args)) ];
+ var _this = getBoundThis(object);
+ return [ 'return', serialize(receiver.apply(_this, args)) ];
} else if (member == '[]' && args.length == 1) {
// Index getter.
- return [ 'return', serialize(receiver[args[0]]) ];
+ var result = bindIfFunction(receiver[args[0]], receiver);
+ return [ 'return', serialize(result) ];
} 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);
@@ -353,6 +387,12 @@ final _JS_BOOTSTRAP = r"""
} else if (message instanceof Element &&
(message.ownerDocument == null || message.ownerDocument == document)) {
return [ 'domref', serializeElement(message) ];
+ } else if (message instanceof BoundFunction &&
+ typeof(message.object) == 'function') {
alexandre.ardhuin 2013/04/07 21:20:20 Could typeof(message.object) be something else tha
vsm 2013/04/07 21:37:36 It shouldn't. I'll change this to assert that. O
+ // Local function proxy.
+ return [ 'funcref',
+ proxiedObjectTable.add(message),
+ proxiedObjectTable.sendPort ];
} else if (typeof(message) == 'function') {
if ('_dart_id' in message) {
// Remote function proxy.
@@ -443,7 +483,7 @@ final _JS_BOOTSTRAP = r"""
// serialized constructor and arguments.
function construct(args) {
args = args.map(deserialize);
- var constructor = args[0];
+ var constructor = unbind(args[0]);
args = Array.prototype.slice.call(args, 1);
// Until 10 args, the 'new' operator is used. With more arguments we use a
@@ -512,12 +552,16 @@ final _JS_BOOTSTRAP = r"""
// Return true if a JavaScript proxy is instance of a given type (instanceof).
function proxyInstanceof(args) {
- return deserialize(args[0]) instanceof deserialize(args[1]);
+ var obj = unbind(deserialize(args[0]));
+ var type = unbind(deserialize(args[1]));
+ return obj instanceof type;
}
// Return true if a JavaScript proxy is instance of a given type (instanceof).
function proxyDeleteProperty(args) {
- delete deserialize(args[0])[deserialize(args[1])];
+ var obj = unbind(deserialize(args[0]));
+ var member = unbind(deserialize(args[1]));
+ delete obj[member];
}
function proxyConvert(args) {
@@ -825,6 +869,19 @@ class Callback {
}
}
+// Detect unspecified arguments.
+class _Undefined {
+ const _Undefined();
+}
+const _undefined = const _Undefined();
+List _pruneUndefined(arg1, arg2, arg3, arg4, arg5, arg6) {
+ // This assumes no argument
+ final args = [arg1, arg2, arg3, arg4, arg5, arg6];
+ final index = args.indexOf(_undefined);
+ if (index < 0) return args;
+ return args.sublist(0, index);
+}
+
/**
* Proxies to JavaScript objects.
*/
@@ -837,19 +894,14 @@ class Proxy implements Serializable<Proxy> {
* JavaScript [constructor]. The arguments should be either
* primitive values, DOM elements, or Proxies.
*/
- factory Proxy(FunctionProxy constructor, [arg1, arg2, arg3, arg4]) {
- var arguments;
- if (?arg4) {
- arguments = [arg1, arg2, arg3, arg4];
- } else if (?arg3) {
- arguments = [arg1, arg2, arg3];
- } else if (?arg2) {
- arguments = [arg1, arg2];
- } else if (?arg1) {
- arguments = [arg1];
- } else {
- arguments = [];
- }
+ factory Proxy(FunctionProxy constructor,
+ [arg1 = _undefined,
+ arg2 = _undefined,
+ arg3 = _undefined,
+ arg4 = _undefined,
+ arg5 = _undefined,
+ arg6 = _undefined]) {
+ var arguments = _pruneUndefined(arg1, arg2, arg3, arg4, arg5, arg6);
return new Proxy.withArgList(constructor, arguments);
}
@@ -897,20 +949,16 @@ class Proxy implements Serializable<Proxy> {
Proxy toJs() => this;
- // TODO(vsm): This is not required in Dartium, but
- // it is in Dart2JS.
// Resolve whether this is needed.
operator[](arg) => _forward(this, '[]', 'method', [ arg ]);
- // TODO(vsm): This is not required in Dartium, but
- // it is in Dart2JS.
// Resolve whether this is needed.
operator[]=(key, value) => _forward(this, '[]=', 'method', [ key, value ]);
// Test if this is equivalent to another Proxy. This essentially
// maps to JavaScript's == operator.
// TODO(vsm): Can we avoid forwarding to JS?
- operator==(Proxy other) => identical(this, other)
+ operator==(other) => identical(this, other)
? true
: (other is Proxy &&
_jsPortEquals.callSync([_serialize(this), _serialize(other)]));
@@ -950,6 +998,10 @@ class Proxy implements Serializable<Proxy> {
} else if (member.startsWith('set:')) {
kind = 'set';
member = member.substring(4);
+ } else if (member == 'call') {
alexandre.ardhuin 2013/04/07 21:20:20 If what I have observed on minification of call is
vsm 2013/04/07 21:37:36 I'm uncomfortable making an assumptions on minifie
+ // A 'call' (probably) means that this proxy was invoked directly
+ // as if it was a function. Map this to JS function application.
+ kind = 'apply';
} else {
kind = 'method';
}
@@ -975,16 +1027,16 @@ class Proxy implements Serializable<Proxy> {
class FunctionProxy extends Proxy /*implements Function*/ {
FunctionProxy._internal(port, id) : super._internal(port, id);
- noSuchMethod(InvocationMirror invocation) {
- if (invocation.isMethod && invocation.memberName == 'call') {
- var message = [_id, '', 'apply',
- invocation.positionalArguments.map(_serialize).toList()];
- var result = _port.callSync(message);
- if (result[0] == 'throws') throw result[1];
- return _deserialize(result[1]);
- } else {
- return super.noSuchMethod(invocation);
- }
+ // TODO(vsm): This allows calls with a limited number of arguments
+ // in the context of dartbug.com/9283. Eliminate pending the resolution
+ // of this bug. Note, if this Proxy is called with more arguments then
+ // allowed below, it will trigger the 'call' path in Proxy.noSuchMethod
+ // - and still work correctly in unminified mode.
+ call([arg1 = _undefined, arg2 = _undefined,
+ arg3 = _undefined, arg4 = _undefined,
+ arg5 = _undefined, arg6 = _undefined]) {
+ var arguments = _pruneUndefined(arg1, arg2, arg3, arg4, arg5, arg6);
+ return Proxy._forward(this, '', 'apply', arguments);
}
}
« no previous file with comments | « lib/dart_interop.js ('k') | test/js/browser_tests.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698