JSC should use a shadow stack version of CHICKEN so that debuggers have the option...
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Apr 2016 22:17:35 +0000 (22:17 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Apr 2016 22:17:35 +0000 (22:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155598

Reviewed by Saam Barati.
PerformanceTests/SunSpider:

* shadow-chicken.yaml: Added.

Source/JavaScriptCore:

JSC is the first JSVM to have proper tail calls. This means that error.stack and the
debugger will appear to "delete" strict mode stack frames, if the call that this frame made
was in tail position. This is exactly what functional programmers expect - they don't want
the VM to waste resources on tail-deleted frames to ensure that it's legal to loop forever
using tail calls. It's also something that non-functional programmers fear. It's not clear
that tail-deleted frames would actually degrade the debugging experience, but the fear is
real, so it's worthwhile to do something about it.

It turns out that there is at least one tail call implementation that doesn't suffer from
this problem. It implements proper tail calls in the sense that you won't run out of memory
by tail-looping. It also has the power to show you tail-deleted frames in a backtrace, so
long as you haven't yet run out of memory. It's called CHICKEN Scheme, and it's one of my
favorite hacks:

http://www.more-magic.net/posts/internals-gc.html

CHICKEN does many awesome things. The intuition from CHICKEN that we use here is a simple
one: what if a tail call still kept the tail-deleted frame, and the GC actually deleted that
frame only once we proved that there was insufficient memory to keep it around.

CHICKEN does this by reshaping the C stack with longjmp/setjmp. We can't do that because we
can have arbitrary native code, and that native code does not have relocatable stack frames.

But we can do something almost like CHICKEN on a shadow stack. It's a common trick to have a
VM maintain two stacks - the actual execution stack plus a shadow stack that has some extra
information. The shadow stack can be reshaped, moved, etc, since the VM tightly controls its
layout. The main stack can then continue to obey ABI rules.

This patch implements a mechanism for being able to display stack traces that include
tail-deleted frames. It uses a shadow stack that behaves like a CHICKEN stack: it has all
frames all the time, though we will collect the tail-deleted ones if the stack gets too big.
This new mechanism is called ShadowChicken, obviously: it's CHICKEN on a shadow stack.

ShadowChicken is always on, but individual CodeBlocks may make their own choices about
whether to opt into it. They will do that at bytecompile time based on the debugger mode on
their global object.

When no CodeBlock opts in, there is no overhead, since ShadowChicken ends up doing nothing
in that case. Well, except when exceptions are thrown. Then it might do some work, but it's
minor.

When all CodeBlocks opt in, there is about 6% overhead. That's too much overhead to enable
this all the time, but it's low enough to justify enabling in the Inspector. It's currently
enabled on all CodeBlocks only when you use an Option. Otherwise it will auto-enable if the
debugger is on.

Note that ShadowChicken attempts to gracefully handle the presence of stack frames that have
no logging. This is essential since we *can* have debugging enabled in one GlobalObject and
disabled in another. Also, some frames don't do ShadowChicken because they just haven't been
hacked to do it yet. Native frames fall into this category, as do the VM entry frames.

This doesn't yet wire ShadowChicken into DebuggerCallFrame. That will take more work. It
just makes a ShadowChicken stack walk function available to jsc. It's used from the
shadow-chicken tests.

* API/JSContextRef.cpp:
(BacktraceFunctor::BacktraceFunctor):
(BacktraceFunctor::operator()):
(JSContextCreateBacktrace):
* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::RecursionCheckFunctor::RecursionCheckFunctor):
(JSC::RecursionCheckFunctor::operator()):
(JSC::CodeBlock::noticeIncomingCall):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitEnter):
(JSC::BytecodeGenerator::emitCallInTailPosition):
(JSC::BytecodeGenerator::emitCallVarargsInTailPosition):
(JSC::BytecodeGenerator::emitCallVarargs):
(JSC::BytecodeGenerator::emitLogShadowChickenPrologueIfNecessary):
(JSC::BytecodeGenerator::emitLogShadowChickenTailIfNecessary):
(JSC::BytecodeGenerator::emitCallDefineProperty):
* bytecompiler/BytecodeGenerator.h:
* debugger/DebuggerCallFrame.cpp:
(JSC::LineAndColumnFunctor::operator()):
(JSC::LineAndColumnFunctor::column):
(JSC::FindCallerMidStackFunctor::FindCallerMidStackFunctor):
(JSC::FindCallerMidStackFunctor::operator()):
(JSC::DebuggerCallFrame::DebuggerCallFrame):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.cpp:
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileSetRegExpObjectLastIndex):
(JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenPrologue):
(JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenTail):
(JSC::FTL::DFG::LowerDFGToB3::didOverflowStack):
(JSC::FTL::DFG::LowerDFGToB3::allocateJSArray):
(JSC::FTL::DFG::LowerDFGToB3::setupShadowChickenPacket):
(JSC::FTL::DFG::LowerDFGToB3::boolify):
* heap/Heap.cpp:
(JSC::Heap::markRoots):
(JSC::Heap::visitSamplingProfiler):
(JSC::Heap::visitShadowChicken):
(JSC::Heap::traceCodeBlocksAndJITStubRoutines):
(JSC::Heap::collectImpl):
* heap/Heap.h:
* inspector/ScriptCallStackFactory.cpp:
(Inspector::CreateScriptCallStackFunctor::CreateScriptCallStackFunctor):
(Inspector::CreateScriptCallStackFunctor::operator()):
(Inspector::createScriptCallStack):
* interpreter/CallFrame.h:
(JSC::ExecState::iterate):
* interpreter/Interpreter.cpp:
(JSC::DumpRegisterFunctor::DumpRegisterFunctor):
(JSC::DumpRegisterFunctor::operator()):
(JSC::GetStackTraceFunctor::GetStackTraceFunctor):
(JSC::GetStackTraceFunctor::operator()):
(JSC::Interpreter::getStackTrace):
(JSC::GetCatchHandlerFunctor::handler):
(JSC::GetCatchHandlerFunctor::operator()):
(JSC::notifyDebuggerOfUnwinding):
(JSC::UnwindFunctor::UnwindFunctor):
(JSC::UnwindFunctor::operator()):
(JSC::UnwindFunctor::copyCalleeSavesToVMCalleeSavesBuffer):
* interpreter/ShadowChicken.cpp: Added.
(JSC::ShadowChicken::Packet::dump):
(JSC::ShadowChicken::Frame::dump):
(JSC::ShadowChicken::ShadowChicken):
(JSC::ShadowChicken::~ShadowChicken):
(JSC::ShadowChicken::log):
(JSC::ShadowChicken::update):
(JSC::ShadowChicken::visitChildren):
(JSC::ShadowChicken::reset):
(JSC::ShadowChicken::dump):
(JSC::ShadowChicken::functionsOnStack):
* interpreter/ShadowChicken.h: Added.
(JSC::ShadowChicken::Packet::Packet):
(JSC::ShadowChicken::Packet::tailMarker):
(JSC::ShadowChicken::Packet::throwMarker):
(JSC::ShadowChicken::Packet::prologue):
(JSC::ShadowChicken::Packet::tail):
(JSC::ShadowChicken::Packet::throwPacket):
(JSC::ShadowChicken::Packet::operator bool):
(JSC::ShadowChicken::Packet::isPrologue):
(JSC::ShadowChicken::Packet::isTail):
(JSC::ShadowChicken::Packet::isThrow):
(JSC::ShadowChicken::Frame::Frame):
(JSC::ShadowChicken::Frame::operator==):
(JSC::ShadowChicken::Frame::operator!=):
(JSC::ShadowChicken::log):
(JSC::ShadowChicken::logSize):
(JSC::ShadowChicken::addressOfLogCursor):
(JSC::ShadowChicken::logEnd):
* interpreter/ShadowChickenInlines.h: Added.
(JSC::ShadowChicken::iterate):
* interpreter/StackVisitor.h:
(JSC::StackVisitor::Frame::callee):
(JSC::StackVisitor::Frame::codeBlock):
(JSC::StackVisitor::Frame::bytecodeOffset):
(JSC::StackVisitor::Frame::inlineCallFrame):
(JSC::StackVisitor::Frame::isJSFrame):
(JSC::StackVisitor::Frame::isInlinedFrame):
(JSC::StackVisitor::visit):
* jit/CCallHelpers.cpp: Added.
(JSC::CCallHelpers::logShadowChickenProloguePacket):
(JSC::CCallHelpers::logShadowChickenTailPacket):
(JSC::CCallHelpers::setupShadowChickenPacket):
* jit/CCallHelpers.h:
(JSC::CCallHelpers::prepareForTailCallSlow):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITExceptions.cpp:
(JSC::genericUnwind):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_resume):
(JSC::JIT::emit_op_log_shadow_chicken_prologue):
(JSC::JIT::emit_op_log_shadow_chicken_tail):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jsc.cpp:
(GlobalObject::finishCreation):
(FunctionJSCStackFunctor::FunctionJSCStackFunctor):
(FunctionJSCStackFunctor::operator()):
(functionClearSamplingFlags):
(functionShadowChickenFunctionsOnStack):
(functionReadline):
* llint/LLIntOffsetsExtractor.cpp:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(JSC::LLInt::llint_throw_stack_overflow_error):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter.asm:
* profiler/ProfileGenerator.cpp:
(JSC::AddParentForConsoleStartFunctor::foundParent):
(JSC::AddParentForConsoleStartFunctor::operator()):
* runtime/Error.cpp:
(JSC::FindFirstCallerFrameWithCodeblockFunctor::FindFirstCallerFrameWithCodeblockFunctor):
(JSC::FindFirstCallerFrameWithCodeblockFunctor::operator()):
(JSC::addErrorInfoAndGetBytecodeOffset):
* runtime/JSFunction.cpp:
(JSC::RetrieveArgumentsFunctor::result):
(JSC::RetrieveArgumentsFunctor::operator()):
(JSC::retrieveArguments):
(JSC::RetrieveCallerFunctionFunctor::result):
(JSC::RetrieveCallerFunctionFunctor::operator()):
(JSC::retrieveCallerFunction):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::GlobalFuncProtoGetterFunctor::result):
(JSC::GlobalFuncProtoGetterFunctor::operator()):
(JSC::globalFuncProtoGetter):
(JSC::GlobalFuncProtoSetterFunctor::allowsAccess):
(JSC::GlobalFuncProtoSetterFunctor::operator()):
* runtime/NullSetterFunction.cpp:
(JSC::GetCallerStrictnessFunctor::GetCallerStrictnessFunctor):
(JSC::GetCallerStrictnessFunctor::operator()):
(JSC::GetCallerStrictnessFunctor::callerIsStrict):
(JSC::callerIsStrict):
* runtime/ObjectConstructor.cpp:
(JSC::ObjectConstructorGetPrototypeOfFunctor::result):
(JSC::ObjectConstructorGetPrototypeOfFunctor::operator()):
(JSC::objectConstructorGetPrototypeOf):
* runtime/Options.h:
* runtime/VM.cpp:
(JSC::VM::VM):
(JSC::SetEnabledProfilerFunctor::operator()):
* runtime/VM.h:
(JSC::VM::shouldBuilderPCToCodeOriginMapping):
(JSC::VM::bytecodeIntrinsicRegistry):
(JSC::VM::shadowChicken):
* tests/stress/resources/shadow-chicken-support.js: Added.
(describeFunction):
(describeArray):
(expectStack):
(initialize):
* tests/stress/shadow-chicken-disabled.js: Added.
(test1.foo):
(test1.bar):
(test1.baz):
(test1):
(test2.foo):
(test2.bar):
(test2.baz):
(test2):
(test3.foo):
(test3.bar):
(test3.baz):
(test3):
* tests/stress/shadow-chicken-enabled.js: Added.
(test1.foo):
(test1.bar):
(test1.baz):
(test1):
(test2.foo):
(test2.bar):
(test2.baz):
(test2):
(test3.bob):
(test3.thingy):
(test3.foo):
(test3.bar):
(test3.baz):
(test3):
(test4.bob):
(test4.thingy):
(test4.foo):
(test4.bar):
(test4.baz):
(test4):
(test5.foo):
(test5):
* tools/JSDollarVMPrototype.cpp:
(JSC::CallerFrameJITTypeFunctor::CallerFrameJITTypeFunctor):
(JSC::CallerFrameJITTypeFunctor::operator()):
(JSC::CallerFrameJITTypeFunctor::jitType):
(JSC::functionLLintTrue):
(JSC::CellAddressCheckFunctor::CellAddressCheckFunctor):
(JSC::CellAddressCheckFunctor::operator()):
(JSC::JSDollarVMPrototype::isValidCell):
(JSC::JSDollarVMPrototype::isValidCodeBlock):
(JSC::JSDollarVMPrototype::codeBlockForFrame):
(JSC::PrintFrameFunctor::PrintFrameFunctor):
(JSC::PrintFrameFunctor::operator()):
(JSC::printCallFrame):

Source/WebCore:

Fixed some uses of the stack walking functor to obey the new lambda-friendly API, which
requires that operator() is const.

No new tests because no change in behavior.

* bindings/js/JSXMLHttpRequestCustom.cpp:
(WebCore::SendFunctor::column):
(WebCore::SendFunctor::url):
(WebCore::SendFunctor::operator()):
(WebCore::JSXMLHttpRequest::send):
* testing/Internals.cpp:
(WebCore::GetCallerCodeBlockFunctor::GetCallerCodeBlockFunctor):
(WebCore::GetCallerCodeBlockFunctor::operator()):
(WebCore::GetCallerCodeBlockFunctor::codeBlock):
(WebCore::Internals::parserMetaData):

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

72 files changed:
PerformanceTests/SunSpider/ChangeLog
PerformanceTests/SunSpider/shadow-chicken.yaml [new file with mode: 0644]
Source/JavaScriptCore/API/JSContextRef.cpp
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/heap/Heap.cpp
Source/JavaScriptCore/heap/Heap.h
Source/JavaScriptCore/inspector/ScriptCallStackFactory.cpp
Source/JavaScriptCore/interpreter/CallFrame.h
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/ShadowChicken.cpp [new file with mode: 0644]
Source/JavaScriptCore/interpreter/ShadowChicken.h [new file with mode: 0644]
Source/JavaScriptCore/interpreter/ShadowChickenInlines.h [new file with mode: 0644]
Source/JavaScriptCore/interpreter/StackVisitor.h
Source/JavaScriptCore/jit/CCallHelpers.cpp [new file with mode: 0644]
Source/JavaScriptCore/jit/CCallHelpers.h
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITExceptions.cpp
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/ThunkGenerators.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/llint/LLIntOffsetsExtractor.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/profiler/ProfileGenerator.cpp
Source/JavaScriptCore/runtime/Error.cpp
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/NullSetterFunction.cpp
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/runtime/StringPrototype.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tests/stress/resources/shadow-chicken-support.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/shadow-chicken-disabled.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/shadow-chicken-enabled.js [new file with mode: 0644]
Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp
Source/WebCore/testing/Internals.cpp
Tools/Scripts/run-javascriptcore-tests
Tools/Scripts/run-jsc-stress-tests

index a98b5c4e012be70e14a2ac3be51058301267faba..21614bd40b00a69a44c98e4db651b0957e20abb4 100644 (file)
@@ -1,3 +1,12 @@
+2016-03-21 Filip Pizlo <fpizlo@apple.com>
+
+        JSC should use a shadow stack version of CHICKEN so that debuggers have the option of retrieving tail-deleted frames
+        https://bugs.webkit.org/show_bug.cgi?id=155598
+
+        Reviewed by Saam Barati.
+
+        * shadow-chicken.yaml: Added.
+
 2015-03-30  Michael Saboff  <msaboff@apple.com>
 
         Fix failing v8-deltablue.js for ARM
diff --git a/PerformanceTests/SunSpider/shadow-chicken.yaml b/PerformanceTests/SunSpider/shadow-chicken.yaml
new file mode 100644 (file)
index 0000000..45bfdd1
--- /dev/null
@@ -0,0 +1,34 @@
+# Copyright (C) 2013 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 AND ITS CONTRIBUTORS "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 OR ITS 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.
+
+# Tests SunSpider with architecture-specific optimizations disabled. This is
+# just here so you can't get away with murder when dealing with the generic
+# (i.e. no ASO) paths. You should still likely actually *test* all of the
+# architectures you care about.
+
+- path: tests/sunspider-1.0
+  cmd: runShadowChicken
+
+- path: tests/v8-v6
+  cmd: runShadowChicken
+
index 98cd8b0302dbcadc12fcfe773728c8e8d6516326..f2af448cb8ca70efacebed9bef04a3909465e707 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -255,7 +255,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (m_remainingCapacityForFrameCapture) {
             // If callee is unknown, but we've not added any frame yet, we should
@@ -292,7 +292,7 @@ public:
 
 private:
     StringBuilder& m_builder;
-    unsigned m_remainingCapacityForFrameCapture;
+    mutable unsigned m_remainingCapacityForFrameCapture;
 };
 
 JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize)
