]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
81345200 | 2 | * Copyright (C) 2008, 2009, 2010, 2012, 2013, 2014 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. | |
81345200 | 14 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
9dae56ea A |
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 | 35 | #include "CallFrameClosure.h" |
81345200 | 36 | #include "CallFrameInlines.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" | |
81345200 | 55 | #include "LLIntThunks.h" |
93a37866 A |
56 | #include "LegacyProfiler.h" |
57 | #include "LiteralParser.h" | |
58 | #include "NameInstance.h" | |
9dae56ea | 59 | #include "ObjectPrototype.h" |
81345200 | 60 | #include "JSCInlines.h" |
9dae56ea | 61 | #include "Parser.h" |
81345200 | 62 | #include "ProtoCallFrame.h" |
9dae56ea A |
63 | #include "RegExpObject.h" |
64 | #include "RegExpPrototype.h" | |
65 | #include "Register.h" | |
9dae56ea | 66 | #include "SamplingTool.h" |
81345200 A |
67 | #include "StackAlignment.h" |
68 | #include "StackVisitor.h" | |
14957cd0 | 69 | #include "StrictEvalActivation.h" |
6fe7ccc8 | 70 | #include "StrongInlines.h" |
81345200 A |
71 | #include "VMEntryScope.h" |
72 | #include "VirtualRegister.h" | |
73 | ||
f9bf01c6 | 74 | #include <limits.h> |
9dae56ea | 75 | #include <stdio.h> |
93a37866 | 76 | #include <wtf/StackStats.h> |
81345200 | 77 | #include <wtf/StdLibExtras.h> |
93a37866 | 78 | #include <wtf/StringPrintStream.h> |
ba379fdc | 79 | #include <wtf/Threading.h> |
93a37866 | 80 | #include <wtf/WTFThreadData.h> |
6fe7ccc8 | 81 | #include <wtf/text/StringBuilder.h> |
9dae56ea A |
82 | |
83 | #if ENABLE(JIT) | |
84 | #include "JIT.h" | |
85 | #endif | |
86 | ||
81345200 | 87 | #define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (!defined(__llvm__)) |
4e4e5a6f | 88 | |
9dae56ea A |
89 | using namespace std; |
90 | ||
91 | namespace JSC { | |
9dae56ea | 92 | |
6fe7ccc8 | 93 | JSValue eval(CallFrame* callFrame) |
9dae56ea | 94 | { |
6fe7ccc8 | 95 | if (!callFrame->argumentCount()) |
9dae56ea A |
96 | return jsUndefined(); |
97 | ||
6fe7ccc8 | 98 | JSValue program = callFrame->argument(0); |
9dae56ea A |
99 | if (!program.isString()) |
100 | return program; | |
6fe7ccc8 | 101 | |
93a37866 A |
102 | TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame); |
103 | String programSource = asString(program)->value(callFrame); | |
4e4e5a6f A |
104 | if (callFrame->hadException()) |
105 | return JSValue(); | |
14957cd0 | 106 | |
6fe7ccc8 A |
107 | CallFrame* callerFrame = callFrame->callerFrame(); |
108 | CodeBlock* callerCodeBlock = callerFrame->codeBlock(); | |
93a37866 | 109 | JSScope* callerScopeChain = callerFrame->scope(); |
6fe7ccc8 A |
110 | EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain); |
111 | ||
112 | if (!eval) { | |
113 | if (!callerCodeBlock->isStrictMode()) { | |
114 | // FIXME: We can use the preparser in strict mode, we just need additional logic | |
115 | // to prevent duplicates. | |
116 | if (programSource.is8Bit()) { | |
117 | LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON); | |
118 | if (JSValue parsedObject = preparser.tryLiteralParse()) | |
119 | return parsedObject; | |
120 | } else { | |
121 | LiteralParser<UChar> preparser(callFrame, programSource.characters16(), programSource.length(), NonStrictJSON); | |
122 | if (JSValue parsedObject = preparser.tryLiteralParse()) | |
123 | return parsedObject; | |
124 | } | |
125 | } | |
93a37866 A |
126 | |
127 | // If the literal parser bailed, it should not have thrown exceptions. | |
81345200 | 128 | ASSERT(!callFrame->vm().exception()); |
6fe7ccc8 | 129 | |
81345200 A |
130 | eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain); |
131 | if (!eval) | |
132 | return jsUndefined(); | |
14957cd0 | 133 | } |
f9bf01c6 | 134 | |
6fe7ccc8 | 135 | JSValue thisValue = callerFrame->thisValue(); |
93a37866 A |
136 | Interpreter* interpreter = callFrame->vm().interpreter; |
137 | return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); | |
6fe7ccc8 A |
138 | } |
139 | ||
81345200 | 140 | CallFrame* sizeFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, int firstFreeRegister, uint32_t firstVarArgOffset) |
6fe7ccc8 A |
141 | { |
142 | if (!arguments) { // f.apply(x, arguments), with arguments unmodified. | |
143 | unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); | |
81345200 A |
144 | if (argumentCountIncludingThis > firstVarArgOffset) |
145 | argumentCountIncludingThis -= firstVarArgOffset; | |
146 | else | |
147 | argumentCountIncludingThis = 1; | |
148 | unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), -firstFreeRegister + argumentCountIncludingThis + JSStack::CallFrameHeaderSize + 1); | |
149 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); | |
150 | if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !stack->ensureCapacityFor(newCallFrame->registers())) { | |
151 | throwStackOverflowError(callFrame); | |
6fe7ccc8 A |
152 | return 0; |
153 | } | |
6fe7ccc8 A |
154 | return newCallFrame; |
155 | } | |
156 | ||
157 | if (arguments.isUndefinedOrNull()) { | |
81345200 A |
158 | unsigned argumentCountIncludingThis = 1; |
159 | unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), -firstFreeRegister + argumentCountIncludingThis + JSStack::CallFrameHeaderSize + 1); | |
160 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); | |
161 | if (!stack->ensureCapacityFor(newCallFrame->registers())) { | |
162 | throwStackOverflowError(callFrame); | |
6fe7ccc8 A |
163 | return 0; |
164 | } | |
6fe7ccc8 A |
165 | return newCallFrame; |
166 | } | |
167 | ||
168 | if (!arguments.isObject()) { | |
81345200 | 169 | callFrame->vm().throwException(callFrame, createInvalidParameterError(callFrame, "Function.prototype.apply", arguments)); |
6fe7ccc8 A |
170 | return 0; |
171 | } | |
9dae56ea | 172 | |
81345200 | 173 | if (asObject(arguments)->classInfo() == Arguments::info()) { |
6fe7ccc8 A |
174 | Arguments* argsObject = asArguments(arguments); |
175 | unsigned argCount = argsObject->length(callFrame); | |
81345200 A |
176 | if (argCount >= firstVarArgOffset) |
177 | argCount -= firstVarArgOffset; | |
178 | else | |
179 | argCount = 0; | |
180 | unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), -firstFreeRegister + CallFrame::offsetFor(argCount + 1)); | |
181 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); | |
182 | if (argCount > Arguments::MaxArguments || !stack->ensureCapacityFor(newCallFrame->registers())) { | |
183 | throwStackOverflowError(callFrame); | |
6fe7ccc8 A |
184 | return 0; |
185 | } | |
6fe7ccc8 A |
186 | return newCallFrame; |
187 | } | |
188 | ||
189 | if (isJSArray(arguments)) { | |
190 | JSArray* array = asArray(arguments); | |
191 | unsigned argCount = array->length(); | |
81345200 A |
192 | if (argCount >= firstVarArgOffset) |
193 | argCount -= firstVarArgOffset; | |
194 | else | |
195 | argCount = 0; | |
196 | unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), -firstFreeRegister + CallFrame::offsetFor(argCount + 1)); | |
197 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); | |
198 | if (argCount > Arguments::MaxArguments || !stack->ensureCapacityFor(newCallFrame->registers())) { | |
199 | throwStackOverflowError(callFrame); | |
6fe7ccc8 A |
200 | return 0; |
201 | } | |
6fe7ccc8 A |
202 | return newCallFrame; |
203 | } | |
9dae56ea | 204 | |
6fe7ccc8 A |
205 | JSObject* argObject = asObject(arguments); |
206 | unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); | |
81345200 A |
207 | if (argCount >= firstVarArgOffset) |
208 | argCount -= firstVarArgOffset; | |
209 | else | |
210 | argCount = 0; | |
211 | unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), -firstFreeRegister + CallFrame::offsetFor(argCount + 1)); | |
212 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); | |
213 | if (argCount > Arguments::MaxArguments || !stack->ensureCapacityFor(newCallFrame->registers())) { | |
214 | throwStackOverflowError(callFrame); | |
6fe7ccc8 A |
215 | return 0; |
216 | } | |
81345200 A |
217 | return newCallFrame; |
218 | } | |
219 | ||
220 | void loadVarargs(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset) | |
221 | { | |
222 | if (!arguments) { // f.apply(x, arguments), with arguments unmodified. | |
223 | unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); | |
224 | if (argumentCountIncludingThis > firstVarArgOffset) | |
225 | argumentCountIncludingThis -= firstVarArgOffset; | |
226 | else | |
227 | argumentCountIncludingThis = 1; | |
228 | newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis); | |
229 | newCallFrame->setThisValue(thisValue); | |
230 | for (size_t i = firstVarArgOffset; i < callFrame->argumentCount(); ++i) | |
231 | newCallFrame->setArgument(i - firstVarArgOffset, callFrame->argumentAfterCapture(i)); | |
232 | return; | |
233 | } | |
234 | ||
235 | if (arguments.isUndefinedOrNull()) { | |
236 | newCallFrame->setArgumentCountIncludingThis(1); | |
237 | newCallFrame->setThisValue(thisValue); | |
238 | return; | |
239 | } | |
240 | ||
241 | if (asObject(arguments)->classInfo() == Arguments::info()) { | |
242 | Arguments* argsObject = asArguments(arguments); | |
243 | unsigned argCount = argsObject->length(callFrame); | |
244 | if (argCount >= firstVarArgOffset) { | |
245 | argCount -= firstVarArgOffset; | |
246 | newCallFrame->setArgumentCountIncludingThis(argCount + 1); | |
247 | argsObject->copyToArguments(callFrame, newCallFrame, argCount, firstVarArgOffset); | |
248 | } else | |
249 | newCallFrame->setArgumentCountIncludingThis(1); | |
250 | newCallFrame->setThisValue(thisValue); | |
251 | return; | |
252 | } | |
253 | ||
254 | if (isJSArray(arguments)) { | |
255 | JSArray* array = asArray(arguments); | |
256 | unsigned argCount = array->length(); | |
257 | if (argCount >= firstVarArgOffset) { | |
258 | argCount -= firstVarArgOffset; | |
259 | newCallFrame->setArgumentCountIncludingThis(argCount + 1); | |
260 | array->copyToArguments(callFrame, newCallFrame, argCount, firstVarArgOffset); | |
261 | } else | |
262 | newCallFrame->setArgumentCountIncludingThis(1); | |
263 | newCallFrame->setThisValue(thisValue); | |
264 | return; | |
265 | } | |
266 | ||
267 | JSObject* argObject = asObject(arguments); | |
268 | unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); | |
269 | if (argCount >= firstVarArgOffset) { | |
270 | argCount -= firstVarArgOffset; | |
271 | newCallFrame->setArgumentCountIncludingThis(argCount + 1); | |
272 | } else | |
273 | newCallFrame->setArgumentCountIncludingThis(1); | |
274 | ||
6fe7ccc8 A |
275 | newCallFrame->setThisValue(thisValue); |
276 | for (size_t i = 0; i < argCount; ++i) { | |
81345200 A |
277 | newCallFrame->setArgument(i, asObject(arguments)->get(callFrame, i + firstVarArgOffset)); |
278 | if (UNLIKELY(callFrame->vm().exception())) | |
279 | return; | |
6fe7ccc8 | 280 | } |
9dae56ea A |
281 | } |
282 | ||
93a37866 | 283 | Interpreter::Interpreter(VM& vm) |
f9bf01c6 | 284 | : m_sampleEntryDepth(0) |
81345200 | 285 | , m_vm(vm) |
93a37866 A |
286 | , m_stack(vm) |
287 | , m_errorHandlingModeReentry(0) | |
6fe7ccc8 A |
288 | #if !ASSERT_DISABLED |
289 | , m_initialized(false) | |
290 | #endif | |
9dae56ea | 291 | { |
6fe7ccc8 A |
292 | } |
293 | ||
294 | Interpreter::~Interpreter() | |
295 | { | |
6fe7ccc8 A |
296 | } |
297 | ||
93a37866 | 298 | void Interpreter::initialize(bool canUseJIT) |
6fe7ccc8 | 299 | { |
6fe7ccc8 A |
300 | UNUSED_PARAM(canUseJIT); |
301 | ||
81345200 | 302 | #if ENABLE(COMPUTED_GOTO_OPCODES) |
93a37866 | 303 | m_opcodeTable = LLInt::opcodeMap(); |
ba379fdc A |
304 | for (int i = 0; i < numOpcodeIDs; ++i) |
305 | m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i)); | |
6fe7ccc8 | 306 | #endif |
93a37866 | 307 | |
6fe7ccc8 A |
308 | #if !ASSERT_DISABLED |
309 | m_initialized = true; | |
310 | #endif | |
f9bf01c6 A |
311 | |
312 | #if ENABLE(OPCODE_SAMPLING) | |
313 | enableSampler(); | |
314 | #endif | |
9dae56ea A |
315 | } |
316 | ||
93a37866 A |
317 | #ifdef NDEBUG |
318 | ||
319 | void Interpreter::dumpCallFrame(CallFrame*) | |
320 | { | |
321 | } | |
322 | ||
323 | #else | |
9dae56ea A |
324 | |
325 | void Interpreter::dumpCallFrame(CallFrame* callFrame) | |
326 | { | |
93a37866 | 327 | callFrame->codeBlock()->dumpBytecode(); |
9dae56ea A |
328 | dumpRegisters(callFrame); |
329 | } | |
330 | ||
81345200 A |
331 | class DumpRegisterFunctor { |
332 | public: | |
333 | DumpRegisterFunctor(const Register*& it) | |
334 | : m_hasSkippedFirstFrame(false) | |
335 | , m_it(it) | |
336 | { | |
337 | } | |
338 | ||
339 | StackVisitor::Status operator()(StackVisitor& visitor) | |
340 | { | |
341 | if (!m_hasSkippedFirstFrame) { | |
342 | m_hasSkippedFirstFrame = true; | |
343 | return StackVisitor::Continue; | |
344 | } | |
345 | ||
346 | unsigned line = 0; | |
347 | unsigned unusedColumn = 0; | |
348 | visitor->computeLineAndColumn(line, unusedColumn); | |
349 | dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", m_it, visitor->bytecodeOffset(), line); | |
350 | --m_it; | |
351 | return StackVisitor::Done; | |
352 | } | |
353 | ||
354 | private: | |
355 | bool m_hasSkippedFirstFrame; | |
356 | const Register*& m_it; | |
357 | }; | |
358 | ||
9dae56ea A |
359 | void Interpreter::dumpRegisters(CallFrame* callFrame) |
360 | { | |
93a37866 A |
361 | dataLogF("Register frame: \n\n"); |
362 | dataLogF("-----------------------------------------------------------------------------\n"); | |
363 | dataLogF(" use | address | value \n"); | |
364 | dataLogF("-----------------------------------------------------------------------------\n"); | |
9dae56ea A |
365 | |
366 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
9dae56ea A |
367 | const Register* it; |
368 | const Register* end; | |
369 | ||
81345200 A |
370 | it = callFrame->registers() + JSStack::ThisArgument + callFrame->argumentCount(); |
371 | end = callFrame->registers() + JSStack::ThisArgument - 1; | |
372 | while (it > end) { | |
93a37866 A |
373 | JSValue v = it->jsValue(); |
374 | int registerNumber = it - callFrame->registers(); | |
81345200 | 375 | String name = codeBlock->nameForRegister(VirtualRegister(registerNumber)); |
93a37866 | 376 | dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v)); |
81345200 | 377 | --it; |
9dae56ea | 378 | } |
93a37866 A |
379 | |
380 | dataLogF("-----------------------------------------------------------------------------\n"); | |
381 | dataLogF("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount()); | |
81345200 | 382 | --it; |
93a37866 | 383 | dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame()); |
81345200 | 384 | --it; |
93a37866 | 385 | dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); |
81345200 | 386 | --it; |
93a37866 | 387 | dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); |
81345200 | 388 | --it; |
93a37866 A |
389 | #if ENABLE(JIT) |
390 | AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm()); | |
391 | if (pc.hasJITReturnAddress()) | |
392 | dataLogF("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value()); | |
393 | #endif | |
81345200 A |
394 | |
395 | DumpRegisterFunctor functor(it); | |
396 | callFrame->iterate(functor); | |
397 | ||
93a37866 | 398 | dataLogF("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock()); |
81345200 | 399 | --it; |
93a37866 | 400 | dataLogF("-----------------------------------------------------------------------------\n"); |
9dae56ea | 401 | |
81345200 | 402 | end = it - codeBlock->m_numVars; |
9dae56ea A |
403 | if (it != end) { |
404 | do { | |
93a37866 A |
405 | JSValue v = it->jsValue(); |
406 | int registerNumber = it - callFrame->registers(); | |
81345200 | 407 | String name = codeBlock->nameForRegister(VirtualRegister(registerNumber)); |
93a37866 | 408 | dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v)); |
81345200 | 409 | --it; |
9dae56ea A |
410 | } while (it != end); |
411 | } | |
93a37866 | 412 | dataLogF("-----------------------------------------------------------------------------\n"); |
9dae56ea | 413 | |
81345200 | 414 | end = it - codeBlock->m_numCalleeRegisters + codeBlock->m_numVars; |
9dae56ea A |
415 | if (it != end) { |
416 | do { | |
93a37866 | 417 | JSValue v = (*it).jsValue(); |
81345200 A |
418 | int registerNumber = it - callFrame->registers(); |
419 | dataLogF("[r% 3d] | %10p | %-16s 0x%lld \n", registerNumber, it, toCString(v).data(), (long long)JSValue::encode(v)); | |
420 | --it; | |
9dae56ea A |
421 | } while (it != end); |
422 | } | |
93a37866 | 423 | dataLogF("-----------------------------------------------------------------------------\n"); |
9dae56ea A |
424 | } |
425 | ||
426 | #endif | |
427 | ||
428 | bool Interpreter::isOpcode(Opcode opcode) | |
429 | { | |
93a37866 | 430 | #if ENABLE(COMPUTED_GOTO_OPCODES) |
9dae56ea A |
431 | return opcode != HashTraits<Opcode>::emptyValue() |
432 | && !HashTraits<Opcode>::isDeletedValue(opcode) | |
433 | && m_opcodeIDTable.contains(opcode); | |
434 | #else | |
435 | return opcode >= 0 && opcode <= op_end; | |
436 | #endif | |
437 | } | |
438 | ||
81345200 | 439 | static bool unwindCallFrame(StackVisitor& visitor) |
9dae56ea | 440 | { |
81345200 A |
441 | CallFrame* callFrame = visitor->callFrame(); |
442 | CodeBlock* codeBlock = visitor->codeBlock(); | |
93a37866 | 443 | JSScope* scope = callFrame->scope(); |
9dae56ea | 444 | |
81345200 A |
445 | if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) { |
446 | ClearExceptionScope scope(&callFrame->vm()); | |
9dae56ea | 447 | if (callFrame->callee()) |
81345200 | 448 | debugger->returnEvent(callFrame); |
9dae56ea | 449 | else |
81345200 A |
450 | debugger->didExecuteProgram(callFrame); |
451 | ASSERT(!callFrame->hadException()); | |
93a37866 A |
452 | } |
453 | ||
454 | JSValue activation; | |
81345200 | 455 | if (codeBlock->codeType() == FunctionCode && codeBlock->needsActivation()) { |
6fe7ccc8 | 456 | #if ENABLE(DFG_JIT) |
81345200 | 457 | RELEASE_ASSERT(!visitor->isInlinedFrame()); |
6fe7ccc8 | 458 | #endif |
81345200 A |
459 | activation = callFrame->uncheckedActivation(); |
460 | // Protect against the activation not being created, or the variable still being | |
461 | // initialized to Undefined inside op_enter. | |
462 | if (activation && activation.isCell()) { | |
463 | JSActivation* activationObject = jsCast<JSActivation*>(activation); | |
464 | // Protect against throwing exceptions after tear-off. | |
465 | if (!activationObject->isTornOff()) | |
466 | activationObject->tearOff(*scope->vm()); | |
467 | } | |
93a37866 | 468 | } |
6fe7ccc8 | 469 | |
81345200 A |
470 | if (codeBlock->codeType() == FunctionCode && codeBlock->usesArguments()) { |
471 | if (Arguments* arguments = visitor->existingArguments()) { | |
472 | if (activation && activation.isCell()) | |
473 | arguments->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation)); | |
6fe7ccc8 | 474 | #if ENABLE(DFG_JIT) |
81345200 A |
475 | else if (visitor->isInlinedFrame()) |
476 | arguments->tearOff(callFrame, visitor->inlineCallFrame()); | |
6fe7ccc8 | 477 | #endif |
81345200 A |
478 | else |
479 | arguments->tearOff(callFrame); | |
93a37866 | 480 | } |
6fe7ccc8 A |
481 | } |
482 | ||
81345200 A |
483 | CallFrame* callerFrame = callFrame->callerFrame(); |
484 | return !callerFrame->isVMEntrySentinel(); | |
6fe7ccc8 A |
485 | } |
486 | ||
81345200 | 487 | static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor) |
6fe7ccc8 | 488 | { |
81345200 A |
489 | switch (visitor->codeType()) { |
490 | case StackVisitor::Frame::Eval: | |
6fe7ccc8 | 491 | return StackFrameEvalCode; |
81345200 | 492 | case StackVisitor::Frame::Function: |
6fe7ccc8 | 493 | return StackFrameFunctionCode; |
81345200 | 494 | case StackVisitor::Frame::Global: |
6fe7ccc8 | 495 | return StackFrameGlobalCode; |
81345200 A |
496 | case StackVisitor::Frame::Native: |
497 | ASSERT_NOT_REACHED(); | |
498 | return StackFrameNativeCode; | |
6fe7ccc8 | 499 | } |
93a37866 | 500 | RELEASE_ASSERT_NOT_REACHED(); |
6fe7ccc8 A |
501 | return StackFrameGlobalCode; |
502 | } | |
503 | ||
93a37866 | 504 | void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column) |
6fe7ccc8 | 505 | { |
93a37866 A |
506 | if (!codeBlock) { |
507 | line = 0; | |
508 | column = 0; | |
6fe7ccc8 | 509 | return; |
93a37866 A |
510 | } |
511 | ||
512 | int divot = 0; | |
513 | int unusedStartOffset = 0; | |
514 | int unusedEndOffset = 0; | |
515 | unsigned divotLine = 0; | |
516 | unsigned divotColumn = 0; | |
517 | expressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); | |
518 | ||
519 | line = divotLine + lineOffset; | |
520 | column = divotColumn + (divotLine ? 1 : firstLineColumnOffset); | |
521 | } | |
522 | ||
523 | void StackFrame::expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) | |
524 | { | |
525 | codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column); | |
526 | divot += characterOffset; | |
527 | } | |
528 | ||
529 | String StackFrame::toString(CallFrame* callFrame) | |
530 | { | |
531 | StringBuilder traceBuild; | |
532 | String functionName = friendlyFunctionName(callFrame); | |
533 | String sourceURL = friendlySourceURL(); | |
534 | traceBuild.append(functionName); | |
535 | if (!sourceURL.isEmpty()) { | |
536 | if (!functionName.isEmpty()) | |
537 | traceBuild.append('@'); | |
538 | traceBuild.append(sourceURL); | |
539 | if (codeType != StackFrameNativeCode) { | |
540 | unsigned line; | |
541 | unsigned column; | |
542 | computeLineAndColumn(line, column); | |
543 | ||
544 | traceBuild.append(':'); | |
545 | traceBuild.appendNumber(line); | |
546 | traceBuild.append(':'); | |
547 | traceBuild.appendNumber(column); | |
548 | } | |
549 | } | |
550 | return traceBuild.toString().impl(); | |
551 | } | |
6fe7ccc8 | 552 | |
81345200 A |
553 | class GetStackTraceFunctor { |
554 | public: | |
555 | GetStackTraceFunctor(VM& vm, Vector<StackFrame>& results, size_t remainingCapacity) | |
556 | : m_vm(vm) | |
557 | , m_results(results) | |
558 | , m_remainingCapacityForFrameCapture(remainingCapacity) | |
559 | { | |
560 | } | |
561 | ||
562 | StackVisitor::Status operator()(StackVisitor& visitor) | |
563 | { | |
564 | VM& vm = m_vm; | |
565 | if (m_remainingCapacityForFrameCapture) { | |
566 | if (visitor->isJSFrame() && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) { | |
567 | CodeBlock* codeBlock = visitor->codeBlock(); | |
568 | StackFrame s = { | |
569 | Strong<JSObject>(vm, visitor->callee()), | |
570 | getStackFrameCodeType(visitor), | |
571 | Strong<ExecutableBase>(vm, codeBlock->ownerExecutable()), | |
572 | Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()), | |
573 | codeBlock->source(), | |
574 | codeBlock->ownerExecutable()->lineNo(), | |
575 | codeBlock->firstLineColumnOffset(), | |
576 | codeBlock->sourceOffset(), | |
577 | visitor->bytecodeOffset(), | |
578 | visitor->sourceURL() | |
579 | }; | |
580 | m_results.append(s); | |
581 | } else { | |
582 | StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; | |
583 | m_results.append(s); | |
584 | } | |
585 | ||
586 | m_remainingCapacityForFrameCapture--; | |
587 | return StackVisitor::Continue; | |
6fe7ccc8 | 588 | } |
81345200 | 589 | return StackVisitor::Done; |
6fe7ccc8 | 590 | } |
6fe7ccc8 | 591 | |
81345200 A |
592 | private: |
593 | VM& m_vm; | |
594 | Vector<StackFrame>& m_results; | |
595 | size_t m_remainingCapacityForFrameCapture; | |
596 | }; | |
6fe7ccc8 | 597 | |
81345200 A |
598 | void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t maxStackSize) |
599 | { | |
600 | VM& vm = m_vm; | |
601 | ASSERT(!vm.topCallFrame->isVMEntrySentinel()); | |
602 | CallFrame* callFrame = vm.topCallFrame; | |
603 | if (!callFrame) | |
6fe7ccc8 | 604 | return; |
93a37866 | 605 | |
81345200 A |
606 | GetStackTraceFunctor functor(vm, results, maxStackSize); |
607 | callFrame->iterate(functor); | |
608 | } | |
93a37866 | 609 | |
81345200 A |
610 | JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> stackTrace) |
611 | { | |
93a37866 | 612 | // FIXME: JSStringJoiner could be more efficient than StringBuilder here. |
6fe7ccc8 A |
613 | StringBuilder builder; |
614 | for (unsigned i = 0; i < stackTrace.size(); i++) { | |
81345200 | 615 | builder.append(String(stackTrace[i].toString(exec))); |
6fe7ccc8 A |
616 | if (i != stackTrace.size() - 1) |
617 | builder.append('\n'); | |
618 | } | |
81345200 | 619 | return jsString(&exec->vm(), builder.toString()); |
6fe7ccc8 A |
620 | } |
621 | ||
81345200 A |
622 | class GetExceptionHandlerFunctor { |
623 | public: | |
624 | GetExceptionHandlerFunctor() | |
625 | : m_handler(0) | |
626 | { | |
627 | } | |
628 | ||
629 | HandlerInfo* handler() { return m_handler; } | |
630 | ||
631 | StackVisitor::Status operator()(StackVisitor& visitor) | |
632 | { | |
633 | CodeBlock* codeBlock = visitor->codeBlock(); | |
634 | if (!codeBlock) | |
635 | return StackVisitor::Continue; | |
636 | ||
637 | unsigned bytecodeOffset = visitor->bytecodeOffset(); | |
638 | m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); | |
639 | if (m_handler) | |
640 | return StackVisitor::Done; | |
641 | ||
642 | return StackVisitor::Continue; | |
643 | } | |
644 | ||
645 | private: | |
646 | HandlerInfo* m_handler; | |
647 | }; | |
648 | ||
649 | class UnwindFunctor { | |
650 | public: | |
651 | UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler) | |
652 | : m_callFrame(callFrame) | |
653 | , m_isTermination(isTermination) | |
654 | , m_codeBlock(codeBlock) | |
655 | , m_handler(handler) | |
656 | { | |
657 | } | |
658 | ||
659 | StackVisitor::Status operator()(StackVisitor& visitor) | |
660 | { | |
661 | VM& vm = m_callFrame->vm(); | |
662 | m_callFrame = visitor->callFrame(); | |
663 | m_codeBlock = visitor->codeBlock(); | |
664 | unsigned bytecodeOffset = visitor->bytecodeOffset(); | |
665 | ||
666 | if (m_isTermination || !(m_handler = m_codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { | |
667 | if (!unwindCallFrame(visitor)) { | |
668 | if (LegacyProfiler* profiler = vm.enabledProfiler()) | |
669 | profiler->exceptionUnwind(m_callFrame); | |
670 | return StackVisitor::Done; | |
671 | } | |
672 | } else | |
673 | return StackVisitor::Done; | |
674 | ||
675 | return StackVisitor::Continue; | |
676 | } | |
677 | ||
678 | private: | |
679 | CallFrame*& m_callFrame; | |
680 | bool m_isTermination; | |
681 | CodeBlock*& m_codeBlock; | |
682 | HandlerInfo*& m_handler; | |
683 | }; | |
684 | ||
685 | NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& exceptionValue) | |
14957cd0 | 686 | { |
81345200 A |
687 | if (callFrame->isVMEntrySentinel()) { |
688 | // This happens when we throw stack overflow in a function that is called | |
689 | // directly from callToJavaScript. Stack overflow throws the exception in the | |
690 | // context of the caller. In that case the caller is the sentinel frame. The | |
691 | // right thing to do is to pretend that the exception is uncaught so that we | |
692 | // go to the uncaught exception handler, which returns through callToJavaScript. | |
693 | return 0; | |
694 | } | |
695 | ||
9dae56ea | 696 | CodeBlock* codeBlock = callFrame->codeBlock(); |
81345200 | 697 | ASSERT(codeBlock); |
93a37866 | 698 | bool isTermination = false; |
14957cd0 | 699 | |
6fe7ccc8 A |
700 | ASSERT(!exceptionValue.isEmpty()); |
701 | ASSERT(!exceptionValue.isCell() || exceptionValue.asCell()); | |
702 | // This shouldn't be possible (hence the assertions), but we're already in the slowest of | |
703 | // slow cases, so let's harden against it anyway to be safe. | |
704 | if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) | |
705 | exceptionValue = jsNull(); | |
706 | ||
81345200 A |
707 | if (exceptionValue.isObject()) |
708 | isTermination = isTerminatedExecutionException(asObject(exceptionValue)); | |
709 | ||
710 | ASSERT(callFrame->vm().exceptionStack().size()); | |
711 | ||
712 | Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); | |
713 | if (debugger && debugger->needsExceptionCallbacks()) { | |
714 | // We need to clear the exception and the exception stack here in order to see if a new exception happens. | |
715 | // Afterwards, the values are put back to continue processing this error. | |
716 | ClearExceptionScope scope(&callFrame->vm()); | |
717 | // This code assumes that if the debugger is enabled then there is no inlining. | |
718 | // If that assumption turns out to be false then we'll ignore the inlined call | |
719 | // frames. | |
720 | // https://bugs.webkit.org/show_bug.cgi?id=121754 | |
721 | ||
722 | bool hasHandler; | |
723 | if (isTermination) | |
724 | hasHandler = false; | |
725 | else { | |
726 | GetExceptionHandlerFunctor functor; | |
727 | callFrame->iterate(functor); | |
728 | hasHandler = !!functor.handler(); | |
93a37866 | 729 | } |
9dae56ea | 730 | |
81345200 A |
731 | debugger->exception(callFrame, exceptionValue, hasHandler); |
732 | ASSERT(!callFrame->hadException()); | |
9dae56ea A |
733 | } |
734 | ||
735 | // Calculate an exception handler vPC, unwinding call frames as necessary. | |
9dae56ea | 736 | HandlerInfo* handler = 0; |
81345200 A |
737 | VM& vm = callFrame->vm(); |
738 | ASSERT(callFrame == vm.topCallFrame); | |
739 | UnwindFunctor functor(callFrame, isTermination, codeBlock, handler); | |
740 | callFrame->iterate(functor); | |
741 | if (!handler) | |
742 | return 0; | |
9dae56ea | 743 | |
81345200 | 744 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
14957cd0 | 745 | profiler->exceptionUnwind(callFrame); |
9dae56ea | 746 | |
14957cd0 | 747 | // Unwind the scope chain within the exception handler's call frame. |
81345200 A |
748 | int targetScopeDepth = handler->scopeDepth; |
749 | if (codeBlock->needsActivation() && callFrame->hasActivation()) | |
750 | ++targetScopeDepth; | |
751 | ||
93a37866 | 752 | JSScope* scope = callFrame->scope(); |
81345200 A |
753 | int scopeDelta = scope->depth() - targetScopeDepth; |
754 | RELEASE_ASSERT(scopeDelta >= 0); | |
755 | ||
9dae56ea | 756 | while (scopeDelta--) |
93a37866 A |
757 | scope = scope->next(); |
758 | callFrame->setScope(scope); | |
9dae56ea A |
759 | |
760 | return handler; | |
761 | } | |
762 | ||
14957cd0 A |
763 | static inline JSValue checkedReturn(JSValue returnValue) |
764 | { | |
765 | ASSERT(returnValue); | |
766 | return returnValue; | |
767 | } | |
768 | ||
769 | static inline JSObject* checkedReturn(JSObject* returnValue) | |
770 | { | |
771 | ASSERT(returnValue); | |
772 | return returnValue; | |
773 | } | |
774 | ||
93a37866 A |
775 | class SamplingScope { |
776 | public: | |
777 | SamplingScope(Interpreter* interpreter) | |
778 | : m_interpreter(interpreter) | |
779 | { | |
780 | interpreter->startSampling(); | |
781 | } | |
782 | ~SamplingScope() | |
783 | { | |
784 | m_interpreter->stopSampling(); | |
785 | } | |
786 | private: | |
787 | Interpreter* m_interpreter; | |
788 | }; | |
789 | ||
790 | JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj) | |
9dae56ea | 791 | { |
93a37866 A |
792 | SamplingScope samplingScope(this); |
793 | ||
794 | JSScope* scope = callFrame->scope(); | |
795 | VM& vm = *scope->vm(); | |
796 | ||
81345200 | 797 | ASSERT(!vm.exception()); |
93a37866 | 798 | ASSERT(!vm.isCollectorBusy()); |
81345200 | 799 | RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); |
93a37866 A |
800 | if (vm.isCollectorBusy()) |
801 | return jsNull(); | |
9dae56ea | 802 | |
81345200 | 803 | if (!vm.isSafeToRecurse()) |
14957cd0 A |
804 | return checkedReturn(throwStackOverflowError(callFrame)); |
805 | ||
93a37866 A |
806 | // First check if the "program" is actually just a JSON object. If so, |
807 | // we'll handle the JSON object here. Else, we'll handle real JS code | |
808 | // below at failedJSONP. | |
81345200 | 809 | |
6fe7ccc8 A |
810 | Vector<JSONPData> JSONPData; |
811 | bool parseResult; | |
93a37866 | 812 | const String programSource = program->source().toString(); |
6fe7ccc8 A |
813 | if (programSource.isNull()) |
814 | return jsUndefined(); | |
815 | if (programSource.is8Bit()) { | |
816 | LiteralParser<LChar> literalParser(callFrame, programSource.characters8(), programSource.length(), JSONP); | |
93a37866 | 817 | parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject())); |
6fe7ccc8 A |
818 | } else { |
819 | LiteralParser<UChar> literalParser(callFrame, programSource.characters16(), programSource.length(), JSONP); | |
93a37866 | 820 | parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject())); |
6fe7ccc8 A |
821 | } |
822 | ||
823 | if (parseResult) { | |
93a37866 | 824 | JSGlobalObject* globalObject = scope->globalObject(); |
14957cd0 A |
825 | JSValue result; |
826 | for (unsigned entry = 0; entry < JSONPData.size(); entry++) { | |
6fe7ccc8 | 827 | Vector<JSONPPathEntry> JSONPPath; |
14957cd0 A |
828 | JSONPPath.swap(JSONPData[entry].m_path); |
829 | JSValue JSONPValue = JSONPData[entry].m_value.get(); | |
6fe7ccc8 | 830 | if (JSONPPath.size() == 1 && JSONPPath[0].m_type == JSONPPathEntryTypeDeclare) { |
81345200 A |
831 | globalObject->addVar(callFrame, JSONPPath[0].m_pathEntryName); |
832 | PutPropertySlot slot(globalObject); | |
833 | globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot); | |
14957cd0 A |
834 | result = jsUndefined(); |
835 | continue; | |
836 | } | |
837 | JSValue baseObject(globalObject); | |
838 | for (unsigned i = 0; i < JSONPPath.size() - 1; i++) { | |
6fe7ccc8 | 839 | ASSERT(JSONPPath[i].m_type != JSONPPathEntryTypeDeclare); |
14957cd0 | 840 | switch (JSONPPath[i].m_type) { |
6fe7ccc8 | 841 | case JSONPPathEntryTypeDot: { |
14957cd0 A |
842 | if (i == 0) { |
843 | PropertySlot slot(globalObject); | |
844 | if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) { | |
845 | if (entry) | |
81345200 | 846 | return callFrame->vm().throwException(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName)); |
14957cd0 A |
847 | goto failedJSONP; |
848 | } | |
849 | baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName); | |
850 | } else | |
851 | baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName); | |
852 | if (callFrame->hadException()) | |
853 | return jsUndefined(); | |
854 | continue; | |
855 | } | |
6fe7ccc8 | 856 | case JSONPPathEntryTypeLookup: { |
14957cd0 A |
857 | baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex); |
858 | if (callFrame->hadException()) | |
859 | return jsUndefined(); | |
860 | continue; | |
861 | } | |
862 | default: | |
93a37866 | 863 | RELEASE_ASSERT_NOT_REACHED(); |
14957cd0 A |
864 | return jsUndefined(); |
865 | } | |
866 | } | |
81345200 | 867 | PutPropertySlot slot(baseObject); |
14957cd0 | 868 | switch (JSONPPath.last().m_type) { |
6fe7ccc8 | 869 | case JSONPPathEntryTypeCall: { |
14957cd0 A |
870 | JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName); |
871 | if (callFrame->hadException()) | |
872 | return jsUndefined(); | |
873 | CallData callData; | |
874 | CallType callType = getCallData(function, callData); | |
875 | if (callType == CallTypeNone) | |
81345200 | 876 | return callFrame->vm().throwException(callFrame, createNotAFunctionError(callFrame, function)); |
14957cd0 A |
877 | MarkedArgumentBuffer jsonArg; |
878 | jsonArg.append(JSONPValue); | |
879 | JSValue thisValue = JSONPPath.size() == 1 ? jsUndefined(): baseObject; | |
880 | JSONPValue = JSC::call(callFrame, function, callType, callData, thisValue, jsonArg); | |
881 | if (callFrame->hadException()) | |
882 | return jsUndefined(); | |
883 | break; | |
884 | } | |
6fe7ccc8 | 885 | case JSONPPathEntryTypeDot: { |
14957cd0 A |
886 | baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot); |
887 | if (callFrame->hadException()) | |
888 | return jsUndefined(); | |
889 | break; | |
890 | } | |
6fe7ccc8 A |
891 | case JSONPPathEntryTypeLookup: { |
892 | baseObject.putByIndex(callFrame, JSONPPath.last().m_pathIndex, JSONPValue, slot.isStrictMode()); | |
14957cd0 A |
893 | if (callFrame->hadException()) |
894 | return jsUndefined(); | |
895 | break; | |
896 | } | |
897 | default: | |
93a37866 | 898 | RELEASE_ASSERT_NOT_REACHED(); |
14957cd0 A |
899 | return jsUndefined(); |
900 | } | |
901 | result = JSONPValue; | |
ba379fdc | 902 | } |
14957cd0 | 903 | return result; |
9dae56ea | 904 | } |
14957cd0 | 905 | failedJSONP: |
93a37866 A |
906 | // If we get here, then we have already proven that the script is not a JSON |
907 | // object. | |
908 | ||
81345200 A |
909 | VMEntryScope entryScope(vm, scope->globalObject()); |
910 | ||
93a37866 A |
911 | // Compile source to bytecode if necessary: |
912 | if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope)) | |
81345200 | 913 | return checkedReturn(callFrame->vm().throwException(callFrame, error)); |
9dae56ea | 914 | |
81345200 A |
915 | if (JSObject* error = program->prepareForExecution(callFrame, nullptr, &scope, CodeForCall)) |
916 | return checkedReturn(callFrame->vm().throwException(callFrame, error)); | |
93a37866 | 917 | |
81345200 | 918 | ProgramCodeBlock* codeBlock = program->codeBlock(); |
93a37866 | 919 | |
81345200 | 920 | if (UNLIKELY(vm.watchdog && vm.watchdog->didFire(callFrame))) |
93a37866 | 921 | return throwTerminatedExecutionException(callFrame); |
9dae56ea | 922 | |
6fe7ccc8 | 923 | ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. |
93a37866 | 924 | |
81345200 A |
925 | ProtoCallFrame protoCallFrame; |
926 | protoCallFrame.init(codeBlock, scope, 0, thisObj, 1); | |
9dae56ea | 927 | |
93a37866 | 928 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
81345200 | 929 | profiler->willExecute(callFrame, program->sourceURL(), program->lineNo(), program->startColumn()); |
9dae56ea | 930 | |
93a37866 | 931 | // Execute the code: |
ba379fdc | 932 | JSValue result; |
9dae56ea | 933 | { |
f9bf01c6 | 934 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
81345200 | 935 | Watchdog::Scope watchdogScope(vm.watchdog.get()); |
9dae56ea | 936 | |
81345200 | 937 | result = program->generatedJITCode()->execute(&vm, &protoCallFrame); |
9dae56ea A |
938 | } |
939 | ||
93a37866 | 940 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
81345200 | 941 | profiler->didExecute(callFrame, program->sourceURL(), program->lineNo(), program->startColumn()); |
9dae56ea | 942 | |
14957cd0 | 943 | return checkedReturn(result); |
9dae56ea A |
944 | } |
945 | ||
14957cd0 | 946 | JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) |
9dae56ea | 947 | { |
93a37866 | 948 | VM& vm = callFrame->vm(); |
14957cd0 | 949 | ASSERT(!callFrame->hadException()); |
93a37866 A |
950 | ASSERT(!vm.isCollectorBusy()); |
951 | if (vm.isCollectorBusy()) | |
14957cd0 | 952 | return jsNull(); |
9dae56ea | 953 | |
93a37866 A |
954 | bool isJSCall = (callType == CallTypeJS); |
955 | JSScope* scope; | |
956 | CodeBlock* newCodeBlock; | |
957 | size_t argsCount = 1 + args.size(); // implicit "this" parameter | |
14957cd0 | 958 | |
93a37866 A |
959 | if (isJSCall) |
960 | scope = callData.js.scope; | |
961 | else { | |
962 | ASSERT(callType == CallTypeHost); | |
963 | scope = callFrame->scope(); | |
964 | } | |
81345200 A |
965 | |
966 | VMEntryScope entryScope(vm, scope->globalObject()); | |
967 | if (!vm.isSafeToRecurse()) | |
968 | return checkedReturn(throwStackOverflowError(callFrame)); | |
14957cd0 | 969 | |
93a37866 A |
970 | if (isJSCall) { |
971 | // Compile the callee: | |
81345200 | 972 | JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), &scope, CodeForCall); |
14957cd0 | 973 | if (UNLIKELY(!!compileError)) { |
81345200 | 974 | return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); |
14957cd0 | 975 | } |
81345200 | 976 | newCodeBlock = callData.js.functionExecutable->codeBlockForCall(); |
93a37866 | 977 | ASSERT(!!newCodeBlock); |
81345200 | 978 | newCodeBlock->m_shouldAlwaysBeInlined = false; |
93a37866 A |
979 | } else |
980 | newCodeBlock = 0; | |
14957cd0 | 981 | |
81345200 | 982 | if (UNLIKELY(vm.watchdog && vm.watchdog->didFire(callFrame))) |
93a37866 | 983 | return throwTerminatedExecutionException(callFrame); |
14957cd0 | 984 | |
81345200 A |
985 | ProtoCallFrame protoCallFrame; |
986 | protoCallFrame.init(newCodeBlock, scope, function, thisValue, argsCount, args.data()); | |
9dae56ea | 987 | |
93a37866 A |
988 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
989 | profiler->willExecute(callFrame, function); | |
9dae56ea | 990 | |
ba379fdc | 991 | JSValue result; |
9dae56ea | 992 | { |
93a37866 | 993 | SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall); |
81345200 | 994 | Watchdog::Scope watchdogScope(vm.watchdog.get()); |
93a37866 A |
995 | |
996 | // Execute the code: | |
81345200 A |
997 | if (isJSCall) |
998 | result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame); | |
999 | else { | |
1000 | result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(callData.native.function), &vm, &protoCallFrame)); | |
1001 | if (callFrame->hadException()) | |
1002 | result = jsNull(); | |
1003 | } | |
14957cd0 | 1004 | } |
9dae56ea | 1005 | |
93a37866 A |
1006 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1007 | profiler->didExecute(callFrame, function); | |
14957cd0 | 1008 | |
14957cd0 A |
1009 | return checkedReturn(result); |
1010 | } | |
1011 | ||
1012 | JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) | |
1013 | { | |
93a37866 | 1014 | VM& vm = callFrame->vm(); |
14957cd0 | 1015 | ASSERT(!callFrame->hadException()); |
93a37866 | 1016 | ASSERT(!vm.isCollectorBusy()); |
14957cd0 A |
1017 | // We throw in this case because we have to return something "valid" but we're |
1018 | // already in an invalid state. | |
93a37866 | 1019 | if (vm.isCollectorBusy()) |
14957cd0 A |
1020 | return checkedReturn(throwStackOverflowError(callFrame)); |
1021 | ||
93a37866 A |
1022 | bool isJSConstruct = (constructType == ConstructTypeJS); |
1023 | JSScope* scope; | |
1024 | CodeBlock* newCodeBlock; | |
1025 | size_t argsCount = 1 + args.size(); // implicit "this" parameter | |
14957cd0 | 1026 | |
93a37866 A |
1027 | if (isJSConstruct) |
1028 | scope = constructData.js.scope; | |
1029 | else { | |
1030 | ASSERT(constructType == ConstructTypeHost); | |
1031 | scope = callFrame->scope(); | |
1032 | } | |
14957cd0 | 1033 | |
81345200 A |
1034 | VMEntryScope entryScope(vm, scope->globalObject()); |
1035 | if (!vm.isSafeToRecurse()) | |
1036 | return checkedReturn(throwStackOverflowError(callFrame)); | |
14957cd0 | 1037 | |
93a37866 A |
1038 | if (isJSConstruct) { |
1039 | // Compile the callee: | |
81345200 | 1040 | JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), &scope, CodeForConstruct); |
14957cd0 | 1041 | if (UNLIKELY(!!compileError)) { |
81345200 | 1042 | return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); |
14957cd0 | 1043 | } |
81345200 | 1044 | newCodeBlock = constructData.js.functionExecutable->codeBlockForConstruct(); |
93a37866 | 1045 | ASSERT(!!newCodeBlock); |
81345200 | 1046 | newCodeBlock->m_shouldAlwaysBeInlined = false; |
93a37866 A |
1047 | } else |
1048 | newCodeBlock = 0; | |
14957cd0 | 1049 | |
81345200 | 1050 | if (UNLIKELY(vm.watchdog && vm.watchdog->didFire(callFrame))) |
93a37866 | 1051 | return throwTerminatedExecutionException(callFrame); |
14957cd0 | 1052 | |
81345200 A |
1053 | ProtoCallFrame protoCallFrame; |
1054 | protoCallFrame.init(newCodeBlock, scope, constructor, jsUndefined(), argsCount, args.data()); | |
14957cd0 | 1055 | |
93a37866 A |
1056 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1057 | profiler->willExecute(callFrame, constructor); | |
14957cd0 A |
1058 | |
1059 | JSValue result; | |
1060 | { | |
93a37866 | 1061 | SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct); |
81345200 | 1062 | Watchdog::Scope watchdogScope(vm.watchdog.get()); |
93a37866 A |
1063 | |
1064 | // Execute the code. | |
81345200 A |
1065 | if (isJSConstruct) |
1066 | result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame); | |
1067 | else { | |
1068 | result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(constructData.native.function), &vm, &protoCallFrame)); | |
1069 | ||
1070 | if (!callFrame->hadException()) | |
1071 | RELEASE_ASSERT(result.isObject()); | |
1072 | } | |
14957cd0 A |
1073 | } |
1074 | ||
93a37866 A |
1075 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1076 | profiler->didExecute(callFrame, constructor); | |
1077 | ||
14957cd0 A |
1078 | if (callFrame->hadException()) |
1079 | return 0; | |
1080 | ASSERT(result.isObject()); | |
1081 | return checkedReturn(asObject(result)); | |
9dae56ea A |
1082 | } |
1083 | ||
81345200 | 1084 | CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, JSValue* args) |
ba379fdc | 1085 | { |
93a37866 | 1086 | VM& vm = *scope->vm(); |
81345200 | 1087 | ASSERT(!vm.exception()); |
ba379fdc | 1088 | |
93a37866 | 1089 | if (vm.isCollectorBusy()) |
6fe7ccc8 A |
1090 | return CallFrameClosure(); |
1091 | ||
93a37866 | 1092 | // Compile the callee: |
81345200 | 1093 | JSObject* error = functionExecutable->prepareForExecution(callFrame, function, &scope, CodeForCall); |
93a37866 | 1094 | if (error) { |
81345200 | 1095 | callFrame->vm().throwException(callFrame, error); |
ba379fdc A |
1096 | return CallFrameClosure(); |
1097 | } | |
81345200 A |
1098 | CodeBlock* newCodeBlock = functionExecutable->codeBlockForCall(); |
1099 | newCodeBlock->m_shouldAlwaysBeInlined = false; | |
ba379fdc | 1100 | |
93a37866 A |
1101 | size_t argsCount = argumentCountIncludingThis; |
1102 | ||
81345200 | 1103 | protoCallFrame->init(newCodeBlock, scope, function, jsUndefined(), argsCount, args); |
93a37866 | 1104 | // Return the successful closure: |
81345200 | 1105 | CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; |
ba379fdc A |
1106 | return result; |
1107 | } | |
1108 | ||
14957cd0 | 1109 | JSValue Interpreter::execute(CallFrameClosure& closure) |
ba379fdc | 1110 | { |
93a37866 A |
1111 | VM& vm = *closure.vm; |
1112 | SamplingScope samplingScope(this); | |
1113 | ||
1114 | ASSERT(!vm.isCollectorBusy()); | |
81345200 | 1115 | RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); |
93a37866 | 1116 | if (vm.isCollectorBusy()) |
14957cd0 | 1117 | return jsNull(); |
93a37866 A |
1118 | |
1119 | StackStats::CheckPoint stackCheckPoint; | |
ba379fdc | 1120 | closure.resetCallFrame(); |
93a37866 A |
1121 | |
1122 | if (LegacyProfiler* profiler = vm.enabledProfiler()) | |
1123 | profiler->willExecute(closure.oldCallFrame, closure.function); | |
1124 | ||
81345200 | 1125 | if (UNLIKELY(vm.watchdog && vm.watchdog->didFire(closure.oldCallFrame))) |
93a37866 | 1126 | return throwTerminatedExecutionException(closure.oldCallFrame); |
6fe7ccc8 | 1127 | |
93a37866 | 1128 | // Execute the code: |
ba379fdc A |
1129 | JSValue result; |
1130 | { | |
f9bf01c6 | 1131 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
81345200 | 1132 | Watchdog::Scope watchdogScope(vm.watchdog.get()); |
93a37866 | 1133 | |
81345200 | 1134 | result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame); |
ba379fdc | 1135 | } |
6fe7ccc8 | 1136 | |
93a37866 A |
1137 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
1138 | profiler->didExecute(closure.oldCallFrame, closure.function); | |
1139 | ||
14957cd0 | 1140 | return checkedReturn(result); |
ba379fdc A |
1141 | } |
1142 | ||
93a37866 | 1143 | JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) |
9dae56ea | 1144 | { |
93a37866 A |
1145 | VM& vm = *scope->vm(); |
1146 | SamplingScope samplingScope(this); | |
1147 | ||
1148 | ASSERT(scope->vm() == &callFrame->vm()); | |
81345200 | 1149 | ASSERT(!vm.exception()); |
93a37866 | 1150 | ASSERT(!vm.isCollectorBusy()); |
81345200 | 1151 | RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); |
93a37866 | 1152 | if (vm.isCollectorBusy()) |
14957cd0 | 1153 | return jsNull(); |
9dae56ea | 1154 | |
81345200 A |
1155 | VMEntryScope entryScope(vm, scope->globalObject()); |
1156 | if (!vm.isSafeToRecurse()) | |
1157 | return checkedReturn(throwStackOverflowError(callFrame)); | |
9dae56ea | 1158 | |
81345200 A |
1159 | unsigned numVariables = eval->numVariables(); |
1160 | int numFunctions = eval->numberOfFunctionDecls(); | |
9dae56ea | 1161 | |
81345200 A |
1162 | JSScope* variableObject; |
1163 | if ((numVariables || numFunctions) && eval->isStrictMode()) { | |
1164 | scope = StrictEvalActivation::create(callFrame); | |
1165 | variableObject = scope; | |
1166 | } else { | |
1167 | for (JSScope* node = scope; ; node = node->next()) { | |
1168 | RELEASE_ASSERT(node); | |
1169 | if (node->isVariableObject() && !node->isNameScopeObject()) { | |
1170 | variableObject = node; | |
1171 | break; | |
1172 | } | |
9dae56ea A |
1173 | } |
1174 | } | |
1175 | ||
81345200 A |
1176 | JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, &scope, CodeForCall); |
1177 | if (UNLIKELY(!!compileError)) | |
1178 | return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); | |
1179 | EvalCodeBlock* codeBlock = eval->codeBlock(); | |
1180 | ||
4e4e5a6f | 1181 | if (numVariables || numFunctions) { |
93a37866 | 1182 | BatchedTransitionOptimizer optimizer(vm, variableObject); |
81345200 A |
1183 | if (variableObject->next()) |
1184 | variableObject->globalObject()->varInjectionWatchpoint()->fireAll(); | |
9dae56ea | 1185 | |
f9bf01c6 A |
1186 | for (unsigned i = 0; i < numVariables; ++i) { |
1187 | const Identifier& ident = codeBlock->variable(i); | |
9dae56ea | 1188 | if (!variableObject->hasProperty(callFrame, ident)) { |
81345200 | 1189 | PutPropertySlot slot(variableObject); |
6fe7ccc8 | 1190 | variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot); |
9dae56ea A |
1191 | } |
1192 | } | |
1193 | ||
f9bf01c6 A |
1194 | for (int i = 0; i < numFunctions; ++i) { |
1195 | FunctionExecutable* function = codeBlock->functionDecl(i); | |
81345200 A |
1196 | PutPropertySlot slot(variableObject); |
1197 | variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot); | |
9dae56ea | 1198 | } |
9dae56ea A |
1199 | } |
1200 | ||
81345200 | 1201 | if (UNLIKELY(vm.watchdog && vm.watchdog->didFire(callFrame))) |
93a37866 | 1202 | return throwTerminatedExecutionException(callFrame); |
6fe7ccc8 A |
1203 | |
1204 | ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. | |
9dae56ea | 1205 | |
81345200 A |
1206 | ProtoCallFrame protoCallFrame; |
1207 | protoCallFrame.init(codeBlock, scope, 0, thisValue, 1); | |
9dae56ea | 1208 | |
93a37866 | 1209 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
81345200 | 1210 | profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo(), eval->startColumn()); |
9dae56ea | 1211 | |
93a37866 | 1212 | // Execute the code: |
ba379fdc | 1213 | JSValue result; |
9dae56ea | 1214 | { |
f9bf01c6 | 1215 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
81345200 | 1216 | Watchdog::Scope watchdogScope(vm.watchdog.get()); |
9dae56ea | 1217 | |
81345200 | 1218 | result = eval->generatedJITCode()->execute(&vm, &protoCallFrame); |
9dae56ea A |
1219 | } |
1220 | ||
93a37866 | 1221 | if (LegacyProfiler* profiler = vm.enabledProfiler()) |
81345200 | 1222 | profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo(), eval->startColumn()); |
9dae56ea | 1223 | |
14957cd0 | 1224 | return checkedReturn(result); |
9dae56ea A |
1225 | } |
1226 | ||
81345200 | 1227 | NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID) |
9dae56ea | 1228 | { |
81345200 | 1229 | Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); |
9dae56ea A |
1230 | if (!debugger) |
1231 | return; | |
1232 | ||
81345200 A |
1233 | ASSERT(callFrame->codeBlock()->hasDebuggerRequests()); |
1234 | ASSERT(!callFrame->hadException()); | |
1235 | ||
9dae56ea A |
1236 | switch (debugHookID) { |
1237 | case DidEnterCallFrame: | |
81345200 A |
1238 | debugger->callEvent(callFrame); |
1239 | break; | |
9dae56ea | 1240 | case WillLeaveCallFrame: |
81345200 A |
1241 | debugger->returnEvent(callFrame); |
1242 | break; | |
9dae56ea | 1243 | case WillExecuteStatement: |
81345200 A |
1244 | debugger->atStatement(callFrame); |
1245 | break; | |
9dae56ea | 1246 | case WillExecuteProgram: |
81345200 A |
1247 | debugger->willExecuteProgram(callFrame); |
1248 | break; | |
9dae56ea | 1249 | case DidExecuteProgram: |
81345200 A |
1250 | debugger->didExecuteProgram(callFrame); |
1251 | break; | |
9dae56ea | 1252 | case DidReachBreakpoint: |
81345200 A |
1253 | debugger->didReachBreakpoint(callFrame); |
1254 | break; | |
9dae56ea | 1255 | } |
81345200 A |
1256 | ASSERT(!callFrame->hadException()); |
1257 | } | |
9dae56ea | 1258 | |
f9bf01c6 A |
1259 | void Interpreter::enableSampler() |
1260 | { | |
1261 | #if ENABLE(OPCODE_SAMPLING) | |
1262 | if (!m_sampler) { | |
6fe7ccc8 | 1263 | m_sampler = adoptPtr(new SamplingTool(this)); |
f9bf01c6 A |
1264 | m_sampler->setup(); |
1265 | } | |
1266 | #endif | |
1267 | } | |
1268 | void Interpreter::dumpSampleData(ExecState* exec) | |
1269 | { | |
1270 | #if ENABLE(OPCODE_SAMPLING) | |
1271 | if (m_sampler) | |
1272 | m_sampler->dump(exec); | |
1273 | #else | |
1274 | UNUSED_PARAM(exec); | |
1275 | #endif | |
1276 | } | |
1277 | void Interpreter::startSampling() | |
1278 | { | |
1279 | #if ENABLE(SAMPLING_THREAD) | |
1280 | if (!m_sampleEntryDepth) | |
1281 | SamplingThread::start(); | |
1282 | ||
1283 | m_sampleEntryDepth++; | |
1284 | #endif | |
1285 | } | |
1286 | void Interpreter::stopSampling() | |
1287 | { | |
1288 | #if ENABLE(SAMPLING_THREAD) | |
1289 | m_sampleEntryDepth--; | |
1290 | if (!m_sampleEntryDepth) | |
1291 | SamplingThread::stop(); | |
1292 | #endif | |
1293 | } | |
1294 | ||
9dae56ea | 1295 | } // namespace JSC |