Arguments* StackVisitor::Frame::arguments() { ASSERT(m_callFrame); CallFrame* physicalFrame = m_callFrame; VM& vm = physicalFrame->vm(); Arguments* arguments; #if ENABLE(DFG_JIT) if (isInlinedFrame()) { ASSERT(m_inlineCallFrame); arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame); arguments->tearOff(physicalFrame, m_inlineCallFrame); } else #endif { arguments = Arguments::create(vm, physicalFrame); arguments->tearOff(physicalFrame); } return arguments; }
PassRefPtr<ScriptCallStack> createScriptCallStack(JSC::ExecState* exec, size_t maxStackSize, bool emptyIsAllowed) { Vector<ScriptCallFrame> frames; if (exec) { CallFrame* frame = exec->vm().topCallFrame; CreateScriptCallStackFunctor functor(false, frames, maxStackSize); frame->iterate(functor); } if (frames.isEmpty() && !emptyIsAllowed) { // No frames found. It may happen in the case where // a bound function is called from native code for example. // Fallback to setting lineNumber to 0, and source and function name to "undefined". frames.append(ScriptCallFrame(ASCIILiteral("undefined"), ASCIILiteral("undefined"), 0, 0)); } return ScriptCallStack::create(frames); }
PassRefPtr<ScriptCallStack> createScriptCallStack(size_t maxStackSize, bool emptyIsAllowed) { Vector<ScriptCallFrame> frames; if (JSC::ExecState* exec = JSMainThreadExecState::currentState()) { CallFrame* frame = exec->vm().topCallFrame; for (StackIterator iter = frame->begin(); iter != frame->end() && maxStackSize--; ++iter) { unsigned line; unsigned column; iter->computeLineAndColumn(line, column); frames.append(ScriptCallFrame(iter->functionName(), iter->sourceURL(), line, column)); } } if (frames.isEmpty() && !emptyIsAllowed) { // No frames found. It may happen in the case where // a bound function is called from native code for example. // Fallback to setting lineNumber to 0, and source and function name to "undefined". frames.append(ScriptCallFrame("undefined", "undefined", 0, 0)); } return ScriptCallStack::create(frames); }
JSValue evaluateInGlobalCallFrame(const String& script, JSValue& exception, JSGlobalObject* globalObject) { CallFrame* globalCallFrame = globalObject->globalExec(); VM& vm = globalObject->vm(); EvalExecutable* eval = EvalExecutable::create(globalCallFrame, makeSource(script), false); if (!eval) { exception = vm.exception; vm.exception = JSValue(); return exception; } JSValue result = vm.interpreter->execute(eval, globalCallFrame, globalObject, globalCallFrame->scope()); if (vm.exception) { exception = vm.exception; vm.exception = JSValue(); } ASSERT(result); return result; }
CallFrame* VM::get_variables_frame(ssize_t up) { CallFrame* frame = call_frame_; while(frame && up-- > 0) { frame = frame->previous; } while(frame) { if(!frame->is_inline_block() && !frame->native_method_p() && frame->scope) { return frame; } frame = frame->previous; } return NULL; }
PassRefPtr<ScriptCallStack> createScriptCallStack(JSC::ExecState* exec, size_t maxStackSize) { Vector<ScriptCallFrame> frames; ASSERT(exec); CallFrame* frame = exec->vm().topCallFrame; StackIterator iter = frame->begin(); if (iter.numberOfFrames() > 1) ++iter; for (; iter != frame->end() && maxStackSize--; ++iter) { // This early exit is necessary to maintain our old behaviour // but the stack trace we produce now is complete and handles all // ways in which code may be running if (!iter->callee() && frames.size()) break; unsigned line; unsigned column; iter->computeLineAndColumn(line, column); frames.append(ScriptCallFrame(iter->functionName(), iter->sourceURL(), line, column)); } return ScriptCallStack::create(frames); }
JSValue JSActivation::argumentsGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) { JSActivation* activation = asActivation(slot.slotBase()); if (activation->d()->functionExecutable->usesArguments()) { PropertySlot slot; activation->symbolTableGet(exec->propertyNames().arguments, slot); return slot.getValue(exec, exec->propertyNames().arguments); } CallFrame* callFrame = CallFrame::create(activation->d()->registers); Arguments* arguments = callFrame->optionalCalleeArguments(); if (!arguments) { arguments = new (callFrame) Arguments(callFrame); arguments->copyRegisters(); callFrame->setCalleeArguments(arguments); } ASSERT(arguments->inherits(&Arguments::info)); return arguments; }
Ref<ScriptCallStack> createScriptCallStackForConsole(JSC::ExecState* exec, size_t maxStackSize) { if (!exec) return ScriptCallStack::create(); JSLockHolder locker(exec); Vector<ScriptCallFrame> frames; CallFrame* frame = exec->vm().topCallFrame; if (!frame) return ScriptCallStack::create(); CreateScriptCallStackFunctor functor(true, frames, maxStackSize); frame->iterate(functor); if (frames.isEmpty()) { CreateScriptCallStackFunctor functor(false, frames, maxStackSize); frame->iterate(functor); } return ScriptCallStack::create(frames); }
void ScriptCallStack::fillFrames() { if (m_filled || !m_caller) return; /* AJValue function = m_exec->interpreter()->retrieveCaller(m_exec, m_caller); while (!function.isNull()) { InternalFunction* internal = asInternalFunction(function); m_frames.append(ScriptCallFrame(internal->name(m_exec), UString(), 0, ArgList(), 0)); function = m_exec->interpreter()->retrieveCaller(m_exec, internal); }*/ CallFrame* callFrame = m_exec->callerFrame(); while(callFrame) { UString functionName; int tmpLineNO; intptr_t sourceID; UString urlString; AJValue function; m_exec->interpreter()->retrieveLastCaller(callFrame, tmpLineNO, sourceID, urlString, function); if (function) { functionName = asInternalFunction(function)->name(m_exec); } else { if (!m_frames.isEmpty()) break; } unsigned lineNO = tmpLineNO >= 0 ? tmpLineNO : 0; m_frames.append(ScriptCallFrame(functionName, urlString, lineNO, ArgList(), 0)); if (!function) break; callFrame = callFrame->callerFrame(); } m_filled = true; }
void JSActivation::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { JSActivation* thisObject = jsCast<JSActivation*>(object); CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(thisObject->m_registers)); if (mode == IncludeDontEnumProperties && !thisObject->isTornOff() && (callFrame->codeBlock()->usesArguments() || callFrame->codeBlock()->usesEval())) propertyNames.add(exec->propertyNames().arguments); { ConcurrentJITLocker locker(thisObject->symbolTable()->m_lock); SymbolTable::Map::iterator end = thisObject->symbolTable()->end(locker); for (SymbolTable::Map::iterator it = thisObject->symbolTable()->begin(locker); it != end; ++it) { if (it->value.getAttributes() & DontEnum && mode != IncludeDontEnumProperties) continue; if (!thisObject->isValid(it->value)) continue; propertyNames.add(Identifier(exec, it->key.get())); } } // Skip the JSVariableObject implementation of getOwnNonIndexPropertyNames JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); }
void StackIterator::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) { ASSERT(codeOrigin); ASSERT(!callFrame->hasHostCallFrameFlag()); unsigned frameOffset = inlinedFrameOffset(codeOrigin); bool isInlined = !!frameOffset; if (isInlined) { InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; m_frame.m_callFrame = callFrame; m_frame.m_inlineCallFrame = inlineCallFrame; m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock(); m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex; JSFunction* callee = inlineCallFrame->callee.get(); if (callee) { m_frame.m_scope = callee->scope(); m_frame.m_callee = callee; } else { CallFrame* inlinedFrame = callFrame + frameOffset; m_frame.m_scope = inlinedFrame->scope(); m_frame.m_callee = inlinedFrame->callee(); } ASSERT(m_frame.scope()); ASSERT(m_frame.callee()); // The callerFrame just needs to be non-null to indicate that we // haven't reached the last frame yet. Setting it to the root // frame (i.e. the callFrame that this inlined frame is called from) // would work just fine. m_frame.m_callerFrame = callFrame; return; } readNonInlinedFrame(callFrame, codeOrigin); }
void ScriptCallStack::initialize() { if (!m_caller || m_initialized) return; int signedLineNumber; intptr_t sourceID; UString urlString; JSValue function; // callFrame must exist if m_caller is not null. CallFrame* callFrame = m_exec->callerFrame(); while (true) { ASSERT(callFrame); m_exec->interpreter()->retrieveLastCaller(callFrame, signedLineNumber, sourceID, urlString, function); if (!function) break; JSFunction* jsFunction = asFunction(function); unsigned lineNumber = signedLineNumber >= 0 ? signedLineNumber : 0; m_frames.append(ScriptCallFrame(jsFunction->name(m_exec), urlString, lineNumber, m_exec, 0)); callFrame = callFrame->callerFrame(); } m_initialized = true; }
void LLVMState::compile_callframe(STATE, CompiledMethod* start, CallFrame* call_frame, int primitive) { if(debug_search) { std::cout << std::endl << "JIT: triggered: " << enclosure_name(start) << "#" << symbol_debug_str(start->name()) << std::endl; } // if(config().jit_inline_debug) { // if(start) { // std::cout << "JIT: target search from " // << symbol_debug_str(start->name()) << "\n"; // } else { // std::cout << "JIT: target search from primitive\n"; // } // } CallFrame* candidate = find_candidate(state, start, call_frame); if(!candidate || candidate->jitted_p() || candidate->inline_method_p()) { if(debug_search) { std::cout << "JIT: invalid candidate returned" << std::endl; } return; } if(debug_search) { std::cout << "! "; candidate->print_backtrace(state, 1); } if(start && candidate->cm != start) { start->backend_method()->call_count = 0; } if(candidate->cm->backend_method()->call_count <= 1) { if(!start || start->backend_method()->jitted()) return; // Ignore it. compile this one. candidate = call_frame; } if(candidate->block_p()) { compile_soon(state, candidate->cm, candidate->block_env(), true); } else { if(candidate->cm->can_specialize_p()) { compile_soon(state, candidate->cm, candidate->self()->class_object(state)); } else { compile_soon(state, candidate->cm, cNil); } } }
// Evaluate some JavaScript code in the scope of this frame. JSValue DebuggerCallFrame::evaluate(const String& script, NakedPtr<Exception>& exception) { ASSERT(isValid()); CallFrame* callFrame = m_callFrame; if (!callFrame) return jsNull(); JSLockHolder lock(callFrame); if (!callFrame->codeBlock()) return JSValue(); DebuggerEvalEnabler evalEnabler(callFrame); VM& vm = callFrame->vm(); auto& codeBlock = *callFrame->codeBlock(); ThisTDZMode thisTDZMode = codeBlock.unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded; VariableEnvironment variablesUnderTDZ; JSScope::collectVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ); EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), codeBlock.isStrictMode(), thisTDZMode, codeBlock.unlinkedCodeBlock()->isDerivedConstructorContext(), codeBlock.unlinkedCodeBlock()->isArrowFunction(), &variablesUnderTDZ); if (vm.exception()) { exception = vm.exception(); vm.clearException(); return jsUndefined(); } JSValue thisValue = thisValueForCallFrame(callFrame); JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, scope()->jsScope()); if (vm.exception()) { exception = vm.exception(); vm.clearException(); } ASSERT(result); return result; }
EncodedJSValue JSLexicalEnvironment::argumentsGetter(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName) { JSLexicalEnvironment* lexicalEnvironment = jsCast<JSLexicalEnvironment*>(slotBase); CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(lexicalEnvironment->m_registers)); return JSValue::encode(jsUndefined()); VirtualRegister argumentsRegister = callFrame->codeBlock()->argumentsRegister(); if (JSValue arguments = callFrame->uncheckedR(argumentsRegister.offset()).jsValue()) return JSValue::encode(arguments); int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister).offset(); JSValue arguments = JSValue(Arguments::create(callFrame->vm(), callFrame)); callFrame->uncheckedR(argumentsRegister.offset()) = arguments; callFrame->uncheckedR(realArgumentsRegister) = arguments; ASSERT(callFrame->uncheckedR(realArgumentsRegister).jsValue().inherits(Arguments::info())); return JSValue::encode(callFrame->uncheckedR(realArgumentsRegister).jsValue()); }
JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source) { JSGlobalData* globalData = &exec->globalData(); addErrorInfo(globalData, error, line, source); JSArray* stack = constructEmptyArray(exec); CallFrame* frame = exec; JSObject* stackFrame; CodeBlock* codeBlock; UString sourceURL; UString functionName; ReturnAddressPtr pc; while (!frame->hasHostCallFrameFlag()) { stackFrame = constructEmptyObject(exec); codeBlock = frame->codeBlock(); // sourceURL sourceURL = codeBlock->ownerExecutable()->sourceURL(); stackFrame->putWithAttributes( globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete ); // line if (frame != exec) { line = codeBlock->lineNumberForBytecodeOffset(codeBlock->bytecodeOffset(pc)); } stackFrame->putWithAttributes( globalData, Identifier(globalData, linePropertyName), jsNumber(line), ReadOnly | DontDelete ); // function JSObject* function = frame->callee(); if (function && function->inherits(&JSFunction::s_info)) { functionName = asFunction(function)->calculatedDisplayName(exec); stackFrame->putWithAttributes( globalData, Identifier(globalData, functionPropertyName), jsString(globalData, functionName), ReadOnly | DontDelete ); } stack->push(exec, JSValue(stackFrame)); pc = frame->returnPC(); frame = frame->callerFrame(); } error->putWithAttributes(globalData, Identifier(globalData, stackPropertyName), stack, ReadOnly | DontDelete); return error; }
JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, PropertyName) { JSActivation* activation = jsCast<JSActivation*>(slotBase); if (activation->isTornOff()) return jsUndefined(); CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(activation->m_registers)); int argumentsRegister = callFrame->codeBlock()->argumentsRegister(); if (JSValue arguments = callFrame->uncheckedR(argumentsRegister).jsValue()) return arguments; int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister); JSValue arguments = JSValue(Arguments::create(callFrame->vm(), callFrame)); callFrame->uncheckedR(argumentsRegister) = arguments; callFrame->uncheckedR(realArgumentsRegister) = arguments; ASSERT(callFrame->uncheckedR(realArgumentsRegister).jsValue().inherits(Arguments::info())); return callFrame->uncheckedR(realArgumentsRegister).jsValue(); }
JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, const Identifier&) { JSActivation* activation = asActivation(slotBase); CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(activation->m_registers)); int argumentsRegister = activation->m_argumentsRegister; if (JSValue arguments = callFrame->uncheckedR(argumentsRegister).jsValue()) return arguments; int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister); JSValue arguments = JSValue(Arguments::create(callFrame->globalData(), callFrame)); callFrame->uncheckedR(argumentsRegister) = arguments; callFrame->uncheckedR(realArgumentsRegister) = arguments; ASSERT(callFrame->uncheckedR(realArgumentsRegister).jsValue().inherits(&Arguments::s_info)); return callFrame->uncheckedR(realArgumentsRegister).jsValue(); }
JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, const Identifier&) { JSActivation* activation = asActivation(slotBase); CallFrame* callFrame = CallFrame::create(activation->d()->registers); int argumentsRegister = activation->d()->functionExecutable->generatedBytecode().argumentsRegister(); if (!callFrame->uncheckedR(argumentsRegister).jsValue()) { JSValue arguments = JSValue(new (callFrame) Arguments(callFrame)); callFrame->uncheckedR(argumentsRegister) = arguments; callFrame->uncheckedR(unmodifiedArgumentsRegister(argumentsRegister)) = arguments; } ASSERT(callFrame->uncheckedR(argumentsRegister).jsValue().inherits(&Arguments::info)); return callFrame->uncheckedR(argumentsRegister).jsValue(); }
CallFrame* CallFrame::trueCallFrame(AbstractPC pc) { // Am I an inline call frame? If so, we're done. if (isInlineCallFrame()) return this; // If I don't have a code block, then I'm not DFG code, so I'm the true call frame. CodeBlock* machineCodeBlock = codeBlock(); if (!machineCodeBlock) return this; // If the code block does not have any code origins, then there was no inlining, so // I'm done. if (!machineCodeBlock->hasCodeOrigins()) return this; // At this point the PC must be due either to the DFG, or it must be unset. ASSERT(pc.hasJITReturnAddress() || !pc); // Try to determine the CodeOrigin. If we don't have a pc set then the only way // that this makes sense is if the CodeOrigin index was set in the call frame. // FIXME: Note that you will see "Not currently in inlined code" comments below. // Currently, we do not record code origins for code that is not inlined, because // the only thing that we use code origins for is determining the inline stack. // But in the future, we'll want to use this same functionality (having a code // origin mapping for any calls out of JIT code) to determine the PC at any point // in the stack even if not in inlined code. When that happens, the code below // will have to change the way it detects the presence of inlining: it will always // get a code origin, but sometimes, that code origin will not have an inline call // frame. In that case, this method should bail and return this. CodeOrigin codeOrigin; if (pc.isSet()) { ReturnAddressPtr currentReturnPC = pc.jitReturnAddress(); bool hasCodeOrigin = machineCodeBlock->codeOriginForReturn(currentReturnPC, codeOrigin); ASSERT_UNUSED(hasCodeOrigin, hasCodeOrigin); } else { unsigned index = codeOriginIndexForDFG(); codeOrigin = machineCodeBlock->codeOrigin(index); } if (!codeOrigin.inlineCallFrame) return this; // Not currently in inlined code. for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) { InlineCallFrame* nextInlineCallFrame = inlineCallFrame->caller.inlineCallFrame; CallFrame* inlinedCaller = this + inlineCallFrame->stackOffset; JSFunction* calleeAsFunction = inlineCallFrame->callee.get(); // Fill in the inlinedCaller inlinedCaller->setCodeBlock(machineCodeBlock); inlinedCaller->setScopeChain(calleeAsFunction->scope()); if (nextInlineCallFrame) inlinedCaller->setCallerFrame(this + nextInlineCallFrame->stackOffset); else inlinedCaller->setCallerFrame(this); inlinedCaller->setInlineCallFrame(inlineCallFrame); inlinedCaller->setArgumentCountIncludingThis(inlineCallFrame->arguments.size()); inlinedCaller->setCallee(calleeAsFunction); inlineCallFrame = nextInlineCallFrame; } return this + codeOrigin.inlineCallFrame->stackOffset; }
void SignalThread::print_backtraces() { STATE = shared_.env()->state; ThreadList* threads = shared_.thread_nexus()->threads(); for(ThreadList::iterator i = threads->begin(); i != threads->end(); ++i) { VM* vm = (*i)->as_vm(); if(!vm) continue; bool first = true; CallFrame* frame = vm->call_frame(); while(frame) { if(first) { logger::fatal("--- %s %d backtrace ---", vm->kind_name(), vm->thread_id()); first = false; } std::ostringstream stream; if(NativeMethodFrame* nmf = frame->native_method_frame()) { stream << static_cast<void*>(frame) << ": "; NativeMethod* nm = try_as<NativeMethod>(nmf->get_object(nmf->method())); if(nm && nm->name()->symbol_p()) { stream << "capi:" << nm->name()->debug_str(state) << " at "; stream << nm->file()->c_str(state); } else { stream << "unknown capi"; } } else if(frame->compiled_code) { if(frame->is_block_p(state)) { stream << "__block__"; } else { if(SingletonClass* sc = try_as<SingletonClass>(frame->module())) { Object* obj = sc->singleton(); if(Module* mod = try_as<Module>(obj)) { stream << mod->debug_str(state) << "."; } else { if(obj == G(main)) { stream << "MAIN."; } else { stream << "#<" << obj->class_object(state)->debug_str(state) << ":" << (void*)obj->id(state)->to_native() << ">."; } } } else if(IncludedModule* im = try_as<IncludedModule>(frame->module())) { stream << im->module()->debug_str(state) << "#"; } else { Symbol* name; std::string mod_name; if(frame->module()->nil_p()) { mod_name = frame->lexical_scope()->module()->debug_str(state); } else { if((name = try_as<Symbol>(frame->module()->module_name()))) { mod_name = name->debug_str(state); } else if((name = try_as<Symbol>( frame->lexical_scope()->module()->module_name()))) { mod_name = name->debug_str(state); } else { mod_name = "<anonymous module>"; } } stream << mod_name << "#"; } Symbol* name = try_as<Symbol>(frame->name()); if(name) { stream << name->debug_str(state); } else { stream << frame->compiled_code->name()->debug_str(state); } } stream << " in "; if(Symbol* file_sym = try_as<Symbol>(frame->compiled_code->file())) { stream << file_sym->debug_str(state) << ":" << frame->line(state); } else { stream << "<unknown>"; } stream << " (+" << frame->ip(); if(frame->is_inline_frame()) { stream << " inline"; } else if(frame->jitted_p()) { stream << " jit"; } stream << ")"; } logger::fatal(stream.str().c_str()); frame = frame->previous; } } }
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"); }
CallFrame* LLVMState::find_candidate(STATE, CallFrame* call_frame, CompiledCode* start) { if(!config_.jit_inline_generic) { return call_frame; } int depth = config().jit_limit_search; if(!start) { throw CompileError("find_candidate: null start"); } if(!call_frame) { throw CompileError("find_candidate: null call frame"); } if(debug_search) { std::cout << "> call_count: " << call_frame->compiled_code->machine_code()->call_count << " size: " << call_frame->compiled_code->machine_code()->total << " sends: " << call_frame->compiled_code->machine_code()->call_site_count() << std::endl; call_frame->print_backtrace(state, 1); } if(start->machine_code()->total > (size_t)config_.jit_limit_inline_method) { if(debug_search) { std::cout << "JIT: STOP. reason: trigger method isn't small: " << start->machine_code()->total << " > " << config_.jit_limit_inline_method << std::endl; } return call_frame; } MachineCode* mcode = start->machine_code(); if(mcode->required_args != mcode->total_args) { if(debug_search) { std::cout << "JIT: STOP. reason: trigger method req_args != total_args" << std::endl; } return call_frame; } if(mcode->no_inline_p()) { if(debug_search) { std::cout << "JIT: STOP. reason: trigger method no_inline_p() = true" << std::endl; } return call_frame; } CallFrame* callee = call_frame; // Now start looking at callers. while(callee) { if(depth-- == 0) break; CompiledCode* cur = callee->compiled_code; if(!cur) { if(debug_search) { std::cout << "JIT: STOP. reason: synthetic CallFrame hit" << std::endl; } return callee; } MachineCode* mcode = cur->machine_code(); if(debug_search) { std::cout << "> call_count: " << mcode->call_count << " size: " << mcode->total << " sends: " << mcode->call_site_count() << std::endl; call_frame->print_backtrace(state, 1); } /* if(call_frame->block_p() || mcode->required_args != mcode->total_args // has a splat || mcode->call_count < 200 // not called much || mcode->jitted() // already jitted || mcode->parent() // is a block ) return callee; */ if(mcode->required_args != mcode->total_args) { if(debug_search) { std::cout << "JIT: STOP. reason: req_args != total_args" << std::endl; } return callee; } if(mcode->call_count < config_.jit_threshold_inline) { if(debug_search) { std::cout << "JIT: STOP. reason: call_count too small: " << mcode->call_count << " < " << config_.jit_threshold_inline << std::endl; } return callee; } if(mcode->jitted_p()) { if(debug_search) { std::cout << "JIT: STOP. reason: already jitted" << std::endl; } return callee; } if(mcode->no_inline_p()) { if(debug_search) { std::cout << "JIT: STOP. reason: no_inline_p() = true" << std::endl; } return callee; } if(callee->jitted_p() || callee->inline_method_p()) { return callee; } if(mcode->call_site_count() > eMaxInlineSendCount) { if(debug_search) { std::cout << "JIT: STOP. reason: high send count" << std::endl; } return callee; } // if(mcode->required_args != mcode->total_args // has a splat // || mcode->call_count < 200 // not called much // || mcode->jitted() // already jitted // || !mcode->no_inline_p() // method marked as not inlineable // ) return callee; // if(cur->machine_code()->total > SMALL_METHOD_SIZE) { // if(debug_search) { // std::cout << "JIT: STOP. reason: big method: " // << cur->machine_code()->total << " > " // << SMALL_METHOD_SIZE // << "\n"; // } // return call_frame; // } // if(!next || cur->machine_code()->total > SMALL_METHOD_SIZE) return call_frame; callee = callee->previous; } return callee; }
VariableScope* VariableScope::of_sender(STATE, CallFrame* call_frame) { CallFrame* dest = static_cast<CallFrame*>(call_frame->previous); // Skip any frames for native methods while(dest->native_method_p()) { dest = dest->previous; } return dest->promote_scope(state); }
Object* const_get(STATE, Symbol* name, ConstantMissingReason* reason, Object* filter, bool replace_autoload) { LexicalScope *cur; Object* result; *reason = vNonExistent; CallFrame* frame = state->vm()->get_ruby_frame(); // Ok, this has to be explained or it will be considered black magic. // The scope chain always ends with an entry at the top that contains // a parent of nil, and a module of Object. This entry is put in // regardless of lexical scoping, it's the fallback scope (the default // scope). This is not case when deriving from BasicObject, which is // explained later. // // When looking up a constant, we don't want to consider the fallback // scope (ie, Object) initially because we need to lookup up // the superclass chain first, because falling back on the default. // // The rub comes from the fact that if a user explicitly opens up // Object in their code, we DO consider it. Like: // // class Idiot // A = 2 // end // // class ::Object // A = 1 // class Stupid < Idiot // def foo // p A // end // end // end // // In this code, when A is looked up, Object must be considering during // the scope walk, NOT during the superclass walk. // // So, in this case, foo would print "1", not "2". // // As indicated above, the fallback scope isn't used when the superclass // chain directly rooted from BasicObject. To determine this is the // case, we record whether Object is seen when looking up the superclass // chain. If Object isn't seen, this means we are directly deriving from // BasicObject. cur = frame->lexical_scope(); while(!cur->nil_p()) { // Detect the toplevel scope (the default) and get outta dodge. if(cur->top_level_p(state)) break; result = cur->module()->get_const(state, name, G(sym_private), reason, false, replace_autoload); if(*reason == vFound) { if(result != filter) return result; *reason = vNonExistent; } cur = cur->parent(); } // Now look up the superclass chain. Module *fallback = G(object); cur = frame->lexical_scope(); if(!cur->nil_p()) { bool object_seen = false; Module* mod = cur->module(); while(!mod->nil_p()) { if(mod == G(object)) { object_seen = true; } if(!object_seen && mod == G(basicobject)) { fallback = NULL; } result = mod->get_const(state, name, G(sym_private), reason, false, replace_autoload); if(*reason == vFound) { if(result != filter) return result; *reason = vNonExistent; } mod = mod->superclass(); } } // Lastly, check the fallback scope (=Object) specifically if needed if(fallback) { result = fallback->get_const(state, name, G(sym_private), reason, true, replace_autoload); if(*reason == vFound) { if(result != filter) return result; *reason = vNonExistent; } } return cNil; }
EncodedJSValue JSActivation::argumentsGetter(ExecState*, EncodedJSValue slotBase, EncodedJSValue, PropertyName) { JSActivation* activation = jsCast<JSActivation*>(JSValue::decode(slotBase)); CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(activation->m_registers)); ASSERT(!activation->isTornOff() && (callFrame->codeBlock()->usesArguments() || callFrame->codeBlock()->usesEval())); if (activation->isTornOff() || !(callFrame->codeBlock()->usesArguments() || callFrame->codeBlock()->usesEval())) return JSValue::encode(jsUndefined()); VirtualRegister argumentsRegister = callFrame->codeBlock()->argumentsRegister(); if (JSValue arguments = callFrame->uncheckedR(argumentsRegister.offset()).jsValue()) return JSValue::encode(arguments); int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister).offset(); JSValue arguments = JSValue(Arguments::create(callFrame->vm(), callFrame)); callFrame->uncheckedR(argumentsRegister.offset()) = arguments; callFrame->uncheckedR(realArgumentsRegister) = arguments; ASSERT(callFrame->uncheckedR(realArgumentsRegister).jsValue().inherits(Arguments::info())); return JSValue::encode(callFrame->uncheckedR(realArgumentsRegister).jsValue()); }
bool QueryAgent::process_commands(int client) { bert::IOReader reader(client); bert::Decoder<bert::IOReader> decoder(reader); bert::IOWriter writer(client); bert::Encoder<bert::IOWriter> encoder(writer); int ver = decoder.read_version(); if(ver != 131) return false; bert::Value* val = decoder.next_value(); if(!val || reader.eof_p()) return false; encoder.write_version(); if(val->type() == bert::Tuple) { bert::Value* cmd = val->get_element(0); if(cmd->equal_atom("uname")) { struct utsname name; if(uname(&name)) { encoder.write_atom("error"); return true; } else { encoder.write_tuple(5); encoder.write_binary(name.sysname); encoder.write_binary(name.nodename); encoder.write_binary(name.release); encoder.write_binary(name.version); encoder.write_binary(name.machine); return true; } } else if(cmd->equal_atom("set_config")) { if(val->elements()->size() == 3) { bert::Value* key = val->get_element(1); bert::Value* value = val->get_element(2); if(key->type() == bert::Binary && value->type() == bert::Binary) { if(shared_.config.import(key->string(), value->string())) { encoder.write_atom("ok"); } else { encoder.write_atom("unknown_key"); } return true; } } encoder.write_atom("error"); return true; } else if(cmd->equal_atom("get_config")) { if(val->elements()->size() == 2) { bert::Value* key = val->get_element(1); if(key->type() == bert::Binary) { if(config::ConfigItem* item = shared_.config.find(key->string())) { std::stringstream ss; item->print_value(ss); encoder.write_tuple(2); encoder.write_atom("ok"); if(config::Integer* i = dynamic_cast<config::Integer*>(item)) { encoder.write_integer(i->value); } else { encoder.write_binary(ss.str().c_str()); } } else { encoder.write_atom("unknown_key"); } return true; } } encoder.write_atom("error"); return true; } else if(cmd->equal_atom("backtrace")) { if(verbose_) { std::cerr << "[QA: Gathering backtraces, pausing threads]\n"; } encoder.write_tuple(2); encoder.write_atom("ok"); { GlobalLock::LockGuard guard(shared_.global_lock()); encoder.write_tuple(shared_.call_frame_locations().size()); for(CallFrameLocationList::iterator i = shared_.call_frame_locations().begin(); i != shared_.call_frame_locations().end(); i++) { CallFrame* loc = *(*i); std::ostringstream ss; loc->print_backtrace(state_, ss); encoder.write_binary(ss.str().c_str()); } } if(verbose_) { std::cerr << "[QA: Threads restarted]\n"; } } return true; } else if(val->equal_atom("close")) { encoder.write_atom("bye"); return false; } encoder.write_atom("unknown"); return true; }
void StackVisitor::Frame::dump(PrintStream& out, Indenter indent, WTF::Function<void(PrintStream&)> prefix) const { if (!this->callFrame()) { out.print(indent, "frame 0x0\n"); return; } CodeBlock* codeBlock = this->codeBlock(); out.print(indent); prefix(out); out.print("frame ", RawPointer(this->callFrame()), " {\n"); { indent++; CallFrame* callFrame = m_callFrame; CallFrame* callerFrame = this->callerFrame(); const void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; out.print(indent, "name: ", functionName(), "\n"); out.print(indent, "sourceURL: ", sourceURL(), "\n"); bool isInlined = false; #if ENABLE(DFG_JIT) isInlined = isInlinedFrame(); out.print(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); if (isInlinedFrame()) out.print(indent, "InlineCallFrame: ", RawPointer(m_inlineCallFrame), "\n"); #endif out.print(indent, "callee: ", RawPointer(callee().rawPtr()), "\n"); out.print(indent, "returnPC: ", RawPointer(returnPC), "\n"); out.print(indent, "callerFrame: ", RawPointer(callerFrame), "\n"); uintptr_t locationRawBits = callFrame->callSiteAsRawBits(); out.print(indent, "rawLocationBits: ", locationRawBits, " ", RawPointer(reinterpret_cast<void*>(locationRawBits)), "\n"); out.print(indent, "codeBlock: ", RawPointer(codeBlock)); if (codeBlock) out.print(" ", *codeBlock); out.print("\n"); if (codeBlock && !isInlined) { indent++; if (callFrame->callSiteBitsAreBytecodeOffset()) { unsigned bytecodeOffset = callFrame->bytecodeOffset(); out.print(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); #if ENABLE(DFG_JIT) } else { out.print(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); if (codeBlock->hasCodeOrigins()) { CallSiteIndex callSiteIndex = callFrame->callSiteIndex(); out.print(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n"); JITCode::JITType jitType = codeBlock->jitType(); if (jitType != JITCode::FTLJIT) { JITCode* jitCode = codeBlock->jitCode().get(); out.print(indent, "jitCode: ", RawPointer(jitCode), " start ", RawPointer(jitCode->start()), " end ", RawPointer(jitCode->end()), "\n"); } } #endif } unsigned line = 0; unsigned column = 0; computeLineAndColumn(line, column); out.print(indent, "line: ", line, "\n"); out.print(indent, "column: ", column, "\n"); indent--; } out.print(indent, "EntryFrame: ", RawPointer(m_entryFrame), "\n"); indent--; } out.print(indent, "}\n"); }
void CallFrame::print_backtrace(STATE, std::ostream& stream, int total, bool filter) { CallFrame* cf = this; int i = -1; while(cf) { i++; if(total > 0 && i == total) return; if(NativeMethodFrame* nmf = cf->native_method_frame()) { stream << static_cast<void*>(cf) << ": "; NativeMethod* nm = try_as<NativeMethod>(nmf->get_object(nmf->method())); if(nm && nm->name()->symbol_p()) { stream << "capi:" << nm->name()->debug_str(state) << " at "; stream << nm->file()->c_str(state); } else { stream << "unknown capi"; } stream << std::endl; cf = cf->previous; continue; } if(!cf->compiled_code) { cf = cf->previous; continue; } if(filter && cf->compiled_code->kernel_method(state)) { cf = cf->previous; continue; } stream << static_cast<void*>(cf) << ": "; if(cf->is_block_p(state)) { stream << "__block__"; } else { if(SingletonClass* sc = try_as<SingletonClass>(cf->module())) { Object* obj = sc->attached_instance(); if(Module* mod = try_as<Module>(obj)) { stream << mod->debug_str(state) << "."; } else { if(obj == G(main)) { stream << "MAIN."; } else { stream << "#<" << obj->class_object(state)->debug_str(state) << ":" << (void*)obj->id(state)->to_native() << ">."; } } } else if(IncludedModule* im = try_as<IncludedModule>(cf->module())) { stream << im->module()->debug_str(state) << "#"; } else { Symbol* name; std::string mod_name; if(cf->module()->nil_p()) { mod_name = cf->constant_scope()->module()->debug_str(state); } else { if((name = try_as<Symbol>(cf->module()->module_name()))) { mod_name = name->debug_str(state); } else if((name = try_as<Symbol>( cf->constant_scope()->module()->module_name()))) { mod_name = name->debug_str(state); } else { mod_name = "<anonymous module>"; } } stream << mod_name << "#"; } Symbol* name = try_as<Symbol>(cf->name()); if(name) { stream << name->debug_str(state); } else { stream << cf->compiled_code->name()->debug_str(state); } } stream << " in "; if(Symbol* file_sym = try_as<Symbol>(cf->compiled_code->file())) { stream << file_sym->debug_str(state) << ":" << cf->line(state); } else { stream << "<unknown>"; } stream << " (+" << cf->ip(); if(cf->is_inline_frame()) { stream << " inline"; } else if(cf->jitted_p()) { stream << " jit"; } stream << ")"; stream << std::endl; cf = cf->previous; } }
void StackVisitor::Frame::print(int indent) { if (!this->callFrame()) { log(indent, "frame 0x0\n"); return; } CodeBlock* codeBlock = this->codeBlock(); logF(indent, "frame %p {\n", this->callFrame()); { indent++; CallFrame* callFrame = m_callFrame; CallFrame* callerFrame = this->callerFrame(); void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; log(indent, "name: ", functionName(), "\n"); log(indent, "sourceURL: ", sourceURL(), "\n"); bool isInlined = false; #if ENABLE(DFG_JIT) isInlined = isInlinedFrame(); log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); if (isInlinedFrame()) logF(indent, "InlineCallFrame: %p\n", m_inlineCallFrame); #endif logF(indent, "callee: %p\n", callee()); logF(indent, "returnPC: %p\n", returnPC); logF(indent, "callerFrame: %p\n", callerFrame); unsigned locationRawBits = callFrame->callSiteAsRawBits(); logF(indent, "rawLocationBits: %u 0x%x\n", locationRawBits, locationRawBits); logF(indent, "codeBlock: %p ", codeBlock); if (codeBlock) dataLog(*codeBlock); dataLog("\n"); if (codeBlock && !isInlined) { indent++; if (callFrame->callSiteBitsAreBytecodeOffset()) { unsigned bytecodeOffset = callFrame->bytecodeOffset(); log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); #if ENABLE(DFG_JIT) } else { log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); if (codeBlock->hasCodeOrigins()) { CallSiteIndex callSiteIndex = callFrame->callSiteIndex(); log(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n"); JITCode::JITType jitType = codeBlock->jitType(); if (jitType != JITCode::FTLJIT) { JITCode* jitCode = codeBlock->jitCode().get(); logF(indent, "jitCode: %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); } } #endif } unsigned line = 0; unsigned column = 0; computeLineAndColumn(line, column); log(indent, "line: ", line, "\n"); log(indent, "column: ", column, "\n"); indent--; } indent--; } log(indent, "}\n"); }