index 2d2bf4a01f916969aa88ad70699ae153a58ea217..99319e21842dc458f9f12303aa73168b5be1c20e 100644 (file)
@@ -507,10 +507,12 @@ set(JavaScriptCore_SOURCES
     interpreter/Interpreter.cpp
     interpreter/JSStack.cpp
     interpreter/ProtoCallFrame.cpp
+    interpreter/ShadowChicken.cpp
     interpreter/StackVisitor.cpp
 
     jit/AssemblyHelpers.cpp
     jit/BinarySwitch.cpp
+    jit/CCallHelpers.cpp
     jit/CachedRecovery.cpp
     jit/CallFrameShuffleData.cpp
     jit/CallFrameShuffler.cpp
index eae9a741980f397f95167d284a53136c91d8744e..ba518d544b034fb8bb62b024b627d03e51960e77 100644 (file)
@@ -1,3 +1,313 @@
+2016-03-18  Filip Pizlo  <fpizlo@apple.com>
+
+        JSC should use a shadow stack version of CHICKEN so that debuggers have the option of retrieving tail-deleted frames
+        https://bugs.webkit.org/show_bug.cgi?id=155598
+
+        Reviewed by Saam Barati.
+        
+        JSC is the first JSVM to have proper tail calls. This means that error.stack and the
+        debugger will appear to "delete" strict mode stack frames, if the call that this frame made
+        was in tail position. This is exactly what functional programmers expect - they don't want
+        the VM to waste resources on tail-deleted frames to ensure that it's legal to loop forever
+        using tail calls. It's also something that non-functional programmers fear. It's not clear
+        that tail-deleted frames would actually degrade the debugging experience, but the fear is
+        real, so it's worthwhile to do something about it.
+
+        It turns out that there is at least one tail call implementation that doesn't suffer from
+        this problem. It implements proper tail calls in the sense that you won't run out of memory
+        by tail-looping. It also has the power to show you tail-deleted frames in a backtrace, so
+        long as you haven't yet run out of memory. It's called CHICKEN Scheme, and it's one of my
+        favorite hacks:
+        
+        http://www.more-magic.net/posts/internals-gc.html
+
+        CHICKEN does many awesome things. The intuition from CHICKEN that we use here is a simple
+        one: what if a tail call still kept the tail-deleted frame, and the GC actually deleted that
+        frame only once we proved that there was insufficient memory to keep it around.
+        
+        CHICKEN does this by reshaping the C stack with longjmp/setjmp. We can't do that because we
+        can have arbitrary native code, and that native code does not have relocatable stack frames.
+        
+        But we can do something almost like CHICKEN on a shadow stack. It's a common trick to have a
+        VM maintain two stacks - the actual execution stack plus a shadow stack that has some extra
+        information. The shadow stack can be reshaped, moved, etc, since the VM tightly controls its
+        layout. The main stack can then continue to obey ABI rules.
+
+        This patch implements a mechanism for being able to display stack traces that include
+        tail-deleted frames. It uses a shadow stack that behaves like a CHICKEN stack: it has all
+        frames all the time, though we will collect the tail-deleted ones if the stack gets too big.
+        This new mechanism is called ShadowChicken, obviously: it's CHICKEN on a shadow stack.
+        
+        ShadowChicken is always on, but individual CodeBlocks may make their own choices about
+        whether to opt into it. They will do that at bytecompile time based on the debugger mode on
+        their global object.
+
+        When no CodeBlock opts in, there is no overhead, since ShadowChicken ends up doing nothing
+        in that case. Well, except when exceptions are thrown. Then it might do some work, but it's
+        minor.
+
+        When all CodeBlocks opt in, there is about 6% overhead. That's too much overhead to enable
+        this all the time, but it's low enough to justify enabling in the Inspector. It's currently
+        enabled on all CodeBlocks only when you use an Option. Otherwise it will auto-enable if the
+        debugger is on.
+
+        Note that ShadowChicken attempts to gracefully handle the presence of stack frames that have
+        no logging. This is essential since we *can* have debugging enabled in one GlobalObject and
+        disabled in another. Also, some frames don't do ShadowChicken because they just haven't been
+        hacked to do it yet. Native frames fall into this category, as do the VM entry frames.
+
+        This doesn't yet wire ShadowChicken into DebuggerCallFrame. That will take more work. It
+        just makes a ShadowChicken stack walk function available to jsc. It's used from the
+        shadow-chicken tests.
+
+        * API/JSContextRef.cpp:
+        (BacktraceFunctor::BacktraceFunctor):
+        (BacktraceFunctor::operator()):
+        (JSContextCreateBacktrace):
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        (JSC::RecursionCheckFunctor::RecursionCheckFunctor):
+        (JSC::RecursionCheckFunctor::operator()):
+        (JSC::CodeBlock::noticeIncomingCall):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitEnter):
+        (JSC::BytecodeGenerator::emitCallInTailPosition):
+        (JSC::BytecodeGenerator::emitCallVarargsInTailPosition):
+        (JSC::BytecodeGenerator::emitCallVarargs):
+        (JSC::BytecodeGenerator::emitLogShadowChickenPrologueIfNecessary):
+        (JSC::BytecodeGenerator::emitLogShadowChickenTailIfNecessary):
+        (JSC::BytecodeGenerator::emitCallDefineProperty):
+        * bytecompiler/BytecodeGenerator.h:
+        * debugger/DebuggerCallFrame.cpp:
+        (JSC::LineAndColumnFunctor::operator()):
+        (JSC::LineAndColumnFunctor::column):
+        (JSC::FindCallerMidStackFunctor::FindCallerMidStackFunctor):
+        (JSC::FindCallerMidStackFunctor::operator()):
+        (JSC::DebuggerCallFrame::DebuggerCallFrame):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLAbstractHeapRepository.cpp:
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileSetRegExpObjectLastIndex):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenPrologue):
+        (JSC::FTL::DFG::LowerDFGToB3::compileLogShadowChickenTail):
+        (JSC::FTL::DFG::LowerDFGToB3::didOverflowStack):
+        (JSC::FTL::DFG::LowerDFGToB3::allocateJSArray):
+        (JSC::FTL::DFG::LowerDFGToB3::setupShadowChickenPacket):
+        (JSC::FTL::DFG::LowerDFGToB3::boolify):
+        * heap/Heap.cpp:
+        (JSC::Heap::markRoots):
+        (JSC::Heap::visitSamplingProfiler):
+        (JSC::Heap::visitShadowChicken):
+        (JSC::Heap::traceCodeBlocksAndJITStubRoutines):
+        (JSC::Heap::collectImpl):
+        * heap/Heap.h:
+        * inspector/ScriptCallStackFactory.cpp:
+        (Inspector::CreateScriptCallStackFunctor::CreateScriptCallStackFunctor):
+        (Inspector::CreateScriptCallStackFunctor::operator()):
+        (Inspector::createScriptCallStack):
+        * interpreter/CallFrame.h:
+        (JSC::ExecState::iterate):
+        * interpreter/Interpreter.cpp:
+        (JSC::DumpRegisterFunctor::DumpRegisterFunctor):
+        (JSC::DumpRegisterFunctor::operator()):
+        (JSC::GetStackTraceFunctor::GetStackTraceFunctor):
+        (JSC::GetStackTraceFunctor::operator()):
+        (JSC::Interpreter::getStackTrace):
+        (JSC::GetCatchHandlerFunctor::handler):
+        (JSC::GetCatchHandlerFunctor::operator()):
+        (JSC::notifyDebuggerOfUnwinding):
+        (JSC::UnwindFunctor::UnwindFunctor):
+        (JSC::UnwindFunctor::operator()):
+        (JSC::UnwindFunctor::copyCalleeSavesToVMCalleeSavesBuffer):
+        * interpreter/ShadowChicken.cpp: Added.
+        (JSC::ShadowChicken::Packet::dump):
+        (JSC::ShadowChicken::Frame::dump):
+        (JSC::ShadowChicken::ShadowChicken):
+        (JSC::ShadowChicken::~ShadowChicken):
+        (JSC::ShadowChicken::log):
+        (JSC::ShadowChicken::update):
+        (JSC::ShadowChicken::visitChildren):
+        (JSC::ShadowChicken::reset):
+        (JSC::ShadowChicken::dump):
+        (JSC::ShadowChicken::functionsOnStack):
+        * interpreter/ShadowChicken.h: Added.
+        (JSC::ShadowChicken::Packet::Packet):
+        (JSC::ShadowChicken::Packet::tailMarker):
+        (JSC::ShadowChicken::Packet::throwMarker):
+        (JSC::ShadowChicken::Packet::prologue):
+        (JSC::ShadowChicken::Packet::tail):
+        (JSC::ShadowChicken::Packet::throwPacket):
+        (JSC::ShadowChicken::Packet::operator bool):
+        (JSC::ShadowChicken::Packet::isPrologue):
+        (JSC::ShadowChicken::Packet::isTail):
+        (JSC::ShadowChicken::Packet::isThrow):
+        (JSC::ShadowChicken::Frame::Frame):
+        (JSC::ShadowChicken::Frame::operator==):
+        (JSC::ShadowChicken::Frame::operator!=):
+        (JSC::ShadowChicken::log):
+        (JSC::ShadowChicken::logSize):
+        (JSC::ShadowChicken::addressOfLogCursor):
+        (JSC::ShadowChicken::logEnd):
+        * interpreter/ShadowChickenInlines.h: Added.
+        (JSC::ShadowChicken::iterate):
+        * interpreter/StackVisitor.h:
+        (JSC::StackVisitor::Frame::callee):
+        (JSC::StackVisitor::Frame::codeBlock):
+        (JSC::StackVisitor::Frame::bytecodeOffset):
+        (JSC::StackVisitor::Frame::inlineCallFrame):
+        (JSC::StackVisitor::Frame::isJSFrame):
+        (JSC::StackVisitor::Frame::isInlinedFrame):
+        (JSC::StackVisitor::visit):
+        * jit/CCallHelpers.cpp: Added.
+        (JSC::CCallHelpers::logShadowChickenProloguePacket):
+        (JSC::CCallHelpers::logShadowChickenTailPacket):
+        (JSC::CCallHelpers::setupShadowChickenPacket):
+        * jit/CCallHelpers.h:
+        (JSC::CCallHelpers::prepareForTailCallSlow):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITExceptions.cpp:
+        (JSC::genericUnwind):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_resume):
+        (JSC::JIT::emit_op_log_shadow_chicken_prologue):
+        (JSC::JIT::emit_op_log_shadow_chicken_tail):
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (FunctionJSCStackFunctor::FunctionJSCStackFunctor):
+        (FunctionJSCStackFunctor::operator()):
+        (functionClearSamplingFlags):
+        (functionShadowChickenFunctionsOnStack):
+        (functionReadline):
+        * llint/LLIntOffsetsExtractor.cpp:
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        (JSC::LLInt::llint_throw_stack_overflow_error):
+        * llint/LLIntSlowPaths.h:
+        * llint/LowLevelInterpreter.asm:
+        * profiler/ProfileGenerator.cpp:
+        (JSC::AddParentForConsoleStartFunctor::foundParent):
+        (JSC::AddParentForConsoleStartFunctor::operator()):
+        * runtime/Error.cpp:
+        (JSC::FindFirstCallerFrameWithCodeblockFunctor::FindFirstCallerFrameWithCodeblockFunctor):
+        (JSC::FindFirstCallerFrameWithCodeblockFunctor::operator()):
+        (JSC::addErrorInfoAndGetBytecodeOffset):
+        * runtime/JSFunction.cpp:
+        (JSC::RetrieveArgumentsFunctor::result):
+        (JSC::RetrieveArgumentsFunctor::operator()):
+        (JSC::retrieveArguments):
+        (JSC::RetrieveCallerFunctionFunctor::result):
+        (JSC::RetrieveCallerFunctionFunctor::operator()):
+        (JSC::retrieveCallerFunction):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::GlobalFuncProtoGetterFunctor::result):
+        (JSC::GlobalFuncProtoGetterFunctor::operator()):
+        (JSC::globalFuncProtoGetter):
+        (JSC::GlobalFuncProtoSetterFunctor::allowsAccess):
+        (JSC::GlobalFuncProtoSetterFunctor::operator()):
+        * runtime/NullSetterFunction.cpp:
+        (JSC::GetCallerStrictnessFunctor::GetCallerStrictnessFunctor):
+        (JSC::GetCallerStrictnessFunctor::operator()):
+        (JSC::GetCallerStrictnessFunctor::callerIsStrict):
+        (JSC::callerIsStrict):
+        * runtime/ObjectConstructor.cpp:
+        (JSC::ObjectConstructorGetPrototypeOfFunctor::result):
+        (JSC::ObjectConstructorGetPrototypeOfFunctor::operator()):
+        (JSC::objectConstructorGetPrototypeOf):
+        * runtime/Options.h:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        (JSC::SetEnabledProfilerFunctor::operator()):
+        * runtime/VM.h:
+        (JSC::VM::shouldBuilderPCToCodeOriginMapping):
+        (JSC::VM::bytecodeIntrinsicRegistry):
+        (JSC::VM::shadowChicken):
+        * tests/stress/resources/shadow-chicken-support.js: Added.
+        (describeFunction):
+        (describeArray):
+        (expectStack):
+        (initialize):
+        * tests/stress/shadow-chicken-disabled.js: Added.
+        (test1.foo):
+        (test1.bar):
+        (test1.baz):
+        (test1):
+        (test2.foo):
+        (test2.bar):
+        (test2.baz):
+        (test2):
+        (test3.foo):
+        (test3.bar):
+        (test3.baz):
+        (test3):
+        * tests/stress/shadow-chicken-enabled.js: Added.
+        (test1.foo):
+        (test1.bar):
+        (test1.baz):
+        (test1):
+        (test2.foo):
+        (test2.bar):
+        (test2.baz):
+        (test2):
+        (test3.bob):
+        (test3.thingy):
+        (test3.foo):
+        (test3.bar):
+        (test3.baz):
+        (test3):
+        (test4.bob):
+        (test4.thingy):
+        (test4.foo):
+        (test4.bar):
+        (test4.baz):
+        (test4):
+        (test5.foo):
+        (test5):
+        * tools/JSDollarVMPrototype.cpp:
+        (JSC::CallerFrameJITTypeFunctor::CallerFrameJITTypeFunctor):
+        (JSC::CallerFrameJITTypeFunctor::operator()):
+        (JSC::CallerFrameJITTypeFunctor::jitType):
+        (JSC::functionLLintTrue):
+        (JSC::CellAddressCheckFunctor::CellAddressCheckFunctor):
+        (JSC::CellAddressCheckFunctor::operator()):
+        (JSC::JSDollarVMPrototype::isValidCell):
+        (JSC::JSDollarVMPrototype::isValidCodeBlock):
+        (JSC::JSDollarVMPrototype::codeBlockForFrame):
+        (JSC::PrintFrameFunctor::PrintFrameFunctor):
+        (JSC::PrintFrameFunctor::operator()):
+        (JSC::printCallFrame):
+
 2016-03-19  Filip Pizlo  <fpizlo@apple.com>
 
         DFG and FTL should constant-fold RegExpExec, RegExpTest, and StringReplace
index 13c032688a603016dc3ca22de29a490b86114a11..bac653907ee4316ff32b89afa5153dbad2de0ae7 100644 (file)
                C4F4B6F51A05C984005CAB76 /* generate_objc_protocol_types_implementation.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D71A05C76F005CAB76 /* generate_objc_protocol_types_implementation.py */; settings = {ATTRIBUTES = (Private, ); }; };
                C4F4B6F61A05C984005CAB76 /* objc_generator_templates.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D81A05C76F005CAB76 /* objc_generator_templates.py */; settings = {ATTRIBUTES = (Private, ); }; };
                DC00039319D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h in Headers */ = {isa = PBXBuildFile; fileRef = DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */; };
