Support caching of custom setters
https://bugs.webkit.org/show_bug.cgi?id=129519

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

This patch adds caching of assignment to properties that
are backed by C functions. This provides most of the leg
work required to start supporting setters, and resolves
the remaining regressions from moving DOM properties up
the prototype chain.

* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/PolymorphicPutByIdList.cpp:
(JSC::PutByIdAccess::visitWeak):
(JSC::PolymorphicPutByIdList::PolymorphicPutByIdList):
(JSC::PolymorphicPutByIdList::from):
* bytecode/PolymorphicPutByIdList.h:
(JSC::PutByIdAccess::transition):
(JSC::PutByIdAccess::replace):
(JSC::PutByIdAccess::customSetter):
(JSC::PutByIdAccess::isCustom):
(JSC::PutByIdAccess::oldStructure):
(JSC::PutByIdAccess::chain):
(JSC::PutByIdAccess::stubRoutine):
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeForStubInfo):
(JSC::PutByIdStatus::computeFor):
(JSC::PutByIdStatus::dump):
* bytecode/PutByIdStatus.h:
(JSC::PutByIdStatus::PutByIdStatus):
(JSC::PutByIdStatus::takesSlowPath):
(JSC::PutByIdStatus::makesCalls):
* bytecode/StructureStubInfo.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::emitPutById):
(JSC::DFG::ByteCodeParser::handlePutById):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGCommon.h:
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasIdentifier):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileIn):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::compile):
* jit/CCallHelpers.h:
(JSC::CCallHelpers::setupArgumentsWithExecState):
* jit/JITInlineCacheGenerator.cpp:
(JSC::JITByIdGenerator::JITByIdGenerator):
(JSC::JITPutByIdGenerator::JITPutByIdGenerator):
* jit/JITInlineCacheGenerator.h:
(JSC::JITGetByIdGenerator::JITGetByIdGenerator):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_get_by_id):
(JSC::JIT::emit_op_put_by_id):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_get_by_id):
(JSC::JIT::emit_op_put_by_id):
* jit/Repatch.cpp:
(JSC::tryCacheGetByID):
(JSC::tryBuildGetByIDList):
(JSC::emitCustomSetterStub):
(JSC::tryCachePutByID):
(JSC::tryBuildPutByIdList):
* jit/SpillRegistersMode.h: Added.
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* runtime/Lookup.h:
(JSC::putEntry):
* runtime/PutPropertySlot.h:
(JSC::PutPropertySlot::setCacheableCustomProperty):
(JSC::PutPropertySlot::customSetter):
(JSC::PutPropertySlot::isCacheablePut):
(JSC::PutPropertySlot::isCacheableCustomProperty):
(JSC::PutPropertySlot::cachedOffset):

Source/WebCore:

Add forwarding header

Tests: js/regress/assign-custom-setter-polymorphic.html
       js/regress/assign-custom-setter.html

* ForwardingHeaders/jit/SpillRegistersMode.h: Added.

LayoutTests:

Add test cases.

* js/regress/assign-custom-setter-expected.txt: Added.
* js/regress/assign-custom-setter-polymorphic-expected.txt: Added.
* js/regress/assign-custom-setter-polymorphic.html: Added.
* js/regress/assign-custom-setter.html: Added.
* js/regress/script-tests/assign-custom-setter-polymorphic.js: Added.
(test):
* js/regress/script-tests/assign-custom-setter.js: Added.
(test):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@165208 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 80a9030..3c619ce 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,21 @@
+2014-03-05  Oliver Hunt  <oliver@apple.com>
+
+        Support caching of custom setters
+        https://bugs.webkit.org/show_bug.cgi?id=129519
+
+        Reviewed by Filip Pizlo.
+
+        Add test cases.
+
+        * js/regress/assign-custom-setter-expected.txt: Added.
+        * js/regress/assign-custom-setter-polymorphic-expected.txt: Added.
+        * js/regress/assign-custom-setter-polymorphic.html: Added.
+        * js/regress/assign-custom-setter.html: Added.
+        * js/regress/script-tests/assign-custom-setter-polymorphic.js: Added.
+        (test):
+        * js/regress/script-tests/assign-custom-setter.js: Added.
+        (test):
+
 2014-03-06  Michał Pakuła vel Rutka  <m.pakula@samsung.com>
 
         Unreviewed EFL gardening
