| /* |
| * Any copyright is dedicated to the Public Domain. |
| * http://creativecommons.org/licenses/publicdomain/ |
| */ |
| |
| var count = 0; |
| |
| function testCaller(obj) { |
| switch (++count) { |
| case 1: |
| case 2: |
| /* |
| * The first two times, obj is objA. The first time, we reference |
| * arguments.callee.caller before obj.go, so the caller getter must |
| * force the joined function object in the stack frame to cross the |
| * method read barrier. The second time, obj.go has been cloned and |
| * it should match the new frame's callee from the get-go. |
| */ |
| assertEq(obj, objA); |
| break; |
| |
| case 3: { |
| assertEq(obj, objB); |
| |
| /* |
| * Store another clone of the joined function object before obj.go has |
| * been read, but after it has been invoked via objB.go(objB). |
| * |
| * In this case, arguments.callee.caller must not lie and return what |
| * is currently stored in objB.go, since that function object (objA.go) |
| * was cloned earlier, when count was 1, and it is not the function |
| * object that was truly invoked. |
| * |
| * But since the invocation of objB.go(objB) did not clone go, and the |
| * following assignment overwrote the invoked value, leaving the only |
| * reference to the joined function object for go in the stack frame's |
| * callee (argv[-2]) member, the arguments.callee.caller reference must |
| * clone a function object for the callee, store it as the callee, and |
| * return it here. |
| * |
| * It won't equal obj.go, but (implementation detail) it should have |
| * the same proto as obj.go |
| */ |
| obj.go = objA.go; |
| |
| let caller = arguments.callee.caller; |
| let obj_go = obj.go; |
| return caller != obj_go && caller.__proto__ == obj_go.__proto__; |
| } |
| |
| case 4: { |
| assertEq(obj, objC); |
| |
| let save = obj.go; |
| delete obj.go; |
| return arguments.callee.caller == save; |
| } |
| |
| case 5: { |
| assertEq(obj, objD); |
| |
| let read = obj.go; |
| break; |
| } |
| } |
| |
| return arguments.callee.caller == obj.go; |
| } |
| |
| function make() { |
| return { |
| go: function(obj) { |
| return testCaller(obj); |
| } |
| }; |
| } |
| |
| var objA = make(), |
| objB = make(), |
| objC = make(), |
| objD = make(); |
| |
| reportCompare(true, objA.go(objA), "1"); |
| reportCompare(true, objA.go(objA), "2"); |
| reportCompare(true, objB.go(objB), "3"); |
| reportCompare(true, objC.go(objC), "4"); |
| reportCompare(true, objD.go(objD), "5"); |