+               DC17E8171C9C91D6008A6AB3 /* ShadowChicken.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC17E8131C9C7FD4008A6AB3 /* ShadowChicken.cpp */; };
+               DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8141C9C7FD4008A6AB3 /* ShadowChicken.h */; };
+               DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8151C9C7FD4008A6AB3 /* ShadowChickenInlines.h */; };
+               DC17E81A1C9C91E9008A6AB3 /* CCallHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */; };
                DE5A0A001BA3AC3E003D4424 /* IntrinsicEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DE5A09FF1BA3AC3E003D4424 /* IntrinsicEmitter.cpp */; };
                DEA7E2441BBC677200D78440 /* JSTypedArrayViewPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53F256E11B87E28000B4B768 /* JSTypedArrayViewPrototype.cpp */; };
                DEA7E2451BBC677F00D78440 /* JSTypedArrayViewPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 53917E7C1B791106000EBD33 /* JSTypedArrayViewPrototype.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D21202280AD4310C00ED79B6 /* DateConversion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DateConversion.cpp; sourceTree = "<group>"; };
                D21202290AD4310C00ED79B6 /* DateConversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DateConversion.h; sourceTree = "<group>"; };
                DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPreciseLocalClobberize.h; path = dfg/DFGPreciseLocalClobberize.h; sourceTree = "<group>"; };
+               DC17E8131C9C7FD4008A6AB3 /* ShadowChicken.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShadowChicken.cpp; sourceTree = "<group>"; };
+               DC17E8141C9C7FD4008A6AB3 /* ShadowChicken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowChicken.h; sourceTree = "<group>"; };
+               DC17E8151C9C7FD4008A6AB3 /* ShadowChickenInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowChickenInlines.h; sourceTree = "<group>"; };
+               DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CCallHelpers.cpp; sourceTree = "<group>"; };
                DE5A09FF1BA3AC3E003D4424 /* IntrinsicEmitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntrinsicEmitter.cpp; sourceTree = "<group>"; };
                E124A8F50E555775003091F1 /* OpaqueJSString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueJSString.h; sourceTree = "<group>"; };
                E124A8F60E555775003091F1 /* OpaqueJSString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpaqueJSString.cpp; sourceTree = "<group>"; };
                                65FB5116184EE9BC00C12B70 /* ProtoCallFrame.cpp */,
                                65FB5115184EE8F800C12B70 /* ProtoCallFrame.h */,
                                149B24FF0D8AF6D1009CB8C7 /* Register.h */,
+                               DC17E8131C9C7FD4008A6AB3 /* ShadowChicken.cpp */,
+                               DC17E8141C9C7FD4008A6AB3 /* ShadowChicken.h */,
+                               DC17E8151C9C7FD4008A6AB3 /* ShadowChickenInlines.h */,
                                A7C1EAEC17987AB600299DB2 /* StackVisitor.cpp */,
                                A7C1EAED17987AB600299DB2 /* StackVisitor.h */,
                                658D3A5519638268003C45D6 /* VMEntryRecord.h */,
                                62D755D11B84FB39001801FA /* CallFrameShuffler.h */,
                                62D755D21B84FB39001801FA /* CallFrameShuffler32_64.cpp */,
                                62D755D31B84FB39001801FA /* CallFrameShuffler64.cpp */,
+                               DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */,
                                0F24E53D17EA9F5900ABB217 /* CCallHelpers.h */,
                                0FD82E37141AB14200179C94 /* CompactJITCodeMap.h */,
                                0FF054F71AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp */,
                                BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */,
                                A5EA70EE19F5B5C40098F5EC /* JSContextRefInspectorSupport.h in Headers */,
                                A5D2E665195E174000A518E7 /* JSContextRefInternal.h in Headers */,
+                               DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */,
                                A5AB49DD1BEC8086007020FB /* PerGlobalObjectWrapperWorld.h in Headers */,
                                148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,
                                A72028B81797601E0098028C /* JSCTestRunnerUtils.h in Headers */,
                                A7CA3AE617DA41AE006538AF /* WeakMapPrototype.h in Headers */,
                                0F242DA713F3B1E8007ADD4C /* WeakReferenceHarvester.h in Headers */,
                                14E84FA114EE1ACC00D6D5D4 /* WeakSet.h in Headers */,
+                               DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */,
                                709FB86A1AE335C60039D069 /* WeakSetConstructor.h in Headers */,
                                14150133154BB13F005D8C98 /* WeakSetInlines.h in Headers */,
                                709FB86C1AE335C60039D069 /* WeakSetPrototype.h in Headers */,
                                FE4238901BE18C3C00514737 /* JITSubGenerator.cpp in Sources */,
                                70B791941C024A28002481E2 /* GeneratorFunctionConstructor.cpp in Sources */,
                                0F5EF91E16878F7A003E5C25 /* JITThunks.cpp in Sources */,
+                               DC17E81A1C9C91E9008A6AB3 /* CCallHelpers.cpp in Sources */,
                                0FC712E217CD8791008CC93C /* JITToDFGDeferredCompilationCallback.cpp in Sources */,
                                140566C4107EC255005DBC8D /* JSAPIValueWrapper.cpp in Sources */,
                                C2CF39C116E15A8100DD69BE /* JSAPIWrapperObject.mm in Sources */,
                                14874AE515EBDE4A002E3587 /* JSScope.cpp in Sources */,
                                A7C0C4AD1681067E0017011D /* JSScriptRef.cpp in Sources */,
                                0F919D10157F3329004A4E7D /* JSSegmentedVariableObject.cpp in Sources */,
+                               DC17E8171C9C91D6008A6AB3 /* ShadowChicken.cpp in Sources */,
                                A7299D9D17D12837005F5FF9 /* JSSet.cpp in Sources */,
                                A790DD6F182F499700588807 /* JSSetIterator.cpp in Sources */,
                                1428083A107EC0750013E7B2 /* JSStack.cpp in Sources */,
index 5e93ef342d8e82e65365bcdedcf40fb6d91d7f0b..e8eb9b6bde484c1701590c1aaf92cc0d46ab755e 100644 (file)
             { "name" : "op_get_rest_length", "length": 3 },
             { "name" : "op_save", "length" : 4 },
             { "name" : "op_resume", "length" : 3 },