diff --git a/LayoutTests/js/regress/assign-custom-setter-expected.txt b/LayoutTests/js/regress/assign-custom-setter-expected.txt
new file mode 100644
index 0000000..2f83054
--- /dev/null
+++ b/LayoutTests/js/regress/assign-custom-setter-expected.txt
@@ -0,0 +1,10 @@
+JSRegress/assign-custom-setter
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/assign-custom-setter-polymorphic-expected.txt b/LayoutTests/js/regress/assign-custom-setter-polymorphic-expected.txt
new file mode 100644
index 0000000..146cf35
--- /dev/null
+++ b/LayoutTests/js/regress/assign-custom-setter-polymorphic-expected.txt
@@ -0,0 +1,10 @@
+JSRegress/assign-custom-setter-polymorphic
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/assign-custom-setter-polymorphic.html b/LayoutTests/js/regress/assign-custom-setter-polymorphic.html
new file mode 100644
index 0000000..27dc626
--- /dev/null
+++ b/LayoutTests/js/regress/assign-custom-setter-polymorphic.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="resources/regress-pre.js"></script>
+<script src="script-tests/assign-custom-setter-polymorphic.js"></script>
+<script src="resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/assign-custom-setter.html b/LayoutTests/js/regress/assign-custom-setter.html
new file mode 100644
index 0000000..2916d6e
--- /dev/null
+++ b/LayoutTests/js/regress/assign-custom-setter.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="resources/regress-pre.js"></script>
+<script src="script-tests/assign-custom-setter.js"></script>
+<script src="resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/assign-custom-setter-polymorphic.js b/LayoutTests/js/regress/script-tests/assign-custom-setter-polymorphic.js
new file mode 100644
index 0000000..1c096b6
--- /dev/null
+++ b/LayoutTests/js/regress/script-tests/assign-custom-setter-polymorphic.js
@@ -0,0 +1,26 @@
+
+o = RegExp;
+j = 0;
+l = 2;
+z = 0;
+function test(o, z) {
+    var k = arguments[(((j << 1 | l) >> 1) ^ 1) & (z *= 1)];
+    k.input = 0;
+    for (var i = 0; i < 25000; i++) {
+        k.input = "foo";
+    }
+
+    return k.input;
+}
+var result = test({__proto__: {bar:"wibble", input:"foo"}});
+var result = test({input:"foo"});
+var result = test(o)
+for (var k = 0; k < 6; k++) {
+    var start = new Date;
+    var newResult = test(o)
+    var end = new Date;
+    if (newResult != result)
+        throw "Failed at " + k + "with " + newResult + " vs. " + result
+    result = newResult;
+    o = {__proto__ : o }
+}
diff --git a/LayoutTests/js/regress/script-tests/assign-custom-setter.js b/LayoutTests/js/regress/script-tests/assign-custom-setter.js
new file mode 100644
index 0000000..63a8c90
--- /dev/null
+++ b/LayoutTests/js/regress/script-tests/assign-custom-setter.js
@@ -0,0 +1,24 @@
+// RegExp.input is a handy setter
+
+var o = RegExp;
+function test(o) {
+    var k = 0;
+    o.input = "bar";
+    for (var i = 0; i < 30000; i++)
+        o.input = "foo";
+
+    return o.input;
+}
+
+var result = test(o);
+
+for (var k = 0; k < 9; k++) {
+    var start = new Date;
+    var newResult = test(o)
+    var end = new Date;
+    if (newResult != result)
+        throw "Failed at " + k + "with " +newResult + " vs. " + result
+    result = newResult; 
+    o = {__proto__ : o }
+}
+
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 09d6a2b..89dc894 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,101 @@
+2014-03-03  Oliver Hunt  <oliver@apple.com>
+
+        Support caching of custom setters
+        https://bugs.webkit.org/show_bug.cgi?id=129519
+
+        Reviewed by Filip Pizlo.
+
+        This patch adds caching of assignment to properties that
+        are backed by C functions. This provides most of the leg
+        work required to start supporting setters, and resolves
+        the remaining regressions from moving DOM properties up
+        the prototype chain.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/PolymorphicPutByIdList.cpp:
+        (JSC::PutByIdAccess::visitWeak):
+        (JSC::PolymorphicPutByIdList::PolymorphicPutByIdList):
+        (JSC::PolymorphicPutByIdList::from):
+        * bytecode/PolymorphicPutByIdList.h:
+        (JSC::PutByIdAccess::transition):
+        (JSC::PutByIdAccess::replace):
+        (JSC::PutByIdAccess::customSetter):
+        (JSC::PutByIdAccess::isCustom):
+        (JSC::PutByIdAccess::oldStructure):
+        (JSC::PutByIdAccess::chain):
+        (JSC::PutByIdAccess::stubRoutine):
+        * bytecode/PutByIdStatus.cpp:
+        (JSC::PutByIdStatus::computeForStubInfo):
+        (JSC::PutByIdStatus::computeFor):
+        (JSC::PutByIdStatus::dump):
+        * bytecode/PutByIdStatus.h:
+        (JSC::PutByIdStatus::PutByIdStatus):
+        (JSC::PutByIdStatus::takesSlowPath):
+        (JSC::PutByIdStatus::makesCalls):
+        * bytecode/StructureStubInfo.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::emitPutById):
+        (JSC::DFG::ByteCodeParser::handlePutById):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGCommon.h:
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasIdentifier):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileIn):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::cachedPutById):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::cachedPutById):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * jit/CCallHelpers.h:
+        (JSC::CCallHelpers::setupArgumentsWithExecState):
+        * jit/JITInlineCacheGenerator.cpp:
+        (JSC::JITByIdGenerator::JITByIdGenerator):
+        (JSC::JITPutByIdGenerator::JITPutByIdGenerator):
+        * jit/JITInlineCacheGenerator.h:
+        (JSC::JITGetByIdGenerator::JITGetByIdGenerator):
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_get_by_id):
+        (JSC::JIT::emit_op_put_by_id):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_get_by_id):
+        (JSC::JIT::emit_op_put_by_id):
+        * jit/Repatch.cpp:
+        (JSC::tryCacheGetByID):
+        (JSC::tryBuildGetByIDList):
+        (JSC::emitCustomSetterStub):
+        (JSC::tryCachePutByID):
+        (JSC::tryBuildPutByIdList):
+        * jit/SpillRegistersMode.h: Added.
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * runtime/Lookup.h:
+        (JSC::putEntry):
+        * runtime/PutPropertySlot.h:
+        (JSC::PutPropertySlot::setCacheableCustomProperty):
+        (JSC::PutPropertySlot::customSetter):
+        (JSC::PutPropertySlot::isCacheablePut):
+        (JSC::PutPropertySlot::isCacheableCustomProperty):
+        (JSC::PutPropertySlot::cachedOffset):
+
 2014-03-06  Filip Pizlo  <fpizlo@apple.com>
 
         FTL arity fixup should work on ARM64
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index b070f74..2eae717 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -1170,6 +1170,7 @@
 		A784A26411D16622005776AC /* SyntaxChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A7EE7711B98B8D0065A14F /* SyntaxChecker.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A78507D617CBC6FD0011F6E7 /* MapData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A78507D417CBC6FD0011F6E7 /* MapData.cpp */; };
 		A78507D717CBC6FD0011F6E7 /* MapData.h in Headers */ = {isa = PBXBuildFile; fileRef = A78507D517CBC6FD0011F6E7 /* MapData.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		A785F6BC18C553FE00F10626 /* SpillRegistersMode.h in Headers */ = {isa = PBXBuildFile; fileRef = A7FF647A18C52E8500B55307 /* SpillRegistersMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A78853F917972629001440E4 /* IntendedStructureChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A78853F717972629001440E4 /* IntendedStructureChain.cpp */; };
 		A78853FA17972629001440E4 /* IntendedStructureChain.h in Headers */ = {isa = PBXBuildFile; fileRef = A78853F817972629001440E4 /* IntendedStructureChain.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A78A9774179738B8009DF744 /* DFGFailedFinalizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A78A976C179738B8009DF744 /* DFGFailedFinalizer.cpp */; };
@@ -2817,6 +2818,7 @@
 		A7FB604B103F5EAB0017A286 /* PropertyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyDescriptor.h; sourceTree = "<group>"; };
 		A7FB60A3103F7DC20017A286 /* PropertyDescriptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PropertyDescriptor.cpp; sourceTree = "<group>"; };
 		A7FCC26C17A0B6AA00786D1A /* FTLSwitchCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLSwitchCase.h; path = ftl/FTLSwitchCase.h; sourceTree = "<group>"; };
+		A7FF647A18C52E8500B55307 /* SpillRegistersMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpillRegistersMode.h; sourceTree = "<group>"; };
 		A8A4748D151A8306004123FF /* libWTF.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libWTF.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCallbackObjectFunctions.h; sourceTree = "<group>"; };
 		A8E894330CD0603F00367179 /* JSGlobalObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalObject.h; sourceTree = "<group>"; };
@@ -3485,6 +3487,7 @@
 				A7386552118697B400540279 /* ThunkGenerators.cpp */,
 				A7386553118697B400540279 /* ThunkGenerators.h */,
 				65987F2F16828A7E003C2F8D /* UnusedPointer.h */,
+				A7FF647A18C52E8500B55307 /* SpillRegistersMode.h */,
 			);
 			path = jit;
 			sourceTree = "<group>";
@@ -5551,6 +5554,7 @@
 				86158AB3155C8B4000B45C9C /* PropertyName.h in Headers */,
 				BC18C4540E16F5CD00B34460 /* PropertyNameArray.h in Headers */,
 				0FF7168C15A3B235008F5DAA /* PropertyOffset.h in Headers */,
