[DOMJIT] Implement Node::ownerDocument
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Oct 2016 03:42:13 +0000 (03:42 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Oct 2016 03:42:13 +0000 (03:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164004

Reviewed by Darin Adler.

Source/WebCore:

Test: js/dom/domjit-accessor-owner-document.html

Implement Node.ownerDocument DOMJIT accessor.
According to the result of the profiler, jQuery's prop()
function is frequently called in Ember.js SpeedoMeter.
And this function calls jQuery.isXMLDoc(). And this isXMLDoc()
function calls element.ownerDocument accessor. And our WebKit
inspector also uses ownerDocument accessor frequently.

Interesting thing is this ownerDocument functionality is used
in CSSJIT, so CSSJIT has similar helper function to look up the
owner document of the element. As a result, all the necessary
functionality is already implemented, DOMJIT just utilizes it.
For example, Node::treeScopeMemoryOffset() and
TreeScope::documentScopeMemoryOffset() is implemented before this
patch.

In the future, we will convert CSSJIT's Assembler& to CCallHelpers&
and share the code with DOMJIT[1].

[1]: https://bugs.webkit.org/show_bug.cgi?id=164006

* dom/Node.idl:
* domjit/DOMJITAbstractHeapRepository.h:
* domjit/JSNodeDOMJIT.cpp:
(WebCore::NodeOwnerDocumentDOMJIT::checkDOM):
(WebCore::NodeOwnerDocumentDOMJIT::callDOM):

LayoutTests:

* js/dom/domjit-accessor-owner-document-expected.txt: Added.
* js/dom/domjit-accessor-owner-document.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/js/dom/domjit-accessor-owner-document-expected.txt [new file with mode: 0644]
LayoutTests/js/dom/domjit-accessor-owner-document.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Node.idl
Source/WebCore/domjit/DOMJITAbstractHeapRepository.h
Source/WebCore/domjit/JSNodeDOMJIT.cpp

index 03ec024..c90cd2b 100644 (file)
@@ -1,3 +1,13 @@
+2016-10-26  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Implement Node::ownerDocument
+        https://bugs.webkit.org/show_bug.cgi?id=164004
+
+        Reviewed by Darin Adler.
+
+        * js/dom/domjit-accessor-owner-document-expected.txt: Added.
+        * js/dom/domjit-accessor-owner-document.html: Added.
+
 2016-10-26  Chris Dumez  <cdumez@apple.com>
 
         Replace IDBKeyPath with a WTF::Variant
diff --git a/LayoutTests/js/dom/domjit-accessor-owner-document-expected.txt b/LayoutTests/js/dom/domjit-accessor-owner-document-expected.txt
new file mode 100644 (file)
index 0000000..0b3b962
--- /dev/null
@@ -0,0 +1,109 @@
+Test DOMJIT nodeType accessor works.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS (
+            function testElement(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testAttr(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testText(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testCDATA(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testProcessingInstruction(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testComment(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testDocument(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testXMLDocument(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testDocumentType(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+PASS (
+            function testDocumentFragment(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        )(target, result) is true
+
diff --git a/LayoutTests/js/dom/domjit-accessor-owner-document.html b/LayoutTests/js/dom/domjit-accessor-owner-document.html
new file mode 100644 (file)
index 0000000..18ff909
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<iframe id="xmlframe" onload="frameLoaded()" style="height:0px" src="data:application/xhtml+xml,<?xml version='1.0' encoding='UTF-8'?><body/>"></iframe>
+<script>
+description('Test DOMJIT nodeType accessor works.');
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+var target = null;
+var result = null;
+function runTest()
+{
+    var xmlDocument = document.getElementById('xmlframe').contentDocument;
+    var targets = [
+        ['Element', document.body, document],
+        ['Attr', document.createAttribute('Cocoa'), document],
+        ['Text', document.createTextNode('Cocoa'), document],
+        ['CDATA', xmlDocument.createCDATASection('test'), xmlDocument],
+        ['ProcessingInstruction', xmlDocument.createProcessingInstruction('target', 'test'), xmlDocument],
+        ['Comment', document.createComment('Cocoa'), document],
+        ['Document', document, null],
+        ['XMLDocument', xmlDocument, null],
+        ['DocumentType', document.doctype, document],
+        ['DocumentFragment', document.createDocumentFragment(), document],
+    ];
+
+    for ([name, target, result] of targets) {
+        var text = `
+            function test${name}(element, result)
+            {
+                for (var i = 0; i < 1e4; ++i) {
+                    if (element.ownerDocument !== result)
+                        return false;
+                }
+                return true;
+            }
+        `;
+        shouldBeTrue(`(${text})(target, result)`);
+    }
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+function frameLoaded()
+{
+    runTest();
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index fde56b9..4e2f70d 100644 (file)
@@ -1,3 +1,38 @@
+2016-10-26  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Implement Node::ownerDocument
+        https://bugs.webkit.org/show_bug.cgi?id=164004
+
+        Reviewed by Darin Adler.
+
+        Test: js/dom/domjit-accessor-owner-document.html
+
+        Implement Node.ownerDocument DOMJIT accessor.
+        According to the result of the profiler, jQuery's prop()
+        function is frequently called in Ember.js SpeedoMeter.
+        And this function calls jQuery.isXMLDoc(). And this isXMLDoc()
+        function calls element.ownerDocument accessor. And our WebKit
+        inspector also uses ownerDocument accessor frequently.
+
+        Interesting thing is this ownerDocument functionality is used
+        in CSSJIT, so CSSJIT has similar helper function to look up the
+        owner document of the element. As a result, all the necessary
+        functionality is already implemented, DOMJIT just utilizes it.
+        For example, Node::treeScopeMemoryOffset() and
+        TreeScope::documentScopeMemoryOffset() is implemented before this
+        patch.
+
+        In the future, we will convert CSSJIT's Assembler& to CCallHelpers&
+        and share the code with DOMJIT[1].
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=164006
+
+        * dom/Node.idl:
+        * domjit/DOMJITAbstractHeapRepository.h:
+        * domjit/JSNodeDOMJIT.cpp:
+        (WebCore::NodeOwnerDocumentDOMJIT::checkDOM):
+        (WebCore::NodeOwnerDocumentDOMJIT::callDOM):
+
 2016-10-26  Chris Dumez  <cdumez@apple.com>
 
         Replace IDBKeyPath with a WTF::Variant
index 58fe6de..d83f9d3 100644 (file)
@@ -53,7 +53,7 @@
     [DOMJIT] readonly attribute Node? lastChild;
     [DOMJIT] readonly attribute Node? previousSibling;
     [DOMJIT] readonly attribute Node? nextSibling;
-    readonly attribute Document? ownerDocument;
+    [DOMJIT] readonly attribute Document? ownerDocument;
 
     [CEReactions, Custom, MayThrowLegacyException] Node insertBefore(Node newChild, Node? refChild);
     [CEReactions, Custom, MayThrowLegacyException] Node replaceChild(Node newChild, Node oldChild);
index 6893d88..e9d0dd9 100644 (file)
@@ -42,6 +42,7 @@ namespace WebCore { namespace DOMJIT {
     V(Node_parentNode, Node) \
     V(Node_nextSibling, Node) \
     V(Node_previousSibling, Node) \
+    V(Node_ownerDocument, Node) \
 
 
 class AbstractHeapRepository {
index 007289f..46ec95b 100644 (file)
@@ -96,7 +96,6 @@ static Ref<JSC::DOMJIT::Patchpoint> checkNode()
     return patchpoint;
 }
 
-// Node#firstChild.
 Ref<JSC::DOMJIT::Patchpoint> NodeFirstChildDOMJIT::checkDOM()
 {
     return checkNode();
@@ -110,7 +109,6 @@ Ref<JSC::DOMJIT::CallDOMPatchpoint> NodeFirstChildDOMJIT::callDOM()
     return patchpoint;
 }
 
-// Node#lastChild.
 Ref<JSC::DOMJIT::Patchpoint> NodeLastChildDOMJIT::checkDOM()
 {
     return checkNode();
@@ -124,7 +122,6 @@ Ref<JSC::DOMJIT::CallDOMPatchpoint> NodeLastChildDOMJIT::callDOM()
     return patchpoint;
 }
 
-// Node#nextSibling.
 Ref<JSC::DOMJIT::Patchpoint> NodeNextSiblingDOMJIT::checkDOM()
 {
     return checkNode();
@@ -138,7 +135,6 @@ Ref<JSC::DOMJIT::CallDOMPatchpoint> NodeNextSiblingDOMJIT::callDOM()
     return patchpoint;
 }
 
-// Node#previousSibling.
 Ref<JSC::DOMJIT::Patchpoint> NodePreviousSiblingDOMJIT::checkDOM()
 {
     return checkNode();
@@ -152,7 +148,6 @@ Ref<JSC::DOMJIT::CallDOMPatchpoint> NodePreviousSiblingDOMJIT::callDOM()
     return patchpoint;
 }
 
-// Node#parentNode.
 Ref<JSC::DOMJIT::Patchpoint> NodeParentNodeDOMJIT::checkDOM()
 {
     return checkNode();
@@ -166,7 +161,6 @@ Ref<JSC::DOMJIT::CallDOMPatchpoint> NodeParentNodeDOMJIT::callDOM()
     return patchpoint;
 }
 
-// Node#nodeType.
 Ref<JSC::DOMJIT::Patchpoint> NodeNodeTypeDOMJIT::checkDOM()
 {
     return checkNode();
@@ -188,6 +182,41 @@ Ref<JSC::DOMJIT::CallDOMPatchpoint> NodeNodeTypeDOMJIT::callDOM()
     return patchpoint;
 }
 
+Ref<JSC::DOMJIT::Patchpoint> NodeOwnerDocumentDOMJIT::checkDOM()
+{
+    return checkNode();
+}
+
+Ref<JSC::DOMJIT::CallDOMPatchpoint> NodeOwnerDocumentDOMJIT::callDOM()
+{
+    const auto& heap = DOMJIT::AbstractHeapRepository::shared();
+    Ref<JSC::DOMJIT::CallDOMPatchpoint> patchpoint = JSC::DOMJIT::CallDOMPatchpoint::create();
+    patchpoint->numGPScratchRegisters = 1;
+    patchpoint->setGenerator([=](CCallHelpers& jit, JSC::DOMJIT::PatchpointParams& params) {
+        JSValueRegs result = params[0].jsValueRegs();
+        GPRReg node = params[1].gpr();
+        GPRReg globalObject = params[2].gpr();
+        JSValue globalObjectValue = params[2].value();
+        GPRReg scratch = params.gpScratch(0);
+
+        // If the given node is a Document, Node#ownerDocument returns null.
+        auto notDocument = DOMJIT::branchIfNotDocumentWrapper(jit, node);
+        jit.moveValue(jsNull(), result);
+        auto done = jit.jump();
+
+        notDocument.link(&jit);
+        jit.loadPtr(CCallHelpers::Address(node, JSNode::offsetOfWrapped()), scratch);
+        jit.loadPtr(CCallHelpers::Address(scratch, Node::treeScopeMemoryOffset()), scratch);
+        jit.loadPtr(CCallHelpers::Address(scratch, TreeScope::documentScopeMemoryOffset()), scratch);
+
+        DOMJIT::toWrapper<Document>(jit, params, scratch, globalObject, result, toWrapperSlow<Document>, globalObjectValue);
+        done.link(&jit);
+        return CCallHelpers::JumpList();
+    });
+    patchpoint->effect = JSC::DOMJIT::Effect::forDef(heap.Node_ownerDocument);
+    return patchpoint;
+}
+
 }
 
 #endif