-            { "name" : "op_watchdog", "length" : 1 }
+            { "name" : "op_watchdog", "length" : 1 },
+            { "name" : "op_log_shadow_chicken_prologue", "length" : 1},
+            { "name" : "op_log_shadow_chicken_tail", "length" : 1}
         ]
     },
     {
index c82f6f95ceeb85924dc9e4fa1edcb15e1669fb17..33c04a879fc783b359c78aa1126af56259d616f4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -55,6 +55,8 @@ void computeUsesForBytecodeOffset(
     case op_create_cloned_arguments:
     case op_get_rest_length:
     case op_watchdog:
+    case op_log_shadow_chicken_prologue:
+    case op_log_shadow_chicken_tail:
         return;
     case op_assert:
     case op_get_scope:
@@ -326,6 +328,8 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* bloc
     case op_put_to_arguments:
     case op_set_function_name:
     case op_watchdog:
+    case op_log_shadow_chicken_prologue:
+    case op_log_shadow_chicken_tail:
 #define LLINT_HELPER_OPCODES(opcode, length) case opcode:
         FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES);
 #undef LLINT_HELPER_OPCODES
index 883444df15eb855a44280f8aeaa9905952bc82b9..73b87bdb3bd18bcd656a06e2fdf3086cc30b5164 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2010, 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2010, 2012-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1315,6 +1315,14 @@ void CodeBlock::dumpBytecode(
             printLocationAndOp(out, exec, location, it, "watchdog");
             break;
         }
+        case op_log_shadow_chicken_prologue: {
+            printLocationAndOp(out, exec, location, it, "log_shadow_chicken_prologue");
+            break;
+        }
+        case op_log_shadow_chicken_tail: {
+            printLocationAndOp(out, exec, location, it, "log_shadow_chicken_tail");
+            break;
+        }
         case op_switch_imm: {
             int tableIndex = (++it)->u.operand;
             int defaultTarget = (++it)->u.operand;
@@ -3394,7 +3402,7 @@ public:
         , m_didRecurse(false)
     { }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         CallFrame* currentCallFrame = visitor->callFrame();
 
@@ -3419,9 +3427,9 @@ public:
 private:
     CallFrame* m_startCallFrame;
     CodeBlock* m_codeBlock;
-    unsigned m_depthToCheck;
-    bool m_foundStartCallFrame;
-    bool m_didRecurse;
+    mutable unsigned m_depthToCheck;
+    mutable bool m_foundStartCallFrame;
+    mutable bool m_didRecurse;
 };
 
 void CodeBlock::noticeIncomingCall(ExecState* callerFrame)
index 30faf582aeb991546d4e7afadb159f4d17267730..b46be5896beb9f813fbefde043c689888b3f684e 100644 (file)
@@ -1092,6 +1092,7 @@ UnlinkedValueProfile BytecodeGenerator::emitProfiledOpcode(OpcodeID opcodeID)
 void BytecodeGenerator::emitEnter()
 {
     emitOpcode(op_enter);
+    emitLogShadowChickenPrologueIfNecessary();
     emitWatchdog();
 }
 
@@ -2972,7 +2973,7 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi
 {
     ASSERT(opcodeID == op_call || opcodeID == op_call_eval || opcodeID == op_tail_call);
     ASSERT(func->refCount());
-
+    
     if (m_shouldEmitProfileHooks)
         emitMove(callArguments.profileHookRegister(), func);
 
@@ -3007,6 +3008,9 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi
     RefPtr<Label> done = newLabel();
     expectedFunction = emitExpectedFunctionSnippet(dst, func, expectedFunction, callArguments, done.get());
     
+    if (opcodeID == op_tail_call)
+        emitLogShadowChickenTailIfNecessary();
+    
     // Emit call.
     UnlinkedArrayProfile arrayProfile = newArrayProfile();
     UnlinkedValueProfile profile = emitProfiledOpcode(opcodeID);
@@ -3057,6 +3061,9 @@ RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst,
     
     emitExpressionInfo(divot, divotStart, divotEnd);
 
+    if (opcode == op_tail_call_varargs)
+        emitLogShadowChickenTailIfNecessary();
+    
     // Emit call.
     UnlinkedArrayProfile arrayProfile = newArrayProfile();
     UnlinkedValueProfile profile = emitProfiledOpcode(opcode);
@@ -3076,6 +3083,20 @@ RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst,
     return dst;
 }
 
+void BytecodeGenerator::emitLogShadowChickenPrologueIfNecessary()
+{
+    if (!m_shouldEmitDebugHooks && !Options::alwaysUseShadowChicken())
+        return;
+    emitOpcode(op_log_shadow_chicken_prologue);
+}
+
+void BytecodeGenerator::emitLogShadowChickenTailIfNecessary()
+{
+    if (!m_shouldEmitDebugHooks && !Options::alwaysUseShadowChicken())
+        return;
+    emitOpcode(op_log_shadow_chicken_tail);
+}
+
 void BytecodeGenerator::emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister,
     RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition& position)
 {
index 8513a08a41ce21948a85e65566b785f7d9ab5d93..ef88e30a924c35443ea84c4a64d5158064d75415 100644 (file)
@@ -836,6 +836,9 @@ namespace JSC {
 
         RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
         RegisterID* emitCallVarargs(OpcodeID, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
+        
+        void emitLogShadowChickenPrologueIfNecessary();
+        void emitLogShadowChickenTailIfNecessary();
 
         void initializeParameters(FunctionParameters&);
         void initializeVarLexicalEnvironment(int symbolTableConstantIndex);
index 8eb25bd98f0c296550766480863d85d89b6535c6..c4fda5a0c003a593f3a202a23daff21c07d9b817 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013-2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 namespace JSC {
 
+// FIXME: Make this use ShadowChicken so that it sees tail-deleted frames.
+// https://bugs.webkit.org/show_bug.cgi?id=155690
+
 class LineAndColumnFunctor {
 public:
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         visitor->computeLineAndColumn(m_line, m_column);
         return StackVisitor::Done;
@@ -54,8 +57,8 @@ public:
     unsigned column() const { return m_column; }
 
 private:
-    unsigned m_line;
-    unsigned m_column;
+    mutable unsigned m_line;
+    mutable unsigned m_column;
 };
 
 class FindCallerMidStackFunctor {
@@ -65,7 +68,7 @@ public:
         , m_callerFrame(nullptr)
     { }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (visitor->callFrame() == m_callFrame) {
             m_callerFrame = visitor->callerFrame();
@@ -78,7 +81,7 @@ public:
 
 private:
     CallFrame* m_callFrame;
-    CallFrame* m_callerFrame;
+    mutable CallFrame* m_callerFrame;
 };
 
 DebuggerCallFrame::DebuggerCallFrame(CallFrame* callFrame)
index f284d18bb6eb7348b0418e62ff9cb45a92631fb9..7ca6bbc1db77a424047d36d827ed743969232a7a 100644 (file)
@@ -2626,6 +2626,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
 
     case CheckWatchdogTimer:
+    case LogShadowChickenPrologue:
+    case LogShadowChickenTail:
         break;
 
     case ProfileWillCall:
index 6716687788d06504e2ed28bdf9b2bb196244b609..843f1ee1456f607ad165ce0b23bd379642197ad8 100644 (file)
@@ -4749,6 +4749,22 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 get(VirtualRegister(currentInstruction[2].u.operand))));
             NEXT_OPCODE(op_to_index_string);
         }
+            
+        case op_log_shadow_chicken_prologue: {
+            if (!m_inlineStackTop->m_inlineCallFrame)
+                addToGraph(LogShadowChickenPrologue);
+            NEXT_OPCODE(op_log_shadow_chicken_prologue);
+        }
+
+        case op_log_shadow_chicken_tail: {
+            if (!m_inlineStackTop->m_inlineCallFrame) {
+                // FIXME: The right solution for inlining is to elide these whenever the tail call
+                // ends up being inlined.
+                // https://bugs.webkit.org/show_bug.cgi?id=155686
+                addToGraph(LogShadowChickenTail);
+            }
+            NEXT_OPCODE(op_log_shadow_chicken_tail);
+        }
 
         default:
             // Parse failed! This should not happen because the capabilities checker
index abe4c4bfab5734fa37803848eeb18201caec3a4d..a22b6394ed875dcdfa4f6e90932d52fcb342b7c4 100644 (file)
@@ -225,6 +225,8 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_catch:
     case op_copy_rest:
     case op_get_rest_length:
+    case op_log_shadow_chicken_prologue:
+    case op_log_shadow_chicken_tail:
         return CanCompileAndInline;
 
     case op_put_to_scope: {
index 98a1e2d9c87720573bd74673fe80ca175db255c1..89b248a0ff40532ecf5af361a247cedd791f1b58 100644 (file)
@@ -1159,6 +1159,11 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         write(InternalState);
         return;
         
+    case LogShadowChickenPrologue:
+    case LogShadowChickenTail:
+        write(SideState);
+        return;
+        
     case LastNodeType:
         RELEASE_ASSERT_NOT_REACHED();
         return;
index adc4ce58cffda9cd4152e7f11e788eb8dd4838e3..00cb27dae8a5ff3a1c0c256442775a166d9b6d81 100644 (file)
@@ -234,6 +234,8 @@ bool doesGC(Graph& graph, Node* node)
     case GetFromArguments:
     case PutToArguments:
     case CopyRest:
+    case LogShadowChickenPrologue:
+    case LogShadowChickenTail:
         return false;
 
     case CreateActivation:
index 577379bbe478ad134cef74a697b136632ca8611f..21f6235dd674f169e5e66a0f57b538c95bfd5a96 100644 (file)
@@ -1508,6 +1508,8 @@ private:
         case CheckBadCell:
         case CheckNotEmpty:
         case CheckWatchdogTimer:
+        case LogShadowChickenPrologue:
+        case LogShadowChickenTail:
         case Unreachable:
         case ExtractOSREntryLocal:
         case LoopHint:
index e3f7c4540332c285f5ea96068c4c0da3c388399f..8a2b7a9d95b4b4f762fce2757564f3654f33406e 100644 (file)
@@ -265,6 +265,10 @@ namespace JSC { namespace DFG {
     macro(TailCallVarargsInlinedCaller, NodeResultJS | NodeMustGenerate) \
     macro(TailCallForwardVarargsInlinedCaller, NodeResultJS | NodeMustGenerate) \
     \
+    /* Shadow Chicken */\
+    macro(LogShadowChickenPrologue, NodeMustGenerate) \
+    macro(LogShadowChickenTail, NodeMustGenerate) \
+    \
     /* Allocations. */\
     macro(NewObject, NodeResultJS) \
     macro(NewArray, NodeResultJS | NodeHasVarArgs) \
index f4359cd4b44005460bb8053fa296c7120f35441b..0ee4c45dd963257cf8612e82976c91a4cb0deb2e 100644 (file)
@@ -788,6 +788,8 @@ char* JIT_OPERATION operationNewInt8ArrayWithSize(
 char* JIT_OPERATION operationNewInt8ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSInt8Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -800,6 +802,8 @@ char* JIT_OPERATION operationNewInt16ArrayWithSize(
 char* JIT_OPERATION operationNewInt16ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSInt16Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -812,6 +816,8 @@ char* JIT_OPERATION operationNewInt32ArrayWithSize(
 char* JIT_OPERATION operationNewInt32ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSInt32Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -824,6 +830,8 @@ char* JIT_OPERATION operationNewUint8ArrayWithSize(
 char* JIT_OPERATION operationNewUint8ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint8Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -836,6 +844,8 @@ char* JIT_OPERATION operationNewUint8ClampedArrayWithSize(
 char* JIT_OPERATION operationNewUint8ClampedArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint8ClampedArray>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -848,6 +858,8 @@ char* JIT_OPERATION operationNewUint16ArrayWithSize(
 char* JIT_OPERATION operationNewUint16ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint16Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -860,6 +872,8 @@ char* JIT_OPERATION operationNewUint32ArrayWithSize(
 char* JIT_OPERATION operationNewUint32ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint32Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -872,6 +886,8 @@ char* JIT_OPERATION operationNewFloat32ArrayWithSize(
 char* JIT_OPERATION operationNewFloat32ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSFloat32Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
@@ -884,6 +900,8 @@ char* JIT_OPERATION operationNewFloat64ArrayWithSize(
 char* JIT_OPERATION operationNewFloat64ArrayWithOneArgument(
     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
     return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSFloat64Array>(exec, structure, encodedValue, 0, Nullopt));
 }
 
index 4d526eeb4208928dc7968ea4e83ed54a98c25204..b49883732499fbf20af748537295dc6c2dd85faa 100644 (file)
@@ -765,6 +765,8 @@ private:
         case Check:
         case PutGlobalVariable:
         case CheckWatchdogTimer:
+        case LogShadowChickenPrologue:
+        case LogShadowChickenTail:
         case Unreachable:
         case LoopHint:
         case NotifyWrite:
index be1a75c0a000070e1efaeeeba7778740e0e8afcf..52bdcfe5e880677f4f903d09ab3b85fcf9e26b46 100644 (file)
@@ -289,6 +289,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CountExecution:
     case ForceOSRExit:
     case CheckWatchdogTimer:
+    case LogShadowChickenPrologue:
+    case LogShadowChickenTail:
     case StringFromCharCode:
     case NewTypedArray:
     case Unreachable:
index 5a7766e28dd331d0974f5df8a183eafe4284c832..f9debb7116018976c2ccdb3b3d2956cf05466e29 100644 (file)
@@ -4946,6 +4946,24 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case LogShadowChickenPrologue: {
+        flushRegisters();
+        prepareForExternalCall();
+        m_jit.emitStoreCodeOrigin(node->origin.semantic);
+        m_jit.logShadowChickenProloguePacket();
+        noResult(node);
+        break;
+    }
+
+    case LogShadowChickenTail: {
+        flushRegisters();
+        prepareForExternalCall();
+        m_jit.emitStoreCodeOrigin(node->origin.semantic);
+        m_jit.logShadowChickenTailPacket();
+        noResult(node);
+        break;
+    }
+
     case ForceOSRExit: {
         terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
         break;
index 772e2baef40bca4c3eb9f5c432b57bbd1776c37e..0df4a303ba6063fa47d9762538a1ee392f201ebd 100644 (file)
@@ -4993,6 +4993,24 @@ void SpeculativeJIT::compile(Node* node)
         noResult(node);
         break;
     }
+        
+    case LogShadowChickenPrologue: {
+        flushRegisters();
+        prepareForExternalCall();
+        m_jit.emitStoreCodeOrigin(node->origin.semantic);
+        m_jit.logShadowChickenProloguePacket();
+        noResult(node);
+        break;
+    }
+
+    case LogShadowChickenTail: {
+        flushRegisters();
+        prepareForExternalCall();
+        m_jit.emitStoreCodeOrigin(node->origin.semantic);
+        m_jit.logShadowChickenTailPacket();
+        noResult(node);
+        break;
+    }
 
     case MaterializeNewObject:
         compileMaterializeNewObject(node);
index 3c4f0719fddbc6de5acedb6b176a1422af15d772..a4b5437e97c47f8ff3feedc1ee90f6077ab5ff05 100644 (file)
@@ -43,6 +43,7 @@
 #include "RegExpObject.h"
 #include "ScopedArguments.h"
 #include "ScopedArgumentsTable.h"
+#include "ShadowChicken.h"
 
 namespace JSC { namespace FTL {
 
index 8344477b99c817b207b31fd9c6d37122045a806d..b11ade4f09a7680600968e7306591143f271db93 100644 (file)
@@ -84,6 +84,9 @@ namespace JSC { namespace FTL {
     macro(RegExpConstructor_cachedResult_reified, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfReified()) \
     macro(RegExpObject_lastIndex, RegExpObject::offsetOfLastIndex()) \
     macro(RegExpObject_lastIndexIsWritable, RegExpObject::offsetOfLastIndexIsWritable()) \
+    macro(ShadowChicken_Packet_callee, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)) \
+    macro(ShadowChicken_Packet_frame, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)) \
+    macro(ShadowChicken_Packet_callerFrame, OBJECT_OFFSETOF(ShadowChicken::Packet, callerFrame)) \
     macro(ScopedArguments_overrodeThings, ScopedArguments::offsetOfOverrodeThings()) \
     macro(ScopedArguments_scope, ScopedArguments::offsetOfScope()) \
     macro(ScopedArguments_table, ScopedArguments::offsetOfTable()) \
index 2d2f61ecec1427a2ec051e82611a36f48ce676c3..31edfb6decc705df80a4a60bb25d1b504939db62 100644 (file)
@@ -230,6 +230,8 @@ inline CapabilityLevel canCompile(Node* node)
     case SetRegExpObjectLastIndex:
     case RecordRegExpCachedResult:
     case SetFunctionName:
+    case LogShadowChickenPrologue:
+    case LogShadowChickenTail:
         // These are OK.
         break;
 
index 5a09b9178e7cee98f9083c9858388869954425c0..60daa7b603613b924291d6c0ce4ec3230bd7184d 100644 (file)
@@ -68,6 +68,7 @@
 #include "ScopedArguments.h"
 #include "ScopedArgumentsTable.h"
 #include "ScratchRegisterAllocator.h"
+#include "ShadowChicken.h"
 #include "SetupVarargsFrame.h"
 #include "VirtualRegister.h"
 #include "Watchdog.h"
@@ -941,6 +942,12 @@ private:
         case SetRegExpObjectLastIndex:
             compileSetRegExpObjectLastIndex();
             break;
+        case LogShadowChickenPrologue:
+            compileLogShadowChickenPrologue();
+            break;
+        case LogShadowChickenTail:
+            compileLogShadowChickenTail();
+            break;
         case RecordRegExpCachedResult:
             compileRecordRegExpCachedResult();
             break;
@@ -6768,6 +6775,23 @@ private:
         
         m_out.store64(value, regExp, m_heaps.RegExpObject_lastIndex);
     }
+    
+    void compileLogShadowChickenPrologue()
+    {
+        LValue packet = setupShadowChickenPacket();
+        
+        m_out.storePtr(m_callFrame, packet, m_heaps.ShadowChicken_Packet_frame);
+        m_out.storePtr(m_out.loadPtr(addressFor(0)), packet, m_heaps.ShadowChicken_Packet_callerFrame);
+        m_out.storePtr(m_out.loadPtr(payloadFor(JSStack::Callee)), packet, m_heaps.ShadowChicken_Packet_callee);
+    }
+    
+    void compileLogShadowChickenTail()
+    {
+        LValue packet = setupShadowChickenPacket();
+        
+        m_out.storePtr(m_callFrame, packet, m_heaps.ShadowChicken_Packet_frame);
+        m_out.storePtr(m_out.constIntPtr(ShadowChicken::Packet::tailMarker()), packet, m_heaps.ShadowChicken_Packet_callee);
+    }
 
     void compileRecordRegExpCachedResult()
     {
@@ -7992,6 +8016,37 @@ private:
             m_out.phi(m_out.intPtr, fastButterfly, slowButterfly));
     }
     
+    LValue setupShadowChickenPacket()
+    {
+        LBasicBlock slowCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+        
+        TypedPointer addressOfLogCursor = m_out.absolute(vm().shadowChicken().addressOfLogCursor());
+        LValue logCursor = m_out.loadPtr(addressOfLogCursor);
+        
+        ValueFromBlock fastResult = m_out.anchor(logCursor);
+        
+        m_out.branch(
+            m_out.below(logCursor, m_out.constIntPtr(vm().shadowChicken().logEnd())),
+            usually(continuation), rarely(slowCase));
+        
+        LBasicBlock lastNext = m_out.appendTo(slowCase, continuation);
+        
+        vmCall(Void, m_out.operation(operationProcessShadowChickenLog), m_callFrame);
+        
+        ValueFromBlock slowResult = m_out.anchor(m_out.loadPtr(addressOfLogCursor));
+        m_out.jump(continuation);
+        
+        m_out.appendTo(continuation, lastNext);
+        LValue result = m_out.phi(pointerType(), fastResult, slowResult);
+        
+        m_out.storePtr(
+            m_out.add(result, m_out.constIntPtr(sizeof(ShadowChicken::Packet))),
+            addressOfLogCursor);
+        
+        return result;
+    }
+    
     LValue boolify(Edge edge)
     {
         switch (edge.useKind()) {
index bf3ece3955e861dd737ca91eb9ddcb0c777dd2da..3b60c4ccebd0445fdab30684a82b2a2fd8f7d4a6 100644 (file)
@@ -45,6 +45,7 @@
 #include "JSLock.h"
 #include "JSVirtualMachineInternal.h"
 #include "SamplingProfiler.h"
+#include "ShadowChicken.h"
 #include "Tracing.h"
 #include "TypeProfilerLog.h"
 #include "UnlinkedCodeBlock.h"
@@ -597,6 +598,7 @@ void Heap::markRoots(double gcStartTime, void* stackOrigin, void* stackTop, Mach
         visitStrongHandles(heapRootVisitor);
         visitHandleStack(heapRootVisitor);
         visitSamplingProfiler();
+        visitShadowChicken();
         traceCodeBlocksAndJITStubRoutines();
         converge();
     }
@@ -899,6 +901,11 @@ void Heap::visitSamplingProfiler()
 #endif // ENABLE(SAMPLING_PROFILER)
 }
 
+void Heap::visitShadowChicken()
+{
+    m_vm->shadowChicken().visitChildren(m_slotVisitor);
+}
+
 void Heap::traceCodeBlocksAndJITStubRoutines()
 {
     GCPHASE(TraceCodeBlocksAndJITStubRoutines);
@@ -1123,7 +1130,9 @@ NEVER_INLINE void Heap::collectImpl(HeapOperation collectionType, void* stackOri
         DeferGCForAWhile awhile(*this);
         vm()->typeProfilerLog()->processLogEntries(ASCIILiteral("GC"));
     }
-    
+
+    vm()->shadowChicken().update(*vm(), vm()->topCallFrame);
+
     RELEASE_ASSERT(!m_deferralDepth);
     ASSERT(vm()->currentThreadIsHoldingAPILock());
     RELEASE_ASSERT(vm()->atomicStringTable() == wtfThreadData().atomicStringTable());
index a01b733d831d9beb32089ee6697e7c2e62ad1802..2afc6421b83f038137ba174b8cc4bab5208837d6 100644 (file)
@@ -317,6 +317,7 @@ private:
     void visitStrongHandles(HeapRootVisitor&);
     void visitHandleStack(HeapRootVisitor&);
     void visitSamplingProfiler();
+    void visitShadowChicken();
     void traceCodeBlocksAndJITStubRoutines();
     void converge();
     void visitWeakHandles(HeapRootVisitor&);
index bf56752d25492ec5dcfdf9af8f794fdd15ac9de3..53ff51377080d213f6c21c271a3c4911eb3bdae9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
  * Copyright (c) 2010 Google Inc. All rights reserved.
  * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
  *
@@ -59,7 +59,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (m_needToSkipAFrame) {
             m_needToSkipAFrame = false;
@@ -84,9 +84,9 @@ public:
     }
 
 private:
-    bool m_needToSkipAFrame;
+    mutable bool m_needToSkipAFrame;
     Vector<ScriptCallFrame>& m_frames;
-    size_t m_remainingCapacityForFrameCapture;
+    mutable size_t m_remainingCapacityForFrameCapture;
 };
 
 Ref<ScriptCallStack> createScriptCallStack(JSC::ExecState* exec, size_t maxStackSize)
index 980b92fb2c1bd469f8481eb973de3ce82c68d593..cc300d246c31f8adf317ac159a6e8c6d6a501a5f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2003, 2007, 2008, 2011, 2013-2015 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003, 2007, 2008, 2011, 2013-2016 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -245,9 +245,10 @@ namespace JSC  {
         String friendlyFunctionName();
 
         // CallFrame::iterate() expects a Functor that implements the following method:
-        //     StackVisitor::Status operator()(StackVisitor&);
-
-        template <typename Functor> void iterate(Functor& functor)
+        //     StackVisitor::Status operator()(StackVisitor&) const;
+        // FIXME: This method is improper. We rely on the fact that we can call it with a null
+        // receiver. We should always be using StackVisitor directly.
+        template <typename Functor> void iterate(const Functor& functor)
         {
             StackVisitor::visit<Functor>(this, functor);
         }
index b6db5d90874de8d7c346a5d2e9f1266aa60065a8..7ff6682b821d658deb12857b83b5aea218d02344 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2010, 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2010, 2012-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -353,7 +353,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_hasSkippedFirstFrame) {
             m_hasSkippedFirstFrame = true;
@@ -369,7 +369,7 @@ public:
     }
 
 private:
-    bool m_hasSkippedFirstFrame;
+    mutable bool m_hasSkippedFirstFrame;
     const Register*& m_it;
 };
 
@@ -543,7 +543,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         VM& vm = m_vm;
         if (m_remainingCapacityForFrameCapture) {
@@ -578,7 +578,7 @@ public:
 private:
     VM& m_vm;
     Vector<StackFrame>& m_results;
-    size_t m_remainingCapacityForFrameCapture;
+    mutable size_t m_remainingCapacityForFrameCapture;
 };
 
 void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t maxStackSize)
@@ -630,7 +630,7 @@ public:
 
     HandlerInfo* handler() { return m_handler; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         visitor.unwindToMachineCodeBlockFrame();
 
@@ -646,7 +646,7 @@ public:
     }
 
 private:
-    HandlerInfo* m_handler;
+    mutable HandlerInfo* m_handler;
 };
 
 ALWAYS_INLINE static void notifyDebuggerOfUnwinding(CallFrame* callFrame)
@@ -671,7 +671,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         visitor.unwindToMachineCodeBlockFrame();
         VM& vm = m_callFrame->vm();
@@ -705,7 +705,7 @@ public:
     }
 
 private:
-    void copyCalleeSavesToVMCalleeSavesBuffer(StackVisitor& visitor)
+    void copyCalleeSavesToVMCalleeSavesBuffer(StackVisitor& visitor) const
     {
 #if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
 
diff --git a/Source/JavaScriptCore/interpreter/ShadowChicken.cpp b/Source/JavaScriptCore/interpreter/ShadowChicken.cpp
new file mode 100644 (file)
index 0000000..b68e794
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2016 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. 
+ */
+
+#include "config.h"
+#include "ShadowChicken.h"
+
+#include "JSCInlines.h"
+#include "ShadowChickenInlines.h"
+#include <wtf/ListDump.h>
+
+namespace JSC {
+
+static const bool verbose = false;
+
+void ShadowChicken::Packet::dump(PrintStream& out) const
+{
+    if (!*this) {
+        out.print("empty");
+        return;
+    }
+    
+    if (isPrologue()) {
+        out.print(
+            "{callee = ", RawPointer(callee), ", frame = ", RawPointer(frame), ", callerFrame = ",
+            RawPointer(callerFrame), "}");
+        return;
+    }
+    
+    if (isTail()) {
+        out.print("tail:{frame = ", RawPointer(frame), "}");
+        return;
+    }
+    
+    ASSERT(isThrow());
+    out.print("throw");
+}
+
+void ShadowChicken::Frame::dump(PrintStream& out) const
+{
+    out.print(
+        "{callee = ", RawPointer(callee), ", frame = ", RawPointer(frame), ", isTailDeleted = ",
+        isTailDeleted, "}");
+}
+
+ShadowChicken::ShadowChicken()
+    : m_logSize(Options::shadowChickenLogSize())
+{
+    m_log = static_cast<Packet*>(fastZeroedMalloc(sizeof(Packet) * m_logSize));
+    m_logCursor = m_log;
+    m_logEnd = m_log + m_logSize;
+}
+
+ShadowChicken::~ShadowChicken()
+{
+    fastFree(m_log);
+}
+
+void ShadowChicken::log(VM& vm, ExecState* exec, const Packet& packet)
+{
+    update(vm, exec);
+    *m_logCursor++ = packet;
+}
+
+void ShadowChicken::update(VM&, ExecState* exec)
+{
+    if (verbose) {
+        dataLog("Running update on: ", *this, "\n");
+        WTFReportBacktrace();
+    }
+    
+    const unsigned logCursorIndex = m_logCursor - m_log;
+    
+    // We need to figure out how to reconcile the current machine stack with our shadow stack. We do
+    // that by figuring out how much of the shadow stack to pop. We apply three different rules. The
+    // precise rule relies on the log. The log contains caller frames, which means that we know
+    // where we bottomed out after making any call. If we bottomed out but made no calls then 'exec'
+    // will tell us. That's why "highestPointSinceLastTime" will go no lower than exec. The third
+    // rule, based on comparing to the current real stack, is executed in a later loop.
+    CallFrame* highestPointSinceLastTime = exec;
+    for (unsigned i = logCursorIndex; i--;) {
+        Packet packet = m_log[i];
+        if (packet.isPrologue()) {
+            CallFrame* watermark;
+            if (i && m_log[i - 1].isTail())
+                watermark = packet.frame;
+            else
+                watermark = packet.callerFrame;
+            highestPointSinceLastTime = std::max(highestPointSinceLastTime, watermark);
+        }
+    }
+    
+    if (verbose)
+        dataLog("    Highest point since last time: ", RawPointer(highestPointSinceLastTime), "\n");
+    
+    while (!m_stack.isEmpty() && m_stack.last().frame < highestPointSinceLastTime)
+        m_stack.removeLast();
+    
+    if (verbose)
+        dataLog("    Revised stack: ", listDump(m_stack), "\n");
+    
+    // It's possible that the top of stack is now tail-deleted. The stack no longer contains any
+    // frames below the log's high watermark. That means that we just need to look for the first
+    // occurence of a tail packet for the current stack top.
+    if (!m_stack.isEmpty()) {
+        ASSERT(!m_stack.last().isTailDeleted);
+        for (unsigned i = 0; i < logCursorIndex; ++i) {
+            Packet& packet = m_log[i];
+            if (packet.isTail() && packet.frame == m_stack.last().frame) {
+                m_stack.last().isTailDeleted = true;
+                break;
+            }
+        }
+    }
+    
+    if (verbose)
+        dataLog("    Revised stack: ", listDump(m_stack), "\n");
+    
+    // The log-based and exec-based rules require that ShadowChicken was enabled. The point of
+    // ShadowChicken is to give sensible-looking results even if we had not logged. This means that
+    // we need to reconcile the shadow stack and the real stack by actually looking at the real
+    // stack. This reconciliation allows the shadow stack to have extra tail-deleted frames, but it
+    // forbids it from diverging from the real stack on normal frames.
+    if (!m_stack.isEmpty()) {
+        Vector<Frame> stackRightNow;
+        StackVisitor::visit(
+            exec, [&] (StackVisitor& visitor) -> StackVisitor::Status {
+                if (visitor->isInlinedFrame())
+                    return StackVisitor::Continue;
+                bool isTailDeleted = false;
+                stackRightNow.append(Frame(visitor->callee(), visitor->callFrame(), isTailDeleted));
+                return StackVisitor::Continue;
+            });
+        stackRightNow.reverse();
+        
+        if (verbose)
+            dataLog("    Stack right now: ", listDump(stackRightNow), "\n");
+        
+        unsigned shadowIndex = 0;
+        unsigned rightNowIndex = 0;
+        while (shadowIndex < m_stack.size() && rightNowIndex < stackRightNow.size()) {
+            if (m_stack[shadowIndex].isTailDeleted) {
+                shadowIndex++;
+                continue;
+            }
+            if (m_stack[shadowIndex] == stackRightNow[rightNowIndex]) {
+                shadowIndex++;
+                rightNowIndex++;
+                continue;
+            }
+            break;
+        }
+        m_stack.resize(shadowIndex);
+        
+        if (verbose)
+            dataLog("    Revised stack: ", listDump(m_stack), "\n");
+    }
+    
+    // It's possible that the top stack frame is actually lower than highestPointSinceLastTime.
+    // Account for that here.
+    highestPointSinceLastTime = nullptr;
+    for (unsigned i = m_stack.size(); i--;) {
+        if (!m_stack[i].isTailDeleted) {
+            highestPointSinceLastTime = m_stack[i].frame;
+            break;
+        }
+    }
+    
+    if (verbose)
+        dataLog("    Highest point since last time: ", RawPointer(highestPointSinceLastTime), "\n");
+    
+    // Set everything up so that we know where the top frame is in the log.
+    unsigned indexInLog = logCursorIndex;
+    
+    auto advanceIndexInLogTo =
+        [&] (CallFrame* frame, JSObject* callee, CallFrame* callerFrame) -> bool {
+        if (verbose)
+            dataLog("    Advancing to frame = ", RawPointer(frame), " from indexInLog = ", indexInLog, "\n");
+        if (indexInLog > logCursorIndex) {
+            if (verbose)
+                dataLog("    Bailing.\n");
+            return false;
+        }
+        
+        unsigned oldIndexInLog = indexInLog;
+        
+        while (indexInLog--) {
+            Packet packet = m_log[indexInLog];
+            
+            // If all callees opt into ShadowChicken, then this search will rapidly terminate when
+            // we find our frame. But if our frame's callee didn't emit a prologue packet because it
+            // didn't opt in, then we will keep looking backwards until we *might* find a different
+            // frame. If we've been given the callee and callerFrame as a filter, then it's unlikely
+            // that we will hit the wrong frame. But we don't always have that information.
+            //
+            // This means it's worth adding other filters. For example, we could track changes in
+            // stack size. Once we've seen a frame at some height, we're no longer interested in
+            // frames below that height. Also, we can break as soon as we see a frame higher than
+            // the one we're looking for.
+            // FIXME: Add more filters.
+            // https://bugs.webkit.org/show_bug.cgi?id=155685
+            
+            if (packet.isPrologue() && packet.frame == frame
+                && (!callee || packet.callee == callee)
+                && (!callerFrame || packet.callerFrame == callerFrame)) {
+                if (verbose)
+                    dataLog("    Found at indexInLog = ", indexInLog, "\n");
+                return true;
+            }
+        }
+        
+        // This is an interesting eventuality. We will see this if ShadowChicken was not
+        // consistently enabled. We have a choice between:
+        //
+        // - Leaving the log index at -1, which will prevent the log from being considered. This is
+        //   the most conservative. It means that we will not be able to recover tail-deleted frames
+        //   from anything that sits above a frame that didn't log a prologue packet. This means
+        //   that everyone who creates prologues must log prologue packets.
+        //
+        // - Restoring the log index to what it was before. This prevents us from considering
+        //   whether this frame has tail-deleted frames behind it, but that's about it. The problem
+        //   with this approach is that it might recover tail-deleted frames that aren't relevant.
+        //   I haven't thought about this too deeply, though.
+        //
+        // It seems like the latter option is less harmful, so that's what we do.
+        indexInLog = oldIndexInLog;
+        
+        if (verbose)
+            dataLog("    Didn't find it.\n");
+        return false;
+    };
+    
+    Vector<Frame> toPush;
+    StackVisitor::visit(
+        exec, [&] (StackVisitor& visitor) -> StackVisitor::Status {
+            if (visitor->isInlinedFrame()) {
+                // FIXME: Handle inlining.
+                // https://bugs.webkit.org/show_bug.cgi?id=155686
+                return StackVisitor::Continue;
+            }
+            CallFrame* callFrame = visitor->callFrame();
+            if (verbose)
+                dataLog("    Examining ", RawPointer(callFrame), "\n");
+            if (!toPush.isEmpty() && indexInLog < logCursorIndex
+                // This condition protects us from the case where advanceIndexInLogTo didn't find
+                // anything.
+                && m_log[indexInLog].frame == toPush.last().frame) {
+                if (verbose)
+                    dataLog("    Going to loop through things with indexInLog = ", indexInLog, " and push-stack top = ", toPush.last(), "\n");
+                for (;;) {
+                    ASSERT(m_log[indexInLog].frame == toPush.last().frame);
+                    
+                    // Right now the index is pointing at a prologue packet of the last frame that
+                    // we pushed. Peek behind that packet to see if there is a tail packet. If there
+                    // is one then we know that there is a corresponding prologue packet that will
+                    // tell us about a tail-deleted frame.
+                    
+                    if (!indexInLog)
+                        break;
+                    Packet lastPacket = m_log[indexInLog - 1];
+                    if (!lastPacket.isTail()) {
+                        // Last frame that we recorded was not the outcome of a tail call. So, there
+                        // will not be any more deleted frames.
+                        // FIXME: We might want to have a filter here. Consider that this was a tail
+                        // marker for a tail call to something that didn't log anything. It should
+                        // be sufficient to give the tail marker a copy of the caller frame.
+                        // https://bugs.webkit.org/show_bug.cgi?id=155687
+                        break;
+                    }
+                    indexInLog--; // Skip over the tail packet.
+                    
+                    if (!advanceIndexInLogTo(lastPacket.frame, nullptr, nullptr)) {
+                        // We were unable to locate the prologue packet for this tail packet. That's
+                        // quite suspect, so give up.
+                        break;
+                    }
+                    Packet packet = m_log[indexInLog];
+                    bool isTailDeleted = true;
+                    toPush.append(Frame(packet.callee, packet.frame, isTailDeleted));
+                }
+            }
+            if (callFrame == highestPointSinceLastTime) {
+                if (verbose)
+                    dataLog("    Bailing at ", RawPointer(callFrame), " because it's the highest point since last time.\n");
+                return StackVisitor::Done;
+            }
+            advanceIndexInLogTo(callFrame, callFrame->callee(), callFrame->callerFrame());
+            bool isTailDeleted = false;
+            toPush.append(Frame(visitor->callee(), callFrame, isTailDeleted));
+            return StackVisitor::Continue;
+        });
+    
+    if (verbose)
+        dataLog("    Pushing: ", listDump(toPush), "\n");
+    
+    for (unsigned i = toPush.size(); i--;)
+        m_stack.append(toPush[i]);
+    
+    // We want to reset the log. There is a fun corner-case: there could be a tail marker at the end
+    // of this log. We could make that work by setting isTailDeleted on the top of stack, but that
+    // would require more corner cases in the complicated reconciliation code above. That code
+    // already knows how to handle a tail packet at the beginning, so we just leverage that here.
+    if (logCursorIndex && m_log[logCursorIndex - 1].isTail()) {
+        m_log[0] = m_log[logCursorIndex - 1];
+        m_logCursor = m_log + 1;
+    } else
+        m_logCursor = m_log;
+
+    if (verbose)
+        dataLog("    After pushing: ", *this, "\n");
+    
+    // Remove tail frames until the stack is small enough again.
+    const unsigned stackSizeLimit = Options::shadowChickenStackSizeLimit();
+    if (m_stack.size() > stackSizeLimit) {
+        unsigned dstIndex = 0;
+        unsigned srcIndex = 0;
+        unsigned size = m_stack.size();
+        while (srcIndex < m_stack.size()) {
+            Frame frame = m_stack[srcIndex++];
+            if (size > stackSizeLimit && frame.isTailDeleted) {
+                size--;
+                continue;
+            }
+            m_stack[dstIndex++] = frame;
+        }
+        RELEASE_ASSERT(dstIndex == size);
+        m_stack.resize(size);
+    }
+
+    if (verbose)
+        dataLog("    After clean-up: ", *this, "\n");
+}
+
+void ShadowChicken::visitChildren(SlotVisitor& visitor)
+{
+    for (unsigned i = m_logCursor - m_log; i--;)
+        visitor.appendUnbarrieredReadOnlyPointer(m_log[i].callee);
+    
+    for (Frame& frame : m_stack)
+        visitor.appendUnbarrieredReadOnlyPointer(frame.callee);
+}
+
+void ShadowChicken::reset()
+{
+    m_logCursor = m_log;
+    m_stack.clear();
+}
+
+void ShadowChicken::dump(PrintStream& out) const
+{
+    out.print("{stack = [", listDump(m_stack), "], log = [");
+    
+    CommaPrinter comma;
+    unsigned limit = static_cast<unsigned>(m_logCursor - m_log);
+    for (unsigned i = 0; i < limit; ++i)
+        out.print(comma, m_log[i]);
+    out.print("]}");
+}
+
+JSArray* ShadowChicken::functionsOnStack(ExecState* exec)
+{
+    JSArray* result = constructEmptyArray(exec, 0);
+
+    iterate(
+        exec->vm(), exec,
+        [&] (const Frame& frame) -> bool {
+            result->push(exec, frame.callee);
+            return true;
+        });
+    
+    return result;
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/interpreter/ShadowChicken.h b/Source/JavaScriptCore/interpreter/ShadowChicken.h
new file mode 100644 (file)
index 0000000..e9d7149
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 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. 
+ */
+
+#pragma once
+
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/PrintStream.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class ExecState;
+class JSArray;
+class JSObject;
+class LLIntOffsetsExtractor;
+class SlotVisitor;
+class VM;
+
+typedef ExecState CallFrame;
+
+// ShadowChicken is a log that can be used to produce a shadow stack of CHICKEN-style stack frames.
+// This enables the debugger to almost always see the tail-deleted stack frames, so long as we have
+// memory inside ShadowChicken to remember them.
+//
+// The ShadowChicken log comprises packets that have one of two shapes:
+//
+// Prologue Packet, which has:
+//     - Callee object.
+//     - Frame pointer.
+//     - Caller frame pointer.
+//
+// Tail Call Packet, which has just:
+//     - Frame pointer.
+//
+// Prologue Packets are placed into the log in any JS function's prologue. Tail Call Packets are
+// placed into the log just before making a proper tail call. We never log returns, since that would
+// require a lot of infrastructure (unwinding, multiple ways of returning, etc). We don't need to
+// see the returns because the prologue packets have a frame pointer. The tail call packets tell us
+// when there was a tail call, and record the FP *before* the tail call.
+//
+// At any time it is possible to construct a shadow stack from the log and the actual machine stack.
+
+class ShadowChicken {
+    WTF_MAKE_NONCOPYABLE(ShadowChicken);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    struct Packet {
+        Packet()
+        {
+        }
+        
+        static const unsigned unlikelyValue = 0x7a11;
+        
+        static JSObject* tailMarker()
+        {
+            return bitwise_cast<JSObject*>(static_cast<intptr_t>(unlikelyValue));
+        }
+        
+        static JSObject* throwMarker()
+        {
+            return bitwise_cast<JSObject*>(static_cast<intptr_t>(unlikelyValue + 1));
+        }
+        
+        static Packet prologue(JSObject* callee, CallFrame* frame, CallFrame* callerFrame)
+        {
+            Packet result;
+            result.callee = callee;
+            result.frame = frame;
+            result.callerFrame = callerFrame;
+            return result;
+        }
+        
+        // FIXME: Tail packets should hold currentScope so that the inspector can look at local
+        // variables in tail-deleted frames.
+        // https://bugs.webkit.org/show_bug.cgi?id=155722
+        static Packet tail(CallFrame* frame)
+        {
+            Packet result;
+            result.callee = tailMarker();
+            result.frame = frame;
+            return result;
+        }
+        
+        static Packet throwPacket()
+        {
+            Packet result;
+            result.callee = throwMarker();
+            return result;
+        }
+        
+        explicit operator bool() const { return !!callee; }
+        
+        bool isPrologue() const { return *this && callee != tailMarker() && callee != throwMarker(); }
+        bool isTail() const { return *this && callee == tailMarker(); }
+        bool isThrow() const { return *this && callee == throwMarker(); }
+        
+        void dump(PrintStream&) const;
+        
+        JSObject* callee { nullptr };
+        CallFrame* frame { nullptr };
+        CallFrame* callerFrame { nullptr };
+    };
+    
+    struct Frame {
+        Frame()
+        {
+        }
+        
+        Frame(JSObject* callee, CallFrame* frame, bool isTailDeleted)
+            : callee(callee)
+            , frame(frame)
+            , isTailDeleted(isTailDeleted)
+        {
+        }
+        
+        bool operator==(const Frame& other) const
+        {
+            return callee == other.callee
+                && frame == other.frame
+                && isTailDeleted == other.isTailDeleted;
+        }
+        
+        bool operator!=(const Frame& other) const
+        {
+            return !(*this == other);
+        }
+        
+        void dump(PrintStream&) const;
+        
+        // FIXME: This should be able to hold the moral equivalent of StackVisitor::Frame, so that
+        // we can support inlining.
+        // https://bugs.webkit.org/show_bug.cgi?id=155686
+        JSObject* callee { nullptr };
+        CallFrame* frame { nullptr };
+        bool isTailDeleted { false };
+    };
+    
+    ShadowChicken();
+    ~ShadowChicken();
+    
+    void log(VM& vm, ExecState* exec, const Packet&);
+    
+    void update(VM&, ExecState*);
+    
+    // Expects this signature: (const Frame& frame) -> bool.
+    // Note that this only works right with inlining disabled, but that's OK since for now we
+    // disable inlining when the inspector is attached. It would be easy to make this work with
+    // inlining, and would mostly require that we can request that StackVisitor doesn't skip tail
+    // frames.
+    template<typename Functor>
+    void iterate(VM&, ExecState*, const Functor&);
+    
+    void visitChildren(SlotVisitor&);
+    void reset();
+    
+    // JIT support.
+    Packet* log() const { return m_log; }
+    unsigned logSize() const { return m_logSize; }
+    Packet** addressOfLogCursor() { return &m_logCursor; }
+    Packet* logEnd() { return m_logEnd; }
+    
+    void dump(PrintStream&) const;
+    
+    JS_EXPORT_PRIVATE JSArray* functionsOnStack(ExecState*);
+
+private:
+    friend class LLIntOffsetsExtractor;
+    
+    Packet* m_log { nullptr };
+    unsigned m_logSize { 0 };
+    Packet* m_logCursor { nullptr };
+    Packet* m_logEnd { nullptr };
+    
+    Vector<Frame> m_stack;
+};
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/interpreter/ShadowChickenInlines.h b/Source/JavaScriptCore/interpreter/ShadowChickenInlines.h
new file mode 100644 (file)
index 0000000..88d3dd3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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. 
+ */
+
+#pragma once
+
+#include "JSCInlines.h"
+#include "ShadowChicken.h"
+
+namespace JSC {
+
+template<typename Functor>
+void ShadowChicken::iterate(VM& vm, ExecState* exec, const Functor& functor)
+{
+    DeferGC deferGC(exec->vm().heap);
+
+    update(vm, exec);
+    
+    for (unsigned i = m_stack.size(); i--;) {
+        if (!functor(m_stack[i]))
+            break;
+    }
+}
+
+} // namespace JSC
+
index bbf37fe9c79cf3a6a299c3d13988d9d67e8a7174..5798e1a7a4de34dd7c981d77e29cd912a5f42ed4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -63,14 +63,16 @@ public:
         JSObject* callee() const { return m_callee; }
         CodeBlock* codeBlock() const { return m_codeBlock; }
         unsigned bytecodeOffset() const { return m_bytecodeOffset; }
+        InlineCallFrame* inlineCallFrame() const {
 #if ENABLE(DFG_JIT)
-        InlineCallFrame* inlineCallFrame() const { return m_inlineCallFrame; }
+            return m_inlineCallFrame;
+#else
+            return nullptr;
 #endif
+        }
 
         bool isJSFrame() const { return !!codeBlock(); }
-#if ENABLE(DFG_JIT)
-        bool isInlinedFrame() const { return !!m_inlineCallFrame; }
-#endif
+        bool isInlinedFrame() const { return !!inlineCallFrame(); }
 
         JS_EXPORT_PRIVATE String functionName();
         JS_EXPORT_PRIVATE String sourceURL();
@@ -115,10 +117,10 @@ public:
     };
 
     // StackVisitor::visit() expects a Functor that implements the following method:
-    //     Status operator()(StackVisitor&);
+    //     Status operator()(StackVisitor&) const;
 
     template <typename Functor>
-    static void visit(CallFrame* startFrame, Functor& functor)
+    static void visit(CallFrame* startFrame, const Functor& functor)
     {
         StackVisitor visitor(startFrame);
         while (visitor->callFrame()) {
@@ -157,7 +159,7 @@ public:
 
     CallFrame* callerFrame() const { return m_callerFrame; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_hasSkippedFirstFrame) {
             m_hasSkippedFirstFrame = true;
@@ -169,8 +171,8 @@ public:
     }
     
 private:
-    bool m_hasSkippedFirstFrame;
-    CallFrame* m_callerFrame;
+    mutable bool m_hasSkippedFirstFrame;
+    mutable CallFrame* m_callerFrame;
 };
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/jit/CCallHelpers.cpp b/Source/JavaScriptCore/jit/CCallHelpers.cpp
new file mode 100644 (file)
index 0000000..89046cc
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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. 
+ */
+
+#include "config.h"
+#include "CCallHelpers.h"
+
+#include "ShadowChicken.h"
+
+namespace JSC {
+
+void CCallHelpers::logShadowChickenProloguePacket()
+{
+    setupShadowChickenPacket();
+    storePtr(GPRInfo::callFrameRegister, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)));
+    loadPtr(Address(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame)), GPRInfo::regT0);
+    storePtr(GPRInfo::regT0, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, callerFrame)));
+    loadPtr(addressFor(JSStack::Callee), GPRInfo::regT0);
+    storePtr(GPRInfo::regT0, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)));
+}
+
+void CCallHelpers::logShadowChickenTailPacket()
+{
+    setupShadowChickenPacket();
+    storePtr(GPRInfo::callFrameRegister, Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, frame)));
+    storePtr(TrustedImmPtr(ShadowChicken::Packet::tailMarker()), Address(GPRInfo::regT1, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)));
+}
+
+void CCallHelpers::setupShadowChickenPacket()
+{
+    move(TrustedImmPtr(vm()->shadowChicken().addressOfLogCursor()), GPRInfo::regT0);
+    loadPtr(GPRInfo::regT0, GPRInfo::regT1);
+    Jump ok = branchPtr(Below, GPRInfo::regT1, TrustedImmPtr(vm()->shadowChicken().logEnd()));
+    setupArgumentsExecState();
+    move(TrustedImmPtr(bitwise_cast<void*>(operationProcessShadowChickenLog)), GPRInfo::nonArgGPR0);
+    call(GPRInfo::nonArgGPR0);
+    move(TrustedImmPtr(vm()->shadowChicken().addressOfLogCursor()), GPRInfo::regT0);
+    loadPtr(GPRInfo::regT0, GPRInfo::regT1);
+    ok.link(this);
+    addPtr(TrustedImm32(sizeof(ShadowChicken::Packet)), GPRInfo::regT1, GPRInfo::regT2);
+    storePtr(GPRInfo::regT2, GPRInfo::regT0);
+}
+
+} // namespace JSC
+
index e649d39e680fe10e614ebb911f8da2d09ab7ea8c..1922201b749cc51d91b6e1553d8938f36c96b493 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -2205,6 +2205,15 @@ public:
         // Ready for a jump!
         move(newFramePointer, stackPointerRegister);
     }
+    
+    // These operations clobber all volatile registers. They assume that there is room on the top of
+    // stack to marshall call arguments.
+    void logShadowChickenProloguePacket();
+    void logShadowChickenTailPacket();
+
+private:
+    // Leaves behind a pointer to the Packet we should write to in regT1.
+    void setupShadowChickenPacket();
 };
 
 } // namespace JSC