+				A785F6BC18C553FE00F10626 /* SpillRegistersMode.h in Headers */,
 				BC18C4550E16F5CD00B34460 /* PropertySlot.h in Headers */,
 				0FB7F39C15ED8E4600F167B2 /* PropertyStorage.h in Headers */,
 				BC18C4560E16F5CD00B34460 /* Protect.h in Headers */,
diff --git a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp
index fd3d9a6..a1e53f6 100644
--- a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp
+++ b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp
@@ -77,6 +77,12 @@
         if (!Heap::isMarked(m_chain.get()))
             return false;
         break;
+    case CustomSetter:
+        if (!Heap::isMarked(m_oldStructure.get()))
+            return false;
+        if (m_chain && !Heap::isMarked(m_chain.get()))
+            return false;
+        break;
     default:
         RELEASE_ASSERT_NOT_REACHED();
         return false;
@@ -88,7 +94,8 @@
     PutKind putKind, StructureStubInfo& stubInfo)
     : m_kind(putKind)
 {
-    m_list.append(PutByIdAccess::fromStructureStubInfo(stubInfo));
+    if (stubInfo.accessType != access_unset)
+        m_list.append(PutByIdAccess::fromStructureStubInfo(stubInfo));
 }
 
 PolymorphicPutByIdList* PolymorphicPutByIdList::from(
@@ -98,8 +105,9 @@
         return stubInfo.u.putByIdList.list;
     
     ASSERT(stubInfo.accessType == access_put_by_id_replace
-           || stubInfo.accessType == access_put_by_id_transition_normal
-           || stubInfo.accessType == access_put_by_id_transition_direct);
+        || stubInfo.accessType == access_put_by_id_transition_normal
+        || stubInfo.accessType == access_put_by_id_transition_direct
+        || stubInfo.accessType == access_unset);
     
     PolymorphicPutByIdList* result =
         new PolymorphicPutByIdList(putKind, stubInfo);
diff --git a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h
index 32e4988..f9db26a 100644
--- a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h
+++ b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h
@@ -32,6 +32,7 @@
 #include "MacroAssembler.h"
 #include "Opcode.h"
 #include "PutKind.h"
+#include "PutPropertySlot.h"
 #include "Structure.h"
 #include <wtf/Vector.h>
 
@@ -45,7 +46,8 @@
     enum AccessType {
         Invalid,
         Transition,
-        Replace
+        Replace,
+        CustomSetter
     };
     
     PutByIdAccess()
@@ -66,10 +68,11 @@
         result.m_oldStructure.set(vm, owner, oldStructure);
         result.m_newStructure.set(vm, owner, newStructure);
         result.m_chain.set(vm, owner, chain);
+        result.m_customSetter = 0;
         result.m_stubRoutine = stubRoutine;
         return result;
     }
-    
+
     static PutByIdAccess replace(
         VM& vm,
         JSCell* owner,
@@ -79,6 +82,26 @@
         PutByIdAccess result;
         result.m_type = Replace;
         result.m_oldStructure.set(vm, owner, structure);
+        result.m_customSetter = 0;
+        result.m_stubRoutine = stubRoutine;
+        return result;
+    }
+
+
+    static PutByIdAccess customSetter(
+        VM& vm,
+        JSCell* owner,
+        Structure* structure,
+        StructureChain* chain,
+        PutPropertySlot::PutValueFunc customSetter,
+        PassRefPtr<JITStubRoutine> stubRoutine)
+    {
+        PutByIdAccess result;
+        result.m_oldStructure.set(vm, owner, structure);
+        result.m_type = CustomSetter;
+        if (chain)
+            result.m_chain.set(vm, owner, chain);
+        result.m_customSetter = customSetter;
         result.m_stubRoutine = stubRoutine;
         return result;
     }
@@ -92,12 +115,13 @@
     
     bool isTransition() const { return m_type == Transition; }
     bool isReplace() const { return m_type == Replace; }
+    bool isCustom() const { return m_type == CustomSetter; }
     
     Structure* oldStructure() const
     {
         // Using this instead of isSet() to make this assertion robust against the possibility
         // of additional access types being added.
-        ASSERT(isTransition() || isReplace());
+        ASSERT(isTransition() || isReplace() || isCustom());
         
         return m_oldStructure.get();
     }
@@ -116,16 +140,18 @@
     
     StructureChain* chain() const
     {
-        ASSERT(isTransition());
+        ASSERT(isTransition() || isCustom());
         return m_chain.get();
     }
     
     JITStubRoutine* stubRoutine() const
     {
-        ASSERT(isTransition() || isReplace());
+        ASSERT(isTransition() || isReplace() || isCustom());
         return m_stubRoutine.get();
     }
-    
+
+    PutPropertySlot::PutValueFunc customSetter() const { ASSERT(isCustom()); return m_customSetter; }
+
     bool visitWeak() const;
     
 private:
@@ -135,6 +161,7 @@
     WriteBarrier<Structure> m_oldStructure;
     WriteBarrier<Structure> m_newStructure;
     WriteBarrier<StructureChain> m_chain;
+    PutPropertySlot::PutValueFunc m_customSetter;
     RefPtr<JITStubRoutine> m_stubRoutine;
 };
 
diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
index bffbde9..17504a6 100644
--- a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
+++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
@@ -205,6 +205,8 @@
                     return PutByIdStatus(TakesSlowPath);
                 break;
             }
+            case PutByIdAccess::CustomSetter:
+                return PutByIdStatus(MakesCalls);
 
             default:
                 return PutByIdStatus(TakesSlowPath);
@@ -265,6 +267,9 @@
     JSCell* specificValue;
     PropertyOffset offset = structure->getConcurrently(vm, uid, attributes, specificValue);
     if (isValidOffset(offset)) {
+        if (attributes & CustomAccessor)
+            return PutByIdStatus(MakesCalls);
+
         if (attributes & (Accessor | ReadOnly))
             return PutByIdStatus(TakesSlowPath);
         if (specificValue) {
@@ -342,6 +347,9 @@
     case TakesSlowPath:
         out.print("(TakesSlowPath)");
         return;
+    case MakesCalls:
+        out.print("(MakesCalls)");
+        return;
     }
     
     RELEASE_ASSERT_NOT_REACHED();
diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.h b/Source/JavaScriptCore/bytecode/PutByIdStatus.h
index 2329386..d2db7a6 100644
--- a/Source/JavaScriptCore/bytecode/PutByIdStatus.h
+++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.h
@@ -47,7 +47,9 @@
         // It's cached as a simple store of some kind.
         Simple,
         // It's known to often take slow path.
-        TakesSlowPath
+        TakesSlowPath,
+        // It's known to take paths that make calls.
+        MakesCalls
     };
     
     PutByIdStatus()
