|
1 /* |
|
2 * Any copyright is dedicated to the Public Domain. |
|
3 * http://creativecommons.org/licenses/publicdomain/ |
|
4 */ |
|
5 |
|
6 var count = 0; |
|
7 |
|
8 function testCaller(obj) { |
|
9 switch (++count) { |
|
10 case 1: |
|
11 case 2: |
|
12 /* |
|
13 * The first two times, obj is objA. The first time, we reference |
|
14 * arguments.callee.caller before obj.go, so the caller getter must |
|
15 * force the joined function object in the stack frame to cross the |
|
16 * method read barrier. The second time, obj.go has been cloned and |
|
17 * it should match the new frame's callee from the get-go. |
|
18 */ |
|
19 assertEq(obj, objA); |
|
20 break; |
|
21 |
|
22 case 3: { |
|
23 assertEq(obj, objB); |
|
24 |
|
25 /* |
|
26 * Store another clone of the joined function object before obj.go has |
|
27 * been read, but after it has been invoked via objB.go(objB). |
|
28 * |
|
29 * In this case, arguments.callee.caller must not lie and return what |
|
30 * is currently stored in objB.go, since that function object (objA.go) |
|
31 * was cloned earlier, when count was 1, and it is not the function |
|
32 * object that was truly invoked. |
|
33 * |
|
34 * But since the invocation of objB.go(objB) did not clone go, and the |
|
35 * following assignment overwrote the invoked value, leaving the only |
|
36 * reference to the joined function object for go in the stack frame's |
|
37 * callee (argv[-2]) member, the arguments.callee.caller reference must |
|
38 * clone a function object for the callee, store it as the callee, and |
|
39 * return it here. |
|
40 * |
|
41 * It won't equal obj.go, but (implementation detail) it should have |
|
42 * the same proto as obj.go |
|
43 */ |
|
44 obj.go = objA.go; |
|
45 |
|
46 let caller = arguments.callee.caller; |
|
47 let obj_go = obj.go; |
|
48 return caller != obj_go && caller.__proto__ == obj_go.__proto__; |
|
49 } |
|
50 |
|
51 case 4: { |
|
52 assertEq(obj, objC); |
|
53 |
|
54 let save = obj.go; |
|
55 delete obj.go; |
|
56 return arguments.callee.caller == save; |
|
57 } |
|
58 |
|
59 case 5: { |
|
60 assertEq(obj, objD); |
|
61 |
|
62 let read = obj.go; |
|
63 break; |
|
64 } |
|
65 } |
|
66 |
|
67 return arguments.callee.caller == obj.go; |
|
68 } |
|
69 |
|
70 function make() { |
|
71 return { |
|
72 go: function(obj) { |
|
73 return testCaller(obj); |
|
74 } |
|
75 }; |
|
76 } |
|
77 |
|
78 var objA = make(), |
|
79 objB = make(), |
|
80 objC = make(), |
|
81 objD = make(); |
|
82 |
|
83 reportCompare(true, objA.go(objA), "1"); |
|
84 reportCompare(true, objA.go(objA), "2"); |
|
85 reportCompare(true, objB.go(objB), "3"); |
|
86 reportCompare(true, objC.go(objC), "4"); |
|
87 reportCompare(true, objD.go(objD), "5"); |