Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 3b7a6bf

Browse files
author
John Messerly
committed
fixes #314, super method tear offs
[email protected] Review URL: https://codereview.chromium.org/1310513013 .
1 parent 1376b71 commit 3b7a6bf

File tree

6 files changed

+318
-5
lines changed

6 files changed

+318
-5
lines changed

lib/runtime/_classes.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,16 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
192192
/// Given an object and a method name, tear off the method.
193193
/// Sets the runtime type of the torn off method appropriately,
194194
/// and also binds the object.
195+
///
196+
/// If the optional `f` argument is passed in, it will be used as the method.
197+
/// This supports cases like `super.foo` where we need to tear off the method
198+
/// from the superclass, not from the `obj` directly.
195199
/// TODO(leafp): Consider caching the tearoff on the object?
196-
function bind(obj, name) {
197-
let f = obj[name].bind(obj);
200+
function bind(obj, name, f) {
201+
if (f === void 0) f = obj[name];
202+
f = f.bind(obj);
203+
// TODO(jmesserly): track the function's signature on the function, instead
204+
// of having to go back to the class?
198205
let sig = _getMethodType(obj, name);
199206
assert(sig);
200207
rtti.tag(f, sig);

lib/src/codegen/js_codegen.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2644,7 +2644,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
26442644
String code;
26452645
if (member != null && member is MethodElement && !isStatic) {
26462646
// Tear-off methods: explicitly bind it.
2647-
if (_requiresStaticDispatch(target, memberId.name)) {
2647+
if (target is SuperExpression) {
2648+
return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]);
2649+
} else if (_requiresStaticDispatch(target, memberId.name)) {
26482650
var type = member.type;
26492651
var clos = js.call('dart.#.bind(#)', [name, _visit(target)]);
26502652
return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]);

test/browser/language_tests.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242
]);
4343
});
4444