@@ -58,7 +60,7 @@
     explicit PutByIdStatus(State state)
         : m_state(state)
     {
-        ASSERT(m_state == NoInformation || m_state == TakesSlowPath);
+        ASSERT(m_state == NoInformation || m_state == TakesSlowPath || m_state == MakesCalls);
     }
     
     PutByIdStatus(const PutByIdVariant& variant)
@@ -77,7 +79,8 @@
     bool isSet() const { return m_state != NoInformation; }
     bool operator!() const { return m_state == NoInformation; }
     bool isSimple() const { return m_state == Simple; }
-    bool takesSlowPath() const { return m_state == TakesSlowPath; }
+    bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls; }
+    bool makesCalls() const { return m_state == MakesCalls; }
     
     size_t numVariants() const { return m_variants.size(); }
     const Vector<PutByIdVariant, 1>& variants() const { return m_variants; }
diff --git a/Source/JavaScriptCore/bytecode/StructureStubInfo.h b/Source/JavaScriptCore/bytecode/StructureStubInfo.h
index 95135f3..52b3ed0 100644
--- a/Source/JavaScriptCore/bytecode/StructureStubInfo.h
+++ b/Source/JavaScriptCore/bytecode/StructureStubInfo.h
@@ -33,6 +33,7 @@
 #include "Opcode.h"
 #include "PolymorphicAccessStructureList.h"
 #include "RegisterSet.h"
+#include "SpillRegistersMode.h"
 #include "Structure.h"
 #include "StructureStubClearingWatchpoint.h"
 #include <wtf/OwnPtr.h>
@@ -193,7 +194,7 @@
     CodeOrigin codeOrigin;
 
     struct {
-        int8_t registersFlushed;
+        SpillRegistersMode spillMode : 8;
         int8_t baseGPR;
 #if USE(JSVALUE32_64)
         int8_t valueTagGPR;
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index dbf88ff..f77db1b 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1737,6 +1737,7 @@
     }
         
     case PutById:
+    case PutByIdFlush:
     case PutByIdDirect:
         node->setCanExit(true);
         if (Structure* structure = forNode(node->child1()).bestProvenStructure()) {
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index b5b2521..9847214 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -180,7 +180,7 @@
         int destinationOperand, SpeculatedType, Node* base, unsigned identifierNumber,
         const GetByIdStatus&);
     void emitPutById(
-        Node* base, unsigned identifierNumber, Node* value, bool isDirect);
+        Node* base, unsigned identifierNumber, Node* value,  const PutByIdStatus&, bool isDirect);
     void handlePutById(
         Node* base, unsigned identifierNumber, Node* value, const PutByIdStatus&,
         bool isDirect);
@@ -1943,12 +1943,12 @@
 }
 
 void ByteCodeParser::emitPutById(
-    Node* base, unsigned identifierNumber, Node* value, bool isDirect)
+    Node* base, unsigned identifierNumber, Node* value, const PutByIdStatus& putByIdStatus, bool isDirect)
 {
     if (isDirect)
         addToGraph(PutByIdDirect, OpInfo(identifierNumber), base, value);
     else
-        addToGraph(PutById, OpInfo(identifierNumber), base, value);
+        addToGraph(putByIdStatus.makesCalls() ? PutByIdFlush : PutById, OpInfo(identifierNumber), base, value);
 }
 
 void ByteCodeParser::handlePutById(
@@ -1958,13 +1958,13 @@
     if (!putByIdStatus.isSimple()) {
         if (!putByIdStatus.isSet())
             addToGraph(ForceOSRExit);
-        emitPutById(base, identifierNumber, value, isDirect);
+        emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
         return;
     }
     
     if (putByIdStatus.numVariants() > 1) {
-        if (!isFTL(m_graph.m_plan.mode)) {
-            emitPutById(base, identifierNumber, value, isDirect);
+        if (!isFTL(m_graph.m_plan.mode) || putByIdStatus.makesCalls()) {
+            emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
             return;
         }
         
@@ -2001,9 +2001,13 @@
         return;
     }
     
-    ASSERT(variant.kind() == PutByIdVariant::Transition);
+    if (variant.kind() != PutByIdVariant::Transition) {
+        emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
+        return;
+    }
+
     if (variant.structureChain() && !variant.structureChain()->isStillValid()) {
-        emitPutById(base, identifierNumber, value, isDirect);
+        emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
         return;
     }
     
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index ca37c92..0af2bda 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -196,6 +196,7 @@
     case GetById:
     case GetByIdFlush:
     case PutById:
+    case PutByIdFlush:
     case PutByIdDirect:
     case ArrayPush:
     case ArrayPop:
diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h
index ed82c1c..9778252 100644
--- a/Source/JavaScriptCore/dfg/DFGCommon.h
+++ b/Source/JavaScriptCore/dfg/DFGCommon.h
@@ -96,8 +96,6 @@
 #endif
 }
 
-enum SpillRegistersMode { NeedToSpill, DontSpill };
-
 enum NoResultTag { NoResult };
 
 enum OptimizationFixpointState { BeforeFixpoint, FixpointNotConverged, FixpointConverged };
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index 44d4136..ca7d3fe 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -224,6 +224,7 @@
             }
                 
             case PutById:
+            case PutByIdFlush:
             case PutByIdDirect: {
                 NodeOrigin origin = node->origin;
                 Edge childEdge = node->child1();
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 5c0aa42..8244a4f 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -851,6 +851,7 @@
         }
             
         case PutById:
