@@ -89,6 +89,36 @@ final _JS_BOOTSTRAP = r"""
89
89
90
90
var globalContext = window;
91
91
92
+ // Support for binding the receiver (this) in proxied functions.
93
+ function bindIfFunction(f, _this) {
94
+ if (typeof(f) != "function") {
95
+ return f;
96
+ } else {
97
+ return new BoundFunction(_this, f);
98
+ }
99
+ }
100
+
101
+ function unbind(obj) {
102
+ if (obj instanceof BoundFunction) {
103
+ return obj.object;
104
+ } else {
105
+ return obj;
106
+ }
107
+ }
108
+
109
+ function getBoundThis(obj) {
110
+ if (obj instanceof BoundFunction) {
111
+ return obj._this;
112
+ } else {
113
+ return globalContext;
114
+ }
115
+ }
116
+
117
+ function BoundFunction(_this, object) {
118
+ this._this = _this;
119
+ this.object = object;
120
+ }
121
+
92
122
// Table for local objects and functions that are proxied.
93
123
function ProxiedObjectTable() {
94
124
// Name for debugging.
@@ -204,15 +234,17 @@ final _JS_BOOTSTRAP = r"""
204
234
this.port.receive(function (message) {
205
235
// TODO(vsm): Support a mechanism to register a handler here.
206
236
try {
207
- var receiver = table.get(message[0]);
237
+ var object = table.get(message[0]);
238
+ var receiver = unbind(object);
208
239
var member = message[1];
209
240
var kind = message[2];
210
241
var args = message[3].map(deserialize);
211
242
if (kind == 'get') {
212
243
// Getter.
213
244
var field = member;
214
245
if (field in receiver && args.length == 0) {
215
- return [ 'return', serialize(receiver[field]) ];
246
+ var result = bindIfFunction(receiver[field], receiver);
247
+ return [ 'return', serialize(result) ];
216
248
}
217
249
} else if (kind == 'set') {
218
250
// Setter.
@@ -222,15 +254,17 @@ final _JS_BOOTSTRAP = r"""
222
254
}
223
255
} else if (kind == 'apply') {
224
256
// Direct function invocation.
225
- // TODO(vsm): Should we capture _this_ automatically?
226
- return [ 'return', serialize(receiver.apply(null , args)) ];
257
+ var _this = getBoundThis(object);
258
+ return [ 'return', serialize(receiver.apply(_this , args)) ];
227
259
} else if (member == '[]' && args.length == 1) {
228
260
// Index getter.
229
- return [ 'return', serialize(receiver[args[0]]) ];
261
+ var result = bindIfFunction(receiver[args[0]], receiver);
262
+ return [ 'return', serialize(result) ];
230
263
} else if (member == '[]=' && args.length == 2) {
231
264
// Index setter.
232
265
return [ 'return', serialize(receiver[args[0]] = args[1]) ];
233
266
} else {
267
+ // Member function invocation.
234
268
var f = receiver[member];
235
269
if (f) {
236
270
var result = f.apply(receiver, args);
@@ -353,6 +387,12 @@ final _JS_BOOTSTRAP = r"""
353
387
} else if (message instanceof Element &&
354
388
(message.ownerDocument == null || message.ownerDocument == document)) {
355
389
return [ 'domref', serializeElement(message) ];
390
+ } else if (message instanceof BoundFunction &&
391
+ typeof(message.object) == 'function') {
392
+ // Local function proxy.
393
+ return [ 'funcref',
394
+ proxiedObjectTable.add(message),
395
+ proxiedObjectTable.sendPort ];
356
396
} else if (typeof(message) == 'function') {
357
397
if ('_dart_id' in message) {
358
398
// Remote function proxy.
@@ -443,7 +483,7 @@ final _JS_BOOTSTRAP = r"""
443
483
// serialized constructor and arguments.
444
484
function construct(args) {
445
485
args = args.map(deserialize);
446
- var constructor = args[0];
486
+ var constructor = unbind( args[0]) ;
447
487
args = Array.prototype.slice.call(args, 1);
448
488
449
489
// Until 10 args, the 'new' operator is used. With more arguments we use a
@@ -512,12 +552,16 @@ final _JS_BOOTSTRAP = r"""
512
552
513
553
// Return true if a JavaScript proxy is instance of a given type (instanceof).
514
554
function proxyInstanceof(args) {
515
- return deserialize(args[0]) instanceof deserialize(args[1]);
555
+ var obj = unbind(deserialize(args[0]));
556
+ var type = unbind(deserialize(args[1]));
557
+ return obj instanceof type;
516
558
}
517
559
518
560
// Return true if a JavaScript proxy is instance of a given type (instanceof).
519
561
function proxyDeleteProperty(args) {
520
- delete deserialize(args[0])[deserialize(args[1])];
562
+ var obj = unbind(deserialize(args[0]));
563
+ var member = unbind(deserialize(args[1]));
564
+ delete obj[member];
521
565
}
522
566
523
567
function proxyConvert(args) {
@@ -825,6 +869,19 @@ class Callback {
825
869
}
826
870
}
827
871
872
+ // Detect unspecified arguments.
873
+ class _Undefined {
874
+ const _Undefined ();
875
+ }
876
+ const _undefined = const _Undefined ();
877
+ List _pruneUndefined (arg1, arg2, arg3, arg4, arg5, arg6) {
878
+ // This assumes no argument
879
+ final args = [arg1, arg2, arg3, arg4, arg5, arg6];
880
+ final index = args.indexOf (_undefined);
881
+ if (index < 0 ) return args;
882
+ return args.sublist (0 , index);
883
+ }
884
+
828
885
/**
829
886
* Proxies to JavaScript objects.
830
887
*/
@@ -837,19 +894,14 @@ class Proxy implements Serializable<Proxy> {
837
894
* JavaScript [constructor] . The arguments should be either
838
895
* primitive values, DOM elements, or Proxies.
839
896
*/
840
- factory Proxy (FunctionProxy constructor, [arg1, arg2, arg3, arg4]) {
841
- var arguments;
842
- if (? arg4) {
843
- arguments = [arg1, arg2, arg3, arg4];
844
- } else if (? arg3) {
845
- arguments = [arg1, arg2, arg3];
846
- } else if (? arg2) {
847
- arguments = [arg1, arg2];
848
- } else if (? arg1) {
849
- arguments = [arg1];
850
- } else {
851
- arguments = [];
852
- }
897
+ factory Proxy (FunctionProxy constructor,
898
+ [arg1 = _undefined,
899
+ arg2 = _undefined,
900
+ arg3 = _undefined,
901
+ arg4 = _undefined,
902
+ arg5 = _undefined,
903
+ arg6 = _undefined]) {
904
+ var arguments = _pruneUndefined (arg1, arg2, arg3, arg4, arg5, arg6);
853
905
return new Proxy .withArgList (constructor, arguments);
854
906
}
855
907
@@ -897,20 +949,16 @@ class Proxy implements Serializable<Proxy> {
897
949
898
950
Proxy toJs () => this ;
899
951
900
- // TODO(vsm): This is not required in Dartium, but
901
- // it is in Dart2JS.
902
952
// Resolve whether this is needed.
903
953
operator [](arg) => _forward (this , '[]' , 'method' , [ arg ]);
904
954
905
- // TODO(vsm): This is not required in Dartium, but
906
- // it is in Dart2JS.
907
955
// Resolve whether this is needed.
908
956
operator []= (key, value) => _forward (this , '[]=' , 'method' , [ key, value ]);
909
957
910
958
// Test if this is equivalent to another Proxy. This essentially
911
959
// maps to JavaScript's == operator.
912
960
// TODO(vsm): Can we avoid forwarding to JS?
913
- operator == (Proxy other) => identical (this , other)
961
+ operator == (other) => identical (this , other)
914
962
? true
915
963
: (other is Proxy &&
916
964
_jsPortEquals.callSync ([_serialize (this ), _serialize (other)]));
@@ -950,6 +998,10 @@ class Proxy implements Serializable<Proxy> {
950
998
} else if (member.startsWith ('set:' )) {
951
999
kind = 'set' ;
952
1000
member = member.substring (4 );
1001
+ } else if (member == 'call' ) {
1002
+ // A 'call' (probably) means that this proxy was invoked directly
1003
+ // as if it was a function. Map this to JS function application.
1004
+ kind = 'apply' ;
953
1005
} else {
954
1006
kind = 'method' ;
955
1007
}
@@ -975,16 +1027,16 @@ class Proxy implements Serializable<Proxy> {
975
1027
class FunctionProxy extends Proxy /*implements Function*/ {
976
1028
FunctionProxy ._internal (port, id) : super ._internal (port, id);
977
1029
978
- noSuchMethod ( InvocationMirror invocation) {
979
- if (invocation.isMethod && invocation.memberName == 'call' ) {
980
- var message = [_id, '' , 'apply' ,
981
- invocation.positionalArguments. map (_serialize). toList ()];
982
- var result = _port. callSync (message);
983
- if (result[ 0 ] == 'throws' ) throw result[ 1 ];
984
- return _deserialize (result[ 1 ]);
985
- } else {
986
- return super . noSuchMethod (invocation );
987
- }
1030
+ // TODO(vsm): This allows calls with a limited number of arguments
1031
+ // in the context of dartbug.com/9283. Eliminate pending the resolution
1032
+ // of this bug. Note, if this Proxy is called with more arguments then
1033
+ // allowed below, it will trigger the 'call' path in Proxy.noSuchMethod
1034
+ // - and still work correctly in unminified mode.
1035
+ call ([arg1 = _undefined, arg2 = _undefined,
1036
+ arg3 = _undefined, arg4 = _undefined,
1037
+ arg5 = _undefined, arg6 = _undefined]) {
1038
+ var arguments = _pruneUndefined (arg1, arg2, arg3, arg4, arg5, arg6 );
1039
+ return Proxy . _forward ( this , '' , 'apply' , arguments);
988
1040
}
989
1041
}
990
1042
0 commit comments