1/*
2 * Copyright (C) 2008-2018 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CallFrame.h"
28
29#include "CodeBlock.h"
30#include "InlineCallFrame.h"
31#include "Interpreter.h"
32#include "JSCInlines.h"
33#include "JSWebAssemblyInstance.h"
34#include "VMEntryScope.h"
35#include "WasmContextInlines.h"
36#include "WasmInstance.h"
37#include <wtf/StringPrintStream.h>
38
39namespace JSC {
40
41void ExecState::initGlobalExec(ExecState* globalExec, JSCallee* globalCallee)
42{
43 globalExec->setCodeBlock(nullptr);
44 globalExec->setCallerFrame(noCaller());
45 globalExec->setReturnPC(0);
46 globalExec->setArgumentCountIncludingThis(0);
47 globalExec->setCallee(globalCallee);
48 ASSERT(globalExec->isGlobalExec());
49}
50
51bool CallFrame::callSiteBitsAreBytecodeOffset() const
52{
53 ASSERT(codeBlock());
54 switch (codeBlock()->jitType()) {
55 case JITType::InterpreterThunk:
56 case JITType::BaselineJIT:
57 return true;
58 case JITType::None:
59 case JITType::HostCallThunk:
60 RELEASE_ASSERT_NOT_REACHED();
61 return false;
62 default:
63 return false;
64 }
65
66 RELEASE_ASSERT_NOT_REACHED();
67 return false;
68}
69
70bool CallFrame::callSiteBitsAreCodeOriginIndex() const
71{
72 ASSERT(codeBlock());
73 switch (codeBlock()->jitType()) {
74 case JITType::DFGJIT:
75 case JITType::FTLJIT:
76 return true;
77 case JITType::None:
78 case JITType::HostCallThunk:
79 RELEASE_ASSERT_NOT_REACHED();
80 return false;
81 default:
82 return false;
83 }
84
85 RELEASE_ASSERT_NOT_REACHED();
86 return false;
87}
88
89unsigned CallFrame::callSiteAsRawBits() const
90{
91 return this[CallFrameSlot::argumentCount].tag();
92}
93
94SUPPRESS_ASAN unsigned CallFrame::unsafeCallSiteAsRawBits() const
95{
96 return this[CallFrameSlot::argumentCount].unsafeTag();
97}
98
99CallSiteIndex CallFrame::callSiteIndex() const
100{
101 return CallSiteIndex(callSiteAsRawBits());
102}
103
104SUPPRESS_ASAN CallSiteIndex CallFrame::unsafeCallSiteIndex() const
105{
106 return CallSiteIndex(unsafeCallSiteAsRawBits());
107}
108
109#if USE(JSVALUE32_64)
110const Instruction* CallFrame::currentVPC() const
111{
112 return bitwise_cast<Instruction*>(callSiteIndex().bits());
113}
114
115void CallFrame::setCurrentVPC(const Instruction* vpc)
116{
117 CallSiteIndex callSite(vpc);
118 this[CallFrameSlot::argumentCount].tag() = callSite.bits();
119}
120
121unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
122{
123 ASSERT(codeBlock());
124 ASSERT(callSiteBitsAreBytecodeOffset());
125 return codeBlock()->bytecodeOffset(currentVPC());
126}
127
128#else // USE(JSVALUE32_64)
129const Instruction* CallFrame::currentVPC() const
130{
131 ASSERT(callSiteBitsAreBytecodeOffset());
132 return codeBlock()->instructions().at(callSiteBitsAsBytecodeOffset()).ptr();
133}
134
135void CallFrame::setCurrentVPC(const Instruction* vpc)
136{
137 CallSiteIndex callSite(codeBlock()->bytecodeOffset(vpc));
138 this[CallFrameSlot::argumentCount].tag() = static_cast<int32_t>(callSite.bits());
139}
140
141unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
142{
143 ASSERT(codeBlock());
144 ASSERT(callSiteBitsAreBytecodeOffset());
145 return callSiteIndex().bits();
146}
147
148#endif
149
150unsigned CallFrame::bytecodeOffset()
151{
152 ASSERT(!callee().isWasm());
153 if (!codeBlock())
154 return 0;
155#if ENABLE(DFG_JIT)
156 if (callSiteBitsAreCodeOriginIndex()) {
157 ASSERT(codeBlock());
158 CodeOrigin codeOrigin = this->codeOrigin();
159 for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame(); inlineCallFrame;) {
160 codeOrigin = inlineCallFrame->directCaller;
161 inlineCallFrame = codeOrigin.inlineCallFrame();
162 }
163 return codeOrigin.bytecodeIndex();
164 }
165#endif
166 ASSERT(callSiteBitsAreBytecodeOffset());
167 return callSiteBitsAsBytecodeOffset();
168}
169
170CodeOrigin CallFrame::codeOrigin()
171{
172 if (!codeBlock())
173 return CodeOrigin(0);
174#if ENABLE(DFG_JIT)
175 if (callSiteBitsAreCodeOriginIndex()) {
176 CallSiteIndex index = callSiteIndex();
177 ASSERT(codeBlock()->canGetCodeOrigin(index));
178 return codeBlock()->codeOrigin(index);
179 }
180#endif
181 return CodeOrigin(callSiteBitsAsBytecodeOffset());
182}
183
184Register* CallFrame::topOfFrameInternal()
185{
186 CodeBlock* codeBlock = this->codeBlock();
187 ASSERT(codeBlock);
188 return registers() + codeBlock->stackPointerOffset();
189}
190
191JSGlobalObject* CallFrame::wasmAwareLexicalGlobalObject(VM& vm)
192{
193#if ENABLE(WEBASSEMBLY)
194 if (!callee().isWasm())
195 return lexicalGlobalObject();
196 return vm.wasmContext.load()->owner<JSWebAssemblyInstance>()->globalObject(vm);
197#else
198 UNUSED_PARAM(vm);
199 return lexicalGlobalObject();
200#endif
201}
202
203bool CallFrame::isAnyWasmCallee()
204{
205 CalleeBits callee = this->callee();
206 if (callee.isWasm())
207 return true;
208
209 ASSERT(callee.isCell());
210 if (!!callee.rawPtr() && isWebAssemblyToJSCallee(callee.asCell()))
211 return true;
212
213 return false;
214}
215
216CallFrame* CallFrame::callerFrame(EntryFrame*& currEntryFrame) const
217{
218 if (callerFrameOrEntryFrame() == currEntryFrame) {
219 VMEntryRecord* currVMEntryRecord = vmEntryRecord(currEntryFrame);
220 currEntryFrame = currVMEntryRecord->prevTopEntryFrame();
221 return currVMEntryRecord->prevTopCallFrame();
222 }
223 return static_cast<CallFrame*>(callerFrameOrEntryFrame());
224}
225
226SUPPRESS_ASAN CallFrame* CallFrame::unsafeCallerFrame(EntryFrame*& currEntryFrame) const
227{
228 if (unsafeCallerFrameOrEntryFrame() == currEntryFrame) {
229 VMEntryRecord* currVMEntryRecord = vmEntryRecord(currEntryFrame);
230 currEntryFrame = currVMEntryRecord->unsafePrevTopEntryFrame();
231 return currVMEntryRecord->unsafePrevTopCallFrame();
232 }
233 return static_cast<CallFrame*>(unsafeCallerFrameOrEntryFrame());
234}
235
236SourceOrigin CallFrame::callerSourceOrigin()
237{
238 RELEASE_ASSERT(callee().isCell());
239 VM* vm = &this->vm();
240 SourceOrigin sourceOrigin;
241 bool haveSkippedFirstFrame = false;
242 StackVisitor::visit(this, vm, [&](StackVisitor& visitor) {
243 if (!std::exchange(haveSkippedFirstFrame, true))
244 return StackVisitor::Status::Continue;
245
246 switch (visitor->codeType()) {
247 case StackVisitor::Frame::CodeType::Function:
248 // Skip the builtin functions since they should not pass the source origin to the dynamic code generation calls.
249 // Consider the following code.
250 //
251 // [ "42 + 44" ].forEach(eval);
252 //
253 // In the above case, the eval function will be interpreted as the indirect call to eval inside forEach function.
254 // At that time, the generated eval code should have the source origin to the original caller of the forEach function
255 // instead of the source origin of the forEach function.
256 if (static_cast<FunctionExecutable*>(visitor->codeBlock()->ownerExecutable())->isBuiltinFunction())
257 return StackVisitor::Status::Continue;
258 FALLTHROUGH;
259
260 case StackVisitor::Frame::CodeType::Eval:
261 case StackVisitor::Frame::CodeType::Module:
262 case StackVisitor::Frame::CodeType::Global:
263 sourceOrigin = visitor->codeBlock()->ownerExecutable()->sourceOrigin();
264 return StackVisitor::Status::Done;
265
266 case StackVisitor::Frame::CodeType::Native:
267 return StackVisitor::Status::Continue;
268
269 case StackVisitor::Frame::CodeType::Wasm:
270 // FIXME: Should return the source origin for WASM.
271 return StackVisitor::Status::Done;
272 }
273
274 RELEASE_ASSERT_NOT_REACHED();
275 return StackVisitor::Status::Done;
276 });
277 return sourceOrigin;
278}
279
280String CallFrame::friendlyFunctionName()
281{
282 CodeBlock* codeBlock = this->codeBlock();
283 if (!codeBlock)
284 return emptyString();
285
286 switch (codeBlock->codeType()) {
287 case EvalCode:
288 return "eval code"_s;
289 case ModuleCode:
290 return "module code"_s;
291 case GlobalCode:
292 return "global code"_s;
293 case FunctionCode:
294 if (jsCallee())
295 return getCalculatedDisplayName(vm(), jsCallee());
296 return emptyString();
297 }
298
299 ASSERT_NOT_REACHED();
300 return emptyString();
301}
302
303void CallFrame::dump(PrintStream& out)
304{
305 if (CodeBlock* codeBlock = this->codeBlock()) {
306 out.print(codeBlock->inferredName(), "#", codeBlock->hashAsStringIfPossible(), " [", codeBlock->jitType(), " bc#", bytecodeOffset(), "]");
307
308 out.print("(");
309 thisValue().dumpForBacktrace(out);
310
311 for (size_t i = 0; i < argumentCount(); ++i) {
312 out.print(", ");
313 JSValue value = argument(i);
314 value.dumpForBacktrace(out);
315 }
316
317 out.print(")");
318
319 return;
320 }
321
322 out.print(returnPC());
323}
324
325const char* CallFrame::describeFrame()
326{
327 const size_t bufferSize = 200;
328 static char buffer[bufferSize + 1];
329
330 WTF::StringPrintStream stringStream;
331
332 dump(stringStream);
333
334 strncpy(buffer, stringStream.toCString().data(), bufferSize);
335 buffer[bufferSize] = '\0';
336
337 return buffer;
338}
339
340void CallFrame::convertToStackOverflowFrame(VM& vm, CodeBlock* codeBlockToKeepAliveUntilFrameIsUnwound)
341{
342 ASSERT(!isGlobalExec());
343 ASSERT(codeBlockToKeepAliveUntilFrameIsUnwound->inherits<CodeBlock>(vm));
344
345 EntryFrame* entryFrame = vm.topEntryFrame;
346 CallFrame* throwOriginFrame = this;
347 do {
348 throwOriginFrame = throwOriginFrame->callerFrame(entryFrame);
349 } while (throwOriginFrame && throwOriginFrame->callee().isWasm());
350
351 JSObject* originCallee = throwOriginFrame ? throwOriginFrame->jsCallee() : vmEntryRecord(vm.topEntryFrame)->callee();
352 JSObject* stackOverflowCallee = originCallee->globalObject()->stackOverflowFrameCallee();
353
354 setCodeBlock(codeBlockToKeepAliveUntilFrameIsUnwound);
355 setCallee(stackOverflowCallee);
356 setArgumentCountIncludingThis(0);
357}
358
359} // namespace JSC
360