+        case PutByIdFlush:
         case PutByIdDirect: {
             fixEdge<CellUse>(node->child1());
             insertStoreBarrier(m_indexInBlock, node->child1(), node->child2());
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index b5b9590..e4730e1 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -654,6 +654,7 @@
         case GetById:
         case GetByIdFlush:
         case PutById:
+        case PutByIdFlush:
         case PutByIdDirect:
             return true;
         default:
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index ce43d1a..84a23e4 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -146,6 +146,7 @@
     macro(GetById, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
     macro(GetByIdFlush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
     macro(PutById, NodeMustGenerate | NodeClobbersWorld) \
+    macro(PutByIdFlush, NodeMustGenerate | NodeMustGenerate | NodeClobbersWorld) \
     macro(PutByIdDirect, NodeMustGenerate | NodeClobbersWorld) \
     macro(CheckStructure, NodeMustGenerate) \
     macro(CheckExecutable, NodeMustGenerate) \
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 28c80e4..3504fd9 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -550,6 +550,7 @@
         case Return:
         case Throw:
         case PutById:
+        case PutByIdFlush:
         case PutByIdDirect:
         case PutByOffset:
         case MultiPutByOffset:
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 06c0d40..d83b93a 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -155,6 +155,7 @@
     case GetById:
     case GetByIdFlush:
     case PutById:
+    case PutByIdFlush:
     case PutByIdDirect:
     case CheckStructure:
     case CheckExecutable:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index b31c5b8..58c83ab 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -880,7 +880,7 @@
             stubInfo->patch.baseGPR = static_cast<int8_t>(baseGPR);
             stubInfo->patch.valueGPR = static_cast<int8_t>(resultGPR);
             stubInfo->patch.usedRegisters = usedRegisters();
-            stubInfo->patch.registersFlushed = false;
+            stubInfo->patch.spillMode = NeedToSpill;
             
             m_jit.addIn(InRecord(jump, done, slowPath.get(), stubInfo));
             addSlowPathGenerator(slowPath.release());
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index d1994f1..422175d 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -715,10 +715,10 @@
 
 #if USE(JSVALUE64)
     void cachedGetById(CodeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
-    void cachedPutById(CodeOrigin, GPRReg base, GPRReg value, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump());
+    void cachedPutById(CodeOrigin, GPRReg base, GPRReg value, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
 #elif USE(JSVALUE32_64)
     void cachedGetById(CodeOrigin, GPRReg baseTagGPROrNone, GPRReg basePayloadGPR, GPRReg resultTagGPR, GPRReg resultPayloadGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
-    void cachedPutById(CodeOrigin, GPRReg basePayloadGPR, GPRReg valueTagGPR, GPRReg valuePayloadGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump());
+    void cachedPutById(CodeOrigin, GPRReg basePayloadGPR, GPRReg valueTagGPR, GPRReg valuePayloadGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
 #endif
     
     void compileIn(Node*);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 406aaec..2752de0 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -174,7 +174,7 @@
     JITGetByIdGenerator gen(
         m_jit.codeBlock(), codeOrigin, usedRegisters(),
         JSValueRegs(baseTagGPROrNone, basePayloadGPR),
-        JSValueRegs(resultTagGPR, resultPayloadGPR), spillMode != NeedToSpill);
+        JSValueRegs(resultTagGPR, resultPayloadGPR), spillMode);
     
     gen.generateFastPath(m_jit);
     
@@ -201,12 +201,12 @@
     addSlowPathGenerator(slowPath.release());
 }
 
-void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg basePayloadGPR, GPRReg valueTagGPR, GPRReg valuePayloadGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind putKind, JITCompiler::Jump slowPathTarget)
+void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg basePayloadGPR, GPRReg valueTagGPR, GPRReg valuePayloadGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind putKind, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode)
 {
     JITPutByIdGenerator gen(
         m_jit.codeBlock(), codeOrigin, usedRegisters(),
         JSValueRegs::payloadOnly(basePayloadGPR), JSValueRegs(valueTagGPR, valuePayloadGPR),
-        scratchGPR, false, m_jit.ecmaModeFor(codeOrigin), putKind);
+        scratchGPR, spillMode, m_jit.ecmaModeFor(codeOrigin), putKind);
     
     gen.generateFastPath(m_jit);
     
@@ -3918,6 +3918,23 @@
         noResult(node);
         break;
     }
+
+    case PutByIdFlush: {
+        SpeculateCellOperand base(this, node->child1());
+        JSValueOperand value(this, node->child2());
+        GPRTemporary scratch(this);
+
+        GPRReg baseGPR = base.gpr();
+        GPRReg valueTagGPR = value.tagGPR();
+        GPRReg valuePayloadGPR = value.payloadGPR();
+        GPRReg scratchGPR = scratch.gpr();
+        flushRegisters();
+
+        cachedPutById(node->origin.semantic, baseGPR, valueTagGPR, valuePayloadGPR, scratchGPR, node->identifierNumber(), NotDirect, MacroAssembler::Jump(), DontSpill);
+
+        noResult(node);
+        break;
+    }
         
     case PutById: {
         SpeculateCellOperand base(this, node->child1());
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index c5a5505..9e51ec6 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -37,6 +37,7 @@
 #include "Debugger.h"
 #include "JSCInlines.h"
 #include "ObjectPrototype.h"
+#include "SpillRegistersMode.h"
 
 namespace JSC { namespace DFG {
 
@@ -191,7 +192,7 @@
 {
     JITGetByIdGenerator gen(
         m_jit.codeBlock(), codeOrigin, usedRegisters(), JSValueRegs(baseGPR),
-        JSValueRegs(resultGPR), spillMode != NeedToSpill);
+        JSValueRegs(resultGPR), spillMode);
     gen.generateFastPath(m_jit);
     
     JITCompiler::JumpList slowCases;
@@ -207,11 +208,12 @@
     addSlowPathGenerator(slowPath.release());
 }
 
-void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg valueGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind putKind, JITCompiler::Jump slowPathTarget)
+void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg valueGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind putKind, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode)
 {
     JITPutByIdGenerator gen(
         m_jit.codeBlock(), codeOrigin, usedRegisters(), JSValueRegs(baseGPR),
-        JSValueRegs(valueGPR), scratchGPR, false, m_jit.ecmaModeFor(codeOrigin), putKind);
+        JSValueRegs(valueGPR), scratchGPR, spillMode, m_jit.ecmaModeFor(codeOrigin), putKind);
+
     gen.generateFastPath(m_jit);
     
     JITCompiler::JumpList slowCases;
@@ -4248,6 +4250,22 @@
         noResult(node);
         break;
     }
+
+    case PutByIdFlush: {
+        SpeculateCellOperand base(this, node->child1());
+        JSValueOperand value(this, node->child2());
+        GPRTemporary scratch(this);
+
+        GPRReg baseGPR = base.gpr();
+        GPRReg valueGPR = value.gpr();
+        GPRReg scratchGPR = scratch.gpr();
+        flushRegisters();
+
+        cachedPutById(node->origin.semantic, baseGPR, valueGPR, scratchGPR, node->identifierNumber(), NotDirect, MacroAssembler::Jump(), DontSpill);
+
+        noResult(node);
+        break;
+    }
         
     case PutById: {
         SpeculateCellOperand base(this, node->child1());
diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp
index 11a3ace..7deeb63 100644
--- a/Source/JavaScriptCore/ftl/FTLCompile.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp
@@ -308,7 +308,7 @@
                 
                 JITGetByIdGenerator gen(
                     codeBlock, getById.codeOrigin(), usedRegisters, JSValueRegs(base),
-                    JSValueRegs(result), false);
+                    JSValueRegs(result), NeedToSpill);
                 
                 MacroAssembler::Label begin = slowPathJIT.label();
                 
@@ -346,7 +346,7 @@
                 
                 JITPutByIdGenerator gen(
                     codeBlock, putById.codeOrigin(), usedRegisters, JSValueRegs(base),
-                    JSValueRegs(value), GPRInfo::patchpointScratchRegister, false,
+                    JSValueRegs(value), GPRInfo::patchpointScratchRegister, NeedToSpill,
                     putById.ecmaMode(), putById.putKind());
                 
                 MacroAssembler::Label begin = slowPathJIT.label();
diff --git a/Source/JavaScriptCore/jit/CCallHelpers.h b/Source/JavaScriptCore/jit/CCallHelpers.h
index db82b93..0f9f5dc 100644
--- a/Source/JavaScriptCore/jit/CCallHelpers.h
+++ b/Source/JavaScriptCore/jit/CCallHelpers.h
@@ -372,6 +372,17 @@
         addCallArgument(arg3);
         addCallArgument(arg4);
     }