45+
suite('method binding', () => {
46+
dartLanguageTests([
47+
['super_bound_closure_test', 'none'],
48+
'method_binding_test'
49+
]);
50+
});
51+
4552
suite('dynamic type literal', () => {
4653
dartLanguageTests([
4754
['const_dynamic_type_literal_test', 'none', 1, 3]

test/codegen/expect/html_input.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
<script src="dev_compiler/runtime/dart/mirrors.js"></script>
2828
<script src="dev_compiler/runtime/dart/_js_mirrors.js"></script>
2929
<script src="dev_compiler/runtime/dart/js.js"></script>
30-
<script src="dir/html_input_d.js"></script>
31-
<script src="dir/html_input_b.js"></script>
3230
<script src="dir/html_input_e.js"></script>
3331
<script src="dir/html_input_c.js"></script>
32+
<script src="dir/html_input_d.js"></script>
33+
<script src="dir/html_input_b.js"></script>
3434
<script src="dir/html_input_a.js"></script>
3535
<script>dart_library.start('dir/html_input_a');</script>
3636

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import "package:expect/expect.dart";
6+
7+
// Bind a method to a variable that can be invoked as a function
8+
9+
class A {
10+
int a;
11+
12+
static var func;
13+
14+
A(this.a) { }
15+
16+
static foo() { return 4; }
17+
18+
bar() { return a; }
19+
20+
int baz() { return a; }
21+
22+
getThis() { return this.bar; }
23+
24+
getNoThis() { return bar; }
25+
26+
methodArgs(arg) { return arg + a; }
27+
28+
selfReference () { return selfReference; }
29+
30+
invokeBaz() { return (baz)(); }
31+
32+
invokeBar(var obj) { return (obj.bar)(); }
33+
34+
invokeThisBar() { return (this.bar)(); }
35+
36+
implicitStaticRef() { return foo; }
37+
}
38+
39+
class B {
40+
static foo() { return -1; }
41+
}
42+
43+
class C {
44+
C() { }
45+
var f;
46+
}
47+
48+
topLevel99() {
49+
return 99;
50+
}
51+
52+
var topFunc;
53+
54+
class D extends A {
55+
D(a): super(a) { }
56+
getSuper() { return super.bar; }
57+
}
58+
59+
class MethodBindingTest {
60+
static test() {
61+
62+
// Create closure from global
63+
Expect.equals(99, topLevel99());
64+
Function f99 = topLevel99;
65+
Expect.equals(99, f99());
66+
67+
// Invoke closure through a global
68+
topFunc = f99;
69+
Expect.equals(99, topFunc());
70+
71+
// Create closure from static method
72+
Function f4 = A.foo;
73+
Expect.equals(4, f4());
74+
75+
// Create closure from instance method
76+
var o5 = new A(5);
77+
Function f5 = o5.bar;
78+
Expect.equals(5, f5());
79+
80+
// Assign closure to field and invoke it
81+
var c = new C();
82+
c.f = () => "success";
83+
Expect.equals("success", c.f());
84+
85+
// referencing instance method with explicit 'this' qualiier
86+
var o6 = new A(6);
87+
var f6 = o6.getThis();
88+
Expect.equals(6, f6());
89+
90+
// referencing an instance method with no qualifier
91+
var o7 = new A(7);
92+
var f7 = o7.getNoThis();
93+
Expect.equals(7, f7());
94+
95+
// bind a method that takes arguments
96+
var o8 = new A(8);
97+
Function f8 = o8.methodArgs;
98+
Expect.equals(9, f8(1));
99+
100+
// Self referential method
101+
var o9 = new A(9);
102+
Function f9 = o9.selfReference;
103+
104+
// invoking a known method as if it were a bound closure...
105+
var o10 = new A(10);
106+
Expect.equals(10, o10.invokeBaz());
107+
108+
// invoking a known method as if it were a bound closure...
109+
var o11 = new A(11);
110+
Expect.equals(10, o11.invokeBar(o10));
111+
112+
// invoking a known method as if it were a bound closure...
113+
var o12 = new A(12);
114+
Expect.equals(12, o12.invokeThisBar());
115+
116+
// bind to a static variable with no explicit class qualifier
117+
var o13 = new A(13);
118+
Function f13 = o13.implicitStaticRef();
119+
Expect.equals(4, f13());
120+
121+
var o14 = new D(14);
122+
Function f14 = o14.getSuper();
123+
Expect.equals(14, f14());
124+
125+
// Assign static field to a function and invoke it.
126+
A.func = A.foo;
127+
Expect.equals(4, A.func());
128+
129+
// bind a function that is possibly native in Javascript.
130+
String o15 = 'hithere';
131+
var f15 = o15.substring;
132+
Expect.equals('i', f15(1, 2));
133+
134+
var o16 = 'hithere';
135+
var f16 = o16.substring;
136+
Expect.equals('i', f16(1, 2));
137+
138+
var f17 = 'hithere'.substring;
139+
Expect.equals('i', f17(1, 2));
140+
}
141+
142+
static testMain() {
143+
test();
144+
}
145+
}
146+
147+
main() {
148+
MethodBindingTest.testMain();
149+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import "package:expect/expect.dart";
6+
7+
class A {
8+
bar([var optional = 1]) => 498 + optional;
9+
bar2({ namedOptional: 2 }) => 40 + namedOptional;
10+
bar3(x, [var optional = 3]) => x + 498 + optional;
11+
bar4(x, { namedOptional: 4 }) => 422 + x + namedOptional;
12+
13+
// Gee is the same as bar, but we make sure that gee is used. Potentially
14+
// this yields different code if the redirecting stub exists.
15+
gee([var optional = 1]) => 498 + optional;
16+
gee2({ namedOptional: 2 }) => 40 + namedOptional;
17+
gee3(x, [var optional = 3]) => x + 498 + optional;
18+
gee4(x, { namedOptional: 4 }) => 422 + x + namedOptional;
19+
20+
// Use identifiers that could be intercepted.
21+
add([var optional = 33]) => 1234 + optional;
22+
trim({ namedOptional: 22 }) => 1313 + namedOptional;
23+
sublist(x, [optional = 44]) => 4321 + optional + x;
24+
splitMapJoin(x, { onMatch: 55, onNonMatch: 66}) =>
25+
111 + x + onMatch + onNonMatch;
26+
27+
// Other interceptable identifiers, but all of them are used.
28+
shuffle([var optional = 121]) => 12342 + optional;
29+
toList({ growable: 2233 }) => 13131 + growable;
30+
lastIndexOf(x, [optional = 424]) => 14321 + optional + x;
31+
lastWhere(x, { orElse: 555 }) => x + 1213 + 555;
32+
}
33+
34+
class B extends A {
35+
// The closure `super.bar` is invoked without the optional argument.
36+
// Dart2js must not generate a `bar$0 => bar$1(null)` closure, since that
37+
// would redirect to B's `bar$1`. Instead it must enforce that `bar$0` in
38+
// `A` redirects to A's bar$1.
39+
foo() => confuse(super.bar)();
40+
foo2() => confuse(super.bar)(2);
41+
foo3() => confuse(super.bar2)();
42+
foo4() => confuse(super.bar2)(namedOptional: 77);
43+
foo5() => confuse(super.bar3)(-3);
44+
foo6() => confuse(super.bar3)(-11, -19);
45+
foo7() => confuse(super.bar4)(0);
46+
foo8() => confuse(super.bar4)(3, namedOptional: 77);
47+
48+
fooGee() => confuse(super.gee)();
49+
fooGee2() => confuse(super.gee)(2);
50+
fooGee3() => confuse(super.gee2)();
51+
fooGee4() => confuse(super.gee2)(namedOptional: 77);
52+
fooGee5() => confuse(super.gee3)(-3);
53+
fooGee6() => confuse(super.gee3)(-11, -19);
54+
fooGee7() => confuse(super.gee4)(0);
55+
fooGee8() => confuse(super.gee4)(3, namedOptional: 77);
56+
57+
fooIntercept() => confuse(super.add)();
58+
fooIntercept2() => confuse(super.add)(2);
59+
fooIntercept3() => confuse(super.trim)();
60+
fooIntercept4() => confuse(super.trim)(namedOptional: 77);
61+
fooIntercept5() => confuse(super.sublist)(-3);
62+
fooIntercept6() => confuse(super.sublist)(-11, -19);
63+
fooIntercept7() => confuse(super.splitMapJoin)(0);
64+
fooIntercept8() => confuse(super.splitMapJoin)(3, onMatch: 77, onNonMatch: 8);
65+
66+
fooIntercept21() => confuse(super.shuffle)();
67+
fooIntercept22() => confuse(super.shuffle)(2);
68+
fooIntercept23() => confuse(super.toList)();
69+
fooIntercept24() => confuse(super.toList)(growable: 77);
70+
fooIntercept25() => confuse(super.lastIndexOf)(-3);
71+
fooIntercept26() => confuse(super.lastIndexOf)(-11, -19);
72+
fooIntercept27() => confuse(super.lastWhere)(0);
73+
fooIntercept28() => confuse(super.lastWhere)(3, orElse: 77);
74+
75+
bar([var optional]) => -1; /// 01: static type warning
76+
bar2({ namedOptional }) => -1; /// 01: continued
77+
bar3(x, [var optional]) => -1; /// 01: continued
78+
bar4(x, { namedOptional }) => -1; /// 01: continued
79+
80+
gee([var optional]) => -1; /// 01: continued
81+
gee2({ namedOptional }) => -1; /// 01: continued
82+
gee3(x, [var optional]) => -1; /// 01: continued
83+
gee4(x, { namedOptional }) => -1; /// 01: continued
84+
85+
add([var optional = 33]) => -1;
86+
trim({ namedOptional: 22 }) => -1;
87+
sublist(x, [optional = 44]) => -1;
88+
splitMapJoin(x, { onMatch: 55, onNonMatch: 66}) => -1;
89+
90+
shuffle([var optional = 121]) => -1;
91+
toList({ growable: 2233 }) => -1;
92+
lastIndexOf(x, [optional = 424]) => -1;
93+
lastWhere(x, { orElse: 555 }) => -1;
94+
}
95+
96+
confuse(x) {
97+
if (new DateTime.now().millisecondsSinceEpoch == 42) return confuse(x - 1);
98+
return x;
99+
}
100+
101+
main() {
102+
var list = [new A(), new B(), [], "foo" ];
103+
var a = list[confuse(0)];
104+
var b = list[confuse(1)];
105+
var ignored = list[confuse(2)];
106+
var ignored2 = list[confuse(3)];
107+
108+
var t = b.gee() + b.gee2() + b.gee3(9) + b.gee4(19);
109+
Expect.equals(-4, t); /// 01: continued
110+
t = b.shuffle() + b.toList() + b.lastIndexOf(1) + b.lastWhere(2);
111+
Expect.equals(-4, t);
112+
113+
Expect.equals(499, b.foo()); /// 01: continued
114+
Expect.equals(500, b.foo2()); /// 01: continued
115+
Expect.equals(42, b.foo3()); /// 01: continued
116+
Expect.equals(117, b.foo4()); /// 01: continued
117+
Expect.equals(498, b.foo5()); /// 01: continued
118+
Expect.equals(468, b.foo6()); /// 01: continued
119+
Expect.equals(426, b.foo7()); /// 01: continued
120+
Expect.equals(502, b.foo8()); /// 01: continued
121+
122+
Expect.equals(499, b.fooGee()); /// 01: continued
123+
Expect.equals(500, b.fooGee2()); /// 01: continued
124+
Expect.equals(42, b.fooGee3()); /// 01: continued
125+
Expect.equals(117, b.fooGee4()); /// 01: continued
126+
Expect.equals(498, b.fooGee5()); /// 01: continued
127+
Expect.equals(468, b.fooGee6()); /// 01: continued
128+
Expect.equals(426, b.fooGee7()); /// 01: continued
129+
Expect.equals(502, b.fooGee8()); /// 01: continued
130+
131+
Expect.equals(1267, b.fooIntercept());
132+
Expect.equals(1236, b.fooIntercept2());
133+
Expect.equals(1335, b.fooIntercept3());
134+
Expect.equals(1390, b.fooIntercept4());
135+
Expect.equals(4362, b.fooIntercept5());
136+
Expect.equals(4291, b.fooIntercept6());
137+
Expect.equals(232, b.fooIntercept7());
138+
Expect.equals(199, b.fooIntercept8());
139+
140+
Expect.equals(12463, b.fooIntercept21());
141+
Expect.equals(12344, b.fooIntercept22());
142+
Expect.equals(15364, b.fooIntercept23());
143+
Expect.equals(13208, b.fooIntercept24());
144+
Expect.equals(14742, b.fooIntercept25());
145+
Expect.equals(14291, b.fooIntercept26());
146+
Expect.equals(1768, b.fooIntercept27());
147+
Expect.equals(1771, b.fooIntercept28());
148+
}

0 commit comments

Comments
 (0)