<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[167963] trunk/Source</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/167963">167963</a></dd>
<dt>Author</dt> <dd>mhahnenberg@apple.com</dd>
<dt>Date</dt> <dd>2014-04-29 15:21:04 -0700 (Tue, 29 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>JSProxies should be cacheable
https://bugs.webkit.org/show_bug.cgi?id=132351

Reviewed by Geoffrey Garen.


Source/JavaScriptCore: 
Whenever we encounter a proxy in an inline cache we should try to cache on the 
proxy's target instead of giving up.

This patch adds support for a simple &quot;recursive&quot; inline cache if the base object
we're accessing is a pure forwarding proxy. JSGlobalObject and its subclasses 
are the only ones to benefit from this right now.

This is performance neutral on the benchmarks we track. Currently we won't
cache on JSDOMWindow due to HasImpureGetOwnPropertySlot, but this issue will be fixed soon.

* jit/Repatch.cpp:
(JSC::generateByIdStub):
(JSC::tryBuildGetByIDList):
(JSC::tryCachePutByID):
(JSC::tryBuildPutByIdList):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionCreateProxy):
* runtime/IntendedStructureChain.cpp:
(JSC::IntendedStructureChain::isNormalized):
* runtime/JSCellInlines.h:
(JSC::JSCell::isProxy):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::finishCreation):
* runtime/JSProxy.h:
(JSC::JSProxy::createStructure):
(JSC::JSProxy::targetOffset):
* runtime/JSType.h:
* runtime/Operations.h:
(JSC::isPrototypeChainNormalized):
* runtime/Structure.h:
(JSC::Structure::isProxy):
* tests/stress/proxy-inline-cache.js: Added.
(cacheOnTarget.getX):
(cacheOnTarget):
(cacheOnPrototypeOfTarget.getX):
(cacheOnPrototypeOfTarget):
(dontCacheOnProxyInPrototypeChain.getX):
(dontCacheOnProxyInPrototypeChain):
(dontCacheOnTargetOfProxyInPrototypeChainOfTarget.getX):
(dontCacheOnTargetOfProxyInPrototypeChainOfTarget):

Source/WebCore: 
No new tests.

Whenever we encounter a proxy in an inline cache we should try to cache on the 
proxy's target instead of giving up.

This patch adds support for a simple &quot;recursive&quot; inline cache if the base object
we're accessing is a pure forwarding proxy. JSGlobalObject and its subclasses 
are the only ones to benefit from this right now.

This is performance neutral on the benchmarks we track. Currently we won't
cache on JSDOMWindow due to HasImpureGetOwnPropertySlot, but this issue will be fixed soon.

