]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
93a37866 | 2 | * Copyright (C) 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. |
9dae56ea A |
3 | * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
15 | * its contributors may be used to endorse or promote products derived | |
16 | * from this software without specific prior written permission. | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | ||
30 | #include "config.h" | |
31 | #include "Interpreter.h" | |
32 | ||
33 | #include "Arguments.h" | |
34 | #include "BatchedTransitionOptimizer.h" | |
ba379fdc A |
35 | #include "CallFrame.h" |
36 | #include "CallFrameClosure.h" | |
9dae56ea | 37 | #include "CodeBlock.h" |
14957cd0 | 38 | #include "Heap.h" |
ba379fdc | 39 | #include "Debugger.h" |
9dae56ea | 40 | #include "DebuggerCallFrame.h" |
14957cd0 | 41 | #include "ErrorInstance.h" |
9dae56ea A |
42 | #include "EvalCodeCache.h" |
43 | #include "ExceptionHelpers.h" | |
4e4e5a6f | 44 | #include "GetterSetter.h" |
9dae56ea A |
45 | #include "JSActivation.h" |
46 | #include "JSArray.h" | |
6fe7ccc8 | 47 | #include "JSBoundFunction.h" |
93a37866 | 48 | #include "JSNameScope.h" |
9dae56ea A |
49 | #include "JSNotAnObject.h" |
50 | #include "JSPropertyNameIterator.h" | |
93a37866 | 51 | #include "JSStackInlines.h" |
9dae56ea | 52 | #include "JSString.h" |
93a37866 A |
53 | #include "JSWithScope.h" |
54 | #include "LLIntCLoop.h" | |
55 | #include "LegacyProfiler.h" | |
56 | #include "LiteralParser.h" | |
57 | #include "NameInstance.h" | |
9dae56ea | 58 | #include "ObjectPrototype.h" |
ba379fdc | 59 | #include "Operations.h" |
9dae56ea | 60 | #include "Parser.h" |
9dae56ea A |
61 | #include "RegExpObject.h" |
62 | #include "RegExpPrototype.h" | |
63 | #include "Register.h" | |
9dae56ea | 64 | #include "SamplingTool.h" |
14957cd0 | 65 | #include "StrictEvalActivation.h" |
6fe7ccc8 | 66 | #include "StrongInlines.h" |
f9bf01c6 | 67 | #include <limits.h> |
9dae56ea | 68 | #include <stdio.h> |
93a37866 A |
69 | #include <wtf/StackStats.h> |
70 | #include <wtf/StringPrintStream.h> | |
ba379fdc | 71 | #include <wtf/Threading.h> |
93a37866 | 72 | #include <wtf/WTFThreadData.h> |
6fe7ccc8 | 73 | #include <wtf/text/StringBuilder.h> |
9dae56ea A |
74 | |
75 | #if ENABLE(JIT) | |
76 | #include "JIT.h" | |
77 | #endif | |
78 | ||
93a37866 | 79 | #define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(LLINT) && !defined(__llvm__)) |
4e4e5a6f | 80 | |
9dae56ea A |
81 | using namespace std; |
82 | ||
83 | namespace JSC { | |
9dae56ea | 84 | |
93a37866 A |
85 | Interpreter::ErrorHandlingMode::ErrorHandlingMode(ExecState *exec) |
86 | : m_interpreter(*exec->interpreter()) | |
4e4e5a6f | 87 | { |
93a37866 A |
88 | if (!m_interpreter.m_errorHandlingModeReentry) |
89 | m_interpreter.stack().enableErrorStackReserve(); | |
90 | m_interpreter.m_errorHandlingModeReentry++; | |
4e4e5a6f A |
91 | } |
92 | ||
93a37866 | 93 | Interpreter::ErrorHandlingMode::~ErrorHandlingMode() |
9dae56ea | 94 | { |
93a37866 A |
95 | m_interpreter.m_errorHandlingModeReentry--; |
96 | ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0); | |
97 | if (!m_interpreter.m_errorHandlingModeReentry) | |
98 | m_interpreter.stack().disableErrorStackReserve(); | |
9dae56ea A |
99 | } |
100 | ||
9dae56ea | 101 | |
93a37866 A |
102 | // The Interpreter::StackPolicy class is used to compute a stack capacity |
103 | // requirement to ensure that we have enough room on the native stack for: | |
104 | // 1. the max cumulative stack used by the interpreter and all code | |
105 | // paths sub of it up till leaf functions. | |
106 | // 2. the max cumulative stack used by the interpreter before it reaches | |
107 | // the next checkpoint (execute...() function) in the interpreter. | |
108 | // | |
109 | // The interpreter can be run on different threads and hence, different | |
110 | // native stacks (with different sizes) before exiting out of the first | |
111 | // frame. Hence, the required capacity needs to be re-computed on every | |
112 | // entry into the interpreter. | |
113 | // | |
114 | // Currently the requiredStack is computed based on a policy. See comments | |
115 | // in StackPolicy::StackPolicy() for details. | |
116 | ||
117 | Interpreter::StackPolicy::StackPolicy(Interpreter& interpreter, const StackBounds& stack) | |
118 | : m_interpreter(interpreter) | |
9dae56ea | 119 | { |
93a37866 | 120 | const size_t size = stack.size(); |
9dae56ea | 121 | |
93a37866 A |
122 | // We have two separate stack limits, one for regular JS execution, and one |
123 | // for when we're handling errors. We need the error stack to be smaller | |
124 | // otherwise there would obviously not be any stack left to execute JS in when | |
125 | // there's a stack overflow. | |
126 | // | |
127 | // These sizes were derived from the stack usage of a number of sites when | |
128 | // layout occurs when we've already consumed most of the C stack. | |
129 | const size_t requiredStack = 256 * KB; | |
130 | const size_t errorModeRequiredStack = 64 * KB; | |
9dae56ea | 131 | |
93a37866 | 132 | size_t requiredCapacity = m_interpreter.m_errorHandlingModeReentry ? errorModeRequiredStack : requiredStack; |
4e4e5a6f | 133 | |
93a37866 | 134 | RELEASE_ASSERT(size > requiredCapacity); |
4e4e5a6f | 135 | |
93a37866 | 136 | m_requiredCapacity = requiredCapacity; |
9dae56ea A |
137 | } |
138 | ||
9dae56ea | 139 | |
93a37866 | 140 | static CallFrame* getCallerInfo(VM*, CallFrame*, unsigned& bytecodeOffset, CodeBlock*& callerOut); |
6fe7ccc8 | 141 | |
93a37866 A |
142 | // Returns the depth of the scope chain within a given call frame. |
143 | static int depth(CodeBlock* codeBlock, JSScope* sc) | |
6fe7ccc8 | 144 | { |
93a37866 | 145 | if (!codeBlock->needsFullScopeChain()) |
6fe7ccc8 | 146 | return 0; |
93a37866 | 147 | return sc->localDepth(); |
ba379fdc | 148 | } |
ba379fdc | 149 | |
6fe7ccc8 | 150 | JSValue eval(CallFrame* callFrame) |
9dae56ea | 151 | { |
6fe7ccc8 | 152 | if (!callFrame->argumentCount()) |
9dae56ea A |
153 | return jsUndefined(); |
154 | ||
6fe7ccc8 | 155 | JSValue program = callFrame->argument(0); |
9dae56ea A |
156 | if (!program.isString()) |
157 | return program; | |
6fe7ccc8 | 158 | |
93a37866 A |
159 | TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame); |
160 | String programSource = asString(program)->value(callFrame); | |
4e4e5a6f A |
161 | if (callFrame->hadException()) |
162 | return JSValue(); | |
14957cd0 | 163 | |
6fe7ccc8 A |
164 | CallFrame* callerFrame = callFrame->callerFrame(); |
165 | CodeBlock* callerCodeBlock = callerFrame->codeBlock(); | |
93a37866 | 166 | JSScope* callerScopeChain = callerFrame->scope(); |
6fe7ccc8 A |
167 | EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain); |
168 | ||
169 | if (!eval) { | |
170 | if (!callerCodeBlock->isStrictMode()) { | |
171 | // FIXME: We can use the preparser in strict mode, we just need additional logic | |
172 | // to prevent duplicates. | |
173 | if (programSource.is8Bit()) { | |
174 | LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON); | |
175 | if (JSValue parsedObject = preparser.tryLiteralParse()) | |
176 | return parsedObject; | |
177 | } else { | |
178 | LiteralParser<UChar> preparser(callFrame, programSource.characters16(), programSource.length(), NonStrictJSON); | |
179 | if (JSValue parsedObject = preparser.tryLiteralParse()) | |
180 | return parsedObject; | |
181 | } | |
182 | } | |
93a37866 A |
183 | |
184 | // If the literal parser bailed, it should not have thrown exceptions. | |
185 | ASSERT(!callFrame->vm().exception); | |
6fe7ccc8 A |
186 | |
187 | JSValue exceptionValue; | |
93a37866 | 188 | eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->unlinkedCodeBlock()->codeCacheForEval().get(), callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain, exceptionValue); |
6fe7ccc8 A |
189 | |
190 | ASSERT(!eval == exceptionValue); | |
191 | if (UNLIKELY(!eval)) | |
192 | return throwError(callFrame, exceptionValue); | |
14957cd0 | 193 | } |
f9bf01c6 | 194 | |
6fe7ccc8 A |
195 | JSValue thisValue = callerFrame->thisValue(); |
196 | ASSERT(isValidThisObject(thisValue, callFrame)); | |
93a37866 A |
197 | Interpreter* interpreter = callFrame->vm().interpreter; |
198 | return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); | |
6fe7ccc8 A |
199 | } |
200 | ||
93a37866 | 201 | CallFrame* loadVarargs(CallFrame* callFrame, JSStack* stack, JSValue thisValue, JSValue arguments, int firstFreeRegister) |
6fe7ccc8 A |
202 | { |
203 | if (!arguments) { // f.apply(x, arguments), with arguments unmodified. | |
204 | unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); | |
93a37866 A |
205 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + argumentCountIncludingThis + JSStack::CallFrameHeaderSize); |
206 | if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !stack->grow(newCallFrame->registers())) { | |
207 | callFrame->vm().exception = createStackOverflowError(callFrame); | |
6fe7ccc8 A |
208 | return 0; |
209 | } | |
210 | ||
211 | newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis); | |
212 | newCallFrame->setThisValue(thisValue); | |
213 | for (size_t i = 0; i < callFrame->argumentCount(); ++i) | |
93a37866 | 214 | newCallFrame->setArgument(i, callFrame->argumentAfterCapture(i)); |
6fe7ccc8 A |
215 | return newCallFrame; |
216 | } | |
217 | ||
218 | if (arguments.isUndefinedOrNull()) { | |
93a37866 A |
219 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + 1 + JSStack::CallFrameHeaderSize); |
220 | if (!stack->grow(newCallFrame->registers())) { | |
221 | callFrame->vm().exception = createStackOverflowError(callFrame); | |
6fe7ccc8 A |
222 | return 0; |
223 | } | |
224 | newCallFrame->setArgumentCountIncludingThis(1); | |
225 | newCallFrame->setThisValue(thisValue); | |
226 | return newCallFrame; | |
227 | } | |
228 | ||
229 | if (!arguments.isObject()) { | |
93a37866 | 230 | callFrame->vm().exception = createInvalidParamError(callFrame, "Function.prototype.apply", arguments); |
6fe7ccc8 A |
231 | return 0; |
232 | } | |
9dae56ea | 233 | |
6fe7ccc8 A |
234 | if (asObject(arguments)->classInfo() == &Arguments::s_info) { |
235 | Arguments* argsObject = asArguments(arguments); | |
236 | unsigned argCount = argsObject->length(callFrame); | |
237 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + CallFrame::offsetFor(argCount + 1)); | |
93a37866 A |
238 | if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { |
239 | callFrame->vm().exception = createStackOverflowError(callFrame); | |
6fe7ccc8 A |
240 | return 0; |
241 | } | |
242 | newCallFrame->setArgumentCountIncludingThis(argCount + 1); | |
243 | newCallFrame->setThisValue(thisValue); | |
244 | argsObject->copyToArguments(callFrame, newCallFrame, argCount); | |
245 | return newCallFrame; | |
246 | } | |
247 | ||
248 | if (isJSArray(arguments)) { | |
249 | JSArray* array = asArray(arguments); | |
250 | unsigned argCount = array->length(); | |
251 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + CallFrame::offsetFor(argCount + 1)); | |
93a37866 A |
252 | if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { |
253 | callFrame->vm().exception = createStackOverflowError(callFrame); | |
6fe7ccc8 A |
254 | return 0; |
255 | } | |
256 | newCallFrame->setArgumentCountIncludingThis(argCount + 1); | |
257 | newCallFrame->setThisValue(thisValue); | |
258 | array->copyToArguments(callFrame, newCallFrame, argCount); | |
259 | return newCallFrame; | |
260 | } | |
9dae56ea | 261 | |
6fe7ccc8 A |
262 | JSObject* argObject = asObject(arguments); |
263 | unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); | |
264 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + CallFrame::offsetFor(argCount + 1)); | |
93a37866 A |
265 | if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { |
266 | callFrame->vm().exception = createStackOverflowError(callFrame); | |
6fe7ccc8 A |
267 | return 0; |
268 | } | |
269 | newCallFrame->setArgumentCountIncludingThis(argCount + 1); | |
270 | newCallFrame->setThisValue(thisValue); | |
271 | for (size_t i = 0; i < argCount; ++i) { | |
272 | newCallFrame->setArgument(i, asObject(arguments)->get(callFrame, i)); | |
93a37866 | 273 | if (UNLIKELY(callFrame->vm().exception)) |
6fe7ccc8 A |
274 | return 0; |
275 | } | |
276 | return newCallFrame; | |
9dae56ea A |
277 | } |
278 | ||
93a37866 | 279 | Interpreter::Interpreter(VM& vm) |
f9bf01c6 | 280 | : m_sampleEntryDepth(0) |
93a37866 A |
281 | , m_stack(vm) |
282 | , m_errorHandlingModeReentry(0) | |
6fe7ccc8 A |
283 | #if !ASSERT_DISABLED |
284 | , m_initialized(false) | |
285 | #endif | |
9dae56ea | 286 | { |
6fe7ccc8 A |
287 | } |
288 | ||
289 | Interpreter::~Interpreter() | |
290 | { | |
6fe7ccc8 A |
291 | } |
292 | ||
93a37866 | 293 | void Interpreter::initialize(bool canUseJIT) |
6fe7ccc8 | 294 | { |
6fe7ccc8 A |
295 | UNUSED_PARAM(canUseJIT); |
296 | ||
93a37866 A |
297 | #if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) |
298 | m_opcodeTable = LLInt::opcodeMap(); | |
ba379fdc A |
299 | for (int i = 0; i < numOpcodeIDs; ++i) |
300 | m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i)); | |
6fe7ccc8 | 301 | #endif |
93a37866 | 302 | |
6fe7ccc8 A |
303 | #if !ASSERT_DISABLED |
304 | m_initialized = true; | |
305 | #endif | |
f9bf01c6 A |
306 | |
307 | #if ENABLE(OPCODE_SAMPLING) | |
308 | enableSampler(); | |
309 | #endif | |
9dae56ea A |
310 | } |
311 | ||
93a37866 A |
312 | #ifdef NDEBUG |
313 | ||
314 | void Interpreter::dumpCallFrame(CallFrame*) | |
315 | { | |
316 | } | |
317 | ||
318 | #else | |
9dae56ea A |
319 | |
320 | void Interpreter::dumpCallFrame(CallFrame* callFrame) | |
321 | { | |
93a37866 | 322 | callFrame->codeBlock()->dumpBytecode(); |
9dae56ea A |
323 | dumpRegisters(callFrame); |
324 | } | |
325 | ||
326 | void Interpreter::dumpRegisters(CallFrame* callFrame) | |
327 | { | |
93a37866 A |
328 | dataLogF("Register frame: \n\n"); |
329 | dataLogF("-----------------------------------------------------------------------------\n"); | |
330 | dataLogF(" use | address | value \n"); | |
331 | dataLogF("-----------------------------------------------------------------------------\n"); | |
9dae56ea A |
332 | |
333 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
9dae56ea A |
334 | const Register* it; |
335 | const Register* end; | |
336 | ||
93a37866 A |
337 | it = callFrame->registers() - JSStack::CallFrameHeaderSize - callFrame->argumentCountIncludingThis(); |
338 | end = callFrame->registers() - JSStack::CallFrameHeaderSize; | |
339 | while (it < end) { | |
340 | JSValue v = it->jsValue(); | |
341 | int registerNumber = it - callFrame->registers(); | |
342 | String name = codeBlock->nameForRegister(registerNumber); | |
343 | dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v)); | |
344 | it++; | |
9dae56ea | 345 | } |
93a37866 A |
346 | |
347 | dataLogF("-----------------------------------------------------------------------------\n"); | |
348 | dataLogF("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount()); | |
349 | ++it; | |
350 | dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame()); | |
351 | ++it; | |
352 | dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); | |
353 | ++it; | |
354 | dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); | |
355 | ++it; | |
356 | #if ENABLE(JIT) | |
357 | AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm()); | |
358 | if (pc.hasJITReturnAddress()) | |
359 | dataLogF("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value()); | |
360 | #endif | |
361 | unsigned bytecodeOffset = 0; | |
362 | int line = 0; | |
363 | CodeBlock* callerCodeBlock = 0; | |
364 | getCallerInfo(&callFrame->vm(), callFrame, bytecodeOffset, callerCodeBlock); | |
365 | line = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset); | |
366 | dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", it, bytecodeOffset, line); | |
367 | ++it; | |
368 | dataLogF("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock()); | |
369 | ++it; | |
370 | dataLogF("-----------------------------------------------------------------------------\n"); | |
9dae56ea A |
371 | |
372 | int registerCount = 0; | |
373 | ||
374 | end = it + codeBlock->m_numVars; | |
375 | if (it != end) { | |
376 | do { | |
93a37866 A |
377 | JSValue v = it->jsValue(); |
378 | int registerNumber = it - callFrame->registers(); | |
379 | String name = codeBlock->nameForRegister(registerNumber); | |
380 | dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v)); | |
9dae56ea A |
381 | ++it; |
382 | ++registerCount; | |
383 | } while (it != end); | |
384 | } | |
93a37866 | 385 | dataLogF("-----------------------------------------------------------------------------\n"); |
9dae56ea | 386 | |
ba379fdc | 387 | end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numVars; |
9dae56ea A |
388 | if (it != end) { |
389 | do { | |
93a37866 A |
390 | JSValue v = (*it).jsValue(); |
391 | dataLogF("[r% 3d] | %10p | %-16s 0x%lld \n", registerCount, it, toCString(v).data(), (long long)JSValue::encode(v)); | |
9dae56ea A |
392 | ++it; |
393 | ++registerCount; | |
394 | } while (it != end); | |
395 | } | |
93a37866 | 396 | dataLogF("-----------------------------------------------------------------------------\n"); |
9dae56ea A |
397 | } |
398 | ||
399 | #endif | |
400 | ||
401 | bool Interpreter::isOpcode(Opcode opcode) | |
402 | { | |
93a37866 | 403 | #if ENABLE(COMPUTED_GOTO_OPCODES) |
6fe7ccc8 | 404 | #if !ENABLE(LLINT) |
93a37866 A |
405 | return static_cast<OpcodeID>(bitwise_cast<uintptr_t>(opcode)) <= op_end; |
406 | #else | |
9dae56ea A |
407 | return opcode != HashTraits<Opcode>::emptyValue() |
408 | && !HashTraits<Opcode>::isDeletedValue(opcode) | |
409 | && m_opcodeIDTable.contains(opcode); | |
93a37866 | 410 | #endif |
9dae56ea A |
411 | #else |
412 | return opcode >= 0 && opcode <= op_end; | |
413 | #endif | |
414 | } | |
415 | ||
ba379fdc | 416 | NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValue exceptionValue, unsigned& bytecodeOffset, CodeBlock*& codeBlock) |
9dae56ea A |
417 | { |
418 | CodeBlock* oldCodeBlock = codeBlock; | |
93a37866 | 419 | JSScope* scope = callFrame->scope(); |
9dae56ea A |
420 | |
421 | if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { | |
422 | DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); | |
423 | if (callFrame->callee()) | |
93a37866 | 424 | debugger->returnEvent(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine(), 0); |
9dae56ea | 425 | else |
93a37866 A |
426 | debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine(), 0); |
427 | } | |
428 | ||
429 | JSValue activation; | |
430 | if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsActivation()) { | |
431 | activation = callFrame->uncheckedR(oldCodeBlock->activationRegister()).jsValue(); | |
432 | if (activation) | |
433 | jsCast<JSActivation*>(activation)->tearOff(*scope->vm()); | |
9dae56ea A |
434 | } |
435 | ||
93a37866 A |
436 | if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->usesArguments()) { |
437 | if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) { | |
438 | if (activation) | |
439 | jsCast<Arguments*>(arguments)->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation)); | |
440 | else | |
441 | jsCast<Arguments*>(arguments)->tearOff(callFrame); | |
14957cd0 | 442 | } |
14957cd0 | 443 | } |
9dae56ea | 444 | |
14957cd0 | 445 | CallFrame* callerFrame = callFrame->callerFrame(); |
93a37866 | 446 | callFrame->vm().topCallFrame = callerFrame; |
4e4e5a6f | 447 | if (callerFrame->hasHostCallFrameFlag()) |
9dae56ea | 448 | return false; |
93a37866 | 449 | callFrame = getCallerInfo(&callFrame->vm(), callFrame, bytecodeOffset, codeBlock); |
9dae56ea A |
450 | return true; |
451 | } | |
452 | ||
14957cd0 | 453 | static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, unsigned bytecodeOffset) |
9dae56ea | 454 | { |
14957cd0 A |
455 | exception->clearAppendSourceToMessage(); |
456 | ||
457 | if (!callFrame->codeBlock()->hasExpressionInfo()) | |
458 | return; | |
459 | ||
460 | int startOffset = 0; | |
461 | int endOffset = 0; | |
462 | int divotPoint = 0; | |
93a37866 A |
463 | unsigned line = 0; |
464 | unsigned column = 0; | |
14957cd0 A |
465 | |
466 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
93a37866 | 467 | codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset, line, column); |
14957cd0 A |
468 | |
469 | int expressionStart = divotPoint - startOffset; | |
470 | int expressionStop = divotPoint + endOffset; | |
471 | ||
93a37866 A |
472 | const String& sourceString = codeBlock->source()->source(); |
473 | if (!expressionStop || expressionStart > static_cast<int>(sourceString.length())) | |
14957cd0 A |
474 | return; |
475 | ||
93a37866 A |
476 | VM* vm = &callFrame->vm(); |
477 | JSValue jsMessage = exception->getDirect(*vm, vm->propertyNames->message); | |
14957cd0 A |
478 | if (!jsMessage || !jsMessage.isString()) |
479 | return; | |
480 | ||
93a37866 | 481 | String message = asString(jsMessage)->value(callFrame); |
14957cd0 A |
482 | |
483 | if (expressionStart < expressionStop) | |
93a37866 | 484 | message = makeString(message, " (evaluating '", codeBlock->source()->getRange(expressionStart, expressionStop), "')"); |
14957cd0 A |
485 | else { |
486 | // No range information, so give a few characters of context | |
93a37866 A |
487 | const StringImpl* data = sourceString.impl(); |
488 | int dataLength = sourceString.length(); | |
14957cd0 A |
489 | int start = expressionStart; |
490 | int stop = expressionStart; | |
491 | // Get up to 20 characters of context to the left and right of the divot, clamping to the line. | |
492 | // then strip whitespace. | |
6fe7ccc8 | 493 | while (start > 0 && (expressionStart - start < 20) && (*data)[start - 1] != '\n') |
14957cd0 | 494 | start--; |
6fe7ccc8 | 495 | while (start < (expressionStart - 1) && isStrWhiteSpace((*data)[start])) |
14957cd0 | 496 | start++; |
6fe7ccc8 | 497 | while (stop < dataLength && (stop - expressionStart < 20) && (*data)[stop] != '\n') |
14957cd0 | 498 | stop++; |
6fe7ccc8 | 499 | while (stop > expressionStart && isStrWhiteSpace((*data)[stop - 1])) |
14957cd0 | 500 | stop--; |
93a37866 | 501 | message = makeString(message, " (near '...", codeBlock->source()->getRange(start, stop), "...')"); |
14957cd0 A |
502 | } |
503 | ||
93a37866 | 504 | exception->putDirect(*vm, vm->propertyNames->message, jsString(vm, message)); |
14957cd0 | 505 | } |
9dae56ea | 506 | |
93a37866 | 507 | static unsigned getBytecodeOffsetForCallFrame(CallFrame* callFrame) |
6fe7ccc8 | 508 | { |
6fe7ccc8 A |
509 | callFrame = callFrame->removeHostCallFrameFlag(); |
510 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
511 | if (!codeBlock) | |
93a37866 | 512 | return 0; |
6fe7ccc8 A |
513 | #if ENABLE(DFG_JIT) |
514 | if (codeBlock->getJITType() == JITCode::DFGJIT) | |
93a37866 | 515 | return codeBlock->codeOrigin(callFrame->codeOriginIndexForDFG()).bytecodeIndex; |
6fe7ccc8 | 516 | #endif |
93a37866 | 517 | return callFrame->bytecodeOffsetForNonDFGCode(); |
6fe7ccc8 A |
518 | } |
519 | ||
93a37866 | 520 | static CallFrame* getCallerInfo(VM* vm, CallFrame* callFrame, unsigned& bytecodeOffset, CodeBlock*& caller) |
6fe7ccc8 | 521 | { |
93a37866 A |
522 | ASSERT_UNUSED(vm, vm); |
523 | bytecodeOffset = 0; | |
6fe7ccc8 | 524 | ASSERT(!callFrame->hasHostCallFrameFlag()); |
93a37866 A |
525 | CallFrame* trueCallerFrame = callFrame->trueCallerFrame(); |
526 | bool wasCalledByHost = callFrame->callerFrame()->hasHostCallFrameFlag(); | |
527 | ASSERT(!trueCallerFrame->hasHostCallFrameFlag()); | |
6fe7ccc8 | 528 | |
93a37866 A |
529 | if (trueCallerFrame == CallFrame::noCaller() || !trueCallerFrame || !trueCallerFrame->codeBlock()) { |
530 | caller = 0; | |
531 | return trueCallerFrame; | |
532 | } | |
6fe7ccc8 | 533 | |
93a37866 | 534 | CodeBlock* callerCodeBlock = trueCallerFrame->codeBlock(); |
6fe7ccc8 | 535 | |
6fe7ccc8 | 536 | if (!callFrame->hasReturnPC()) |
93a37866 | 537 | wasCalledByHost = true; |
6fe7ccc8 | 538 | |
93a37866 | 539 | if (wasCalledByHost) { |
6fe7ccc8 A |
540 | #if ENABLE(DFG_JIT) |
541 | if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) { | |
93a37866 A |
542 | unsigned codeOriginIndex = callFrame->callerFrame()->removeHostCallFrameFlag()->codeOriginIndexForDFG(); |
543 | CodeOrigin origin = callerCodeBlock->codeOrigin(codeOriginIndex); | |
544 | bytecodeOffset = origin.bytecodeIndex; | |
545 | if (InlineCallFrame* inlineCallFrame = origin.inlineCallFrame) | |
546 | callerCodeBlock = inlineCallFrame->baselineCodeBlock(); | |
6fe7ccc8 A |
547 | } else |
548 | #endif | |
93a37866 | 549 | bytecodeOffset = trueCallerFrame->bytecodeOffsetForNonDFGCode(); |
6fe7ccc8 | 550 | } else { |
93a37866 | 551 | #if ENABLE(DFG_JIT) |
6fe7ccc8 A |
552 | if (callFrame->isInlineCallFrame()) { |
553 | InlineCallFrame* icf = callFrame->inlineCallFrame(); | |
554 | bytecodeOffset = icf->caller.bytecodeIndex; | |
555 | if (InlineCallFrame* parentCallFrame = icf->caller.inlineCallFrame) { | |
556 | FunctionExecutable* executable = static_cast<FunctionExecutable*>(parentCallFrame->executable.get()); | |
557 | CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(parentCallFrame->isCall ? CodeForCall : CodeForConstruct); | |
558 | ASSERT(newCodeBlock); | |
559 | ASSERT(newCodeBlock->instructionCount() > bytecodeOffset); | |
560 | callerCodeBlock = newCodeBlock; | |
561 | } | |
562 | } else if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) { | |
563 | CodeOrigin origin; | |
93a37866 A |
564 | if (!callerCodeBlock->codeOriginForReturn(callFrame->returnPC(), origin)) { |
565 | // This should not be possible, but we're seeing cases where it does happen | |
566 | // CallFrame already has robustness against bogus stack walks, so | |
567 | // we'll extend that to here as well. | |
6fe7ccc8 | 568 | ASSERT_NOT_REACHED(); |
93a37866 A |
569 | caller = 0; |
570 | return 0; | |
571 | } | |
6fe7ccc8 A |
572 | bytecodeOffset = origin.bytecodeIndex; |
573 | if (InlineCallFrame* icf = origin.inlineCallFrame) { | |
574 | FunctionExecutable* executable = static_cast<FunctionExecutable*>(icf->executable.get()); | |
575 | CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(icf->isCall ? CodeForCall : CodeForConstruct); | |
576 | ASSERT(newCodeBlock); | |
577 | ASSERT(newCodeBlock->instructionCount() > bytecodeOffset); | |
578 | callerCodeBlock = newCodeBlock; | |
579 | } | |
580 | } else | |
6fe7ccc8 | 581 | #endif |
93a37866 A |
582 | { |
583 | RELEASE_ASSERT(callerCodeBlock); | |
584 | bytecodeOffset = callerCodeBlock->bytecodeOffset(trueCallerFrame, callFrame->returnPC()); | |
585 | } | |
6fe7ccc8 A |
586 | } |
587 | ||
93a37866 A |
588 | RELEASE_ASSERT(callerCodeBlock); |
589 | caller = callerCodeBlock; | |
590 | return trueCallerFrame; | |
6fe7ccc8 A |
591 | } |
592 | ||
93a37866 | 593 | static ALWAYS_INLINE const String getSourceURLFromCallFrame(CallFrame* callFrame) |
6fe7ccc8 A |
594 | { |
595 | ASSERT(!callFrame->hasHostCallFrameFlag()); | |
6fe7ccc8 | 596 | return callFrame->codeBlock()->ownerExecutable()->sourceURL(); |
6fe7ccc8 A |
597 | } |
598 | ||
599 | static StackFrameCodeType getStackFrameCodeType(CallFrame* callFrame) | |
600 | { | |
601 | ASSERT(!callFrame->hasHostCallFrameFlag()); | |
602 | ||
603 | switch (callFrame->codeBlock()->codeType()) { | |
604 | case EvalCode: | |
605 | return StackFrameEvalCode; | |
606 | case FunctionCode: | |
607 | return StackFrameFunctionCode; | |
608 | case GlobalCode: | |
609 | return StackFrameGlobalCode; | |
610 | } | |
93a37866 | 611 | RELEASE_ASSERT_NOT_REACHED(); |
6fe7ccc8 A |
612 | return StackFrameGlobalCode; |
613 | } | |
614 | ||
93a37866 | 615 | void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column) |
6fe7ccc8 | 616 | { |
93a37866 A |
617 | if (!codeBlock) { |
618 | line = 0; | |
619 | column = 0; | |
6fe7ccc8 | 620 | return; |
93a37866 A |
621 | } |
622 | ||
623 | int divot = 0; | |
624 | int unusedStartOffset = 0; | |
625 | int unusedEndOffset = 0; | |
626 | unsigned divotLine = 0; | |
627 | unsigned divotColumn = 0; | |
628 | expressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); | |
629 | ||
630 | line = divotLine + lineOffset; | |
631 | column = divotColumn + (divotLine ? 1 : firstLineColumnOffset); | |
632 | } | |
633 | ||
634 | void StackFrame::expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) | |
635 | { | |
636 | codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column); | |
637 | divot += characterOffset; | |
638 | } | |
639 | ||
640 | String StackFrame::toString(CallFrame* callFrame) | |
641 | { | |
642 | StringBuilder traceBuild; | |
643 | String functionName = friendlyFunctionName(callFrame); | |
644 | String sourceURL = friendlySourceURL(); | |
645 | traceBuild.append(functionName); | |
646 | if (!sourceURL.isEmpty()) { | |
647 | if (!functionName.isEmpty()) | |
648 | traceBuild.append('@'); | |
649 | traceBuild.append(sourceURL); | |
650 | if (codeType != StackFrameNativeCode) { | |
651 | unsigned line; | |
652 | unsigned column; | |
653 | computeLineAndColumn(line, column); | |
654 | ||
655 | traceBuild.append(':'); | |
656 | traceBuild.appendNumber(line); | |
657 | traceBuild.append(':'); | |
658 | traceBuild.appendNumber(column); | |
659 | } | |
660 | } | |
661 | return traceBuild.toString().impl(); | |
662 | } | |
6fe7ccc8 | 663 | |
93a37866 A |
664 | void Interpreter::getStackTrace(VM* vm, Vector<StackFrame>& results, size_t maxStackSize) |
665 | { | |
666 | CallFrame* callFrame = vm->topCallFrame->removeHostCallFrameFlag(); | |
667 | if (!callFrame || callFrame == CallFrame::noCaller()) | |
668 | return; | |
669 | unsigned bytecodeOffset = getBytecodeOffsetForCallFrame(callFrame); | |
6fe7ccc8 | 670 | callFrame = callFrame->trueCallFrameFromVMCode(); |
93a37866 A |
671 | if (!callFrame) |
672 | return; | |
673 | CodeBlock* callerCodeBlock = callFrame->codeBlock(); | |
6fe7ccc8 | 674 | |
93a37866 A |
675 | while (callFrame && callFrame != CallFrame::noCaller() && maxStackSize--) { |
676 | String sourceURL; | |
677 | if (callerCodeBlock) { | |
6fe7ccc8 | 678 | sourceURL = getSourceURLFromCallFrame(callFrame); |
93a37866 A |
679 | StackFrame s = { |
680 | Strong<JSObject>(*vm, callFrame->callee()), | |
681 | getStackFrameCodeType(callFrame), | |
682 | Strong<ExecutableBase>(*vm, callerCodeBlock->ownerExecutable()), | |
683 | Strong<UnlinkedCodeBlock>(*vm, callerCodeBlock->unlinkedCodeBlock()), | |
684 | callerCodeBlock->source(), | |
685 | callerCodeBlock->ownerExecutable()->lineNo(), | |
686 | callerCodeBlock->firstLineColumnOffset(), | |
687 | callerCodeBlock->sourceOffset(), | |
688 | bytecodeOffset, | |
689 | sourceURL | |
690 | }; | |
691 | ||
6fe7ccc8 A |
692 | results.append(s); |
693 | } else { | |
93a37866 | 694 | StackFrame s = { Strong<JSObject>(*vm, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; |
6fe7ccc8 A |
695 | results.append(s); |
696 | } | |
93a37866 | 697 | callFrame = getCallerInfo(vm, callFrame, bytecodeOffset, callerCodeBlock); |
6fe7ccc8 A |
698 | } |
699 | } | |
700 | ||
93a37866 | 701 | void Interpreter::addStackTraceIfNecessary(CallFrame* callFrame, JSValue error) |
6fe7ccc8 | 702 | { |
93a37866 A |
703 | VM* vm = &callFrame->vm(); |
704 | ASSERT(callFrame == vm->topCallFrame || callFrame == callFrame->lexicalGlobalObject()->globalExec() || callFrame == callFrame->dynamicGlobalObject()->globalExec()); | |
6fe7ccc8 A |
705 | |
706 | Vector<StackFrame> stackTrace; | |
93a37866 A |
707 | getStackTrace(&callFrame->vm(), stackTrace); |
708 | vm->exceptionStack() = RefCountedArray<StackFrame>(stackTrace); | |
709 | if (stackTrace.isEmpty() || !error.isObject()) | |
6fe7ccc8 | 710 | return; |
93a37866 A |
711 | |
712 | JSObject* errorObject = asObject(error); | |
6fe7ccc8 | 713 | JSGlobalObject* globalObject = 0; |
93a37866 A |
714 | if (isTerminatedExecutionException(error)) |
715 | globalObject = vm->dynamicGlobalObject; | |
6fe7ccc8 | 716 | else |
93a37866 A |
717 | globalObject = errorObject->globalObject(); |
718 | ||
719 | // FIXME: JSStringJoiner could be more efficient than StringBuilder here. | |
6fe7ccc8 A |
720 | StringBuilder builder; |
721 | for (unsigned i = 0; i < stackTrace.size(); i++) { | |
722 | builder.append(String(stackTrace[i].toString(globalObject->globalExec()).impl())); | |
723 | if (i != stackTrace.size() - 1) | |
724 | builder.append('\n'); | |
725 | } | |
93a37866 A |
726 | |
727 | if (errorObject->hasProperty(callFrame, vm->propertyNames->stack)) | |
728 | return; | |
729 | errorObject->putDirect(*vm, vm->propertyNames->stack, jsString(vm, builder.toString()), ReadOnly | DontDelete); | |
6fe7ccc8 A |
730 | } |
731 | ||
14957cd0 A |
732 | NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset) |
733 | { | |
9dae56ea | 734 | CodeBlock* codeBlock = callFrame->codeBlock(); |
93a37866 | 735 | bool isTermination = false; |
14957cd0 | 736 | |
6fe7ccc8 A |
737 | ASSERT(!exceptionValue.isEmpty()); |
738 | ASSERT(!exceptionValue.isCell() || exceptionValue.asCell()); | |
739 | // This shouldn't be possible (hence the assertions), but we're already in the slowest of | |
740 | // slow cases, so let's harden against it anyway to be safe. | |
741 | if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) | |
742 | exceptionValue = jsNull(); | |
743 | ||
14957cd0 | 744 | // Set up the exception object |
9dae56ea A |
745 | if (exceptionValue.isObject()) { |
746 | JSObject* exception = asObject(exceptionValue); | |
4e4e5a6f | 747 | |
14957cd0 A |
748 | if (exception->isErrorInstance() && static_cast<ErrorInstance*>(exception)->appendSourceToMessage()) |
749 | appendSourceToError(callFrame, static_cast<ErrorInstance*>(exception), bytecodeOffset); | |
750 | ||
6fe7ccc8 | 751 | if (!hasErrorInfo(callFrame, exception)) { |
14957cd0 A |
752 | // FIXME: should only really be adding these properties to VM generated exceptions, |
753 | // but the inspector currently requires these for all thrown objects. | |
754 | addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source()); | |
9dae56ea | 755 | } |
14957cd0 | 756 | |
93a37866 A |
757 | isTermination = isTerminatedExecutionException(exception); |
758 | } else { | |
759 | if (!callFrame->vm().exceptionStack().size()) { | |
760 | Vector<StackFrame> stack; | |
761 | Interpreter::getStackTrace(&callFrame->vm(), stack); | |
762 | callFrame->vm().exceptionStack() = RefCountedArray<StackFrame>(stack); | |
763 | } | |
9dae56ea A |
764 | } |
765 | ||
766 | if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { | |
767 | DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); | |
12899fa2 A |
768 | bool hasHandler = false; |
769 | if (!isTermination) { | |
770 | VM* vm = &callFrame->vm(); | |
771 | CallFrame* currentFrame = callFrame; | |
772 | CodeBlock* currentCB = codeBlock; | |
773 | unsigned currentOffset = bytecodeOffset; | |
774 | while (currentFrame) { | |
775 | if (currentCB && currentCB->handlerForBytecodeOffset(currentOffset)) { | |
776 | hasHandler = true; | |
777 | break; | |
778 | } | |
779 | currentFrame = getCallerInfo(vm, currentFrame, currentOffset, currentCB); | |
780 | } | |
781 | } | |
93a37866 | 782 | debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), 0, hasHandler); |
9dae56ea A |
783 | } |
784 | ||
785 | // Calculate an exception handler vPC, unwinding call frames as necessary. | |
9dae56ea | 786 | HandlerInfo* handler = 0; |
93a37866 | 787 | while (isTermination || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { |
14957cd0 | 788 | if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) { |
93a37866 | 789 | if (LegacyProfiler* profiler = callFrame->vm().enabledProfiler()) |
14957cd0 | 790 | profiler->exceptionUnwind(callFrame); |
9dae56ea | 791 | return 0; |
14957cd0 | 792 | } |
9dae56ea A |
793 | } |
794 | ||
93a37866 | 795 | if (LegacyProfiler* profiler = callFrame->vm().enabledProfiler()) |
14957cd0 | 796 | profiler->exceptionUnwind(callFrame); |
9dae56ea | 797 | |
14957cd0 | 798 | // Unwind the scope chain within the exception handler's call frame. |
93a37866 | 799 | JSScope* scope = callFrame->scope(); |
14957cd0 A |
800 | int scopeDelta = 0; |
801 | if (!codeBlock->needsFullScopeChain() || codeBlock->codeType() != FunctionCode | |
93a37866 A |
802 | || callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) { |
803 | int currentDepth = depth(codeBlock, scope); | |
804 | int targetDepth = handler->scopeDepth; | |
805 | scopeDelta = currentDepth - targetDepth; | |
806 | RELEASE_ASSERT(scopeDelta >= 0); | |
807 | } | |
9dae56ea | 808 | while (scopeDelta--) |
93a37866 A |
809 | scope = scope->next(); |
810 | callFrame->setScope(scope); | |
9dae56ea A |
811 | |
812 | return handler; | |
813 | } | |
814 | ||
14957cd0 A |
815 | static inline JSValue checkedReturn(JSValue returnValue) |
816 | { | |
817 | ASSERT(returnValue); | |
818 | return returnValue; | |
819 | } | |
820 | ||
821 | static inline JSObject* checkedReturn(JSObject* returnValue) | |
822 | { | |
823 | ASSERT(returnValue); | |
824 | return returnValue; | |
825 | } | |
826 | ||
93a37866 A |
827 | class SamplingScope { |
828 | public: | |
829 | SamplingScope(Interpreter* interpreter) | |
830 | : m_interpreter(interpreter) | |
831 | { | |
832 | interpreter->startSampling(); | |
833 | } | |
834 | ~SamplingScope() | |
835 | { | |
836 | m_interpreter->stopSampling(); | |
837 | } | |
838 | private: | |
839 | Interpreter* m_interpreter; | |
840 | }; | |
841 | ||
842 | JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj) | |
9dae56ea | 843 | { |
93a37866 A |
844 | SamplingScope samplingScope(this); |
845 | ||
846 | JSScope* scope = callFrame->scope(); | |
847 | VM& vm = *scope->vm(); | |
848 | ||
6fe7ccc8 | 849 | ASSERT(isValidThisObject(thisObj, callFrame)); |
93a37866 A |
850 | ASSERT(!vm.exception); |
851 | ASSERT(!vm.isCollectorBusy()); | |
852 | if (vm.isCollectorBusy()) | |
853 | return jsNull(); | |
9dae56ea | 854 | |
93a37866 A |
855 | StackStats::CheckPoint stackCheckPoint; |
856 | const StackBounds& nativeStack = wtfThreadData().stack(); | |
857 | StackPolicy policy(*this, nativeStack); | |
858 | if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) | |
14957cd0 A |
859 | return checkedReturn(throwStackOverflowError(callFrame)); |
860 | ||
93a37866 A |
861 | // First check if the "program" is actually just a JSON object. If so, |
862 | // we'll handle the JSON object here. Else, we'll handle real JS code | |
863 | // below at failedJSONP. | |
864 | DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); | |
6fe7ccc8 A |
865 | Vector<JSONPData> JSONPData; |
866 | bool parseResult; | |
93a37866 | 867 | const String programSource = program->source().toString(); |
6fe7ccc8 A |
868 | if (programSource.isNull()) |
869 | return jsUndefined(); | |
870 | if (programSource.is8Bit()) { | |
871 | LiteralParser<LChar> literalParser(callFrame, programSource.characters8(), programSource.length(), JSONP); | |
93a37866 | 872 | parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject())); |
6fe7ccc8 A |
873 | } else { |
874 | LiteralParser<UChar> literalParser(callFrame, programSource.characters16(), programSource.length(), JSONP); | |
93a37866 | 875 | parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject())); |
6fe7ccc8 A |
876 | } |
877 | ||
878 | if (parseResult) { | |
93a37866 | 879 | JSGlobalObject* globalObject = scope->globalObject(); |
14957cd0 A |
880 | JSValue result; |
881 | for (unsigned entry = 0; entry < JSONPData.size(); entry++) { | |
6fe7ccc8 | 882 | Vector<JSONPPathEntry> JSONPPath; |
14957cd0 A |
883 | JSONPPath.swap(JSONPData[entry].m_path); |
884 | JSValue JSONPValue = JSONPData[entry].m_value.get(); | |
6fe7ccc8 | 885 | if (JSONPPath.size() == 1 && JSONPPath[0].m_type == JSONPPathEntryTypeDeclare) { |
14957cd0 A |
886 | if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) { |
887 | PutPropertySlot slot; | |
6fe7ccc8 | 888 | globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot); |
14957cd0 | 889 | } else |
6fe7ccc8 | 890 | globalObject->methodTable()->putDirectVirtual(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete); |
14957cd0 A |
891 | // var declarations return undefined |
892 | result = jsUndefined(); | |
893 | continue; | |
894 | } | |
895 | JSValue baseObject(globalObject); | |
896 | for (unsigned i = 0; i < JSONPPath.size() - 1; i++) { | |
6fe7ccc8 | 897 | ASSERT(JSONPPath[i].m_type != JSONPPathEntryTypeDeclare); |
14957cd0 | 898 | switch (JSONPPath[i].m_type) { |
6fe7ccc8 | 899 | case JSONPPathEntryTypeDot: { |
14957cd0 A |
900 | if (i == 0) { |
901 | PropertySlot slot(globalObject); | |
902 | if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) { | |
903 | if (entry) | |
904 | return throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName)); | |
905 | goto failedJSONP; | |
906 | } | |
907 | baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName); | |
908 | } else | |
909 | baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName); | |
910 | if (callFrame->hadException()) | |
911 | return jsUndefined(); | |
912 | continue; | |
913 | } | |
6fe7ccc8 | 914 | case JSONPPathEntryTypeLookup: { |
14957cd0 A |
915 | baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex); |
916 | if (callFrame->hadException()) | |
917 | return jsUndefined(); | |
918 | continue; | |
919 | } | |
920 | default: | |
93a37866 | 921 | RELEASE_ASSERT_NOT_REACHED(); |
14957cd0 A |
922 | return jsUndefined(); |
923 | } | |
924 | } | |
925 | PutPropertySlot slot; | |
926 | switch (JSONPPath.last().m_type) { | |
6fe7ccc8 | 927 | case JSONPPathEntryTypeCall: { |
14957cd0 A |
928 | JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName); |
929 | if (callFrame->hadException()) | |
930 | return jsUndefined(); | |
931 | CallData callData; | |
932 | CallType callType = getCallData(function, callData); | |
933 | if (callType == CallTypeNone) | |
934 | return throwError(callFrame, createNotAFunctionError(callFrame, function)); | |
935 | MarkedArgumentBuffer jsonArg; | |
936 | jsonArg.append(JSONPValue); | |
937 | JSValue thisValue = JSONPPath.size() == 1 ? jsUndefined(): baseObject; | |
938 | JSONPValue = JSC::call(callFrame, function, callType, callData, thisValue, jsonArg); | |
939 | if (callFrame->hadException()) | |
940 | return jsUndefined(); | |
941 | break; | |
942 | } | |
6fe7ccc8 | 943 | case JSONPPathEntryTypeDot: { |
14957cd0 A |
944 | baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot); |
945 | if (callFrame->hadException()) | |
946 | return jsUndefined(); | |
947 | break; | |
948 | } | |
6fe7ccc8 A |
949 | case JSONPPathEntryTypeLookup: { |
950 | baseObject.putByIndex(callFrame, JSONPPath.last().m_pathIndex, JSONPValue, slot.isStrictMode()); | |
14957cd0 A |
951 | if (callFrame->hadException()) |
952 | return jsUndefined(); | |
953 | break; | |
954 | } | |
955 | default: | |
93a37866 | 956 | RELEASE_ASSERT_NOT_REACHED(); |
14957cd0 A |
957 | return jsUndefined(); |
958 | } | |
959 | result = JSONPValue; | |
ba379fdc | 960 | } |
14957cd0 | 961 | return result; |
9dae56ea | 962 | } |
14957cd0 | 963 | failedJSONP: |
93a37866 A |
964 | // If we get here, then we have already proven that the script is not a JSON |
965 | // object. | |
966 | ||
967 | // Compile source to bytecode if necessary: | |
968 | if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope)) | |
14957cd0 | 969 | return checkedReturn(throwError(callFrame, error)); |
9dae56ea | 970 | |
93a37866 A |
971 | if (JSObject* error = program->compile(callFrame, scope)) |
972 | return checkedReturn(throwError(callFrame, error)); | |
973 | ||
974 | ProgramCodeBlock* codeBlock = &program->generatedBytecode(); | |
975 | ||
976 | if (UNLIKELY(vm.watchdog.didFire(callFrame))) | |
977 | return throwTerminatedExecutionException(callFrame); | |
9dae56ea | 978 | |
93a37866 | 979 | // Push the call frame for this invocation: |
6fe7ccc8 | 980 | ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. |
93a37866 A |
981 | CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0); |
982 | if (UNLIKELY(!newCallFrame)) | |
983 | return checkedReturn(throwStackOverflowError(callFrame)); | |
984 | ||
985 | // Set the arguments for the callee: | |
6fe7ccc8 | 986 | newCallFrame->setThisValue(thisObj); |
9dae56ea | 987 | |
93a37866 A |
988 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
989 | profiler->willExecute(callFrame, program->sourceURL(), program->lineNo()); | |
9dae56ea | 990 | |
93a37866 | 991 | // Execute the code: |
ba379fdc | 992 | JSValue result; |
9dae56ea | 993 | { |
f9bf01c6 | 994 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
93a37866 | 995 | Watchdog::Scope watchdogScope(vm.watchdog); |
9dae56ea | 996 | |
93a37866 A |
997 | #if ENABLE(LLINT_C_LOOP) |
998 | result = LLInt::CLoop::execute(newCallFrame, llint_program_prologue); | |
999 | #elif ENABLE(JIT) | |
1000 | result = program->generatedJITCode().execute(&m_stack, newCallFrame, &vm); | |
1001 | #endif // ENABLE(JIT) | |
9dae56ea A |
1002 | } |
1003 | ||
93a37866 A |
1004 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1005 | profiler->didExecute(callFrame, program->sourceURL(), program->lineNo()); | |
9dae56ea | 1006 | |
93a37866 | 1007 | m_stack.popFrame(newCallFrame); |
9dae56ea | 1008 | |
14957cd0 | 1009 | return checkedReturn(result); |
9dae56ea A |
1010 | } |
1011 | ||
14957cd0 | 1012 | JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) |
9dae56ea | 1013 | { |
93a37866 | 1014 | VM& vm = callFrame->vm(); |
6fe7ccc8 | 1015 | ASSERT(isValidThisObject(thisValue, callFrame)); |
14957cd0 | 1016 | ASSERT(!callFrame->hadException()); |
93a37866 A |
1017 | ASSERT(!vm.isCollectorBusy()); |
1018 | if (vm.isCollectorBusy()) | |
14957cd0 | 1019 | return jsNull(); |
9dae56ea | 1020 | |
93a37866 A |
1021 | StackStats::CheckPoint stackCheckPoint; |
1022 | const StackBounds& nativeStack = wtfThreadData().stack(); | |
1023 | StackPolicy policy(*this, nativeStack); | |
1024 | if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) | |
14957cd0 | 1025 | return checkedReturn(throwStackOverflowError(callFrame)); |
9dae56ea | 1026 | |
93a37866 A |
1027 | bool isJSCall = (callType == CallTypeJS); |
1028 | JSScope* scope; | |
1029 | CodeBlock* newCodeBlock; | |
1030 | size_t argsCount = 1 + args.size(); // implicit "this" parameter | |
14957cd0 | 1031 | |
93a37866 A |
1032 | if (isJSCall) |
1033 | scope = callData.js.scope; | |
1034 | else { | |
1035 | ASSERT(callType == CallTypeHost); | |
1036 | scope = callFrame->scope(); | |
1037 | } | |
1038 | DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); | |
14957cd0 | 1039 | |
93a37866 A |
1040 | if (isJSCall) { |
1041 | // Compile the callee: | |
1042 | JSObject* compileError = callData.js.functionExecutable->compileForCall(callFrame, scope); | |
14957cd0 | 1043 | if (UNLIKELY(!!compileError)) { |
14957cd0 A |
1044 | return checkedReturn(throwError(callFrame, compileError)); |
1045 | } | |
93a37866 A |
1046 | newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); |
1047 | ASSERT(!!newCodeBlock); | |
1048 | } else | |
1049 | newCodeBlock = 0; | |
14957cd0 | 1050 | |
93a37866 A |
1051 | if (UNLIKELY(vm.watchdog.didFire(callFrame))) |
1052 | return throwTerminatedExecutionException(callFrame); | |
14957cd0 | 1053 | |
93a37866 A |
1054 | CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function); |
1055 | if (UNLIKELY(!newCallFrame)) | |
1056 | return checkedReturn(throwStackOverflowError(callFrame)); | |
6fe7ccc8 | 1057 | |
93a37866 A |
1058 | // Set the arguments for the callee: |
1059 | newCallFrame->setThisValue(thisValue); | |
1060 | for (size_t i = 0; i < args.size(); ++i) | |
1061 | newCallFrame->setArgument(i, args.at(i)); | |
9dae56ea | 1062 | |
93a37866 A |
1063 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1064 | profiler->willExecute(callFrame, function); | |
9dae56ea | 1065 | |
ba379fdc | 1066 | JSValue result; |
9dae56ea | 1067 | { |
93a37866 A |
1068 | SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall); |
1069 | Watchdog::Scope watchdogScope(vm.watchdog); | |
1070 | ||
1071 | // Execute the code: | |
1072 | if (isJSCall) { | |
1073 | #if ENABLE(LLINT_C_LOOP) | |
1074 | result = LLInt::CLoop::execute(newCallFrame, llint_function_for_call_prologue); | |
1075 | #elif ENABLE(JIT) | |
1076 | result = callData.js.functionExecutable->generatedJITCodeForCall().execute(&m_stack, newCallFrame, &vm); | |
1077 | #endif // ENABLE(JIT) | |
1078 | } else | |
1079 | result = JSValue::decode(callData.native.function(newCallFrame)); | |
14957cd0 | 1080 | } |
9dae56ea | 1081 | |
93a37866 A |
1082 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1083 | profiler->didExecute(callFrame, function); | |
14957cd0 | 1084 | |
93a37866 | 1085 | m_stack.popFrame(newCallFrame); |
14957cd0 A |
1086 | return checkedReturn(result); |
1087 | } | |
1088 | ||
1089 | JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) | |
1090 | { | |
93a37866 | 1091 | VM& vm = callFrame->vm(); |
14957cd0 | 1092 | ASSERT(!callFrame->hadException()); |
93a37866 | 1093 | ASSERT(!vm.isCollectorBusy()); |
14957cd0 A |
1094 | // We throw in this case because we have to return something "valid" but we're |
1095 | // already in an invalid state. | |
93a37866 | 1096 | if (vm.isCollectorBusy()) |
14957cd0 A |
1097 | return checkedReturn(throwStackOverflowError(callFrame)); |
1098 | ||
93a37866 A |
1099 | StackStats::CheckPoint stackCheckPoint; |
1100 | const StackBounds& nativeStack = wtfThreadData().stack(); | |
1101 | StackPolicy policy(*this, nativeStack); | |
1102 | if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) | |
14957cd0 A |
1103 | return checkedReturn(throwStackOverflowError(callFrame)); |
1104 | ||
93a37866 A |
1105 | bool isJSConstruct = (constructType == ConstructTypeJS); |
1106 | JSScope* scope; | |
1107 | CodeBlock* newCodeBlock; | |
1108 | size_t argsCount = 1 + args.size(); // implicit "this" parameter | |
14957cd0 | 1109 | |
93a37866 A |
1110 | if (isJSConstruct) |
1111 | scope = constructData.js.scope; | |
1112 | else { | |
1113 | ASSERT(constructType == ConstructTypeHost); | |
1114 | scope = callFrame->scope(); | |
1115 | } | |
14957cd0 | 1116 | |
93a37866 | 1117 | DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); |
14957cd0 | 1118 | |
93a37866 A |
1119 | if (isJSConstruct) { |
1120 | // Compile the callee: | |
1121 | JSObject* compileError = constructData.js.functionExecutable->compileForConstruct(callFrame, scope); | |
14957cd0 | 1122 | if (UNLIKELY(!!compileError)) { |
14957cd0 A |
1123 | return checkedReturn(throwError(callFrame, compileError)); |
1124 | } | |
93a37866 A |
1125 | newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); |
1126 | ASSERT(!!newCodeBlock); | |
1127 | } else | |
1128 | newCodeBlock = 0; | |
14957cd0 | 1129 | |
93a37866 A |
1130 | if (UNLIKELY(vm.watchdog.didFire(callFrame))) |
1131 | return throwTerminatedExecutionException(callFrame); | |
14957cd0 | 1132 | |
93a37866 A |
1133 | CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, constructor); |
1134 | if (UNLIKELY(!newCallFrame)) | |
1135 | return checkedReturn(throwStackOverflowError(callFrame)); | |
6fe7ccc8 | 1136 | |
93a37866 A |
1137 | // Set the arguments for the callee: |
1138 | newCallFrame->setThisValue(jsUndefined()); | |
1139 | for (size_t i = 0; i < args.size(); ++i) | |
1140 | newCallFrame->setArgument(i, args.at(i)); | |
14957cd0 | 1141 | |
93a37866 A |
1142 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1143 | profiler->willExecute(callFrame, constructor); | |
14957cd0 A |
1144 | |
1145 | JSValue result; | |
1146 | { | |
93a37866 A |
1147 | SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct); |
1148 | Watchdog::Scope watchdogScope(vm.watchdog); | |
1149 | ||
1150 | // Execute the code. | |
1151 | if (isJSConstruct) { | |
1152 | #if ENABLE(LLINT_C_LOOP) | |
1153 | result = LLInt::CLoop::execute(newCallFrame, llint_function_for_construct_prologue); | |
1154 | #elif ENABLE(JIT) | |
1155 | result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_stack, newCallFrame, &vm); | |
1156 | #endif // ENABLE(JIT) | |
1157 | } else | |
1158 | result = JSValue::decode(constructData.native.function(newCallFrame)); | |
14957cd0 A |
1159 | } |
1160 | ||
93a37866 A |
1161 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1162 | profiler->didExecute(callFrame, constructor); | |
1163 | ||
1164 | m_stack.popFrame(newCallFrame); | |
9dae56ea | 1165 | |
14957cd0 A |
1166 | if (callFrame->hadException()) |
1167 | return 0; | |
1168 | ASSERT(result.isObject()); | |
1169 | return checkedReturn(asObject(result)); | |
9dae56ea A |
1170 | } |
1171 | ||
93a37866 | 1172 | CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope) |
ba379fdc | 1173 | { |
93a37866 A |
1174 | VM& vm = *scope->vm(); |
1175 | ASSERT(!vm.exception); | |
ba379fdc | 1176 | |
93a37866 | 1177 | if (vm.isCollectorBusy()) |
6fe7ccc8 A |
1178 | return CallFrameClosure(); |
1179 | ||
93a37866 A |
1180 | StackStats::CheckPoint stackCheckPoint; |
1181 | const StackBounds& nativeStack = wtfThreadData().stack(); | |
1182 | StackPolicy policy(*this, nativeStack); | |
1183 | if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) { | |
6fe7ccc8 A |
1184 | throwStackOverflowError(callFrame); |
1185 | return CallFrameClosure(); | |
ba379fdc | 1186 | } |
6fe7ccc8 | 1187 | |
93a37866 A |
1188 | // Compile the callee: |
1189 | JSObject* error = functionExecutable->compileForCall(callFrame, scope); | |
1190 | if (error) { | |
1191 | throwError(callFrame, error); | |
ba379fdc A |
1192 | return CallFrameClosure(); |
1193 | } | |
93a37866 | 1194 | CodeBlock* newCodeBlock = &functionExecutable->generatedBytecodeForCall(); |
ba379fdc | 1195 | |
93a37866 A |
1196 | size_t argsCount = argumentCountIncludingThis; |
1197 | ||
1198 | CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function); | |
1199 | if (UNLIKELY(!newCallFrame)) { | |
1200 | throwStackOverflowError(callFrame); | |
14957cd0 A |
1201 | return CallFrameClosure(); |
1202 | } | |
14957cd0 | 1203 | |
ba379fdc | 1204 | if (UNLIKELY(!newCallFrame)) { |
14957cd0 | 1205 | throwStackOverflowError(callFrame); |
ba379fdc A |
1206 | return CallFrameClosure(); |
1207 | } | |
93a37866 A |
1208 | |
1209 | // Return the successful closure: | |
1210 | CallFrameClosure result = { callFrame, newCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; | |
ba379fdc A |
1211 | return result; |
1212 | } | |
1213 | ||
14957cd0 | 1214 | JSValue Interpreter::execute(CallFrameClosure& closure) |
ba379fdc | 1215 | { |
93a37866 A |
1216 | VM& vm = *closure.vm; |
1217 | SamplingScope samplingScope(this); | |
1218 | ||
1219 | ASSERT(!vm.isCollectorBusy()); | |
1220 | if (vm.isCollectorBusy()) | |
14957cd0 | 1221 | return jsNull(); |
93a37866 A |
1222 | |
1223 | StackStats::CheckPoint stackCheckPoint; | |
1224 | m_stack.validateFence(closure.newCallFrame, "BEFORE"); | |
ba379fdc | 1225 | closure.resetCallFrame(); |
93a37866 A |
1226 | m_stack.validateFence(closure.newCallFrame, "STEP 1"); |
1227 | ||
1228 | if (LegacyProfiler* profiler = vm.enabledProfiler()) | |
1229 | profiler->willExecute(closure.oldCallFrame, closure.function); | |
1230 | ||
1231 | if (UNLIKELY(vm.watchdog.didFire(closure.oldCallFrame))) | |
1232 | return throwTerminatedExecutionException(closure.oldCallFrame); | |
6fe7ccc8 | 1233 | |
93a37866 A |
1234 | // The code execution below may push more frames and point the topCallFrame |
1235 | // to those newer frames, or it may pop to the top frame to the caller of | |
1236 | // the current repeat frame, or it may leave the top frame pointing to the | |
1237 | // current repeat frame. | |
1238 | // | |
1239 | // Hence, we need to preserve the topCallFrame here ourselves before | |
1240 | // repeating this call on a second callback function. | |
6fe7ccc8 | 1241 | |
93a37866 A |
1242 | TopCallFrameSetter topCallFrame(vm, closure.newCallFrame); |
1243 | ||
1244 | // Execute the code: | |
ba379fdc A |
1245 | JSValue result; |
1246 | { | |
f9bf01c6 | 1247 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
93a37866 A |
1248 | Watchdog::Scope watchdogScope(vm.watchdog); |
1249 | ||
1250 | #if ENABLE(LLINT_C_LOOP) | |
1251 | result = LLInt::CLoop::execute(closure.newCallFrame, llint_function_for_call_prologue); | |
1252 | #elif ENABLE(JIT) | |
1253 | result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_stack, closure.newCallFrame, &vm); | |
1254 | #endif // ENABLE(JIT) | |
ba379fdc | 1255 | } |
6fe7ccc8 | 1256 | |
93a37866 A |
1257 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1258 | profiler->didExecute(closure.oldCallFrame, closure.function); | |
1259 | ||
1260 | m_stack.validateFence(closure.newCallFrame, "AFTER"); | |
14957cd0 | 1261 | return checkedReturn(result); |
ba379fdc A |
1262 | } |
1263 | ||
1264 | void Interpreter::endRepeatCall(CallFrameClosure& closure) | |
1265 | { | |
93a37866 | 1266 | m_stack.popFrame(closure.newCallFrame); |
ba379fdc A |
1267 | } |
1268 | ||
93a37866 | 1269 | JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) |
9dae56ea | 1270 | { |
93a37866 A |
1271 | VM& vm = *scope->vm(); |
1272 | SamplingScope samplingScope(this); | |
1273 | ||
1274 | ASSERT(scope->vm() == &callFrame->vm()); | |
6fe7ccc8 | 1275 | ASSERT(isValidThisObject(thisValue, callFrame)); |
93a37866 A |
1276 | ASSERT(!vm.exception); |
1277 | ASSERT(!vm.isCollectorBusy()); | |
1278 | if (vm.isCollectorBusy()) | |
14957cd0 | 1279 | return jsNull(); |
9dae56ea | 1280 | |
93a37866 | 1281 | DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); |
9dae56ea | 1282 | |
93a37866 A |
1283 | StackStats::CheckPoint stackCheckPoint; |
1284 | const StackBounds& nativeStack = wtfThreadData().stack(); | |
1285 | StackPolicy policy(*this, nativeStack); | |
1286 | if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) | |
14957cd0 | 1287 | return checkedReturn(throwStackOverflowError(callFrame)); |
9dae56ea | 1288 | |
93a37866 A |
1289 | // Compile the callee: |
1290 | JSObject* compileError = eval->compile(callFrame, scope); | |
14957cd0 A |
1291 | if (UNLIKELY(!!compileError)) |
1292 | return checkedReturn(throwError(callFrame, compileError)); | |
1293 | EvalCodeBlock* codeBlock = &eval->generatedBytecode(); | |
9dae56ea | 1294 | |
14957cd0 | 1295 | JSObject* variableObject; |
93a37866 A |
1296 | for (JSScope* node = scope; ; node = node->next()) { |
1297 | RELEASE_ASSERT(node); | |
1298 | if (node->isVariableObject() && !node->isNameScopeObject()) { | |
1299 | variableObject = node; | |
9dae56ea A |
1300 | break; |
1301 | } | |
1302 | } | |
1303 | ||
4e4e5a6f A |
1304 | unsigned numVariables = codeBlock->numVariables(); |
1305 | int numFunctions = codeBlock->numberOfFunctionDecls(); | |
1306 | if (numVariables || numFunctions) { | |
14957cd0 | 1307 | if (codeBlock->isStrictMode()) { |
93a37866 A |
1308 | scope = StrictEvalActivation::create(callFrame); |
1309 | variableObject = scope; | |
14957cd0 | 1310 | } |
4e4e5a6f | 1311 | // Scope for BatchedTransitionOptimizer |
93a37866 | 1312 | BatchedTransitionOptimizer optimizer(vm, variableObject); |
9dae56ea | 1313 | |
f9bf01c6 A |
1314 | for (unsigned i = 0; i < numVariables; ++i) { |
1315 | const Identifier& ident = codeBlock->variable(i); | |
9dae56ea A |
1316 | if (!variableObject->hasProperty(callFrame, ident)) { |
1317 | PutPropertySlot slot; | |
6fe7ccc8 | 1318 | variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot); |
9dae56ea A |
1319 | } |
1320 | } | |
1321 | ||
f9bf01c6 A |
1322 | for (int i = 0; i < numFunctions; ++i) { |
1323 | FunctionExecutable* function = codeBlock->functionDecl(i); | |
9dae56ea | 1324 | PutPropertySlot slot; |
93a37866 | 1325 | variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(callFrame, function, scope), slot); |
9dae56ea | 1326 | } |
9dae56ea A |
1327 | } |
1328 | ||
93a37866 A |
1329 | if (UNLIKELY(vm.watchdog.didFire(callFrame))) |
1330 | return throwTerminatedExecutionException(callFrame); | |
6fe7ccc8 | 1331 | |
93a37866 | 1332 | // Push the frame: |
6fe7ccc8 | 1333 | ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. |
93a37866 A |
1334 | CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0); |
1335 | if (UNLIKELY(!newCallFrame)) | |
1336 | return checkedReturn(throwStackOverflowError(callFrame)); | |
9dae56ea | 1337 | |
93a37866 A |
1338 | // Set the arguments for the callee: |
1339 | newCallFrame->setThisValue(thisValue); | |
9dae56ea | 1340 | |
93a37866 A |
1341 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1342 | profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); | |
9dae56ea | 1343 | |
93a37866 | 1344 | // Execute the code: |
ba379fdc | 1345 | JSValue result; |
9dae56ea | 1346 | { |
f9bf01c6 | 1347 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
93a37866 | 1348 | Watchdog::Scope watchdogScope(vm.watchdog); |
9dae56ea | 1349 | |
93a37866 A |
1350 | #if ENABLE(LLINT_C_LOOP) |
1351 | result = LLInt::CLoop::execute(newCallFrame, llint_eval_prologue); | |
1352 | #elif ENABLE(JIT) | |
1353 | result = eval->generatedJITCode().execute(&m_stack, newCallFrame, &vm); | |
1354 | #endif // ENABLE(JIT) | |
9dae56ea A |
1355 | } |
1356 | ||
93a37866 A |
1357 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1358 | profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); | |
9dae56ea | 1359 | |
93a37866 | 1360 | m_stack.popFrame(newCallFrame); |
14957cd0 | 1361 | return checkedReturn(result); |
9dae56ea A |
1362 | } |
1363 | ||
93a37866 | 1364 | NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID, int firstLine, int lastLine, int column) |
9dae56ea A |
1365 | { |
1366 | Debugger* debugger = callFrame->dynamicGlobalObject()->debugger(); | |
1367 | if (!debugger) | |
1368 | return; | |
1369 | ||
1370 | switch (debugHookID) { | |
1371 | case DidEnterCallFrame: | |
93a37866 | 1372 | debugger->callEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column); |
9dae56ea A |
1373 | return; |
1374 | case WillLeaveCallFrame: | |
93a37866 | 1375 | debugger->returnEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column); |
9dae56ea A |
1376 | return; |
1377 | case WillExecuteStatement: | |
93a37866 | 1378 | debugger->atStatement(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column); |
9dae56ea A |
1379 | return; |
1380 | case WillExecuteProgram: | |
93a37866 | 1381 | debugger->willExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column); |
9dae56ea A |
1382 | return; |
1383 | case DidExecuteProgram: | |
93a37866 | 1384 | debugger->didExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column); |
9dae56ea A |
1385 | return; |
1386 | case DidReachBreakpoint: | |
93a37866 | 1387 | debugger->didReachBreakpoint(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column); |
9dae56ea A |
1388 | return; |
1389 | } | |
1390 | } | |
9dae56ea | 1391 | |
93a37866 | 1392 | JSValue Interpreter::retrieveArgumentsFromVMCode(CallFrame* callFrame, JSFunction* function) const |
9dae56ea | 1393 | { |
93a37866 A |
1394 | CallFrame* functionCallFrame = findFunctionCallFrameFromVMCode(callFrame, function); |
1395 | if (!functionCallFrame) | |
1396 | return jsNull(); | |
9dae56ea | 1397 | |
93a37866 A |
1398 | Arguments* arguments = Arguments::create(functionCallFrame->vm(), functionCallFrame); |
1399 | arguments->tearOff(functionCallFrame); | |
1400 | return JSValue(arguments); | |
9dae56ea A |
1401 | } |
1402 | ||
93a37866 | 1403 | JSValue Interpreter::retrieveCallerFromVMCode(CallFrame* callFrame, JSFunction* function) const |
9dae56ea | 1404 | { |
93a37866 | 1405 | CallFrame* functionCallFrame = findFunctionCallFrameFromVMCode(callFrame, function); |
9dae56ea | 1406 | |
93a37866 A |
1407 | if (!functionCallFrame) |
1408 | return jsNull(); | |
9dae56ea | 1409 | |
93a37866 A |
1410 | unsigned bytecodeOffset; |
1411 | CodeBlock* unusedCallerCodeBlock = 0; | |
1412 | CallFrame* callerFrame = getCallerInfo(&callFrame->vm(), functionCallFrame, bytecodeOffset, unusedCallerCodeBlock); | |
1413 | if (!callerFrame) | |
1414 | return jsNull(); | |
1415 | JSValue caller = callerFrame->callee(); | |
1416 | if (!caller) | |
1417 | return jsNull(); | |
f9bf01c6 | 1418 | |
93a37866 A |
1419 | // Skip over function bindings. |
1420 | ASSERT(caller.isObject()); | |
1421 | while (asObject(caller)->inherits(&JSBoundFunction::s_info)) { | |
1422 | callerFrame = getCallerInfo(&callFrame->vm(), callerFrame, bytecodeOffset, unusedCallerCodeBlock); | |
1423 | if (!callerFrame) | |
1424 | return jsNull(); | |
1425 | caller = callerFrame->callee(); | |
1426 | if (!caller) | |
1427 | return jsNull(); | |
9dae56ea A |
1428 | } |
1429 | ||
93a37866 | 1430 | return caller; |
9dae56ea A |
1431 | } |
1432 | ||
93a37866 | 1433 | void Interpreter::retrieveLastCaller(CallFrame* callFrame, int& lineNumber, intptr_t& sourceID, String& sourceURL, JSValue& function) const |
9dae56ea | 1434 | { |
93a37866 A |
1435 | function = JSValue(); |
1436 | lineNumber = -1; | |
1437 | sourceURL = String(); | |
ba379fdc | 1438 | |
93a37866 A |
1439 | CallFrame* callerFrame = callFrame->callerFrame(); |
1440 | if (callerFrame->hasHostCallFrameFlag()) | |
9dae56ea | 1441 | return; |
9dae56ea | 1442 | |
93a37866 A |
1443 | CodeBlock* callerCodeBlock = callerFrame->codeBlock(); |
1444 | if (!callerCodeBlock) | |
9dae56ea | 1445 | return; |
4e4e5a6f | 1446 | unsigned bytecodeOffset = 0; |
6fe7ccc8 | 1447 | bytecodeOffset = callerCodeBlock->bytecodeOffset(callerFrame, callFrame->returnPC()); |
14957cd0 | 1448 | lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1); |
f9bf01c6 A |
1449 | sourceID = callerCodeBlock->ownerExecutable()->sourceID(); |
1450 | sourceURL = callerCodeBlock->ownerExecutable()->sourceURL(); | |
9dae56ea A |
1451 | function = callerFrame->callee(); |
1452 | } | |
1453 | ||
6fe7ccc8 | 1454 | CallFrame* Interpreter::findFunctionCallFrameFromVMCode(CallFrame* callFrame, JSFunction* function) |
9dae56ea | 1455 | { |
6fe7ccc8 | 1456 | for (CallFrame* candidate = callFrame->trueCallFrameFromVMCode(); candidate; candidate = candidate->trueCallerFrame()) { |
9dae56ea A |
1457 | if (candidate->callee() == function) |
1458 | return candidate; | |
1459 | } | |
1460 | return 0; | |
1461 | } | |
1462 | ||
f9bf01c6 A |
1463 | void Interpreter::enableSampler() |
1464 | { | |
1465 | #if ENABLE(OPCODE_SAMPLING) | |
1466 | if (!m_sampler) { | |
6fe7ccc8 | 1467 | m_sampler = adoptPtr(new SamplingTool(this)); |
f9bf01c6 A |
1468 | m_sampler->setup(); |
1469 | } | |
1470 | #endif | |
1471 | } | |
1472 | void Interpreter::dumpSampleData(ExecState* exec) | |
1473 | { | |
1474 | #if ENABLE(OPCODE_SAMPLING) | |
1475 | if (m_sampler) | |
1476 | m_sampler->dump(exec); | |
1477 | #else | |
1478 | UNUSED_PARAM(exec); | |
1479 | #endif | |
1480 | } | |
1481 | void Interpreter::startSampling() | |
1482 | { | |
1483 | #if ENABLE(SAMPLING_THREAD) | |
1484 | if (!m_sampleEntryDepth) | |
1485 | SamplingThread::start(); | |
1486 | ||
1487 | m_sampleEntryDepth++; | |
1488 | #endif | |
1489 | } | |
1490 | void Interpreter::stopSampling() | |
1491 | { | |
1492 | #if ENABLE(SAMPLING_THREAD) | |
1493 | m_sampleEntryDepth--; | |
1494 | if (!m_sampleEntryDepth) | |
1495 | SamplingThread::stop(); | |
1496 | #endif | |
1497 | } | |
1498 | ||
9dae56ea | 1499 | } // namespace JSC |