Bug 602139: Watchpoint tests. r=jorendorff
authorJim Blandy <jimb@mozilla.com>
Tue, 09 Nov 2010 15:04:11 -0800
changeset 57750 c56444630bb0aaecee74700b966951f71cbca93c
parent 57749 049145d64214b6a8c16598357e46cfb3077322eb
child 57751 94543969fd6d48d57019aa3ee5a28a90048497bc
push id17032
push userrsayre@mozilla.com
push dateWed, 17 Nov 2010 21:55:39 +0000
treeherdermozilla-central@78a42f77bb90 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs602139
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 602139: Watchpoint tests. r=jorendorff This includes: - a test showing how adding and deleting watchpoints can lose a property's JSPropertyOp setter; - tests for watchpoints on properties that change from setters to value properties and vice versa, or from one setter to another; and - tests for watchpoints set on inherited setter properties.
js/src/tests/ecma_5/extensions/jstests.list
js/src/tests/ecma_5/extensions/watch-inherited-property.js
js/src/tests/ecma_5/extensions/watch-replaced-setter.js
js/src/tests/ecma_5/extensions/watch-setter-become-setter.js
js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js
js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js
--- a/js/src/tests/ecma_5/extensions/jstests.list
+++ b/js/src/tests/ecma_5/extensions/jstests.list
@@ -2,10 +2,15 @@ url-prefix ../../jsreftest.html?test=ecm
 script 15.4.4.11.js
 script 8.12.5-01.js
 script Boolean-toSource.js
 script Number-toSource.js
 script String-toSource.js
 script proxy-strict.js
 script regress-bug567606.js
 script string-literal-getter-setter-decompilation.js
+script watch-inherited-property.js
+script watch-replaced-setter.js
+script watch-setter-become-setter.js
+script watch-value-prop-becoming-setter.js
+script watchpoint-deletes-JSPropertyOp-setter.js
 script eval-native-callback-is-indirect.js
 script regress-bug607284.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/watch-inherited-property.js
