Named lookups on HTML documents produce inconsistent results in JavaScriptCore bindings
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Dec 2012 03:24:28 +0000 (03:24 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Dec 2012 03:24:28 +0000 (03:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=104623

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Add the notion of objects that HasImpureGetOwnPropertySlot, and use that to inhibit prototype chain caching
in some cases. This appears to be perf-neutral on benchmarks that we track.

* dfg/DFGRepatch.cpp:
(JSC::DFG::tryCacheGetByID):
(JSC::DFG::tryBuildGetByIDProtoList):
* jit/JITStubs.cpp:
(JSC::JITThunks::tryCacheGetByID):
(JSC::DEFINE_STUB_FUNCTION):
* runtime/JSTypeInfo.h:
(JSC):
(JSC::TypeInfo::hasImpureGetOwnPropertySlot):
* runtime/Operations.h:
(JSC::normalizePrototypeChainForChainAccess):

Source/WebCore:

All DOM objects that have named getters or directly override getOwnPropertySlot are now marked as
HasImpureGetOwnPropertySlot.

Tests: fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps
       fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps

* bindings/scripts/CodeGeneratorJS.pm:
(GenerateHeader):

LayoutTests:

* fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt: Added.
* fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.html: Added.
* fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt: Added.
* fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps.html: Added.
* fast/js/script-tests/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.js: Added.
(f):
* fast/js/script-tests/prototype-chain-caching-with-impure-get-own-property-slot-traps.js: Added.
(f):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@137700 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.html [new file with mode: 0644]
LayoutTests/fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps.html [new file with mode: 0644]
LayoutTests/fast/js/script-tests/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.js [new file with mode: 0644]
LayoutTests/fast/js/script-tests/prototype-chain-caching-with-impure-get-own-property-slot-traps.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGRepatch.cpp
Source/JavaScriptCore/jit/JITStubs.cpp
Source/JavaScriptCore/runtime/JSTypeInfo.h
Source/JavaScriptCore/runtime/Operations.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm

index b0795b9..ae416ef 100644 (file)
@@ -1,3 +1,19 @@
+2012-12-13  Filip Pizlo  <fpizlo@apple.com>
+
+        Named lookups on HTML documents produce inconsistent results in JavaScriptCore bindings
+        https://bugs.webkit.org/show_bug.cgi?id=104623
+
+        Reviewed by Geoffrey Garen.
+
+        * fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt: Added.
+        * fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.html: Added.
+        * fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt: Added.
+        * fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps.html: Added.
+        * fast/js/script-tests/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.js: Added.
+        (f):
+        * fast/js/script-tests/prototype-chain-caching-with-impure-get-own-property-slot-traps.js: Added.
+        (f):
+
 2012-12-13  Noel Gordon  <noel.gordon@gmail.com>
 
         [chromium] Unreviewed gardening.
diff --git a/LayoutTests/fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt b/LayoutTests/fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt
new file mode 100644 (file)
index 0000000..450392c
--- /dev/null
@@ -0,0 +1,409 @@
+Tests what happens when you make prototype chain accesses with impure GetOwnPropertySlot traps in the way.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.html b/LayoutTests/fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.html
new file mode 100644 (file)
index 0000000..ad38bbd
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="script-tests/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt b/LayoutTests/fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps-expected.txt
new file mode 100644 (file)
index 0000000..5e521d3
--- /dev/null
@@ -0,0 +1,49 @@
+Tests what happens when you make prototype chain accesses with impure GetOwnPropertySlot traps in the way.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "function"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS typeof f() is "object"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps.html b/LayoutTests/fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps.html
new file mode 100644 (file)
index 0000000..c8e39e2
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="script-tests/prototype-chain-caching-with-impure-get-own-property-slot-traps.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/js/script-tests/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.js b/LayoutTests/fast/js/script-tests/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps.js
new file mode 100644 (file)
index 0000000..1c0793e
--- /dev/null
@@ -0,0 +1,21 @@
+description(
+"Tests what happens when you make prototype chain accesses with impure GetOwnPropertySlot traps in the way."
+);
+
+var doc = document;
+
+function f() {
+    return doc.getElementsByTagName;
+}
+
+var expected = "\"function\"";
+for (var i = 0; i < 400; ++i) {
+    if (i == 350) {
+        var img = new Image();
+        img.name = "getElementsByTagName";
+        document.body.appendChild(img);
+        expected = "\"object\"";
+    }
+    shouldBe("typeof f()", expected);
+}
+
diff --git a/LayoutTests/fast/js/script-tests/prototype-chain-caching-with-impure-get-own-property-slot-traps.js b/LayoutTests/fast/js/script-tests/prototype-chain-caching-with-impure-get-own-property-slot-traps.js
new file mode 100644 (file)
index 0000000..b419161
--- /dev/null
@@ -0,0 +1,21 @@
+description(
+"Tests what happens when you make prototype chain accesses with impure GetOwnPropertySlot traps in the way."
+);
+
+var doc = document;
+
+function f() {
+    return doc.getElementsByTagName;
+}
+
+var expected = "\"function\"";
+for (var i = 0; i < 40; ++i) {
+    if (i == 35) {
+        var img = new Image();
+        img.name = "getElementsByTagName";
+        document.body.appendChild(img);
+        expected = "\"object\"";
+    }
+    shouldBe("typeof f()", expected);
+}
+
index d53045e..386b595 100644 (file)
@@ -1,5 +1,27 @@
 2012-12-13  Filip Pizlo  <fpizlo@apple.com>
 
+        Named lookups on HTML documents produce inconsistent results in JavaScriptCore bindings
+        https://bugs.webkit.org/show_bug.cgi?id=104623
+
+        Reviewed by Geoffrey Garen.
+
+        Add the notion of objects that HasImpureGetOwnPropertySlot, and use that to inhibit prototype chain caching
+        in some cases. This appears to be perf-neutral on benchmarks that we track.
+
+        * dfg/DFGRepatch.cpp:
+        (JSC::DFG::tryCacheGetByID):
+        (JSC::DFG::tryBuildGetByIDProtoList):
+        * jit/JITStubs.cpp:
+        (JSC::JITThunks::tryCacheGetByID):
+        (JSC::DEFINE_STUB_FUNCTION):
+        * runtime/JSTypeInfo.h:
+        (JSC):
+        (JSC::TypeInfo::hasImpureGetOwnPropertySlot):
+        * runtime/Operations.h:
+        (JSC::normalizePrototypeChainForChainAccess):
+
+2012-12-13  Filip Pizlo  <fpizlo@apple.com>
+
         Unreviewed, roll out http://trac.webkit.org/changeset/137683.
         It broke gmail.
 
index 07a5090..9a7e7df 100644 (file)
@@ -341,7 +341,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier
         return false;
     
     PropertyOffset offset = slot.cachedOffset();
-    size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset);
+    size_t count = normalizePrototypeChainForChainAccess(exec, baseValue, slot.slotBase(), propertyName, offset);
     if (count == InvalidPrototypeChain)
         return false;
 
@@ -569,7 +569,7 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I
     ASSERT(slot.slotBase().isObject());
     
     PropertyOffset offset = slot.cachedOffset();
-    size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset);
+    size_t count = normalizePrototypeChainForChainAccess(exec, baseValue, slot.slotBase(), propertyName, offset);
     if (count == InvalidPrototypeChain)
         return false;
 
