| /* |
| * 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 |
| * 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 "JSDollarVMPrototype.h" |
| |
| #include "CodeBlock.h" |
| #include "Heap.h" |
| #include "HeapIterationScope.h" |
| #include "JSCInlines.h" |
| #include "JSFunction.h" |
| #include "MarkedSpaceInlines.h" |
| #include "StackVisitor.h" |
| #include <wtf/DataLog.h> |
| #include <wtf/StringPrintStream.h> |
| |
| namespace JSC { |
| |
| const ClassInfo JSDollarVMPrototype::s_info = { "DollarVMPrototype", &Base::s_info, 0, CREATE_METHOD_TABLE(JSDollarVMPrototype) }; |
| |
| |
| bool JSDollarVMPrototype::currentThreadOwnsJSLock(ExecState* exec) |
| { |
| return exec->vm().apiLock().currentThreadIsHoldingLock(); |
| } |
| |
| static bool ensureCurrentThreadOwnsJSLock(ExecState* exec) |
| { |
| if (JSDollarVMPrototype::currentThreadOwnsJSLock(exec)) |
| return true; |
| dataLog("ERROR: current thread does not own the JSLock\n"); |
| return false; |
| } |
| |
| void JSDollarVMPrototype::addFunction(VM& vm, JSGlobalObject* globalObject, const char* name, NativeFunction function, unsigned arguments) |
| { |
| Identifier identifier = Identifier::fromString(&vm, name); |
| putDirect(vm, identifier, JSFunction::create(vm, globalObject, arguments, identifier.string(), function)); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionCrash(ExecState*) |
| { |
| CRASH(); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionDFGTrue(ExecState*) |
| { |
| return JSValue::encode(jsBoolean(false)); |
| } |
| |
| class CallerFrameJITTypeFunctor { |
| public: |
| CallerFrameJITTypeFunctor() |
| : m_currentFrame(0) |
| , m_jitType(JITCode::None) |
| { |
| } |
| |
| StackVisitor::Status operator()(StackVisitor& visitor) const |
| { |
| if (m_currentFrame++ > 1) { |
| m_jitType = visitor->codeBlock()->jitType(); |
| return StackVisitor::Done; |
| } |
| return StackVisitor::Continue; |
| } |
| |
| JITCode::JITType jitType() { return m_jitType; } |
| |
| private: |
| mutable unsigned m_currentFrame; |
| mutable JITCode::JITType m_jitType; |
| }; |
| |
| static EncodedJSValue JSC_HOST_CALL functionLLintTrue(ExecState* exec) |
| { |
| if (!exec) |
| return JSValue::encode(jsUndefined()); |
| CallerFrameJITTypeFunctor functor; |
| exec->iterate(functor); |
| return JSValue::encode(jsBoolean(functor.jitType() == JITCode::InterpreterThunk)); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionJITTrue(ExecState* exec) |
| { |
| if (!exec) |
| return JSValue::encode(jsUndefined()); |
| CallerFrameJITTypeFunctor functor; |
| exec->iterate(functor); |
| return JSValue::encode(jsBoolean(functor.jitType() == JITCode::BaselineJIT)); |
| } |
| |
| void JSDollarVMPrototype::gc(ExecState* exec) |
| { |
| if (!ensureCurrentThreadOwnsJSLock(exec)) |
| return; |
| exec->heap()->collectAllGarbage(); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec) |
| { |
| JSDollarVMPrototype::gc(exec); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| void JSDollarVMPrototype::edenGC(ExecState* exec) |
| { |
| if (!ensureCurrentThreadOwnsJSLock(exec)) |
| return; |
| exec->heap()->collect(EdenCollection); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState* exec) |
| { |
| JSDollarVMPrototype::edenGC(exec); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| bool JSDollarVMPrototype::isInHeap(Heap* heap, void* ptr) |
| { |
| return isInObjectSpace(heap, ptr) || isInStorageSpace(heap, ptr); |
| } |
| |
| bool JSDollarVMPrototype::isInObjectSpace(Heap* heap, void* ptr) |
| { |
| MarkedBlock* candidate = MarkedBlock::blockFor(ptr); |
| if (heap->objectSpace().blocks().set().contains(candidate)) |
| return true; |
| for (LargeAllocation* allocation : heap->objectSpace().largeAllocations()) { |
| if (allocation->contains(ptr)) |
| return true; |
| } |
| return false; |
| } |
| |
| bool JSDollarVMPrototype::isInStorageSpace(Heap*, void*) |
| { |
| // FIXME: Do something with this. |
| // https://bugs.webkit.org/show_bug.cgi?id=161753 |
| return false; |
| } |
| |
| struct CellAddressCheckFunctor : MarkedBlock::CountFunctor { |
| CellAddressCheckFunctor(JSCell* candidate) |
| : candidate(candidate) |
| { |
| } |
| |
| IterationStatus operator()(HeapCell* cell, HeapCell::Kind) const |
| { |
| if (cell == candidate) { |
| found = true; |
| return IterationStatus::Done; |
| } |
| return IterationStatus::Continue; |
| } |
| |
| JSCell* candidate; |
| mutable bool found { false }; |
| }; |
| |
| bool JSDollarVMPrototype::isValidCell(Heap* heap, JSCell* candidate) |
| { |
| HeapIterationScope iterationScope(*heap); |
| CellAddressCheckFunctor functor(candidate); |
| heap->objectSpace().forEachLiveCell(iterationScope, functor); |
| return functor.found; |
| } |
| |
| bool JSDollarVMPrototype::isValidCodeBlock(ExecState* exec, CodeBlock* candidate) |
| { |
| if (!ensureCurrentThreadOwnsJSLock(exec)) |
| return false; |
| |
| struct CodeBlockValidationFunctor { |
| CodeBlockValidationFunctor(CodeBlock* candidate) |
| : candidate(candidate) |
| { |
| } |
| |
| bool operator()(CodeBlock* codeBlock) const |
| { |
| if (codeBlock == candidate) |
| found = true; |
| return found; |
| } |
| |
| CodeBlock* candidate; |
| mutable bool found { false }; |
| }; |
| |
| VM& vm = exec->vm(); |
| CodeBlockValidationFunctor functor(candidate); |
| vm.heap.forEachCodeBlock(functor); |
| return functor.found; |
| } |
| |
| CodeBlock* JSDollarVMPrototype::codeBlockForFrame(CallFrame* topCallFrame, unsigned frameNumber) |
| { |
| if (!ensureCurrentThreadOwnsJSLock(topCallFrame)) |
| return nullptr; |
| |
| if (!topCallFrame) |
| return nullptr; |
| |
| struct FetchCodeBlockFunctor { |
| public: |
| FetchCodeBlockFunctor(unsigned targetFrameNumber) |
| : targetFrame(targetFrameNumber) |
| { |
| } |
| |
| StackVisitor::Status operator()(StackVisitor& visitor) const |
| { |
| currentFrame++; |
| if (currentFrame == targetFrame) { |
| codeBlock = visitor->codeBlock(); |
| return StackVisitor::Done; |
| } |
| return StackVisitor::Continue; |
| } |
| |
| unsigned targetFrame; |
| mutable unsigned currentFrame { 0 }; |
| mutable CodeBlock* codeBlock { nullptr }; |
| }; |
| |
| FetchCodeBlockFunctor functor(frameNumber); |
| topCallFrame->iterate(functor); |
| return functor.codeBlock; |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionCodeBlockForFrame(ExecState* exec) |
| { |
| if (exec->argumentCount() < 1) |
| return JSValue::encode(jsUndefined()); |
| |
| JSValue value = exec->uncheckedArgument(0); |
| if (!value.isUInt32()) |
| return JSValue::encode(jsUndefined()); |
| |
| // We need to inc the frame number because the caller would consider |
| // its own frame as frame 0. Hence, we need discount the frame for this |
| // function. |
| unsigned frameNumber = value.asUInt32() + 1; |
| CodeBlock* codeBlock = JSDollarVMPrototype::codeBlockForFrame(exec, frameNumber); |
| return JSValue::encode(JSValue(bitwise_cast<double>(reinterpret_cast<uint64_t>(codeBlock)))); |
| } |
| |
| static CodeBlock* codeBlockFromArg(ExecState* exec) |
| { |
| if (exec->argumentCount() < 1) |
| return nullptr; |
| |
| JSValue value = exec->uncheckedArgument(0); |
| if (!value.isDouble()) { |
| dataLog("Invalid codeBlock: ", value, "\n"); |
| return nullptr; |
| } |
| |
| CodeBlock* codeBlock = reinterpret_cast<CodeBlock*>(bitwise_cast<uint64_t>(value.asDouble())); |
| if (JSDollarVMPrototype::isValidCodeBlock(exec, codeBlock)) |
| return codeBlock; |
| |
| dataLogF("Invalid codeBlock: %p ", codeBlock); |
| dataLog(value, "\n"); |
| return nullptr; |
| |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionPrintSourceFor(ExecState* exec) |
| { |
| CodeBlock* codeBlock = codeBlockFromArg(exec); |
| if (codeBlock) |
| codeBlock->dumpSource(); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionPrintByteCodeFor(ExecState* exec) |
| { |
| CodeBlock* codeBlock = codeBlockFromArg(exec); |
| if (codeBlock) |
| codeBlock->dumpBytecode(); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec) |
| { |
| auto scope = DECLARE_THROW_SCOPE(exec->vm()); |
| for (unsigned i = 0; i < exec->argumentCount(); ++i) { |
| String argStr = exec->uncheckedArgument(i).toString(exec)->value(exec); |
| RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| dataLog(argStr); |
| } |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| class PrintFrameFunctor { |
| public: |
| enum Action { |
| PrintOne, |
| PrintAll |
| }; |
| |
| PrintFrameFunctor(Action action, unsigned framesToSkip) |
| : m_action(action) |
| , m_framesToSkip(framesToSkip) |
| { |
| } |
| |
| StackVisitor::Status operator()(StackVisitor& visitor) const |
| { |
| m_currentFrame++; |
| if (m_currentFrame > m_framesToSkip) { |
| visitor->dump(WTF::dataFile(), Indenter(2), [&] (PrintStream& out) { |
| out.print("[", (m_currentFrame - m_framesToSkip - 1), "] "); |
| }); |
| } |
| if (m_action == PrintOne && m_currentFrame > m_framesToSkip) |
| return StackVisitor::Done; |
| return StackVisitor::Continue; |
| } |
| |
| private: |
| Action m_action; |
| unsigned m_framesToSkip; |
| mutable unsigned m_currentFrame { 0 }; |
| }; |
| |
| static void printCallFrame(CallFrame* callFrame, unsigned framesToSkip) |
| { |
| if (!ensureCurrentThreadOwnsJSLock(callFrame)) |
| return; |
| PrintFrameFunctor functor(PrintFrameFunctor::PrintOne, framesToSkip); |
| callFrame->iterate(functor); |
| } |
| |
| void JSDollarVMPrototype::printCallFrame(CallFrame* callFrame) |
| { |
| JSC::printCallFrame(callFrame, 0); |
| } |
| |
| static void printStack(CallFrame* topCallFrame, unsigned framesToSkip) |
| { |
| if (!ensureCurrentThreadOwnsJSLock(topCallFrame)) |
| return; |
| if (!topCallFrame) |
| return; |
| PrintFrameFunctor functor(PrintFrameFunctor::PrintAll, framesToSkip); |
| topCallFrame->iterate(functor); |
| } |
| |
| void JSDollarVMPrototype::printStack(CallFrame* topCallFrame) |
| { |
| JSC::printStack(topCallFrame, 0); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionPrintCallFrame(ExecState* exec) |
| { |
| // When the callers call this function, they are expecting to print their |
| // own frame. So skip 1 for this frame. |
| printCallFrame(exec, 1); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionPrintStack(ExecState* exec) |
| { |
| // When the callers call this function, they are expecting to print the |
| // stack starting their own frame. So skip 1 for this frame. |
| printStack(exec, 1); |
| return JSValue::encode(jsUndefined()); |
| } |
| |
| void JSDollarVMPrototype::printValue(JSValue value) |
| { |
| dataLog(value); |
| } |
| |
| static EncodedJSValue JSC_HOST_CALL functionValue(ExecState* exec) |
| { |
| WTF::StringPrintStream stream; |
| for (unsigned i = 0; i < exec->argumentCount(); ++i) { |
| if (i) |
| stream.print(", "); |
| stream.print(exec->uncheckedArgument(i)); |
| } |
| |
| return JSValue::encode(jsString(exec, stream.toString())); |
| } |
| |
| void JSDollarVMPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) |
| { |
| Base::finishCreation(vm); |
| |
| addFunction(vm, globalObject, "crash", functionCrash, 0); |
| |
| putDirectNativeFunction(vm, globalObject, Identifier::fromString(&vm, "dfgTrue"), 0, functionDFGTrue, DFGTrueIntrinsic, DontEnum); |
| |
| addFunction(vm, globalObject, "llintTrue", functionLLintTrue, 0); |
| addFunction(vm, globalObject, "jitTrue", functionJITTrue, 0); |
| |
| addFunction(vm, globalObject, "gc", functionGC, 0); |
| addFunction(vm, globalObject, "edenGC", functionEdenGC, 0); |
| |
| addFunction(vm, globalObject, "codeBlockForFrame", functionCodeBlockForFrame, 1); |
| addFunction(vm, globalObject, "printSourceFor", functionPrintSourceFor, 1); |
| addFunction(vm, globalObject, "printByteCodeFor", functionPrintByteCodeFor, 1); |
| |
| addFunction(vm, globalObject, "print", functionPrint, 1); |
| addFunction(vm, globalObject, "printCallFrame", functionPrintCallFrame, 0); |
| addFunction(vm, globalObject, "printStack", functionPrintStack, 0); |
| |
| addFunction(vm, globalObject, "value", functionValue, 1); |
| } |
| |
| } // namespace JSC |