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 a98b5c4..21614bd 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 98cd8b0..f2af448 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 2d2bf4a..99319e2 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 eae9a74..ba518d5 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 13c0326..bac6539 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 5e93ef3..e8eb9b6 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 c82f6f9..33c04a8 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 883444d..73b87bd 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 30faf58..b46be58 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 8513a08..ef88e30 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 8eb25bd..c4fda5a 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 f284d18..7ca6bbc 100644 (file)
@@ -2626,6 +2626,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
 
     case CheckWatchdogTimer:
+    case LogShadowChickenPrologue:
+    case LogShadowChickenTail:
         break;
 
     case ProfileWillCall:
index 6716687..843f1ee 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 abe4c4b..a22b639 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 98a1e2d..89b248a 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 adc4ce5..00cb27d 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 577379b..21f6235 100644 (file)
@@ -1508,6 +1508,8 @@ private:
         case CheckBadCell:
         case CheckNotEmpty:
         case CheckWatchdogTimer:
+        case LogShadowChickenPrologue:
+        case LogShadowChickenTail:
         case Unreachable:
         case ExtractOSREntryLocal:
         case LoopHint:
index e3f7c45..8a2b7a9 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 f4359cd..0ee4c45 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 4d526ee..b498837 100644 (file)
@@ -765,6 +765,8 @@ private:
         case Check:
         case PutGlobalVariable:
         case CheckWatchdogTimer:
+        case LogShadowChickenPrologue:
+        case LogShadowChickenTail:
         case Unreachable:
         case LoopHint:
         case NotifyWrite:
index be1a75c..52bdcfe 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 5a7766e..f9debb7 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 772e2ba..0df4a30 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 3c4f071..a4b5437 100644 (file)
@@ -43,6 +43,7 @@
 #include "RegExpObject.h"
 #include "ScopedArguments.h"
 #include "ScopedArgumentsTable.h"
+#include "ShadowChicken.h"
 
 namespace JSC { namespace FTL {
 
index 8344477..b11ade4 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 2d2f61e..31edfb6 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 5a09b91..60daa7b 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 bf3ece3..3b60c4c 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 a01b733..2afc642 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 bf56752..53ff513 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 980b92f..cc300d2 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 b6db5d9..7ff6682 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 bbf37fe..5798e1a 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 e649d39..1922201 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 0ffd120..2d74cf6 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 8a52921..d355a4c 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 cf2ea28..b8b3747 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 42c14e8..e1eeec3 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 0f682cd..f1364ff 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 ab3d380..ed44349 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 c6c75f7..d19c01c 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 ead285e..8e82b3b 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 0649197..779c4c7 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 f5a3983..85621bb 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 2b4e619..57d29c8 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 f642078..42407bc 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 9b43196..e3d41e4 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 5ef99db..70907a4 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 f70e4a3..f3b8f3c 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 897f990..1bcc9cb 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 28ee4ae..defb5b6 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 1d26d8b..3813faf 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 7496be4..032950d 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 1ad0779..15eeba5 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 cedf0be..a802022 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 634d628..f6b1ea4 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 8264b11..e54022a 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 e9abb71..0872a88 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 9ba8a56..463d535 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 6886cae..b5425cb 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 89b3bcf..261440f 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 9f2353e..514fcb4 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 bbce8d2..9dcc68e 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 d399ebb..30af17f 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