index 0ffd1209f7425efa78c3f366519f15ef9ff60e33..2d74cf6f19575e326c7b7efa3cc61d1d09e79f29 100644 (file)
@@ -326,6 +326,9 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_enumerator_structure_pname)
         DEFINE_OP(op_enumerator_generic_pname)
         DEFINE_OP(op_to_index_string)
+            
+        DEFINE_OP(op_log_shadow_chicken_prologue)
+        DEFINE_OP(op_log_shadow_chicken_tail)
         default:
             RELEASE_ASSERT_NOT_REACHED();
         }
index 8a52921f51e02bcd3e7815c7bad302a0f1247e87..d355a4c2a62fdf885182619fcbf0c6ed60a2e6d5 100644 (file)
@@ -595,6 +595,8 @@ namespace JSC {
         void emit_op_enumerator_structure_pname(Instruction*);
         void emit_op_enumerator_generic_pname(Instruction*);
         void emit_op_to_index_string(Instruction*);
+        void emit_op_log_shadow_chicken_prologue(Instruction*);
+        void emit_op_log_shadow_chicken_tail(Instruction*);
 
         void emitSlow_op_add(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_bitand(Instruction*, Vector<SlowCaseEntry>::iterator&);
index cf2ea28afb19a8718a155a444f9033b0b07bdc7a..b8b3747bb26050653d8e7b69a48f47b644f7a16f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "CallFrame.h"
 #include "CodeBlock.h"
 #include "Interpreter.h"
+#include "JSCInlines.h"
 #include "JSCJSValue.h"
 #include "LLIntData.h"
 #include "LLIntOpcode.h"
 #include "LLIntThunks.h"
 #include "Opcode.h"
-#include "JSCInlines.h"
+#include "ShadowChicken.h"
 #include "VM.h"
 
 namespace JSC {
@@ -50,6 +51,8 @@ void genericUnwind(VM* vm, ExecState* callFrame, UnwindStart unwindStart)
         CRASH();
     }
     
+    vm->shadowChicken().log(*vm, callFrame, ShadowChicken::Packet::throwPacket());
+    
     Exception* exception = vm->exception();
     RELEASE_ASSERT(exception);
     HandlerInfo* handler = vm->interpreter->unwind(*vm, callFrame, exception, unwindStart); // This may update callFrame.
index 42c14e8a68c247f7db09065311b9cb0f80b0324a..e1eeec307ef0274c23387a953bb7cd8321b5951c 100644 (file)
@@ -143,6 +143,10 @@ ALWAYS_INLINE void JIT::updateTopCallFrame()
     uint32_t locationBits = CallSiteIndex(m_bytecodeOffset + 1).bits();
 #endif
     store32(TrustedImm32(locationBits), intTagFor(JSStack::ArgumentCount));
+    
+    // FIXME: It's not clear that this is needed. JITOperations tend to update the top call frame on
+    // the C++ side.
+    // https://bugs.webkit.org/show_bug.cgi?id=155693
     storePtr(callFrameRegister, &m_vm->topCallFrame);
 }
 
index 0f682cd0fe48f9f5df7206c8b836998c11429490..f1364ffd9faf7327b173f44d563a8a4c95a00263 100644 (file)
@@ -1450,6 +1450,18 @@ void JIT::emit_op_resume(Instruction* currentInstruction)
     slowPathCall.call();
 }
 