+    
+    ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4, GPRReg arg5)
+    {
+        resetCallArguments();
+        addCallArgument(GPRInfo::callFrameRegister);
+        addCallArgument(arg1);
+        addCallArgument(arg2);
+        addCallArgument(arg3);
+        addCallArgument(arg4);
+        addCallArgument(arg5);
+    }
 
     ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, TrustedImm32 arg3, GPRReg arg4, GPRReg arg5)
     {
diff --git a/Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp b/Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp
index b2f2127..f8ed51f 100644
--- a/Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp
+++ b/Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp
@@ -49,12 +49,12 @@
 
 JITByIdGenerator::JITByIdGenerator(
     CodeBlock* codeBlock, CodeOrigin codeOrigin, const RegisterSet& usedRegisters,
-    JSValueRegs base, JSValueRegs value, bool registersFlushed)
+    JSValueRegs base, JSValueRegs value, SpillRegistersMode spillMode)
     : JITInlineCacheGenerator(codeBlock, codeOrigin)
     , m_base(base)
     , m_value(value)
 {
-    m_stubInfo->patch.registersFlushed = registersFlushed;
+    m_stubInfo->patch.spillMode = spillMode;
     m_stubInfo->patch.usedRegisters = usedRegisters;
     
     // This is a convenience - in cases where the only registers you're using are base/value,
@@ -129,9 +129,9 @@
 
 JITPutByIdGenerator::JITPutByIdGenerator(
     CodeBlock* codeBlock, CodeOrigin codeOrigin, const RegisterSet& usedRegisters,
-    JSValueRegs base, JSValueRegs value, GPRReg scratch, bool registersFlushed,
+    JSValueRegs base, JSValueRegs value, GPRReg scratch, SpillRegistersMode spillMode,
     ECMAMode ecmaMode, PutKind putKind)
-    : JITByIdGenerator(codeBlock, codeOrigin, usedRegisters, base, value, registersFlushed)
+    : JITByIdGenerator(codeBlock, codeOrigin, usedRegisters, base, value, spillMode)
     , m_scratch(scratch)
     , m_ecmaMode(ecmaMode)
     , m_putKind(putKind)
diff --git a/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h b/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h
index 0e446e9..b8a5e08 100644
--- a/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h
+++ b/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h
@@ -57,7 +57,7 @@
 
     JITByIdGenerator(
         CodeBlock*, CodeOrigin, const RegisterSet&, JSValueRegs base, JSValueRegs value,
-        bool registersFlushed);
+        SpillRegistersMode spillMode);
     
 public:
     void reportSlowPathCall(MacroAssembler::Label slowPathBegin, MacroAssembler::Call call)
@@ -95,9 +95,9 @@
     JITGetByIdGenerator() { }
 
     JITGetByIdGenerator(
-        CodeBlock* codeBlock, CodeOrigin codeOrigin, const RegisterSet& usedRegisters,
-        JSValueRegs base, JSValueRegs value, bool registersFlushed)
-        : JITByIdGenerator(codeBlock, codeOrigin, usedRegisters, base, value, registersFlushed)
+    CodeBlock* codeBlock, CodeOrigin codeOrigin, const RegisterSet& usedRegisters,
+    JSValueRegs base, JSValueRegs value, SpillRegistersMode spillMode)
+    : JITByIdGenerator(codeBlock, codeOrigin, usedRegisters, base, value, spillMode)
     {
     }
     
@@ -110,7 +110,7 @@
 
     JITPutByIdGenerator(
         CodeBlock*, CodeOrigin, const RegisterSet& usedRegisters, JSValueRegs base,
-        JSValueRegs value, GPRReg scratch, bool registersFlushed, ECMAMode, PutKind);
+        JSValueRegs, GPRReg scratch, SpillRegistersMode spillMode, ECMAMode, PutKind);
     
     void generateFastPath(MacroAssembler&);
     
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp
index 336a4ae..e8c99a7 100644
--- a/Source/JavaScriptCore/jit/JITOperations.cpp
+++ b/Source/JavaScriptCore/jit/JITOperations.cpp
@@ -1730,7 +1730,7 @@
 
     // Covers implicit globals. Since they don't exist until they first execute, we didn't know how to cache them at compile time.
     if (modeAndType.type() == GlobalProperty || modeAndType.type() == GlobalPropertyWithVarInjectionChecks) {
-        if (slot.isCacheable() && slot.base() == scope && scope->structure()->propertyAccessesAreCacheable()) {
+        if (slot.isCacheablePut() && slot.base() == scope && scope->structure()->propertyAccessesAreCacheable()) {
             ConcurrentJITLocker locker(codeBlock->m_lock);
             pc[5].u.structure.set(exec->vm(), codeBlock->ownerExecutable(), scope->structure());
             pc[6].u.operand = slot.cachedOffset();
diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h
index 5727ecb..b89317d 100644
--- a/Source/JavaScriptCore/jit/JITOperations.h
+++ b/Source/JavaScriptCore/jit/JITOperations.h
@@ -35,9 +35,11 @@
 #include "JSCJSValue.h"
 #include "MacroAssembler.h"
 #include "PutKind.h"
+#include "SpillRegistersMode.h"
 #include "StructureStubInfo.h"
 #include "VariableWatchpointSet.h"
 
+
 namespace JSC {
 
 class ArrayAllocationProfile;
diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp
index 671a3b1..a746504 100644
--- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp
+++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp
@@ -521,7 +521,7 @@
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), RegisterSet::specialRegisters(),
-        JSValueRegs(regT0), JSValueRegs(regT0), true);
+        JSValueRegs(regT0), JSValueRegs(regT0), DontSpill);
     gen.generateFastPath(*this);
     addSlowCase(gen.slowPathJump());
     m_getByIds.append(gen);
@@ -567,7 +567,7 @@
 
     JITPutByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), RegisterSet::specialRegisters(),
-        JSValueRegs(regT0), JSValueRegs(regT1), regT2, true, m_codeBlock->ecmaMode(),
+        JSValueRegs(regT0), JSValueRegs(regT1), regT2, DontSpill, m_codeBlock->ecmaMode(),
         direct ? Direct : NotDirect);
     
     gen.generateFastPath(*this);
diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
index daf6d2a..b2253c7 100644
--- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
@@ -478,7 +478,7 @@
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), RegisterSet::specialRegisters(),
-        JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), true);
+        JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), DontSpill);
     gen.generateFastPath(*this);
     addSlowCase(gen.slowPathJump());
     m_getByIds.append(gen);
@@ -527,7 +527,7 @@
     JITPutByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), RegisterSet::specialRegisters(),
         JSValueRegs::payloadOnly(regT0), JSValueRegs(regT3, regT2),
-        regT1, true, m_codeBlock->ecmaMode(), direct ? Direct : NotDirect);
+        regT1, DontSpill, m_codeBlock->ecmaMode(), direct ? Direct : NotDirect);
     
     gen.generateFastPath(*this);
     addSlowCase(gen.slowPathJump());