@@ -0,0 +1,38 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+/* Create a prototype object with a setter property. */
+var protoSetterCount;
+var proto = ({ set x(v) { protoSetterCount++; } });
+
+/* Put a watchpoint on that setter. */
+var protoWatchCount;
+proto.watch('x', function() { protoWatchCount++; });
+
+/* Make an object with the above as its prototype. */
+function C() { }
+C.prototype = proto;
+var o = new C();
+
+/*
+ * Set a watchpoint on the property in the inheriting object. We have
+ * defined this to mean "duplicate the property, setter and all, in the
+ * inheriting object." I don't think debugging observation mechanisms
+ * should mutate the program being run, but that's what we've got.
+ */
+var oWatchCount;
+o.watch('x', function() { oWatchCount++; });
+
+/*
+ * Assign to the property. This should trip the watchpoint on the inheriting object and
+ * the setter.
+ */
+protoSetterCount = protoWatchCount = oWatchCount = 0;
+o.x = 1;
+assertEq(protoWatchCount, 0);
+assertEq(oWatchCount, 1);
+assertEq(protoSetterCount, 1);
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/watch-replaced-setter.js
@@ -0,0 +1,46 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+/* A stock watcher function. */
+var watcherCount;
+function watcher(id, oldval, newval) { watcherCount++; return newval; }
+
+/* Create an object with a JavaScript setter. */
+var setterCount;
+var o = { w:2, set x(v) { setterCount++; } };
+
+/*
+ * Put the object in dictionary mode, so that JSObject::putProperty will 
+ * mutate its shapes instead of creating new ones.
+ */
+delete o.w;
+
+/*
+ * Place a watchpoint on the property. The watchpoint structure holds the
+ * original JavaScript setter, and a pointer to the shape.
+ */
+o.watch('x', watcher);
+
+/*
+ * Replace the accessor property with a value property. The shape's setter
+ * should become a non-JS setter, js_watch_set, and the watchpoint
+ * structure's saved setter should be updated (in this case, cleared).
+ */
+Object.defineProperty(o, 'x', { value:3,
+                                writable:true,
+                                enumerable:true,
+                                configurable:true });
+
+/*
+ * Assign to the property. This should trigger js_watch_set, which should
+ * call the handler, and then see that there is no JS-level setter to pass
+ * control on to, and return.
+ */
+watcherCount = setterCount = 0;
+o.x = 3;
+assertEq(watcherCount, 1);
+assertEq(setterCount, 0);
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js
@@ -0,0 +1,44 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+/* Create an object with a JavaScript setter. */
+var firstSetterCount;
+var o = { w:2, set x(v) { firstSetterCount++; } };
+
+/*
+ * Put the object in dictionary mode, so that JSObject::putProperty will 
+ * mutate its shapes instead of creating new ones.
+ */
+delete o.w;
+
+/* A stock watcher function. */
+var watcherCount;
+function watcher(id, oldval, newval) { watcherCount++; return newval; }
+
+/*
+ * Place a watchpoint on the property. The property's shape now has the
+ * watchpoint setter, with the original setter saved in the watchpoint
+ * structure.
+ */
+o.watch('x', watcher);
+
+/*
+ * Replace the setter with a new setter. The shape should get updated to
+ * refer to the new setter, and then the watchpoint setter should be
+ * re-established.
+ */
+var secondSetterCount;
+Object.defineProperty(o, 'x', { set: function () { secondSetterCount++ } });
+
+/*
+ * Assign to the property. This should trigger the watchpoint and the new setter.
+ */
+watcherCount = firstSetterCount = secondSetterCount = 0;
+o.x = 3;
+assertEq(watcherCount, 1);
+assertEq(firstSetterCount, 0);
+assertEq(secondSetterCount, 1);
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js
@@ -0,0 +1,43 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+/* A stock watcher function. */
+var watcherCount;
+function watcher(id, old, newval) {
+    watcherCount++;
+    return newval; 
+}
+
+/* Create an object with a value property. */
+var o = { w:2, x:3 };
+
+/*
+ * Place a watchpoint on the value property. The watchpoint structure holds
+ * the original JavaScript setter, and a pointer to the shape.
+ */
+o.watch('x', watcher);
+
+/*
+ * Put the object in dictionary mode, so that JSObject::putProperty will 
+ * mutate its shapes instead of creating new ones.
+ */
+delete o.w;
+
+/*
+ * Replace the value property with a setter.
+ */
+var setterCount;
+o.__defineSetter__('x', function() { setterCount++; });
+
+/*
+ * Trigger the watchpoint. The watchpoint handler should run, and then the
+ * setter should run.
+ */
+watcherCount = setterCount = 0;
+o.x = 4;
+assertEq(watcherCount, 1);
+assertEq(setterCount, 1);
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js
@@ -0,0 +1,56 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+function make_watcher(name) {
+    return function (id, oldv, newv) {
+        print("watched " + name + "[0]");
+    };
+}
+
+var o, p;
+function f(flag) {
+    if (flag) {
+        o = arguments;
+    } else {
+        p = arguments;
+        o.watch(0, make_watcher('o'));
+        p.watch(0, make_watcher('p'));
+
+        /*
+         * Previously, the watchpoint implementation actually substituted its magic setter
+         * functions for the setters of shared shapes, and then 1) carefully ignored calls
+         * to its magic setter from unrelated objects, and 2) avoided restoring the
+         * original setter until all watchpoints on that shape had been removed.
+         * 
+         * However, when the watchpoint code began using JSObject::changeProperty and
+         * js_ChangeNativePropertyAttrs to change shapes' setters, the shape tree code
+         * became conscious of the presence of watchpoints, and shared shapes between
+         * objects only when their watchpoint nature coincided. Clearing the magic setter
+         * from one object's shape would not affect other objects, because the
+         * watchpointed and non-watchpointed shapes were distinct if they were shared.
+         * 
+         * Thus, the first unwatch call must go ahead and fix p's shape, even though a
+         * watchpoint exists on the same shape in o. o's watchpoint's presence shouldn't
+         * cause 'unwatch' to leave p's magic setter in place.
+         */
+
+        /* DropWatchPointAndUnlock would see o's watchpoint, and not change p's property. */
+        p.unwatch(0);
+
+        /* DropWatchPointAndUnlock would fix o's property, but not p's; p's setter would be gone. */
+        o.unwatch(0);
+
+        /* This would fail to invoke the arguments object's setter. */
+        p[0] = 4;
+
+        /* And the formal parameter would not get updated. */
+        assertEq(flag, 4);
+    }
+}
+
+f(true);
+f(false);
+
+reportCompare(true, true);