+void JIT::emit_op_log_shadow_chicken_prologue(Instruction*)
+{
+    updateTopCallFrame();
+    logShadowChickenProloguePacket();
+}
+
+void JIT::emit_op_log_shadow_chicken_tail(Instruction*)
+{
+    updateTopCallFrame();
+    logShadowChickenTailPacket();
+}
+
 } // namespace JSC
 
 #endif // ENABLE(JIT)
index ab3d380dd3458f8478c9ecdd6b51b90c6c4cd0ae..ed44349a792e0d3ee2103093ced476d4da98b98a 100644 (file)
@@ -49,6 +49,9 @@ namespace JSC {
 
 JIT::CodeRef JIT::privateCompileCTINativeCall(VM* vm, NativeFunction func)
 {
+    // FIXME: This should be able to log ShadowChicken prologue packets.
+    // https://bugs.webkit.org/show_bug.cgi?id=155689
+    
     Call nativeCall;
 
     emitFunctionPrologue();
index c6c75f76b2e503f9012e267135c9ac8e5df58c00..d19c01c38e25d07ac0c1084c83395773d1b2cd7a 100644 (file)
@@ -57,6 +57,7 @@
 #include "PropertyName.h"
 #include "Repatch.h"
 #include "ScopedArguments.h"
+#include "ShadowChicken.h"
 #include "SuperSampler.h"
 #include "TestRunnerUtils.h"
 #include "TypeProfilerLog.h"
@@ -2118,7 +2119,16 @@ JSCell* JIT_OPERATION operationToIndexString(ExecState* exec, int32_t index)
 
 void JIT_OPERATION operationProcessTypeProfilerLog(ExecState* exec)
 {
-    exec->vm().typeProfilerLog()->processLogEntries(ASCIILiteral("Log Full, called from inside baseline JIT"));
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    vm.typeProfilerLog()->processLogEntries(ASCIILiteral("Log Full, called from inside baseline JIT"));
+}
+
+void JIT_OPERATION operationProcessShadowChickenLog(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    vm.shadowChicken().update(vm, exec);
 }
 
 int32_t JIT_OPERATION operationCheckIfExceptionIsUncatchableAndNotifyProfiler(ExecState* exec)
index ead285ed375fc94fb8748ff801c32cab3b2e9322..8e82b3b9075d0af370e7ee569ddf3c957c141e93 100644 (file)
@@ -394,6 +394,7 @@ EncodedJSValue JIT_OPERATION operationNextEnumeratorPname(ExecState*, JSCell*, i
 JSCell* JIT_OPERATION operationToIndexString(ExecState*, int32_t);
 
 void JIT_OPERATION operationProcessTypeProfilerLog(ExecState*) WTF_INTERNAL;
+void JIT_OPERATION operationProcessShadowChickenLog(ExecState*) WTF_INTERNAL;
 
 } // extern "C"
 
index 0649197a04fdaf5fb02ea651b9dd3614121a09b5..779c4c79713b01f98ccb59b7fedde85d4e6f2640 100644 (file)
@@ -238,6 +238,9 @@ enum ThunkEntryType { EnterViaCall, EnterViaJump };
 
 static MacroAssemblerCodeRef nativeForGenerator(VM* vm, CodeSpecializationKind kind, ThunkEntryType entryType = EnterViaCall)
 {
+    // FIXME: This should be able to log ShadowChicken prologue packets.
+    // https://bugs.webkit.org/show_bug.cgi?id=155689
+    
     int executableOffsetToFunction = NativeExecutable::offsetOfNativeFunctionFor(kind);
     
     JSInterfaceJIT jit(vm);
index f5a39831da3f569456b720781df5fd4aaa435325..85621bb8e36d85c7016d1ea1720e927a929faa89 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2004-2008, 2012-2013, 2015 Apple Inc. All rights reserved.
+ *  Copyright (C) 2004-2008, 2012-2013, 2015-2016 Apple Inc. All rights reserved.
  *  Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com)
  *
  *  This library is free software; you can redistribute it and/or
@@ -53,6 +53,7 @@
 #include "JSWASMModule.h"
 #include "ProfilerDatabase.h"
 #include "SamplingProfiler.h"
+#include "ShadowChicken.h"
 #include "StackVisitor.h"
 #include "StructureInlines.h"
 #include "StructureRareDataInlines.h"
@@ -629,6 +630,8 @@ static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
 #endif
 
+static EncodedJSValue JSC_HOST_CALL functionShadowChickenFunctionsOnStack(ExecState*);
+
 struct Script {
     bool isFile;
     char* argument;
@@ -768,6 +771,7 @@ protected:
         addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1);
         addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1);
 #endif
+        addFunction(vm, "shadowChickenFunctionsOnStack", functionShadowChickenFunctionsOnStack, 0);
         addConstructableFunction(vm, "Root", functionCreateRoot, 0);
         addConstructableFunction(vm, "Element", functionCreateElement, 1);
         addFunction(vm, "getElement", functionGetElement, 1);
@@ -1154,7 +1158,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         m_trace.append(String::format("    %zu   %s\n", visitor->index(), visitor->toString().utf8().data()));
         return StackVisitor::Continue;
@@ -1471,6 +1475,11 @@ EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
 }
 #endif
 