index 6a92823..ad058ae 100644 (file)
@@ -908,6 +908,7 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
 
     // Uncacheable: give up.
     if (!slot.isCacheable()) {
+        stubInfo->accessType = access_get_by_id_generic;
         ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
         return;
     }
@@ -916,6 +917,7 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
     Structure* structure = baseCell->structure();
 
     if (structure->isUncacheableDictionary() || structure->typeInfo().prohibitsPropertyCaching()) {
+        stubInfo->accessType = access_get_by_id_generic;
         ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
         return;
     }
@@ -934,6 +936,7 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
     }
 
     if (structure->isDictionary()) {
+        stubInfo->accessType = access_get_by_id_generic;
         ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
         return;
     }
@@ -943,6 +946,12 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
         
         JSObject* slotBaseObject = asObject(slot.slotBase());
         size_t offset = slot.cachedOffset();
+
+        if (structure->typeInfo().hasImpureGetOwnPropertySlot()) {
+            stubInfo->accessType = access_get_by_id_generic;
+            ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
+            return;
+        }
         
         // Since we're accessing a prototype in a loop, it's a good bet that it
         // should not be treated as a dictionary.
@@ -960,9 +969,10 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
     }
 
     PropertyOffset offset = slot.cachedOffset();
-    size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase(), propertyName, offset);
+    size_t count = normalizePrototypeChainForChainAccess(callFrame, baseValue, slot.slotBase(), propertyName, offset);
     if (count == InvalidPrototypeChain) {
         stubInfo->accessType = access_get_by_id_generic;
+        ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
         return;
     }
 
@@ -1689,6 +1699,12 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list)
         ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail));
     else if (slot.slotBase() == baseValue.asCell()->structure()->prototypeForLookup(callFrame)) {
         ASSERT(!baseValue.asCell()->structure()->isDictionary());
+        
+        if (baseValue.asCell()->structure()->typeInfo().hasImpureGetOwnPropertySlot()) {
+            ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail));
+            return JSValue::encode(result);
+        }
+        
         // Since we're accessing a prototype in a loop, it's a good bet that it
         // should not be treated as a dictionary.
         if (slotBaseObject->structure()->isDictionary()) {
@@ -1705,7 +1721,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list)
                 ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_list_full));
         }
     } else {
-        size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase(), propertyName, offset);
+        size_t count = normalizePrototypeChainForChainAccess(callFrame, baseValue, slot.slotBase(), propertyName, offset);
         if (count == InvalidPrototypeChain) {
             ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail));
             return JSValue::encode(result);