* bindings/js/JSDOMWindowShell.h:
(WebCore::JSDOMWindowShell::createStructure):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorejitRepatchcpp">trunk/Source/JavaScriptCore/jit/Repatch.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejsccpp">trunk/Source/JavaScriptCore/jsc.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntendedStructureChaincpp">trunk/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSCellInlinesh">trunk/Source/JavaScriptCore/runtime/JSCellInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSGlobalObjecth">trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSProxyh">trunk/Source/JavaScriptCore/runtime/JSProxy.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSTypeh">trunk/Source/JavaScriptCore/runtime/JSType.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeOperationsh">trunk/Source/JavaScriptCore/runtime/Operations.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStructureh">trunk/Source/JavaScriptCore/runtime/Structure.h</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorebindingsjsJSDOMWindowShellh">trunk/Source/WebCore/bindings/js/JSDOMWindowShell.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressproxyinlinecachejs">trunk/Source/JavaScriptCore/tests/stress/proxy-inline-cache.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/ChangeLog        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -1,3 +1,52 @@
</span><ins>+2014-04-29  Mark Hahnenberg  &lt;mhahnenberg@apple.com&gt;
+
+        JSProxies should be cacheable
+        https://bugs.webkit.org/show_bug.cgi?id=132351
+
+        Reviewed by Geoffrey Garen.
+
+        Whenever we encounter a proxy in an inline cache we should try to cache on the 
+        proxy's target instead of giving up.
+
+        This patch adds support for a simple &quot;recursive&quot; inline cache if the base object
+        we're accessing is a pure forwarding proxy. JSGlobalObject and its subclasses 
+        are the only ones to benefit from this right now.
+
+        This is performance neutral on the benchmarks we track. Currently we won't
+        cache on JSDOMWindow due to HasImpureGetOwnPropertySlot, but this issue will be fixed soon.
+
+        * jit/Repatch.cpp:
+        (JSC::generateByIdStub):
+        (JSC::tryBuildGetByIDList):
+        (JSC::tryCachePutByID):
+        (JSC::tryBuildPutByIdList):
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionCreateProxy):
+        * runtime/IntendedStructureChain.cpp:
+        (JSC::IntendedStructureChain::isNormalized):
+        * runtime/JSCellInlines.h:
+        (JSC::JSCell::isProxy):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::finishCreation):
+        * runtime/JSProxy.h:
+        (JSC::JSProxy::createStructure):
+        (JSC::JSProxy::targetOffset):
+        * runtime/JSType.h:
+        * runtime/Operations.h:
+        (JSC::isPrototypeChainNormalized):
+        * runtime/Structure.h:
+        (JSC::Structure::isProxy):
+        * tests/stress/proxy-inline-cache.js: Added.
+        (cacheOnTarget.getX):
+        (cacheOnTarget):
+        (cacheOnPrototypeOfTarget.getX):
+        (cacheOnPrototypeOfTarget):
+        (dontCacheOnProxyInPrototypeChain.getX):
+        (dontCacheOnProxyInPrototypeChain):
+        (dontCacheOnTargetOfProxyInPrototypeChainOfTarget.getX):
+        (dontCacheOnTargetOfProxyInPrototypeChainOfTarget):
+
</ins><span class="cx"> 2014-04-29  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Use LLVM as a backend for the fourth-tier DFG JIT (a.k.a. the FTL JIT)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitRepatchcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/Repatch.cpp (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/Repatch.cpp        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/jit/Repatch.cpp        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -285,7 +285,7 @@
</span><span class="cx"> static void generateByIdStub(
</span><span class="cx">     ExecState* exec, ByIdStubKind kind, const Identifier&amp; propertyName,
</span><span class="cx">     FunctionPtr custom, StructureStubInfo&amp; stubInfo, StructureChain* chain, size_t count,
</span><del>-    PropertyOffset offset, Structure* structure, CodeLocationLabel successLabel,
</del><ins>+    PropertyOffset offset, Structure* structure, bool loadTargetFromProxy, CodeLocationLabel successLabel,
</ins><span class="cx">     CodeLocationLabel slowCaseLabel, RefPtr&lt;JITStubRoutine&gt;&amp; stubRoutine)
</span><span class="cx"> {
</span><span class="cx">     VM* vm = &amp;exec-&gt;vm();
</span><span class="lines">@@ -308,10 +308,21 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     MacroAssembler::JumpList failureCases;
</span><del>-    
</del><ins>+
+    GPRReg baseForGetGPR;
+    if (loadTargetFromProxy) {
+        baseForGetGPR = valueRegs.payloadGPR();
+        failureCases.append(stubJit.branch8(
+            MacroAssembler::NotEqual, 
+            MacroAssembler::Address(baseGPR, JSCell::typeInfoTypeOffset()), 
+            MacroAssembler::TrustedImm32(PureForwardingProxyType)));
+        stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSProxy::targetOffset()), baseForGetGPR);
+    } else
+        baseForGetGPR = baseGPR;
+
</ins><span class="cx">     failureCases.append(branchStructure(stubJit,
</span><span class="cx">         MacroAssembler::NotEqual, 
</span><del>-        MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()), 
</del><ins>+        MacroAssembler::Address(baseForGetGPR, JSCell::structureIDOffset()), 
</ins><span class="cx">         structure));
</span><span class="cx"> 
</span><span class="cx">     CodeBlock* codeBlock = exec-&gt;codeBlock();
</span><span class="lines">@@ -339,7 +350,7 @@
</span><span class="cx">         stubJit.move(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR);
</span><span class="cx">         baseForAccessGPR = scratchGPR;
</span><span class="cx">     } else
</span><del>-        baseForAccessGPR = baseGPR;
</del><ins>+        baseForAccessGPR = baseForGetGPR;
</ins><span class="cx">     
</span><span class="cx">     GPRReg loadedValueGPR = InvalidGPRReg;
</span><span class="cx">     if (kind != CallCustomGetter &amp;&amp; kind != CallCustomSetter) {
</span><span class="lines">@@ -450,7 +461,7 @@
</span><span class="cx">                 loadedValueGPR, calleeFrame.withOffset(JSStack::Callee * sizeof(Register)));
</span><span class="cx"> 
</span><span class="cx">             stubJit.storeCell(
</span><del>-                baseGPR,
</del><ins>+                baseForGetGPR,
</ins><span class="cx">                 calleeFrame.withOffset(
</span><span class="cx">                     virtualRegisterForArgument(0).offset() * sizeof(Register)));
</span><span class="cx">             
</span><span class="lines">@@ -508,14 +519,14 @@
</span><span class="cx">             // setter: void (*PutValueFunc)(ExecState*, JSObject* base, EncodedJSValue thisObject, EncodedJSValue value);
</span><span class="cx"> #if USE(JSVALUE64)
</span><span class="cx">             if (kind == CallCustomGetter)
</span><del>-                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseGPR, MacroAssembler::TrustedImmPtr(propertyName.impl()));
</del><ins>+                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImmPtr(propertyName.impl()));
</ins><span class="cx">             else
</span><del>-                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseGPR, valueRegs.gpr());
</del><ins>+                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, valueRegs.gpr());
</ins><span class="cx"> #else
</span><span class="cx">             if (kind == CallCustomGetter)
</span><del>-                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::TrustedImmPtr(propertyName.impl()));
</del><ins>+                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::TrustedImmPtr(propertyName.impl()));
</ins><span class="cx">             else
</span><del>-                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), valueRegs.payloadGPR(), valueRegs.tagGPR());
</del><ins>+                stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), valueRegs.payloadGPR(), valueRegs.tagGPR());
</ins><span class="cx"> #endif
</span><span class="cx">             stubJit.storePtr(GPRInfo::callFrameRegister, &amp;vm-&gt;topCallFrame);
</span><span class="cx"> 
</span><span class="lines">@@ -715,15 +726,24 @@
</span><span class="cx"> static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier&amp; ident, const PropertySlot&amp; slot, StructureStubInfo&amp; stubInfo)
</span><span class="cx"> {
</span><span class="cx">     if (!baseValue.isCell()
</span><del>-        || !slot.isCacheable()
-        || !baseValue.asCell()-&gt;structure()-&gt;propertyAccessesAreCacheable())
</del><ins>+        || !slot.isCacheable())
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+    JSCell* baseCell = baseValue.asCell();
+    bool loadTargetFromProxy = false;
+    if (baseCell-&gt;type() == PureForwardingProxyType) {
+        baseValue = jsCast&lt;JSProxy*&gt;(baseCell)-&gt;target();
+        baseCell = baseValue.asCell();
+        loadTargetFromProxy = true;
+    }
+
+    VM* vm = &amp;exec-&gt;vm();
</ins><span class="cx">     CodeBlock* codeBlock = exec-&gt;codeBlock();
</span><del>-    VM* vm = &amp;exec-&gt;vm();
-    JSCell* baseCell = baseValue.asCell();
-    Structure* structure = baseCell-&gt;structure();
-    
</del><ins>+    Structure* structure = baseCell-&gt;structure(*vm);
+
+    if (!structure-&gt;propertyAccessesAreCacheable())
+        return false;
+
</ins><span class="cx">     if (stubInfo.patch.spillMode == NeedToSpill) {
</span><span class="cx">         // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
</span><span class="cx">         // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
</span><span class="lines">@@ -756,8 +776,8 @@
</span><span class="cx">     
</span><span class="cx">     RefPtr&lt;JITStubRoutine&gt; stubRoutine;
</span><span class="cx">     generateByIdStub(
</span><del>-        exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count, offset,
-        structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
</del><ins>+        exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count, offset, 
+        structure, loadTargetFromProxy, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
</ins><span class="cx">         CodeLocationLabel(list-&gt;currentSlowPathTarget(stubInfo)), stubRoutine);
</span><span class="cx">     
</span><span class="cx">     GetByIdAccess::AccessType accessType;
</span><span class="lines">@@ -1175,7 +1195,7 @@
</span><span class="cx"> 
</span><span class="cx">         generateByIdStub(
</span><span class="cx">             exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count,
</span><del>-            offset, structure,
</del><ins>+            offset, structure, false,
</ins><span class="cx">             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
</span><span class="cx">             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
</span><span class="cx">             stubRoutine);
</span><span class="lines">@@ -1307,7 +1327,7 @@
</span><span class="cx"> 
</span><span class="cx">         generateByIdStub(
</span><span class="cx">             exec, kindFor(slot), propertyName, customFor(slot), stubInfo, prototypeChain, count,
</span><del>-            offset, structure,
</del><ins>+            offset, structure, false,
</ins><span class="cx">             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
</span><span class="cx">             CodeLocationLabel(list-&gt;currentSlowPathTarget()),
</span><span class="cx">             stubRoutine);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejsccpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jsc.cpp (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jsc.cpp        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/jsc.cpp        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -241,6 +241,8 @@
</span><span class="cx"> 
</span><span class="cx"> static bool fillBufferWithContentsOfFile(const String&amp; fileName, Vector&lt;char&gt;&amp; buffer);
</span><span class="cx"> 
</span><ins>+static EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState*);
+
</ins><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionSetElementRoot(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionCreateRoot(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionCreateElement(ExecState*);
</span><span class="lines">@@ -413,6 +415,8 @@
</span><span class="cx">         
</span><span class="cx">         addFunction(vm, &quot;effectful42&quot;, functionEffectful42, 0);
</span><span class="cx">         addFunction(vm, &quot;makeMasquerader&quot;, functionMakeMasquerader, 0);
</span><ins>+
+        addFunction(vm, &quot;createProxy&quot;, functionCreateProxy, 1);
</ins><span class="cx">         
</span><span class="cx">         JSArray* array = constructEmptyArray(globalExec(), 0);
</span><span class="cx">         for (size_t i = 0; i &lt; arguments.size(); ++i)
</span><span class="lines">@@ -571,6 +575,18 @@
</span><span class="cx">     return JSValue::encode(jsUndefined());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    JSValue target = exec-&gt;argument(0);
+    if (!target.isObject())
+        return JSValue::encode(jsUndefined());
+    JSObject* jsTarget = asObject(target.asCell());
+    Structure* structure = JSProxy::createStructure(exec-&gt;vm(), exec-&gt;lexicalGlobalObject(), jsTarget-&gt;prototype());
+    JSProxy* proxy = JSProxy::create(exec-&gt;vm(), structure, jsTarget);
+    return JSValue::encode(proxy);
+}
+
</ins><span class="cx"> EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx">     JSLockHolder lock(exec);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntendedStructureChaincpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -118,11 +118,11 @@
</span><span class="cx"> 
</span><span class="cx"> bool IntendedStructureChain::isNormalized()
</span><span class="cx"> {
</span><del>-    if (m_head-&gt;typeInfo().type() == ProxyType)
</del><ins>+    if (m_head-&gt;isProxy())
</ins><span class="cx">         return false;
</span><span class="cx">     for (unsigned i = 0; i &lt; m_vector.size(); ++i) {
</span><span class="cx">         Structure* structure = m_vector[i];
</span><del>-        if (structure-&gt;typeInfo().type() == ProxyType)
</del><ins>+        if (structure-&gt;isProxy())
</ins><span class="cx">             return false;
</span><span class="cx">         if (structure-&gt;isDictionary())
</span><span class="cx">             return false;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSCellInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSCellInlines.h (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSCellInlines.h        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/runtime/JSCellInlines.h        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -158,7 +158,7 @@
</span><span class="cx"> 
</span><span class="cx"> inline bool JSCell::isProxy() const
</span><span class="cx"> {
</span><del>-    return m_type == ProxyType;
</del><ins>+    return m_type == ImpureProxyType || m_type == PureForwardingProxyType;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> inline bool JSCell::isAPIValueWrapper() const
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSGlobalObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -298,7 +298,7 @@
</span><span class="cx">         structure()-&gt;setGlobalObject(vm, this);
</span><span class="cx">         m_experimentsEnabled = m_globalObjectMethodTable-&gt;javaScriptExperimentsEnabled(this);
</span><span class="cx">         init();
</span><del>-        setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, this, prototype()), this));
</del><ins>+        setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, this, prototype(), PureForwardingProxyType), this));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void finishCreation(VM&amp; vm, JSObject* thisValue)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSProxy.h (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSProxy.h        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/runtime/JSProxy.h        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -41,14 +41,15 @@
</span><span class="cx">         return proxy;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static Structure* createStructure(VM&amp; vm, JSGlobalObject* globalObject, JSValue prototype)
</del><ins>+    static Structure* createStructure(VM&amp; vm, JSGlobalObject* globalObject, JSValue prototype, JSType proxyType = ImpureProxyType)
</ins><span class="cx">     {
</span><del>-        return Structure::create(vm, globalObject, prototype, TypeInfo(ProxyType, StructureFlags), info());
</del><ins>+        return Structure::create(vm, globalObject, prototype, TypeInfo(proxyType, StructureFlags), info());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     DECLARE_EXPORT_INFO;
</span><span class="cx"> 
</span><span class="cx">     JSObject* target() const { return m_target.get(); }
</span><ins>+    static ptrdiff_t targetOffset() { return OBJECT_OFFSETOF(JSProxy, m_target); }
</ins><span class="cx"> 
</span><span class="cx"> protected:
</span><span class="cx">     JSProxy(VM&amp; vm, Structure* structure)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSType.h (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSType.h        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/runtime/JSType.h        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -53,7 +53,8 @@
</span><span class="cx">     NameInstanceType,
</span><span class="cx">     NumberObjectType,
</span><span class="cx">     ErrorInstanceType,
</span><del>-    ProxyType,
</del><ins>+    PureForwardingProxyType,
+    ImpureProxyType,
</ins><span class="cx">     WithScopeType,
</span><span class="cx">     ArgumentsType,
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeOperationsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Operations.h (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Operations.h        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/runtime/Operations.h        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -261,7 +261,7 @@
</span><span class="cx"> inline bool isPrototypeChainNormalized(JSGlobalObject* globalObject, Structure* structure)
</span><span class="cx"> {
</span><span class="cx">     for (;;) {
</span><del>-        if (structure-&gt;typeInfo().type() == ProxyType)
</del><ins>+        if (structure-&gt;isProxy())
</ins><span class="cx">             return false;
</span><span class="cx">             
</span><span class="cx">         JSValue v = structure-&gt;prototypeForLookup(globalObject);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStructureh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Structure.h (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Structure.h        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/JavaScriptCore/runtime/Structure.h        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -103,6 +103,12 @@
</span><span class="cx">     int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
</span><span class="cx">     int64_t idBlob() const { return m_blob.blob(); }
</span><span class="cx"> 
</span><ins>+    bool isProxy() const
+    {
+        JSType type = m_blob.type();
+        return type == ImpureProxyType || type == PureForwardingProxyType;
+    }
+
</ins><span class="cx">     static void dumpStatistics();
</span><span class="cx"> 
</span><span class="cx">     JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&amp;, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&amp;, PutPropertySlot::Context = PutPropertySlot::UnknownContext);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressproxyinlinecachejs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/proxy-inline-cache.js (0 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/proxy-inline-cache.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/proxy-inline-cache.js        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -0,0 +1,74 @@
</span><ins>+var niters = 100000;
+
+// proxy -&gt; target -&gt; x
+function cacheOnTarget() {
+    var target = {x:42};
+    var proxy = createProxy(target);
+
+    var getX = function(o) { return o.x; };
+    noInline(getX);
+
+    var sum = 0;
+    for (var i = 0; i &lt; niters; ++i)
+        sum += getX(proxy);
+
+    if (sum != 42 * niters)
+        throw new Error(&quot;Incorrect result&quot;);
+};
+
+// proxy -&gt; target -&gt; proto -&gt; x
+function cacheOnPrototypeOfTarget() {
+    var proto = {x:42};
+    var target = Object.create(proto);
+    var proxy = createProxy(target);
+
+    var getX = function(o) { return o.x; };
+    noInline(getX);
+
+    var sum = 0;
+    for (var i = 0; i &lt; niters; ++i)
+        sum += getX(proxy);
+
+    if (sum != 42 * niters)
+        throw new Error(&quot;Incorrect result&quot;);
+};
+
+// base -&gt; proto (proxy) -&gt; target -&gt; x
+function dontCacheOnProxyInPrototypeChain() {
+    var target = {x:42};
+    var protoProxy = createProxy(target);
+    var base = Object.create(protoProxy);
+
+    var getX = function(o) { return o.x; };
+    noInline(getX);
+
+    var sum = 0;
+    for (var i = 0; i &lt; niters; ++i)
+        sum += getX(base);
+
+    if (sum != 42 * niters)
+        throw new Error(&quot;Incorrect result&quot;);
+};
+
+// proxy -&gt; target 1 -&gt; proto (proxy) -&gt; target 2 -&gt; x
+function dontCacheOnTargetOfProxyInPrototypeChainOfTarget() {
+    var target2 = {x:42};
+    var protoProxy = createProxy(target2);
+    var target1 = Object.create(protoProxy);
+    var proxy = createProxy(target1);
+
+    var getX = function(o) { return o.x; };
+    noInline(getX);
+
+    var sum = 0;
+    for (var i = 0; i &lt; niters; ++i)
+        sum += getX(proxy);
+
+    if (sum != 42 * niters)
+        throw new Error(&quot;Incorrect result&quot;);
+};
+
+cacheOnTarget();
+cacheOnPrototypeOfTarget();
+dontCacheOnProxyInPrototypeChain();
+dontCacheOnTargetOfProxyInPrototypeChainOfTarget();
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/WebCore/ChangeLog        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -1,3 +1,25 @@
</span><ins>+2014-04-29  Mark Hahnenberg  &lt;mhahnenberg@apple.com&gt;
+
+        JSProxies should be cacheable
+        https://bugs.webkit.org/show_bug.cgi?id=132351
+
+        Reviewed by Geoffrey Garen.
+
+        No new tests.
+
+        Whenever we encounter a proxy in an inline cache we should try to cache on the 
+        proxy's target instead of giving up.
+
+        This patch adds support for a simple &quot;recursive&quot; inline cache if the base object
+        we're accessing is a pure forwarding proxy. JSGlobalObject and its subclasses 
+        are the only ones to benefit from this right now.
+
+        This is performance neutral on the benchmarks we track. Currently we won't
+        cache on JSDOMWindow due to HasImpureGetOwnPropertySlot, but this issue will be fixed soon.
+
+        * bindings/js/JSDOMWindowShell.h:
+        (WebCore::JSDOMWindowShell::createStructure):
+
</ins><span class="cx"> 2014-04-29  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Mac, iOS] Support caption activation via JS webkitHasClosedCaptions method
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsjsJSDOMWindowShellh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/JSDOMWindowShell.h (167962 => 167963)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/bindings/js/JSDOMWindowShell.h        2014-04-29 22:19:16 UTC (rev 167962)
+++ trunk/Source/WebCore/bindings/js/JSDOMWindowShell.h        2014-04-29 22:21:04 UTC (rev 167963)
</span><span class="lines">@@ -59,7 +59,7 @@
</span><span class="cx"> 
</span><span class="cx">         static JSC::Structure* createStructure(JSC::VM&amp; vm, JSC::JSValue prototype) 
</span><span class="cx">         {
</span><del>-            return JSC::Structure::create(vm, 0, prototype, JSC::TypeInfo(JSC::ProxyType, StructureFlags), info());
</del><ins>+            return JSC::Structure::create(vm, 0, prototype, JSC::TypeInfo(JSC::PureForwardingProxyType, StructureFlags), info());
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         DOMWrapperWorld&amp; world() { return m_world.get(); }
</span></span></pre>
</div>
</div>

</body>
</html>