+EncodedJSValue JSC_HOST_CALL functionShadowChickenFunctionsOnStack(ExecState* exec)
+{
+    return JSValue::encode(exec->vm().shadowChicken().functionsOnStack(exec));
+}
+
 EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
 {
     Vector<char, 256> line;
index 2b4e61986d312ac9e4f87373ce7aa0b9fd0ce75a..57d29c81540fd04b7d29635d4fb436125b238c14 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -49,6 +49,7 @@
 #include "LLIntOfflineAsmConfig.h"
 #include "MarkedSpace.h"
 #include "ProtoCallFrame.h"
+#include "ShadowChicken.h"
 #include "Structure.h"
 #include "StructureChain.h"
 #include "TypeProfiler.h"
index f6420780b05da28236df68cc9cf3a620fba92c93..42407bcc12e992ddabb66e157e3e0f3bd1c4b727 100644 (file)
@@ -53,6 +53,7 @@
 #include "LowLevelInterpreter.h"
 #include "ObjectConstructor.h"
 #include "ProtoCallFrame.h"
+#include "ShadowChicken.h"
 #include "StructureRareDataInlines.h"
 #include "VMInlines.h"
 #include <wtf/StringPrintStream.h>
@@ -1514,6 +1515,25 @@ LLINT_SLOW_PATH_DECL(slow_path_check_if_exception_is_uncatchable_and_notify_prof
     LLINT_RETURN_TWO(pc, 0);
 }
 
+LLINT_SLOW_PATH_DECL(slow_path_log_shadow_chicken_prologue)
+{
+    LLINT_BEGIN();
+    
+    vm.shadowChicken().log(
+        vm, exec, ShadowChicken::Packet::prologue(exec->callee(), exec, exec->callerFrame()));
+    
+    LLINT_END();
+}
+
+LLINT_SLOW_PATH_DECL(slow_path_log_shadow_chicken_tail)
+{
+    LLINT_BEGIN();
+    
+    vm.shadowChicken().log(vm, exec, ShadowChicken::Packet::tail(exec));
+    
+    LLINT_END();
+}
+
 extern "C" SlowPathReturnType llint_throw_stack_overflow_error(VM* vm, ProtoCallFrame* protoFrame)
 {
     ExecState* exec = vm->topCallFrame;
index 9b431968e708a038b651d029e01f4dd7dfee3445..e3d41e45827fb0dc5bfd515e6307c21103a03f36 100644 (file)
@@ -123,6 +123,8 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_handle_exception);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_from_scope);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_to_scope);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_check_if_exception_is_uncatchable_and_notify_profiler);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_log_shadow_chicken_prologue);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_log_shadow_chicken_tail);
 extern "C" SlowPathReturnType llint_throw_stack_overflow_error(VM*, ProtoCallFrame*) WTF_INTERNAL;
 #if !ENABLE(JIT)
 extern "C" SlowPathReturnType llint_stack_check_at_vm_entry(VM*, Register*) WTF_INTERNAL;
index 5ef99db6fb45af3d9760620b4559e025cfeef293..70907a423e2fcb7854586d3619d385a0dbec2c4a 100644 (file)
@@ -1456,6 +1456,42 @@ _llint_op_watchdog:
     jmp _llint_throw_from_slow_path_trampoline
 
 
+# Returns the packet pointer in t0.
+macro acquireShadowChickenPacket(slow)
+    loadp CodeBlock[cfr], t1
+    loadp CodeBlock::m_vm[t1], t1
+    loadp VM::m_shadowChicken[t1], t2
+    loadp ShadowChicken::m_logCursor[t2], t0
+    bpaeq t0, ShadowChicken::m_logEnd[t2], slow
+    addp sizeof ShadowChicken::Packet, t0, t1
+    storep t1, ShadowChicken::m_logCursor[t2]
+end
+
+_llint_op_log_shadow_chicken_prologue:
+    traceExecution()
+    acquireShadowChickenPacket(.opLogShadowChickenPrologueSlow)
+    storep cfr, ShadowChicken::Packet::frame[t0]
+    loadp CallerFrame[cfr], t1
+    storep t1, ShadowChicken::Packet::callerFrame[t0]
+    loadp Callee + PayloadOffset[cfr], t1
+    storep t1, ShadowChicken::Packet::callee[t0]
+    dispatch(1)
+.opLogShadowChickenPrologueSlow:
+    callSlowPath(_llint_slow_path_log_shadow_chicken_prologue)
+    dispatch(1)
+
+
+_llint_op_log_shadow_chicken_tail:
+    traceExecution()
+    acquireShadowChickenPacket(.opLogShadowChickenTailSlow)
+    storep cfr, ShadowChicken::Packet::frame[t0]
+    storep 0x7a11, ShadowChicken::Packet::callee[t0]
+    dispatch(1)
+.opLogShadowChickenTailSlow:
+    callSlowPath(_llint_slow_path_log_shadow_chicken_tail)
+    dispatch(1)
+
+
 _llint_op_switch_string:
     traceExecution()
     callSlowPath(_llint_slow_path_switch_string)
index f70e4a3f6499d4d0b486c12e00d839015d9d52a7..f3b8f3c75d1cc5a394c16f10f9d38374f8df6d4d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2014 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2014, 2016 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -72,7 +72,7 @@ public:
 
     bool foundParent() const { return m_foundParent; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_hasSkippedFirstFrame) {
             m_hasSkippedFirstFrame = true;
@@ -92,8 +92,8 @@ public:
 
 private:
     ExecState* m_exec;
-    bool m_hasSkippedFirstFrame;
-    bool m_foundParent;
+    mutable bool m_hasSkippedFirstFrame;
+    mutable bool m_foundParent;
     RefPtr<ProfileNode>& m_rootNode;
     RefPtr<ProfileNode>& m_currentNode;
     double m_startTime;
index 897f9907804dd27afc20d8548d23ee5e362e4929..1bcc9cb0c0d744ef85aee2773aae1f8271b00290 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2016 Apple Inc. All rights reserved.
  *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
  *
  *  This library is free software; you can redistribute it and/or
@@ -113,7 +113,7 @@ public:
         , m_index(0)
     { }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_foundStartCallFrame && (visitor->callFrame() == m_startCallFrame))
             m_foundStartCallFrame = true;
@@ -134,9 +134,9 @@ public:
 
 private:
     CallFrame* m_startCallFrame;
-    CallFrame* m_foundCallFrame;
-    bool m_foundStartCallFrame;
-    unsigned m_index;
+    mutable CallFrame* m_foundCallFrame;
+    mutable bool m_foundStartCallFrame;
+    mutable unsigned m_index;
 };
 
 bool addErrorInfoAndGetBytecodeOffset(ExecState* exec, VM& vm, JSObject* obj, bool useCurrentFrame, CallFrame*& callFrame, unsigned &bytecodeOffset) 
index 28ee4aed39ed021e5505386b904783a15b09572f..defb5b61a3bbf76577dd1146d3004122ef9b0581 100644 (file)
@@ -250,7 +250,7 @@ public:
 
     JSValue result() const { return m_result; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         JSObject* callee = visitor->callee();
         if (callee != m_targetCallee)
@@ -262,7 +262,7 @@ public:
 
 private:
     JSObject* m_targetCallee;
-    JSValue m_result;
+    mutable JSValue m_result;
 };
 
 static JSValue retrieveArguments(ExecState* exec, JSFunction* functionObj)
@@ -292,7 +292,7 @@ public:
 
     JSValue result() const { return m_result; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         JSObject* callee = visitor->callee();
 
@@ -315,9 +315,9 @@ public:
 
 private:
     JSObject* m_targetCallee;
-    bool m_hasFoundFrame;
-    bool m_hasSkippedToCallerFrame;
-    JSValue m_result;
+    mutable bool m_hasFoundFrame;
+    mutable bool m_hasSkippedToCallerFrame;
+    mutable JSValue m_result;
 };
 
 static JSValue retrieveCallerFunction(ExecState* exec, JSFunction* functionObj)
index 1d26d8bde551e2ba73c4b140612fd00d38a4664c..3813faf6c43e7089aa834e92ef7db8876bd776ec 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012, 2016 Apple Inc. All rights reserved.
  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
  *  Copyright (C) 2007 Maks Orlovich
  *
@@ -794,7 +794,7 @@ public:
 
     EncodedJSValue result() { return m_result; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_hasSkippedFirstFrame) {
             m_hasSkippedFirstFrame = true;
@@ -809,9 +809,9 @@ public:
 
 private:
     ExecState* m_exec;
-    bool m_hasSkippedFirstFrame;
+    mutable bool m_hasSkippedFirstFrame;
     JSObject* m_thisObject;
-    EncodedJSValue m_result;
+    mutable EncodedJSValue m_result;
 };
 
 EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec)
@@ -847,7 +847,7 @@ public:
 
     bool allowsAccess() const { return m_allowsAccess; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_hasSkippedFirstFrame) {
             m_hasSkippedFirstFrame = true;
@@ -859,8 +859,8 @@ public:
     }
 
 private:
-    bool m_hasSkippedFirstFrame;
-    bool m_allowsAccess;
+    mutable bool m_hasSkippedFirstFrame;
+    mutable bool m_allowsAccess;
     JSObject* m_thisObject;
 };
 
index 7496be4d3a36410d0e3e6e00bb6cf19e3113e44c..032950da6cc72cb32db2d68bac3bb18c1057b9cc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -45,7 +45,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         ++m_iterations;
         if (m_iterations < 2)
@@ -59,8 +59,8 @@ public:
     bool callerIsStrict() const { return m_callerIsStrict; }
 
 private:
-    int m_iterations;
-    bool m_callerIsStrict;
+    mutable int m_iterations;
+    mutable bool m_callerIsStrict;
 };
 
 static bool callerIsStrict(ExecState* exec)
index 1ad0779a3030e3388c60b16be33a613b781ff77f..15eeba5081990850880a67525e904061889a690d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2008 Apple Inc. All rights reserved.
+ *  Copyright (C) 2008, 2016 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -162,7 +162,7 @@ public:
 
     JSValue result() const { return m_result; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_hasSkippedFirstFrame) {
             m_hasSkippedFirstFrame = true;
@@ -176,9 +176,9 @@ public:
 
 private:
     ExecState* m_exec;
-    bool m_hasSkippedFirstFrame;
+    mutable bool m_hasSkippedFirstFrame;
     JSObject* m_object;
-    JSValue m_result;
+    mutable JSValue m_result;
 };
 
 JSValue objectConstructorGetPrototypeOf(ExecState* exec, JSObject* object)
index cedf0be0758a97416c767331856421648afa7a2c..a8020229b1c68cd154f92d322845e87ce6654250 100644 (file)
@@ -134,6 +134,9 @@ typedef const char* optionString;
     \
     v(bool, useFunctionDotArguments, true, nullptr) \
     v(bool, useTailCalls, true, nullptr) \
+    v(bool, alwaysUseShadowChicken, false, nullptr) \
+    v(unsigned, shadowChickenLogSize, 1000, nullptr) \
+    v(unsigned, shadowChickenStackSizeLimit, 100000, nullptr) \
     \
     /* dumpDisassembly implies dumpDFGDisassembly. */ \
     v(bool, dumpDisassembly, false, "dumps disassembly of all JIT compiled code upon compilation") \
index 634d6286e1d5610d7bd1a0dee45a92fb8a446dde..f6b1ea4be641fde8f9fbfb7a3fe7c708715155ce 100644 (file)
@@ -452,7 +452,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
     return jsString(exec, impl.release());
 }
 
-static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
+static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, const String& source, RegExp* regExp)
 {
     SuperSamplerScope superSamplerScope(false);
     
@@ -460,12 +460,11 @@ static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSS
     unsigned startPosition = 0;
 
     Vector<StringRange, 16> sourceRanges;
-    VM* vm = &exec->vm();
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
     unsigned sourceLen = source.length();
 
     while (true) {
-        MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition);
+        MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition);
         if (!result)
             break;
 
@@ -493,8 +492,8 @@ static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSS
 }
 
 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
-    ExecState* exec, JSString* string, JSValue searchValue, CallData& callData, CallType callType,
-    String& replacementString, JSValue replaceValue)
+    VM& vm, ExecState* exec, JSString* string, JSValue searchValue, CallData& callData,
+    CallType callType, String& replacementString, JSValue replaceValue)
 {
     const String& source = string->value(exec);
     unsigned sourceLen = source.length();
@@ -511,7 +510,7 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
             return JSValue::encode(jsUndefined());
 
         if (callType == CallType::None && !replacementString.length())
-            return removeUsingRegExpSearch(exec, string, source, regExp);
+            return removeUsingRegExpSearch(vm, exec, string, source, regExp);
     }
 
     // FIXME: This is wrong because we may be called directly from the FTL.
@@ -532,11 +531,10 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
         CachedCall cachedCall(exec, func, argCount);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