diff --git a/Source/JavaScriptCore/jit/Repatch.cpp b/Source/JavaScriptCore/jit/Repatch.cpp
index 59064f3..c6e417c 100644
--- a/Source/JavaScriptCore/jit/Repatch.cpp
+++ b/Source/JavaScriptCore/jit/Repatch.cpp
@@ -449,7 +449,7 @@
     if (structure->isDictionary())
         return false;
 
-    if (!stubInfo.patch.registersFlushed) {
+    if (stubInfo.patch.spillMode == NeedToSpill) {
         // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
         // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
         // if registers were not flushed, don't do non-Value caching.
@@ -551,7 +551,7 @@
     Structure* structure = baseCell->structure();
     
     if (slot.slotBase() == baseValue) {
-        if (!stubInfo.patch.registersFlushed) {
+        if (stubInfo.patch.spillMode == NeedToSpill) {
             // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
             // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
             // if registers were not flushed, don't do non-Value caching.
@@ -702,7 +702,7 @@
         || baseValue.asCell()->structure()->isDictionary())
         return false;
     
-    if (!stubInfo.patch.registersFlushed) {
+    if (stubInfo.patch.spillMode == NeedToSpill) {
         // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
         // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
         // if registers were not flushed, don't do non-Value caching.
@@ -1126,6 +1126,71 @@
             structure);
 }
 
+static void emitCustomSetterStub(ExecState* exec, const PutPropertySlot& slot,
+    StructureStubInfo& stubInfo, Structure* structure, StructureChain* prototypeChain,
+    CodeLocationLabel failureLabel, RefPtr<JITStubRoutine>& stubRoutine)
+{
+    VM* vm = &exec->vm();
+    ASSERT(stubInfo.patch.spillMode == DontSpill);
+    GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
+#if USE(JSVALUE32_64)
+    GPRReg valueTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
+#endif
+    GPRReg valueGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
+    TempRegisterSet tempRegisters(stubInfo.patch.usedRegisters);
+
+    CCallHelpers stubJit(vm);
+    GPRReg scratchGPR = tempRegisters.getFreeGPR();
+    RELEASE_ASSERT(scratchGPR != InvalidGPRReg);
+    RELEASE_ASSERT(scratchGPR != baseGPR);
+    RELEASE_ASSERT(scratchGPR != valueGPR);
+    MacroAssembler::JumpList failureCases;
+    failureCases.append(branchStructure(stubJit,
+        MacroAssembler::NotEqual,
+        MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()),
+        structure));
+    
+    if (prototypeChain) {
+        for (WriteBarrier<Structure>* it = prototypeChain->head(); *it; ++it)
+            addStructureTransitionCheck((*it)->storedPrototype(), exec->codeBlock(), stubInfo, stubJit, failureCases, scratchGPR);
+    }
+
+    // typedef void (*PutValueFunc)(ExecState*, JSObject* base, EncodedJSValue thisObject, EncodedJSValue value);
+#if USE(JSVALUE64)
+    stubJit.setupArgumentsWithExecState(MacroAssembler::TrustedImmPtr(slot.base()), baseGPR, valueGPR);
+#else
+    stubJit.setupArgumentsWithExecState(MacroAssembler::TrustedImmPtr(slot.base()), baseGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), valueGPR, valueTagGPR);
+#endif
+
+    // Need to make sure that whenever this call is made in the future, we remember the
+    // place that we made it from. It just so happens to be the place that we are at
+    // right now!
+    stubJit.store32(MacroAssembler::TrustedImm32(exec->locationAsRawBits()),
+        CCallHelpers::tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
+    stubJit.storePtr(GPRInfo::callFrameRegister, &vm->topCallFrame);
+
+    MacroAssembler::Call setterCall = stubJit.call();
+    
+    MacroAssembler::Jump success = stubJit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
+
+    stubJit.setupArguments(CCallHelpers::TrustedImmPtr(vm), GPRInfo::callFrameRegister);
+
+    MacroAssembler::Call handlerCall = stubJit.call();
+
+    stubJit.jumpToExceptionHandler();
+    LinkBuffer patchBuffer(*vm, &stubJit, exec->codeBlock());
+
+    patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
+    patchBuffer.link(failureCases, failureLabel);
+    patchBuffer.link(setterCall, FunctionPtr(slot.customSetter()));
+    patchBuffer.link(handlerCall, lookupExceptionHandler);
+
+    stubRoutine = createJITStubRoutine(
+        FINALIZE_CODE_FOR(exec->codeBlock(), patchBuffer, ("PutById custom setter stub for %s, return point %p",
+        toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone).executableAddress())), *vm, exec->codeBlock()->ownerExecutable(), structure);
+}
+
+
 static bool tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
 {
     CodeBlock* codeBlock = exec->codeBlock();
@@ -1137,13 +1202,13 @@
     Structure* structure = baseCell->structure();
     Structure* oldStructure = structure->previousID();
     
-    if (!slot.isCacheable())
+    if (!slot.isCacheablePut() && !slot.isCacheableCustomProperty())
         return false;
     if (!structure->propertyAccessesAreCacheable())
         return false;
 
     // Optimize self access.
-    if (slot.base() == baseValue) {
+    if (slot.base() == baseValue && slot.isCacheablePut()) {
         if (slot.type() == PutPropertySlot::NewProperty) {
             if (structure->isDictionary())
                 return false;
@@ -1190,6 +1255,33 @@
         stubInfo.initPutByIdReplace(*vm, codeBlock->ownerExecutable(), structure);
         return true;
     }
+    if (slot.isCacheableCustomProperty() && stubInfo.patch.spillMode == DontSpill) {
+        RefPtr<JITStubRoutine> stubRoutine;
+
+        StructureChain* prototypeChain = 0;
+        if (baseValue != slot.base()) {
+            PropertyOffset offsetIgnored;
+            if (normalizePrototypeChainForChainAccess(exec, baseCell, slot.base(), ident, offsetIgnored) == InvalidPrototypeChain)
+                return false;
+
+            prototypeChain = structure->prototypeChain(exec);
+        }
+        PolymorphicPutByIdList* list;
+        list = PolymorphicPutByIdList::from(putKind, stubInfo);
+
+        emitCustomSetterStub(exec, slot, stubInfo,
+            structure, prototypeChain,
+            stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
+            stubRoutine);
+
+        list->addAccess(PutByIdAccess::customSetter(*vm, codeBlock->ownerExecutable(), structure, prototypeChain, slot.customSetter(), stubRoutine));
+
+        RepatchBuffer repatchBuffer(codeBlock);
+        repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
+        repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind));
+        RELEASE_ASSERT(!list->isFull());
+        return true;
+    }
 
     return false;
 }
@@ -1214,13 +1306,15 @@
     Structure* structure = baseCell->structure();
     Structure* oldStructure = structure->previousID();
     
-    if (!slot.isCacheable())
+    
+    if (!slot.isCacheablePut() && !slot.isCacheableCustomProperty())
         return false;
+
     if (!structure->propertyAccessesAreCacheable())
         return false;
 
     // Optimize self access.
-    if (slot.base() == baseValue) {
+    if (slot.base() == baseValue && slot.isCacheablePut()) {
         PolymorphicPutByIdList* list;
         RefPtr<JITStubRoutine> stubRoutine;
         
@@ -1282,6 +1376,33 @@
         return true;
     }
 
+    if (slot.isCacheableCustomProperty() && stubInfo.patch.spillMode == DontSpill) {
+        RefPtr<JITStubRoutine> stubRoutine;
+        StructureChain* prototypeChain = 0;
+        if (baseValue != slot.base()) {
+            PropertyOffset offsetIgnored;
+            if (normalizePrototypeChainForChainAccess(exec, baseCell, slot.base(), propertyName, offsetIgnored) == InvalidPrototypeChain)
+                return false;
+
+            prototypeChain = structure->prototypeChain(exec);
+        }
+        PolymorphicPutByIdList* list;
+        list = PolymorphicPutByIdList::from(putKind, stubInfo);
+
+        emitCustomSetterStub(exec, slot, stubInfo,
+            structure, prototypeChain,
+            CodeLocationLabel(list->currentSlowPathTarget()),
+            stubRoutine);
+
+        list->addAccess(PutByIdAccess::customSetter(*vm, codeBlock->ownerExecutable(), structure, prototypeChain, slot.customSetter(), stubRoutine));
+
+        RepatchBuffer repatchBuffer(codeBlock);
+        repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
+        if (list->isFull())
+            repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
+
+        return true;
+    }
     return false;
 }
 
diff --git a/Source/JavaScriptCore/jit/SpillRegistersMode.h b/Source/JavaScriptCore/jit/SpillRegistersMode.h
new file mode 100644
index 0000000..160df2c
--- /dev/null
+++ b/Source/JavaScriptCore/jit/SpillRegistersMode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SpillRegistersMode_h
+#define SpillRegistersMode_h
+
+namespace JSC {
+
+enum SpillRegistersMode { NeedToSpill, DontSpill };
+
+}
+
+#endif
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index 2615ede..a723938 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -636,7 +636,7 @@
     
     if (!LLINT_ALWAYS_ACCESS_SLOW
         && baseValue.isCell()
-        && slot.isCacheable()) {
+        && slot.isCacheablePut()) {
         
         JSCell* baseCell = baseValue.asCell();
         Structure* structure = baseCell->structure();
@@ -1418,7 +1418,7 @@
 
     // Covers implicit globals. Since they don't exist until they first execute, we didn't know how to cache them at compile time.
     if (modeAndType.type() == GlobalProperty || modeAndType.type() == GlobalPropertyWithVarInjectionChecks) {
-        if (slot.isCacheable() && slot.base() == scope && scope->structure()->propertyAccessesAreCacheable()) {
+        if (slot.isCacheablePut() && slot.base() == scope && scope->structure()->propertyAccessesAreCacheable()) {
             ConcurrentJITLocker locker(codeBlock->m_lock);
             pc[5].u.structure.set(exec->vm(), codeBlock->ownerExecutable(), scope->structure());
             pc[6].u.operand = slot.cachedOffset();
diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h
index dba389e..b5ab998 100644
--- a/Source/JavaScriptCore/runtime/Lookup.h
+++ b/Source/JavaScriptCore/runtime/Lookup.h
@@ -306,7 +306,7 @@
                 thisObject->putDirect(exec->vm(), propertyName, value);
         } else if (!(entry->attributes() & ReadOnly)) {
             entry->propertyPutter()(exec, base, JSValue::encode(slot.thisValue()), JSValue::encode(value));
-            slot.setCustomProperty(base, entry->propertyPutter());
+            slot.setCacheableCustomProperty(base, entry->propertyPutter());
         } else if (slot.isStrictMode())
             throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
     }
diff --git a/Source/JavaScriptCore/runtime/PutPropertySlot.h b/Source/JavaScriptCore/runtime/PutPropertySlot.h
index 2004b45..6a505e8 100644
--- a/Source/JavaScriptCore/runtime/PutPropertySlot.h
+++ b/Source/JavaScriptCore/runtime/PutPropertySlot.h
@@ -38,7 +38,7 @@
     
     class PutPropertySlot {
     public:
-        enum Type { Uncachable, ExistingProperty, NewProperty, CustomProperty };
+        enum Type { Uncachable, ExistingProperty, NewProperty, CustomProperty, CacheableCustomProperty };
         enum Context { UnknownContext, PutById, PutByIdEval };
         typedef void (*PutValueFunc)(ExecState*, JSObject* base, EncodedJSValue thisObject, EncodedJSValue value);
 
@@ -72,7 +72,15 @@
             m_base = base;
             m_putFunction = function;
         }
-        
+
+        void setCacheableCustomProperty(JSObject* base, PutValueFunc function)
+        {
+            m_type = CacheableCustomProperty;
+            m_base = base;
+            m_putFunction = function;
+        }
+        PutValueFunc customSetter() const { return m_putFunction; }
+
         Context context() const { return static_cast<Context>(m_context); }
 
         Type type() const { return m_type; }
@@ -80,10 +88,12 @@
         JSValue thisValue() const { return m_thisValue; }
 
         bool isStrictMode() const { return m_isStrictMode; }
-        bool isCacheable() const { return m_type != Uncachable && m_type != CustomProperty; }
+        bool isCacheablePut() const { return m_type == NewProperty || m_type == ExistingProperty; }
+        bool isCacheableCustomProperty() const { return m_type == CacheableCustomProperty; }
+
         PropertyOffset cachedOffset() const
         {
-            ASSERT(isCacheable());
+            ASSERT(isCacheablePut());
             return m_offset;
         }
 
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 94cf6c4..0fd6706 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,17 @@
+2014-03-05  Oliver Hunt  <oliver@apple.com>
+
+        Support caching of custom setters
+        https://bugs.webkit.org/show_bug.cgi?id=129519
+
+        Reviewed by Filip Pizlo.
+
+        Add forwarding header
+
+        Tests: js/regress/assign-custom-setter-polymorphic.html
+               js/regress/assign-custom-setter.html
+
+        * ForwardingHeaders/jit/SpillRegistersMode.h: Added.
+
 2014-03-05  Jon Honeycutt  <jhoneycutt@apple.com>
 
         Invalid cast in WebCore::RenderLayer::FilterInfo::updateReferenceFilterClients()
diff --git a/Source/WebCore/ForwardingHeaders/jit/SpillRegistersMode.h b/Source/WebCore/ForwardingHeaders/jit/SpillRegistersMode.h
new file mode 100644
index 0000000..ffe2249
--- /dev/null
+++ b/Source/WebCore/ForwardingHeaders/jit/SpillRegistersMode.h
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_JITCode_h
+#define WebCore_FWD_JITCode_h
+#include <JavaScriptCore/SpillRegistersMode.h>
+#endif