summaryrefslogtreecommitdiffstats
path: root/js/src/tests/js1_8_5/regress/regress-577648-1.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/js1_8_5/regress/regress-577648-1.js')
-rw-r--r--js/src/tests/js1_8_5/regress/regress-577648-1.js87
1 files changed, 87 insertions, 0 deletions
diff --git a/js/src/tests/js1_8_5/regress/regress-577648-1.js b/js/src/tests/js1_8_5/regress/regress-577648-1.js
new file mode 100644
index 000000000..d40c7b0e4
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-577648-1.js
@@ -0,0 +1,87 @@
+/*
+ * 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");