-        VM* vm = &exec->vm();
         if (source.is8Bit()) {
             while (true) {
                 int* ovector;
-                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
+                MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
                 if (!result)
                     break;
 
@@ -550,7 +548,7 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
                     if (matchStart < 0)
                         cachedCall.setArgument(i, jsUndefined());
                     else
-                        cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
+                        cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
                 }
 
                 cachedCall.setArgument(i++, jsNumber(result.start));
@@ -575,7 +573,7 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
         } else {
             while (true) {
                 int* ovector;
-                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
+                MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
                 if (!result)
                     break;
 
@@ -589,7 +587,7 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
                     if (matchStart < 0)
                         cachedCall.setArgument(i, jsUndefined());
                     else
-                        cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
+                        cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
                 }
 
                 cachedCall.setArgument(i++, jsNumber(result.start));
@@ -613,10 +611,9 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
             }
         }
     } else {
-        VM* vm = &exec->vm();
         do {
             int* ovector;
-            MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
+            MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
             if (!result)
                 break;
 
@@ -677,31 +674,37 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
     ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    
     RegExp* regExp = searchValue->regExp();
     if (regExp->global()) {
         // ES5.1 15.5.4.10 step 8.a.
         searchValue->setLastIndex(exec, 0);
         if (exec->hadException())
             return JSValue::encode(jsUndefined());
-        return removeUsingRegExpSearch(exec, thisValue, thisValue->value(exec), regExp);
+        return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
     }
 
     CallData callData;
     String replacementString = emptyString();
     return replaceUsingRegExpSearch(
-        exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
+        vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
 }
 
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
     ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    
     CallData callData;
     String replacementString = replaceString->value(exec);
     return replaceUsingRegExpSearch(
-        exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
+        vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
 }
 
-static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
+static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
 {
     String replacementString;
     CallData callData;
@@ -713,10 +716,10 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JS
     }
 
     return replaceUsingRegExpSearch(
-        exec, string, searchValue, callData, callType, replacementString, replaceValue);
+        vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
 }
 
-static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
+static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM&, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
 {
     const String& string = jsString->value(exec);
     String searchString = searchValue.toString(exec)->value(exec);
@@ -910,35 +913,38 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncPadStart(ExecState* exec)
 }
 
 ALWAYS_INLINE EncodedJSValue replace(
-    ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
+    VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
 {
     if (searchValue.inherits(RegExpObject::info()))
-        return replaceUsingRegExpSearch(exec, string, searchValue, replaceValue);
-    return replaceUsingStringSearch(exec, string, searchValue, replaceValue);
+        return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
+    return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
 }
 
 ALWAYS_INLINE EncodedJSValue replace(
-    ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
+    VM& vm, ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
 {
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec);
     JSString* string = thisValue.toString(exec);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
-    return replace(exec, string, searchValue, replaceValue);
+    return replace(vm, exec, string, searchValue, replaceValue);
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
 {
-    return replace(exec, exec->thisValue(), exec->argument(0), exec->argument(1));
+    return replace(exec->vm(), exec, exec->thisValue(), exec->argument(0), exec->argument(1));
 }
 
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
     ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
     EncodedJSValue replaceValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    
     return replace(
-        exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
+        vm, exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
         JSValue::decode(replaceValue));
 }
 
index 8264b110b32935f33413f034d76c663c7f75abe8..e54022a71eff43615feb4e460d957fa7b6471591 100644 (file)
@@ -82,6 +82,7 @@
 #include "RegisterAtOffsetList.h"
 #include "RuntimeType.h"
 #include "SamplingProfiler.h"
+#include "ShadowChicken.h"
 #include "SimpleTypedArrayController.h"
 #include "SourceProviderCache.h"
 #include "StackVisitor.h"
@@ -197,6 +198,7 @@ VM::VM(VMType vmType, HeapType heapType)
     , m_builtinExecutables(std::make_unique<BuiltinExecutables>(*this))
     , m_typeProfilerEnabledCount(0)
     , m_controlFlowProfilerEnabledCount(0)
+    , m_shadowChicken(std::make_unique<ShadowChicken>())
 {
     interpreter = new Interpreter(*this);
     StackBounds stack = wtfThreadData().stack();
@@ -767,7 +769,7 @@ void VM::addImpureProperty(const String& propertyName)
 
 class SetEnabledProfilerFunctor {
 public:
-    bool operator()(CodeBlock* codeBlock)
+    bool operator()(CodeBlock* codeBlock) const
     {
         if (JITCode::isOptimizingJIT(codeBlock->jitType()))
             codeBlock->jettison(Profiler::JettisonDueToLegacyProfiler);
index e9abb71d93a6e93bb57d92c9e0a9e9c0bc1d9bfe..0872a88966a01dac4dd8b9d0319ebdea895c55af 100644 (file)
@@ -102,6 +102,7 @@ class RegisterAtOffsetList;
 #if ENABLE(SAMPLING_PROFILER)
 class SamplingProfiler;
 #endif
+class ShadowChicken;
 class ScriptExecutable;
 class SourceProvider;
 class SourceProviderCache;
@@ -607,6 +608,8 @@ public:
     bool shouldBuilderPCToCodeOriginMapping() const { return m_shouldBuildPCToCodeOriginMapping; }
 
     BytecodeIntrinsicRegistry& bytecodeIntrinsicRegistry() { return *m_bytecodeIntrinsicRegistry; }
+    
+    ShadowChicken& shadowChicken() { return *m_shadowChicken; }
 
 private:
     friend class LLIntOffsetsExtractor;
@@ -676,6 +679,7 @@ private:
 #if ENABLE(SAMPLING_PROFILER)
     RefPtr<SamplingProfiler> m_samplingProfiler;
 #endif
+    std::unique_ptr<ShadowChicken> m_shadowChicken;
     std::unique_ptr<BytecodeIntrinsicRegistry> m_bytecodeIntrinsicRegistry;
 };
 
diff --git a/Source/JavaScriptCore/tests/stress/resources/shadow-chicken-support.js b/Source/JavaScriptCore/tests/stress/resources/shadow-chicken-support.js
new file mode 100644 (file)
index 0000000..d842f91
--- /dev/null
@@ -0,0 +1,65 @@
+"use strict";
+
+function describeFunction(f)
+{
+    var name;
+    try {
+        name = f.name;
+    } catch (e) {}
+    if (!name)
+        name = "<" + describe(f) + ">";
+    return name;
+}
+
+function describeArray(array) {
+    var result = "[";
+    for (var i = 0; i < array.length; ++i) {
+        if (i)
+            result += ", ";
+        result += describeFunction(array[i]);
+    }
+    return result + "]";
+}
+
+function compareStacks(stack, array) {
+    if (stack.length != array.length)
+        throw new Error("Bad stack length: " + describeArray(stack) + " (expected " + describeArray(array) + ")");
+    for (var i = 0; i < stack.length; ++i) {
+        if (stack[i] != array[i])
+            throw new Error("Bad stack at i = " + i + ": " + describeArray(stack) + " (expected " + describeArray(array) + ")");
+    }
+}
+
+function expectStack(array) {
+    var stack = shadowChickenFunctionsOnStack();
+    if (verbose)
+        print("stack = " + describeArray(stack));
+    var myTop = stack.pop();
+    if (myTop != stackTop)
+        throw new Error("Bad stack top: " + myTop);
+    var myBottom = stack.shift();
+    if (myBottom != shadowChickenFunctionsOnStack)
+        throw new Error("Bad stack bottom: " + myBottom);
+    myBottom = stack.shift();
+    if (myBottom != expectStack)
+        throw new Error("Bad stack next-to-bottom: " + myBottom);
+    compareStacks(stack, array);
+}
+
+var initialShadow;
+var stackTop;
+
+function initialize()
+{
+    initialShadow = shadowChickenFunctionsOnStack();
+    if (initialShadow.length != 3)
+        throw new Error("bad initial shadow length: " + initialShadow.length);
+    if (initialShadow[0] != shadowChickenFunctionsOnStack)
+        throw new Error("bad top of stack: " + describeFunction(initialShadow[0]));
+    if (initialShadow[1] != initialize)
+        throw new Error("bad middle of stack: " + describeFunction(initialShadow[1]));
+    stackTop = initialShadow[2];
+    
+    expectStack([initialize]);
+}
+
diff --git a/Source/JavaScriptCore/tests/stress/shadow-chicken-disabled.js b/Source/JavaScriptCore/tests/stress/shadow-chicken-disabled.js
new file mode 100644 (file)
index 0000000..936b9aa
--- /dev/null
@@ -0,0 +1,56 @@
+//@ runDefault; runNoLLInt; runFTLNoCJITValidate
+
+"use strict";
+
+var verbose = false;
+load("resources/shadow-chicken-support.js");
+initialize();
+
+(function test1() {
+    function foo() {
+        expectStack([foo, test1]);
+    }
+    
+    function bar() {
+        return foo();
+    }
+
+    function baz() {
+        return bar();
+    }
+    
+    baz();
+})();
+
+(function test2() {
+    function foo() {
+    }
+    
+    function bar() {
+        return foo();
+    }
+
+    function baz() {
+        return bar();
+    }
+    
+    baz();
+})();
+
+(function test3() {
+    function foo() {
+        expectStack([foo, test3]);
+    }
+    
+    function bar() {
+        return foo();
+    }
+
+    function baz() {
+        return bar();
+    }
+    
+    baz();
+})();
+
+
diff --git a/Source/JavaScriptCore/tests/stress/shadow-chicken-enabled.js b/Source/JavaScriptCore/tests/stress/shadow-chicken-enabled.js
new file mode 100644 (file)
index 0000000..ed56fdb
--- /dev/null
@@ -0,0 +1,208 @@
+//@ runShadowChicken
+
+"use strict";
+
+var verbose = false;
+
+load("resources/shadow-chicken-support.js");
+initialize();
+
+(function test1() {
+    function foo() {
+        expectStack([foo, bar, baz, test1]);
+    }
+    
+    function bar() {
+        return foo();
+    }
+
+    function baz() {
+        return bar();
+    }
+    
+    baz();
+})();
+
+(function test2() {
+    function foo() {
+    }
+    
+    function bar() {
+        return foo();
+    }
+
+    function baz() {
+        return bar();
+    }
+    
+    baz();
+})();
+
+(function test3() {
+    if (verbose) {
+        print("test3:");
+        print("bob = " + describe(bob));
+        print("thingy = " + describe(thingy));
+        print("foo = " + describe(foo));
+        print("bar = " + describe(bar));
+        print("baz = " + describe(baz));
+    }
+    
+    function bob() {
+        if (verbose)
+            print("Doing bob...");
+        expectStack([bob, thingy, foo, bar, baz, test3]);
+    }
+    
+    function thingy() {
+        return bob();
+    }
+    
+    function foo() {
+        if (verbose)
+            print("Doing foo...");
+        expectStack([foo, bar, baz, test3]);
+        return thingy();
+    }
+    
+    function bar() {
+        return foo();
+    }
+
+    function baz() {
+        return bar();
+    }
+    
+    baz();
+})();
+
+(function test4() {
+    if (verbose) {
+        print("test4:");
+        print("bob = " + describe(bob));
+        print("thingy = " + describe(thingy));
+        print("foo = " + describe(foo));
+        print("bar = " + describe(bar));
+        print("baz = " + describe(baz));
+    }
+    
+    function bob(thingyIsTail) {
+        if (verbose)
+            print("Doing bob...");
+        expectStack([bob, thingy, foo, bar, baz, test4]);
+    }
+    
+    function thingy(isTail) {
+        bob(false);
+        return bob(isTail);
+    }
+    
+    function foo() {
+        if (verbose)
+            print("Doing foo...");
+        expectStack([foo, bar, baz, test4]);
+        thingy(false);
+        return thingy(true);
+    }
+    
+    function bar() {
+        foo();
+        return foo();
+    }
+
+    function baz() {
+        bar();
+        return bar();
+    }
+    
+    baz();
+})();
+
+(function test5a() {
+    if (verbose)
+        print("In test5a:");
+    var foos = 990;
+    
+    function foo(ttl) {
+        if (ttl <= 1) {
+            var array = [];
+            for (var i = 0; i < foos; ++i)
+                array.push(foo);
+            array.push(test5a);
+            expectStack(array);
+            return;
+        }
+        return foo(ttl - 1);
+    }
+    
+    foo(foos);
+})();
+
+(function test5b() {
+    if (verbose)
+        print("In test5b:");
+    var foos = 9990;
+    
+    function foo(ttl) {
+        if (ttl <= 1) {
+            var array = [];
+            for (var i = 0; i < foos; ++i)
+                array.push(foo);
+            array.push(test5b);
+            expectStack(array);
+            return;
+        }
+        return foo(ttl - 1);
+    }
+    
+    foo(foos);
+})();
+
+(function test6() {
+    if (verbose) {
+        print("In test6");
+        print("foo = " + describe(foo));
+        print("array.push = " + describe([].push));
+    }
+    
+    var foos = 99990;
+    
+    function foo(ttl) {
+        if (ttl <= 1) {
+            var array = [];
+            for (var i = 0; i < foos; ++i)
+                array.push(foo);
+            array.push(test6);
+            expectStack(array);
+            return;
+        }
+        return foo(ttl - 1);
+    }
+    
+    foo(foos);
+    
+    if (verbose)
+        print("Done with test6.");
+})();
+
+(function test7() {
+    var foos = 200000;
+    
+    function foo(ttl) {
+        if (ttl <= 1) {
+            var stack = shadowChickenFunctionsOnStack();
+            var expectedStack = [];
+            expectedStack.push(shadowChickenFunctionsOnStack);
+            while (expectedStack.length < stack.length - 2)
+                expectedStack.push(foo);
+            expectedStack.push(test7);
+            expectedStack.push(stackTop);
+            compareStacks(stack, expectedStack);
+            return;
+        }
+        return foo(ttl - 1);
+    }
+    
+    foo(foos);
+})();
+
index 9ba8a56ce483f85519024b17c2aa9b882b5ee890..463d535eed247b1c4e08b1100b500b7dfd5c8ee6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -77,7 +77,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (m_currentFrame++ > 1) {
             m_jitType = visitor->codeBlock()->jitType();
@@ -89,8 +89,8 @@ public:
     JITCode::JITType jitType() { return m_jitType; }
 
 private:
-    unsigned m_currentFrame;
-    JITCode::JITType m_jitType;
+    mutable unsigned m_currentFrame;
+    mutable JITCode::JITType m_jitType;
 };
 
 static EncodedJSValue JSC_HOST_CALL functionLLintTrue(ExecState* exec)
@@ -160,7 +160,7 @@ struct CellAddressCheckFunctor : MarkedBlock::CountFunctor {
     {
     }
 
-    IterationStatus operator()(JSCell* cell)
+    IterationStatus operator()(JSCell* cell) const
     {
         if (cell == candidate) {
             found = true;
@@ -170,7 +170,7 @@ struct CellAddressCheckFunctor : MarkedBlock::CountFunctor {
     }
 
     JSCell* candidate;
-    bool found { false };
+    mutable bool found { false };
 };
 
 bool JSDollarVMPrototype::isValidCell(Heap* heap, JSCell* candidate)
@@ -192,7 +192,7 @@ bool JSDollarVMPrototype::isValidCodeBlock(ExecState* exec, CodeBlock* candidate
         {
         }
         
-        bool operator()(CodeBlock* codeBlock)
+        bool operator()(CodeBlock* codeBlock) const
         {
             if (codeBlock == candidate)
                 found = true;
@@ -200,7 +200,7 @@ bool JSDollarVMPrototype::isValidCodeBlock(ExecState* exec, CodeBlock* candidate
         }
         
         CodeBlock* candidate;
-        bool found { false };
+        mutable bool found { false };
     };
     
     VM& vm = exec->vm();
@@ -224,7 +224,7 @@ CodeBlock* JSDollarVMPrototype::codeBlockForFrame(CallFrame* topCallFrame, unsig
         {
         }
         
-        StackVisitor::Status operator()(StackVisitor& visitor)
+        StackVisitor::Status operator()(StackVisitor& visitor) const
         {
             currentFrame++;
             if (currentFrame == targetFrame) {
@@ -235,8 +235,8 @@ CodeBlock* JSDollarVMPrototype::codeBlockForFrame(CallFrame* topCallFrame, unsig
         }
         
         unsigned targetFrame;
-        unsigned currentFrame { 0 };
-        CodeBlock* codeBlock { nullptr };
+        mutable unsigned currentFrame { 0 };
+        mutable CodeBlock* codeBlock { nullptr };
     };
     
     FetchCodeBlockFunctor functor(frameNumber);
@@ -322,7 +322,7 @@ public:
     {
     }
     
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         m_currentFrame++;
         if (m_currentFrame > m_framesToSkip)
@@ -336,7 +336,7 @@ public:
 private:
     Action m_action;
     unsigned m_framesToSkip;
-    unsigned m_currentFrame { 0 };
+    mutable unsigned m_currentFrame { 0 };
 };
 
 static void printCallFrame(CallFrame* callFrame, unsigned framesToSkip)
index 6886caec4cb1a2b1c919347ab85d82b5f8a56b8b..b5425cb9b7b801c73931c9301ea120c5828ed061 100644 (file)
@@ -1,3 +1,26 @@
+2016-03-19  Filip Pizlo  <fpizlo@apple.com>
+
+        JSC should use a shadow stack version of CHICKEN so that debuggers have the option of retrieving tail-deleted frames
+        https://bugs.webkit.org/show_bug.cgi?id=155598
+
+        Reviewed by Saam Barati.
+
+        Fixed some uses of the stack walking functor to obey the new lambda-friendly API, which
+        requires that operator() is const.
+
+        No new tests because no change in behavior.
+
+        * bindings/js/JSXMLHttpRequestCustom.cpp:
+        (WebCore::SendFunctor::column):
+        (WebCore::SendFunctor::url):
+        (WebCore::SendFunctor::operator()):
+        (WebCore::JSXMLHttpRequest::send):
+        * testing/Internals.cpp:
+        (WebCore::GetCallerCodeBlockFunctor::GetCallerCodeBlockFunctor):
+        (WebCore::GetCallerCodeBlockFunctor::operator()):
+        (WebCore::GetCallerCodeBlockFunctor::codeBlock):
+        (WebCore::Internals::parserMetaData):
+
 2016-04-05  Brady Eidson  <beidson@apple.com>
 
         Modern IDB: Replace use of SerializedScriptValue with IDBValue.
index 89b3bcfbb2c64b2e3b8ef1bf8f63796d6b8b2473..261440fff873422012534701795899ce3c363d97 100644 (file)
@@ -113,7 +113,7 @@ public:
     unsigned column() const { return m_column; }
     String url() const { return m_url; }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         if (!m_hasSkippedFirstFrame) {
             m_hasSkippedFirstFrame = true;
@@ -130,10 +130,10 @@ public:
     }
 
 private:
-    bool m_hasSkippedFirstFrame;
-    unsigned m_line;
-    unsigned m_column;
-    String m_url;
+    mutable bool m_hasSkippedFirstFrame;
+    mutable unsigned m_line;
+    mutable unsigned m_column;
+    mutable String m_url;
 };
 
 JSValue JSXMLHttpRequest::send(ExecState& state)
@@ -158,6 +158,9 @@ JSValue JSXMLHttpRequest::send(ExecState& state)
     } else
         wrapped().send(val.toString(&state)->value(&state), ec);
 
+    // FIXME: This should probably use ShadowChicken so that we get the right frame even when it did
+    // a tail call.
+    // https://bugs.webkit.org/show_bug.cgi?id=155688
     SendFunctor functor;
     state.iterate(functor);
     wrapped().setLastSendLineAndColumnNumber(functor.line(), functor.column());
index 9f2353e51aa5d2490641fcd101fb59c1b6ac9e66..514fcb47f34eeacaeb6762dec56f1e51eec401f3 100644 (file)
@@ -1661,7 +1661,7 @@ public:
     {
     }
 
-    StackVisitor::Status operator()(StackVisitor& visitor)
+    StackVisitor::Status operator()(StackVisitor& visitor) const
     {
         ++m_iterations;
         if (m_iterations < 2)
@@ -1674,8 +1674,8 @@ public:
     CodeBlock* codeBlock() const { return m_codeBlock; }
 
 private:
-    int m_iterations;
-    CodeBlock* m_codeBlock;
+    mutable int m_iterations;
+    mutable CodeBlock* m_codeBlock;
 };
 
 String Internals::parserMetaData(Deprecated::ScriptValue value)
index bbce8d2423f0606c4e24cb943bb4777fe1f67fbe..9dcc68e5beb81c3cb901b34e2c397e37454ecd7a 100755 (executable)
@@ -223,6 +223,7 @@ sub runJSCStressTests
             "Source/JavaScriptCore/tests/executableAllocationFuzz.yaml",
             "Source/JavaScriptCore/tests/exceptionFuzz.yaml",
             "PerformanceTests/SunSpider/no-architecture-specific-optimizations.yaml",
+            "PerformanceTests/SunSpider/shadow-chicken.yaml",
             "PerformanceTests/SunSpider/tests/v8-v6",
             "Source/JavaScriptCore/tests/mozilla/mozilla-tests.yaml",
             "Source/JavaScriptCore/tests/stress",
index d399ebba37f090838c71be1726059e183f9fed7f..30af17f661eedf8a178161ee48d4f084483186c7 100755 (executable)
@@ -878,6 +878,10 @@ def runDFGMaximalFlushPhase
     run("dfg-maximal-flush-validate-no-cjit", "--validateGraph=true", "--useMaximalFlushInsertionPhase=true", *NO_CJIT_OPTIONS)
 end
 
+def runShadowChicken
+    run("shadow-chicken", "--useDFGJIT=false", "--alwaysUseShadowChicken=true")
+end
+
 def defaultRun
     if $quickMode
         defaultQuickRun