index 6f63260..07dd0c9 100644 (file)
@@ -46,6 +46,7 @@ namespace JSC {
     static const unsigned OverridesVisitChildren = 1 << 7;
     static const unsigned OverridesGetPropertyNames = 1 << 8;
     static const unsigned ProhibitsPropertyCaching = 1 << 9;
+    static const unsigned HasImpureGetOwnPropertySlot = 1 << 10;
 
     class TypeInfo {
     public:
@@ -80,6 +81,7 @@ namespace JSC {
         bool overridesVisitChildren() const { return isSetOnFlags1(OverridesVisitChildren); }
         bool overridesGetPropertyNames() const { return isSetOnFlags2(OverridesGetPropertyNames); }
         bool prohibitsPropertyCaching() const { return isSetOnFlags2(ProhibitsPropertyCaching); }
+        bool hasImpureGetOwnPropertySlot() const { return isSetOnFlags2(HasImpureGetOwnPropertySlot); }
 
         static ptrdiff_t flagsOffset()
         {
index 7301bf6..8e0a0a3 100644 (file)
@@ -302,15 +302,18 @@ namespace JSC {
 
 #define InvalidPrototypeChain (std::numeric_limits<size_t>::max())
 
-    inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, PropertyOffset& slotOffset)
+    inline size_t normalizePrototypeChainForChainAccess(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, PropertyOffset& slotOffset)
     {
         JSCell* cell = base.asCell();
         size_t count = 0;
-
+        
         while (slotBase != cell) {
             if (cell->isProxy())
                 return InvalidPrototypeChain;
             
+            if (cell->structure()->typeInfo().hasImpureGetOwnPropertySlot())
+                return InvalidPrototypeChain;
+            
             JSValue v = cell->structure()->prototypeForLookup(callFrame);
 
             // If we didn't find slotBase in base's prototype chain, then base
@@ -328,7 +331,7 @@ namespace JSC {
                 if (slotBase == cell)
                     slotOffset = cell->structure()->get(callFrame->globalData(), propertyName); 
             }
-
+            
             ++count;
         }
         
index 9709361..00a3e4c 100644 (file)
@@ -1,3 +1,19 @@
+2012-12-13  Filip Pizlo  <fpizlo@apple.com>
+
+        Named lookups on HTML documents produce inconsistent results in JavaScriptCore bindings
+        https://bugs.webkit.org/show_bug.cgi?id=104623
+
+        Reviewed by Geoffrey Garen.
+
+        All DOM objects that have named getters or directly override getOwnPropertySlot are now marked as
+        HasImpureGetOwnPropertySlot.
+
+        Tests: fast/js/prototype-chain-caching-with-impure-get-own-property-slot-traps
+               fast/js/dfg-prototype-chain-caching-with-impure-get-own-property-slot-traps
+
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GenerateHeader):
+
 2012-12-13  Xianzhu Wang  <wangxianzhu@chromium.org>
 
         Out-of-view check of fixed position element in frame is incorrect when page is scaled
index 373a9ff..10e90c4 100644 (file)
@@ -784,16 +784,23 @@ sub GenerateHeader
 
     $implIncludes{"${className}Custom.h"} = 1 if !$interface->extendedAttributes->{"JSCustomHeader"} && ($interface->extendedAttributes->{"CustomPutFunction"} || $interface->extendedAttributes->{"CustomNamedSetter"});
 
+    my $hasImpureNamedGetter =
+        $interface->extendedAttributes->{"NamedGetter"}
+        || $interface->extendedAttributes->{"CustomNamedGetter"}
+        || $interface->extendedAttributes->{"CustomGetOwnPropertySlot"};
+
     my $hasComplexGetter =
         $interface->extendedAttributes->{"IndexedGetter"}
         || $interface->extendedAttributes->{"NumericIndexedGetter"}
-        || $interface->extendedAttributes->{"CustomGetOwnPropertySlot"}
         || $interface->extendedAttributes->{"JSCustomGetOwnPropertySlotAndDescriptor"}
-        || $interface->extendedAttributes->{"NamedGetter"}
-        || $interface->extendedAttributes->{"CustomNamedGetter"};
+        || $hasImpureNamedGetter;
     
     my $hasGetter = $numAttributes > 0 || !$interface->extendedAttributes->{"OmitConstructor"} || $hasComplexGetter;
 
+    if ($hasImpureNamedGetter) {
+        $structureFlags{"JSC::HasImpureGetOwnPropertySlot"} = 1;
+    }
+
     # Getters
     if ($hasGetter) {
         push(@headerContent, "    static bool getOwnPropertySlot(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n");