]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
fb8617cd | 2 | * Copyright (C) 2008, 2009, 2010 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" | |
47 | #include "JSByteArray.h" | |
48 | #include "JSFunction.h" | |
49 | #include "JSNotAnObject.h" | |
50 | #include "JSPropertyNameIterator.h" | |
ba379fdc | 51 | #include "LiteralParser.h" |
9dae56ea A |
52 | #include "JSStaticScopeObject.h" |
53 | #include "JSString.h" | |
54 | #include "ObjectPrototype.h" | |
ba379fdc | 55 | #include "Operations.h" |
9dae56ea A |
56 | #include "Parser.h" |
57 | #include "Profiler.h" | |
58 | #include "RegExpObject.h" | |
59 | #include "RegExpPrototype.h" | |
60 | #include "Register.h" | |
9dae56ea | 61 | #include "SamplingTool.h" |
14957cd0 A |
62 | #include "StrictEvalActivation.h" |
63 | #include "UStringConcatenate.h" | |
f9bf01c6 | 64 | #include <limits.h> |
9dae56ea | 65 | #include <stdio.h> |
ba379fdc | 66 | #include <wtf/Threading.h> |
9dae56ea A |
67 | |
68 | #if ENABLE(JIT) | |
69 | #include "JIT.h" | |
70 | #endif | |
71 | ||
4e4e5a6f A |
72 | #define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(COMPUTED_GOTO_INTERPRETER) && !defined(__llvm__)) |
73 | ||
9dae56ea A |
74 | using namespace std; |
75 | ||
76 | namespace JSC { | |
9dae56ea A |
77 | |
78 | // Returns the depth of the scope chain within a given call frame. | |
14957cd0 | 79 | static int depth(CodeBlock* codeBlock, ScopeChainNode* sc) |
9dae56ea A |
80 | { |
81 | if (!codeBlock->needsFullScopeChain()) | |
82 | return 0; | |
14957cd0 | 83 | return sc->localDepth(); |
9dae56ea A |
84 | } |
85 | ||
4e4e5a6f A |
86 | #if ENABLE(INTERPRETER) |
87 | static NEVER_INLINE JSValue concatenateStrings(ExecState* exec, Register* strings, unsigned count) | |
88 | { | |
89 | return jsString(exec, strings, count); | |
90 | } | |
91 | ||
ba379fdc | 92 | NEVER_INLINE bool Interpreter::resolve(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) |
9dae56ea | 93 | { |
f9bf01c6 A |
94 | int dst = vPC[1].u.operand; |
95 | int property = vPC[2].u.operand; | |
9dae56ea A |
96 | |
97 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
98 | ScopeChainIterator iter = scopeChain->begin(); | |
99 | ScopeChainIterator end = scopeChain->end(); | |
100 | ASSERT(iter != end); | |
101 | ||
102 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
103 | Identifier& ident = codeBlock->identifier(property); | |
104 | do { | |
14957cd0 | 105 | JSObject* o = iter->get(); |
9dae56ea A |
106 | PropertySlot slot(o); |
107 | if (o->getPropertySlot(callFrame, ident, slot)) { | |
ba379fdc | 108 | JSValue result = slot.getValue(callFrame, ident); |
9dae56ea A |
109 | exceptionValue = callFrame->globalData().exception; |
110 | if (exceptionValue) | |
111 | return false; | |
14957cd0 | 112 | callFrame->uncheckedR(dst) = JSValue(result); |
9dae56ea A |
113 | return true; |
114 | } | |
115 | } while (++iter != end); | |
14957cd0 | 116 | exceptionValue = createUndefinedVariableError(callFrame, ident); |
9dae56ea A |
117 | return false; |
118 | } | |
119 | ||
ba379fdc | 120 | NEVER_INLINE bool Interpreter::resolveSkip(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) |
9dae56ea A |
121 | { |
122 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
123 | ||
f9bf01c6 A |
124 | int dst = vPC[1].u.operand; |
125 | int property = vPC[2].u.operand; | |
14957cd0 | 126 | int skip = vPC[3].u.operand; |
9dae56ea A |
127 | |
128 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
129 | ScopeChainIterator iter = scopeChain->begin(); | |
130 | ScopeChainIterator end = scopeChain->end(); | |
131 | ASSERT(iter != end); | |
14957cd0 A |
132 | bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); |
133 | ASSERT(skip || !checkTopLevel); | |
134 | if (checkTopLevel && skip--) { | |
135 | if (callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) | |
136 | ++iter; | |
137 | } | |
9dae56ea A |
138 | while (skip--) { |
139 | ++iter; | |
140 | ASSERT(iter != end); | |
141 | } | |
142 | Identifier& ident = codeBlock->identifier(property); | |
143 | do { | |
14957cd0 | 144 | JSObject* o = iter->get(); |
9dae56ea A |
145 | PropertySlot slot(o); |
146 | if (o->getPropertySlot(callFrame, ident, slot)) { | |
ba379fdc | 147 | JSValue result = slot.getValue(callFrame, ident); |
9dae56ea A |
148 | exceptionValue = callFrame->globalData().exception; |
149 | if (exceptionValue) | |
150 | return false; | |
14957cd0 A |
151 | ASSERT(result); |
152 | callFrame->uncheckedR(dst) = JSValue(result); | |
9dae56ea A |
153 | return true; |
154 | } | |
155 | } while (++iter != end); | |
14957cd0 | 156 | exceptionValue = createUndefinedVariableError(callFrame, ident); |
9dae56ea A |
157 | return false; |
158 | } | |
159 | ||
ba379fdc | 160 | NEVER_INLINE bool Interpreter::resolveGlobal(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) |
9dae56ea | 161 | { |
f9bf01c6 | 162 | int dst = vPC[1].u.operand; |
14957cd0 A |
163 | CodeBlock* codeBlock = callFrame->codeBlock(); |
164 | JSGlobalObject* globalObject = codeBlock->globalObject(); | |
9dae56ea | 165 | ASSERT(globalObject->isGlobalObject()); |
14957cd0 A |
166 | int property = vPC[2].u.operand; |
167 | Structure* structure = vPC[3].u.structure.get(); | |
168 | int offset = vPC[4].u.operand; | |
9dae56ea A |
169 | |
170 | if (structure == globalObject->structure()) { | |
14957cd0 | 171 | callFrame->uncheckedR(dst) = JSValue(globalObject->getDirectOffset(offset)); |
9dae56ea A |
172 | return true; |
173 | } | |
174 | ||
9dae56ea A |
175 | Identifier& ident = codeBlock->identifier(property); |
176 | PropertySlot slot(globalObject); | |
177 | if (globalObject->getPropertySlot(callFrame, ident, slot)) { | |
ba379fdc | 178 | JSValue result = slot.getValue(callFrame, ident); |
4e4e5a6f | 179 | if (slot.isCacheableValue() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) { |
14957cd0 A |
180 | vPC[3].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), globalObject->structure()); |
181 | vPC[4] = slot.cachedOffset(); | |
182 | callFrame->uncheckedR(dst) = JSValue(result); | |
9dae56ea A |
183 | return true; |
184 | } | |
185 | ||
186 | exceptionValue = callFrame->globalData().exception; | |
187 | if (exceptionValue) | |
188 | return false; | |
14957cd0 | 189 | callFrame->uncheckedR(dst) = JSValue(result); |
9dae56ea A |
190 | return true; |
191 | } | |
192 | ||
14957cd0 | 193 | exceptionValue = createUndefinedVariableError(callFrame, ident); |
9dae56ea A |
194 | return false; |
195 | } | |
196 | ||
4e4e5a6f A |
197 | NEVER_INLINE bool Interpreter::resolveGlobalDynamic(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) |
198 | { | |
199 | int dst = vPC[1].u.operand; | |
4e4e5a6f | 200 | CodeBlock* codeBlock = callFrame->codeBlock(); |
14957cd0 A |
201 | JSGlobalObject* globalObject = codeBlock->globalObject(); |
202 | ASSERT(globalObject->isGlobalObject()); | |
203 | int property = vPC[2].u.operand; | |
204 | Structure* structure = vPC[3].u.structure.get(); | |
205 | int offset = vPC[4].u.operand; | |
206 | int skip = vPC[5].u.operand; | |
4e4e5a6f A |
207 | |
208 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
209 | ScopeChainIterator iter = scopeChain->begin(); | |
210 | ScopeChainIterator end = scopeChain->end(); | |
211 | ASSERT(iter != end); | |
14957cd0 A |
212 | bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); |
213 | ASSERT(skip || !checkTopLevel); | |
214 | if (checkTopLevel && skip--) { | |
215 | if (callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) | |
216 | ++iter; | |
217 | } | |
4e4e5a6f | 218 | while (skip--) { |
14957cd0 | 219 | JSObject* o = iter->get(); |
4e4e5a6f A |
220 | if (o->hasCustomProperties()) { |
221 | Identifier& ident = codeBlock->identifier(property); | |
222 | do { | |
223 | PropertySlot slot(o); | |
224 | if (o->getPropertySlot(callFrame, ident, slot)) { | |
225 | JSValue result = slot.getValue(callFrame, ident); | |
226 | exceptionValue = callFrame->globalData().exception; | |
227 | if (exceptionValue) | |
228 | return false; | |
14957cd0 A |
229 | ASSERT(result); |
230 | callFrame->uncheckedR(dst) = JSValue(result); | |
4e4e5a6f A |
231 | return true; |
232 | } | |
233 | if (iter == end) | |
234 | break; | |
14957cd0 | 235 | o = iter->get(); |
4e4e5a6f A |
236 | ++iter; |
237 | } while (true); | |
14957cd0 | 238 | exceptionValue = createUndefinedVariableError(callFrame, ident); |
4e4e5a6f A |
239 | return false; |
240 | } | |
241 | ++iter; | |
242 | } | |
243 | ||
244 | if (structure == globalObject->structure()) { | |
14957cd0 A |
245 | callFrame->uncheckedR(dst) = JSValue(globalObject->getDirectOffset(offset)); |
246 | ASSERT(callFrame->uncheckedR(dst).jsValue()); | |
4e4e5a6f A |
247 | return true; |
248 | } | |
249 | ||
250 | Identifier& ident = codeBlock->identifier(property); | |
251 | PropertySlot slot(globalObject); | |
252 | if (globalObject->getPropertySlot(callFrame, ident, slot)) { | |
253 | JSValue result = slot.getValue(callFrame, ident); | |
254 | if (slot.isCacheableValue() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) { | |
14957cd0 A |
255 | vPC[3].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), globalObject->structure()); |
256 | vPC[4] = slot.cachedOffset(); | |
257 | ASSERT(result); | |
258 | callFrame->uncheckedR(dst) = JSValue(result); | |
4e4e5a6f A |
259 | return true; |
260 | } | |
261 | ||
262 | exceptionValue = callFrame->globalData().exception; | |
263 | if (exceptionValue) | |
264 | return false; | |
14957cd0 A |
265 | ASSERT(result); |
266 | callFrame->uncheckedR(dst) = JSValue(result); | |
4e4e5a6f A |
267 | return true; |
268 | } | |
269 | ||
14957cd0 | 270 | exceptionValue = createUndefinedVariableError(callFrame, ident); |
4e4e5a6f A |
271 | return false; |
272 | } | |
273 | ||
9dae56ea A |
274 | NEVER_INLINE void Interpreter::resolveBase(CallFrame* callFrame, Instruction* vPC) |
275 | { | |
f9bf01c6 A |
276 | int dst = vPC[1].u.operand; |
277 | int property = vPC[2].u.operand; | |
14957cd0 A |
278 | bool isStrictPut = vPC[3].u.operand; |
279 | Identifier ident = callFrame->codeBlock()->identifier(property); | |
280 | JSValue result = JSC::resolveBase(callFrame, ident, callFrame->scopeChain(), isStrictPut); | |
281 | if (result) { | |
282 | callFrame->uncheckedR(dst) = result; | |
283 | ASSERT(callFrame->uncheckedR(dst).jsValue()); | |
284 | } else | |
285 | callFrame->globalData().exception = createErrorForInvalidGlobalAssignment(callFrame, ident.ustring()); | |
9dae56ea A |
286 | } |
287 | ||
ba379fdc | 288 | NEVER_INLINE bool Interpreter::resolveBaseAndProperty(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) |
9dae56ea | 289 | { |
f9bf01c6 A |
290 | int baseDst = vPC[1].u.operand; |
291 | int propDst = vPC[2].u.operand; | |
292 | int property = vPC[3].u.operand; | |
9dae56ea A |
293 | |
294 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
295 | ScopeChainIterator iter = scopeChain->begin(); | |
296 | ScopeChainIterator end = scopeChain->end(); | |
297 | ||
298 | // FIXME: add scopeDepthIsZero optimization | |
299 | ||
300 | ASSERT(iter != end); | |
301 | ||
302 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
303 | Identifier& ident = codeBlock->identifier(property); | |
304 | JSObject* base; | |
305 | do { | |
14957cd0 | 306 | base = iter->get(); |
9dae56ea A |
307 | PropertySlot slot(base); |
308 | if (base->getPropertySlot(callFrame, ident, slot)) { | |
ba379fdc | 309 | JSValue result = slot.getValue(callFrame, ident); |
9dae56ea A |
310 | exceptionValue = callFrame->globalData().exception; |
311 | if (exceptionValue) | |
312 | return false; | |
14957cd0 A |
313 | callFrame->uncheckedR(propDst) = JSValue(result); |
314 | callFrame->uncheckedR(baseDst) = JSValue(base); | |
9dae56ea A |
315 | return true; |
316 | } | |
317 | ++iter; | |
318 | } while (iter != end); | |
319 | ||
14957cd0 | 320 | exceptionValue = createUndefinedVariableError(callFrame, ident); |
9dae56ea A |
321 | return false; |
322 | } | |
323 | ||
4e4e5a6f | 324 | #endif // ENABLE(INTERPRETER) |
ba379fdc | 325 | |
9dae56ea A |
326 | ALWAYS_INLINE CallFrame* Interpreter::slideRegisterWindowForCall(CodeBlock* newCodeBlock, RegisterFile* registerFile, CallFrame* callFrame, size_t registerOffset, int argc) |
327 | { | |
328 | Register* r = callFrame->registers(); | |
329 | Register* newEnd = r + registerOffset + newCodeBlock->m_numCalleeRegisters; | |
330 | ||
331 | if (LIKELY(argc == newCodeBlock->m_numParameters)) { // correct number of arguments | |
332 | if (UNLIKELY(!registerFile->grow(newEnd))) | |
333 | return 0; | |
334 | r += registerOffset; | |
335 | } else if (argc < newCodeBlock->m_numParameters) { // too few arguments -- fill in the blanks | |
336 | size_t omittedArgCount = newCodeBlock->m_numParameters - argc; | |
337 | registerOffset += omittedArgCount; | |
338 | newEnd += omittedArgCount; | |
339 | if (!registerFile->grow(newEnd)) | |
340 | return 0; | |
341 | r += registerOffset; | |
342 | ||
343 | Register* argv = r - RegisterFile::CallFrameHeaderSize - omittedArgCount; | |
344 | for (size_t i = 0; i < omittedArgCount; ++i) | |
345 | argv[i] = jsUndefined(); | |
346 | } else { // too many arguments -- copy expected arguments, leaving the extra arguments behind | |
347 | size_t numParameters = newCodeBlock->m_numParameters; | |
348 | registerOffset += numParameters; | |
349 | newEnd += numParameters; | |
350 | ||
351 | if (!registerFile->grow(newEnd)) | |
352 | return 0; | |
353 | r += registerOffset; | |
354 | ||
355 | Register* argv = r - RegisterFile::CallFrameHeaderSize - numParameters - argc; | |
356 | for (size_t i = 0; i < numParameters; ++i) | |
357 | argv[i + argc] = argv[i]; | |
358 | } | |
359 | ||
360 | return CallFrame::create(r); | |
361 | } | |
362 | ||
4e4e5a6f | 363 | #if ENABLE(INTERPRETER) |
14957cd0 | 364 | static NEVER_INLINE bool isInvalidParamForIn(CallFrame* callFrame, JSValue value, JSValue& exceptionData) |
9dae56ea A |
365 | { |
366 | if (value.isObject()) | |
367 | return false; | |
14957cd0 | 368 | exceptionData = createInvalidParamError(callFrame, "in" , value); |
9dae56ea A |
369 | return true; |
370 | } | |
371 | ||
14957cd0 | 372 | static NEVER_INLINE bool isInvalidParamForInstanceOf(CallFrame* callFrame, JSValue value, JSValue& exceptionData) |
ba379fdc A |
373 | { |
374 | if (value.isObject() && asObject(value)->structure()->typeInfo().implementsHasInstance()) | |
375 | return false; | |
14957cd0 | 376 | exceptionData = createInvalidParamError(callFrame, "instanceof" , value); |
ba379fdc A |
377 | return true; |
378 | } | |
379 | #endif | |
380 | ||
14957cd0 | 381 | NEVER_INLINE JSValue Interpreter::callEval(CallFrame* callFrame, RegisterFile* registerFile, Register* argv, int argc, int registerOffset) |
9dae56ea A |
382 | { |
383 | if (argc < 2) | |
384 | return jsUndefined(); | |
385 | ||
ba379fdc | 386 | JSValue program = argv[1].jsValue(); |
9dae56ea A |
387 | |
388 | if (!program.isString()) | |
389 | return program; | |
390 | ||
f9bf01c6 | 391 | UString programSource = asString(program)->value(callFrame); |
4e4e5a6f A |
392 | if (callFrame->hadException()) |
393 | return JSValue(); | |
14957cd0 A |
394 | |
395 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
396 | if (!codeBlock->isStrictMode()) { | |
397 | // FIXME: We can use the preparser in strict mode, we just need additional logic | |
398 | // to prevent duplicates. | |
399 | LiteralParser preparser(callFrame, programSource.characters(), programSource.length(), LiteralParser::NonStrictJSON); | |
400 | if (JSValue parsedObject = preparser.tryLiteralParse()) | |
401 | return parsedObject; | |
402 | } | |
f9bf01c6 | 403 | |
9dae56ea | 404 | ScopeChainNode* scopeChain = callFrame->scopeChain(); |
14957cd0 A |
405 | JSValue exceptionValue; |
406 | EvalExecutable* eval = codeBlock->evalCodeCache().get(callFrame, codeBlock->ownerExecutable(), codeBlock->isStrictMode(), programSource, scopeChain, exceptionValue); | |
9dae56ea | 407 | |
14957cd0 A |
408 | ASSERT(!eval == exceptionValue); |
409 | if (UNLIKELY(!eval)) | |
410 | return throwError(callFrame, exceptionValue); | |
9dae56ea | 411 | |
14957cd0 | 412 | return callFrame->globalData().interpreter->execute(eval, callFrame, callFrame->uncheckedR(codeBlock->thisRegister()).jsValue().toThisObject(callFrame), callFrame->registers() - registerFile->start() + registerOffset, scopeChain); |
9dae56ea A |
413 | } |
414 | ||
14957cd0 | 415 | Interpreter::Interpreter(JSGlobalData& globalData) |
f9bf01c6 | 416 | : m_sampleEntryDepth(0) |
9dae56ea | 417 | , m_reentryDepth(0) |
14957cd0 | 418 | , m_registerFile(globalData) |
9dae56ea | 419 | { |
4e4e5a6f | 420 | #if ENABLE(COMPUTED_GOTO_INTERPRETER) |
14957cd0 | 421 | privateExecute(InitializeAndReturn, 0, 0); |
9dae56ea | 422 | |
ba379fdc A |
423 | for (int i = 0; i < numOpcodeIDs; ++i) |
424 | m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i)); | |
4e4e5a6f | 425 | #endif // ENABLE(COMPUTED_GOTO_INTERPRETER) |
f9bf01c6 A |
426 | |
427 | #if ENABLE(OPCODE_SAMPLING) | |
428 | enableSampler(); | |
429 | #endif | |
9dae56ea A |
430 | } |
431 | ||
432 | #ifndef NDEBUG | |
433 | ||
434 | void Interpreter::dumpCallFrame(CallFrame* callFrame) | |
435 | { | |
436 | callFrame->codeBlock()->dump(callFrame); | |
437 | dumpRegisters(callFrame); | |
438 | } | |
439 | ||
440 | void Interpreter::dumpRegisters(CallFrame* callFrame) | |
441 | { | |
442 | printf("Register frame: \n\n"); | |
ba379fdc A |
443 | printf("-----------------------------------------------------------------------------\n"); |
444 | printf(" use | address | value \n"); | |
445 | printf("-----------------------------------------------------------------------------\n"); | |
9dae56ea A |
446 | |
447 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
14957cd0 | 448 | RegisterFile* registerFile = &callFrame->scopeChain()->globalObject->globalData().interpreter->registerFile(); |
9dae56ea A |
449 | const Register* it; |
450 | const Register* end; | |
ba379fdc | 451 | JSValue v; |
9dae56ea A |
452 | |
453 | if (codeBlock->codeType() == GlobalCode) { | |
454 | it = registerFile->lastGlobal(); | |
455 | end = it + registerFile->numGlobals(); | |
456 | while (it != end) { | |
ba379fdc A |
457 | v = (*it).jsValue(); |
458 | #if USE(JSVALUE32_64) | |
459 | printf("[global var] | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v)); | |
460 | #else | |
461 | printf("[global var] | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v)); | |
462 | #endif | |
9dae56ea A |
463 | ++it; |
464 | } | |
ba379fdc | 465 | printf("-----------------------------------------------------------------------------\n"); |
9dae56ea A |
466 | } |
467 | ||
468 | it = callFrame->registers() - RegisterFile::CallFrameHeaderSize - codeBlock->m_numParameters; | |
ba379fdc A |
469 | v = (*it).jsValue(); |
470 | #if USE(JSVALUE32_64) | |
471 | printf("[this] | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v)); ++it; | |
472 | #else | |
473 | printf("[this] | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v)); ++it; | |
474 | #endif | |
9dae56ea A |
475 | end = it + max(codeBlock->m_numParameters - 1, 0); // - 1 to skip "this" |
476 | if (it != end) { | |
477 | do { | |
ba379fdc A |
478 | v = (*it).jsValue(); |
479 | #if USE(JSVALUE32_64) | |
480 | printf("[param] | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v)); | |
481 | #else | |
482 | printf("[param] | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v)); | |
483 | #endif | |
9dae56ea A |
484 | ++it; |
485 | } while (it != end); | |
486 | } | |
ba379fdc A |
487 | printf("-----------------------------------------------------------------------------\n"); |
488 | printf("[CodeBlock] | %10p | %p \n", it, (*it).codeBlock()); ++it; | |
489 | printf("[ScopeChain] | %10p | %p \n", it, (*it).scopeChain()); ++it; | |
490 | printf("[CallerRegisters] | %10p | %d \n", it, (*it).i()); ++it; | |
491 | printf("[ReturnPC] | %10p | %p \n", it, (*it).vPC()); ++it; | |
ba379fdc A |
492 | printf("[ArgumentCount] | %10p | %d \n", it, (*it).i()); ++it; |
493 | printf("[Callee] | %10p | %p \n", it, (*it).function()); ++it; | |
ba379fdc | 494 | printf("-----------------------------------------------------------------------------\n"); |
9dae56ea A |
495 | |
496 | int registerCount = 0; | |
497 | ||
498 | end = it + codeBlock->m_numVars; | |
499 | if (it != end) { | |
500 | do { | |
ba379fdc A |
501 | v = (*it).jsValue(); |
502 | #if USE(JSVALUE32_64) | |
503 | printf("[r%2d] | %10p | %-16s 0x%llx \n", registerCount, it, v.description(), JSValue::encode(v)); | |
504 | #else | |
505 | printf("[r%2d] | %10p | %-16s %p \n", registerCount, it, v.description(), JSValue::encode(v)); | |
506 | #endif | |
9dae56ea A |
507 | ++it; |
508 | ++registerCount; | |
509 | } while (it != end); | |
510 | } | |
ba379fdc | 511 | printf("-----------------------------------------------------------------------------\n"); |
9dae56ea | 512 | |
ba379fdc | 513 | end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numVars; |
9dae56ea A |
514 | if (it != end) { |
515 | do { | |
ba379fdc A |
516 | v = (*it).jsValue(); |
517 | #if USE(JSVALUE32_64) | |
518 | printf("[r%2d] | %10p | %-16s 0x%llx \n", registerCount, it, v.description(), JSValue::encode(v)); | |
519 | #else | |
520 | printf("[r%2d] | %10p | %-16s %p \n", registerCount, it, v.description(), JSValue::encode(v)); | |
521 | #endif | |
9dae56ea A |
522 | ++it; |
523 | ++registerCount; | |
524 | } while (it != end); | |
525 | } | |
ba379fdc | 526 | printf("-----------------------------------------------------------------------------\n"); |
9dae56ea A |
527 | } |
528 | ||
529 | #endif | |
530 | ||
531 | bool Interpreter::isOpcode(Opcode opcode) | |
532 | { | |
4e4e5a6f | 533 | #if ENABLE(COMPUTED_GOTO_INTERPRETER) |
9dae56ea A |
534 | return opcode != HashTraits<Opcode>::emptyValue() |
535 | && !HashTraits<Opcode>::isDeletedValue(opcode) | |
536 | && m_opcodeIDTable.contains(opcode); | |
537 | #else | |
538 | return opcode >= 0 && opcode <= op_end; | |
539 | #endif | |
540 | } | |
541 | ||
ba379fdc | 542 | NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValue exceptionValue, unsigned& bytecodeOffset, CodeBlock*& codeBlock) |
9dae56ea A |
543 | { |
544 | CodeBlock* oldCodeBlock = codeBlock; | |
545 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
546 | ||
547 | if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { | |
548 | DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); | |
549 | if (callFrame->callee()) | |
f9bf01c6 | 550 | debugger->returnEvent(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine()); |
9dae56ea | 551 | else |
f9bf01c6 | 552 | debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine()); |
9dae56ea A |
553 | } |
554 | ||
9dae56ea A |
555 | // If this call frame created an activation or an 'arguments' object, tear it off. |
556 | if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsFullScopeChain()) { | |
14957cd0 A |
557 | if (!callFrame->uncheckedR(oldCodeBlock->activationRegister()).jsValue()) { |
558 | oldCodeBlock->createActivation(callFrame); | |
559 | scopeChain = callFrame->scopeChain(); | |
560 | } | |
561 | while (!scopeChain->object->inherits(&JSActivation::s_info)) | |
9dae56ea | 562 | scopeChain = scopeChain->pop(); |
9dae56ea | 563 | |
14957cd0 A |
564 | callFrame->setScopeChain(scopeChain); |
565 | JSActivation* activation = asActivation(scopeChain->object.get()); | |
566 | activation->copyRegisters(*scopeChain->globalData); | |
567 | if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) { | |
568 | if (!oldCodeBlock->isStrictMode()) | |
569 | asArguments(arguments)->setActivation(callFrame->globalData(), activation); | |
570 | } | |
571 | } else if (oldCodeBlock->usesArguments() && !oldCodeBlock->isStrictMode()) { | |
572 | if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) | |
573 | asArguments(arguments)->copyRegisters(callFrame->globalData()); | |
574 | } | |
9dae56ea | 575 | |
14957cd0 | 576 | CallFrame* callerFrame = callFrame->callerFrame(); |
4e4e5a6f | 577 | if (callerFrame->hasHostCallFrameFlag()) |
9dae56ea A |
578 | return false; |
579 | ||
4e4e5a6f | 580 | codeBlock = callerFrame->codeBlock(); |
14957cd0 A |
581 | |
582 | // Because of how the JIT records call site->bytecode offset | |
583 | // information the JIT reports the bytecodeOffset for the returnPC | |
584 | // to be at the beginning of the opcode that has caused the call. | |
585 | // In the interpreter we have an actual return address, which is | |
586 | // the beginning of next instruction to execute. To get an offset | |
587 | // inside the call instruction that triggered the exception we | |
588 | // have to subtract 1. | |
589 | #if ENABLE(JIT) && ENABLE(INTERPRETER) | |
4e4e5a6f | 590 | if (callerFrame->globalData().canUseJIT()) |
14957cd0 | 591 | bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnPC()); |
4e4e5a6f | 592 | else |
14957cd0 A |
593 | bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnVPC()) - 1; |
594 | #elif ENABLE(JIT) | |
595 | bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnPC()); | |
4e4e5a6f | 596 | #else |
14957cd0 | 597 | bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnVPC()) - 1; |
4e4e5a6f | 598 | #endif |
14957cd0 | 599 | |
4e4e5a6f | 600 | callFrame = callerFrame; |
9dae56ea A |
601 | return true; |
602 | } | |
603 | ||
14957cd0 | 604 | static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, unsigned bytecodeOffset) |
9dae56ea | 605 | { |
14957cd0 A |
606 | exception->clearAppendSourceToMessage(); |
607 | ||
608 | if (!callFrame->codeBlock()->hasExpressionInfo()) | |
609 | return; | |
610 | ||
611 | int startOffset = 0; | |
612 | int endOffset = 0; | |
613 | int divotPoint = 0; | |
614 | ||
615 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
616 | codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset); | |
617 | ||
618 | int expressionStart = divotPoint - startOffset; | |
619 | int expressionStop = divotPoint + endOffset; | |
620 | ||
621 | if (!expressionStop || expressionStart > codeBlock->source()->length()) | |
622 | return; | |
623 | ||
624 | JSGlobalData* globalData = &callFrame->globalData(); | |
625 | JSValue jsMessage = exception->getDirect(*globalData, globalData->propertyNames->message); | |
626 | if (!jsMessage || !jsMessage.isString()) | |
627 | return; | |
628 | ||
629 | UString message = asString(jsMessage)->value(callFrame); | |
630 | ||
631 | if (expressionStart < expressionStop) | |
632 | message = makeUString(message, " (evaluating '", codeBlock->source()->getRange(expressionStart, expressionStop), "')"); | |
633 | else { | |
634 | // No range information, so give a few characters of context | |
635 | const UChar* data = codeBlock->source()->data(); | |
636 | int dataLength = codeBlock->source()->length(); | |
637 | int start = expressionStart; | |
638 | int stop = expressionStart; | |
639 | // Get up to 20 characters of context to the left and right of the divot, clamping to the line. | |
640 | // then strip whitespace. | |
641 | while (start > 0 && (expressionStart - start < 20) && data[start - 1] != '\n') | |
642 | start--; | |
643 | while (start < (expressionStart - 1) && isStrWhiteSpace(data[start])) | |
644 | start++; | |
645 | while (stop < dataLength && (stop - expressionStart < 20) && data[stop] != '\n') | |
646 | stop++; | |
647 | while (stop > expressionStart && isStrWhiteSpace(data[stop - 1])) | |
648 | stop--; | |
649 | message = makeUString(message, " (near '...", codeBlock->source()->getRange(start, stop), "...')"); | |
650 | } | |
651 | ||
652 | exception->putDirect(*globalData, globalData->propertyNames->message, jsString(globalData, message)); | |
653 | } | |
9dae56ea | 654 | |
14957cd0 A |
655 | NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset) |
656 | { | |
9dae56ea | 657 | CodeBlock* codeBlock = callFrame->codeBlock(); |
14957cd0 A |
658 | bool isInterrupt = false; |
659 | ||
660 | // Set up the exception object | |
9dae56ea A |
661 | if (exceptionValue.isObject()) { |
662 | JSObject* exception = asObject(exceptionValue); | |
4e4e5a6f | 663 | |
14957cd0 A |
664 | if (exception->isErrorInstance() && static_cast<ErrorInstance*>(exception)->appendSourceToMessage()) |
665 | appendSourceToError(callFrame, static_cast<ErrorInstance*>(exception), bytecodeOffset); | |
666 | ||
667 | // Using hasExpressionInfo to imply we are interested in rich exception info. | |
668 | if (codeBlock->hasExpressionInfo() && !hasErrorInfo(callFrame, exception)) { | |
669 | ASSERT(codeBlock->hasLineInfo()); | |
670 | ||
671 | // FIXME: should only really be adding these properties to VM generated exceptions, | |
672 | // but the inspector currently requires these for all thrown objects. | |
673 | addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source()); | |
9dae56ea | 674 | } |
14957cd0 A |
675 | |
676 | ComplType exceptionType = exception->exceptionType(); | |
677 | isInterrupt = exceptionType == Interrupted || exceptionType == Terminated; | |
9dae56ea A |
678 | } |
679 | ||
680 | if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { | |
681 | DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); | |
f9bf01c6 | 682 | bool hasHandler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); |
14957cd0 | 683 | debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), hasHandler); |
9dae56ea A |
684 | } |
685 | ||
686 | // Calculate an exception handler vPC, unwinding call frames as necessary. | |
9dae56ea | 687 | HandlerInfo* handler = 0; |
14957cd0 A |
688 | while (isInterrupt || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { |
689 | if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) { | |
690 | if (Profiler* profiler = *Profiler::enabledProfilerReference()) | |
691 | profiler->exceptionUnwind(callFrame); | |
9dae56ea | 692 | return 0; |
14957cd0 | 693 | } |
9dae56ea A |
694 | } |
695 | ||
14957cd0 A |
696 | if (Profiler* profiler = *Profiler::enabledProfilerReference()) |
697 | profiler->exceptionUnwind(callFrame); | |
9dae56ea | 698 | |
14957cd0 A |
699 | // Shrink the JS stack, in case stack overflow made it huge. |
700 | Register* highWaterMark = 0; | |
701 | for (CallFrame* callerFrame = callFrame; callerFrame; callerFrame = callerFrame->callerFrame()->removeHostCallFrameFlag()) { | |
702 | CodeBlock* codeBlock = callerFrame->codeBlock(); | |
703 | if (!codeBlock) | |
704 | continue; | |
705 | Register* callerHighWaterMark = callerFrame->registers() + codeBlock->m_numCalleeRegisters; | |
706 | highWaterMark = max(highWaterMark, callerHighWaterMark); | |
707 | } | |
708 | m_registerFile.shrink(highWaterMark); | |
709 | ||
710 | // Unwind the scope chain within the exception handler's call frame. | |
9dae56ea | 711 | ScopeChainNode* scopeChain = callFrame->scopeChain(); |
14957cd0 A |
712 | int scopeDelta = 0; |
713 | if (!codeBlock->needsFullScopeChain() || codeBlock->codeType() != FunctionCode | |
714 | || callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) | |
715 | scopeDelta = depth(codeBlock, scopeChain) - handler->scopeDepth; | |
9dae56ea A |
716 | ASSERT(scopeDelta >= 0); |
717 | while (scopeDelta--) | |
718 | scopeChain = scopeChain->pop(); | |
719 | callFrame->setScopeChain(scopeChain); | |
720 | ||
721 | return handler; | |
722 | } | |
723 | ||
14957cd0 A |
724 | static inline JSValue checkedReturn(JSValue returnValue) |
725 | { | |
726 | ASSERT(returnValue); | |
727 | return returnValue; | |
728 | } | |
729 | ||
730 | static inline JSObject* checkedReturn(JSObject* returnValue) | |
731 | { | |
732 | ASSERT(returnValue); | |
733 | return returnValue; | |
734 | } | |
735 | ||
736 | JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, ScopeChainNode* scopeChain, JSObject* thisObj) | |
9dae56ea A |
737 | { |
738 | ASSERT(!scopeChain->globalData->exception); | |
14957cd0 A |
739 | ASSERT(!callFrame->globalData().isCollectorBusy()); |
740 | if (callFrame->globalData().isCollectorBusy()) | |
741 | return jsNull(); | |
9dae56ea | 742 | |
14957cd0 A |
743 | if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) |
744 | return checkedReturn(throwStackOverflowError(callFrame)); | |
745 | ||
746 | DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); | |
747 | LiteralParser literalParser(callFrame, program->source().data(), program->source().length(), LiteralParser::JSONP); | |
748 | Vector<LiteralParser::JSONPData> JSONPData; | |
749 | if (literalParser.tryJSONPParse(JSONPData, scopeChain->globalObject->supportsRichSourceInfo())) { | |
750 | JSGlobalObject* globalObject = scopeChain->globalObject.get(); | |
751 | JSValue result; | |
752 | for (unsigned entry = 0; entry < JSONPData.size(); entry++) { | |
753 | Vector<LiteralParser::JSONPPathEntry> JSONPPath; | |
754 | JSONPPath.swap(JSONPData[entry].m_path); | |
755 | JSValue JSONPValue = JSONPData[entry].m_value.get(); | |
756 | if (JSONPPath.size() == 1 && JSONPPath[0].m_type == LiteralParser::JSONPPathEntryTypeDeclare) { | |
757 | if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) { | |
758 | PutPropertySlot slot; | |
759 | globalObject->put(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot); | |
760 | } else | |
761 | globalObject->putWithAttributes(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete); | |
762 | // var declarations return undefined | |
763 | result = jsUndefined(); | |
764 | continue; | |
765 | } | |
766 | JSValue baseObject(globalObject); | |
767 | for (unsigned i = 0; i < JSONPPath.size() - 1; i++) { | |
768 | ASSERT(JSONPPath[i].m_type != LiteralParser::JSONPPathEntryTypeDeclare); | |
769 | switch (JSONPPath[i].m_type) { | |
770 | case LiteralParser::JSONPPathEntryTypeDot: { | |
771 | if (i == 0) { | |
772 | PropertySlot slot(globalObject); | |
773 | if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) { | |
774 | if (entry) | |
775 | return throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName)); | |
776 | goto failedJSONP; | |
777 | } | |
778 | baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName); | |
779 | } else | |
780 | baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName); | |
781 | if (callFrame->hadException()) | |
782 | return jsUndefined(); | |
783 | continue; | |
784 | } | |
785 | case LiteralParser::JSONPPathEntryTypeLookup: { | |
786 | baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex); | |
787 | if (callFrame->hadException()) | |
788 | return jsUndefined(); | |
789 | continue; | |
790 | } | |
791 | default: | |
792 | ASSERT_NOT_REACHED(); | |
793 | return jsUndefined(); | |
794 | } | |
795 | } | |
796 | PutPropertySlot slot; | |
797 | switch (JSONPPath.last().m_type) { | |
798 | case LiteralParser::JSONPPathEntryTypeCall: { | |
799 | JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName); | |
800 | if (callFrame->hadException()) | |
801 | return jsUndefined(); | |
802 | CallData callData; | |
803 | CallType callType = getCallData(function, callData); | |
804 | if (callType == CallTypeNone) | |
805 | return throwError(callFrame, createNotAFunctionError(callFrame, function)); | |
806 | MarkedArgumentBuffer jsonArg; | |
807 | jsonArg.append(JSONPValue); | |
808 | JSValue thisValue = JSONPPath.size() == 1 ? jsUndefined(): baseObject; | |
809 | JSONPValue = JSC::call(callFrame, function, callType, callData, thisValue, jsonArg); | |
810 | if (callFrame->hadException()) | |
811 | return jsUndefined(); | |
812 | break; | |
813 | } | |
814 | case LiteralParser::JSONPPathEntryTypeDot: { | |
815 | baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot); | |
816 | if (callFrame->hadException()) | |
817 | return jsUndefined(); | |
818 | break; | |
819 | } | |
820 | case LiteralParser::JSONPPathEntryTypeLookup: { | |
821 | baseObject.put(callFrame, JSONPPath.last().m_pathIndex, JSONPValue); | |
822 | if (callFrame->hadException()) | |
823 | return jsUndefined(); | |
824 | break; | |
825 | } | |
826 | default: | |
827 | ASSERT_NOT_REACHED(); | |
828 | return jsUndefined(); | |
829 | } | |
830 | result = JSONPValue; | |
ba379fdc | 831 | } |
14957cd0 | 832 | return result; |
9dae56ea | 833 | } |
14957cd0 A |
834 | failedJSONP: |
835 | JSObject* error = program->compile(callFrame, scopeChain); | |
836 | if (error) | |
837 | return checkedReturn(throwError(callFrame, error)); | |
838 | CodeBlock* codeBlock = &program->generatedBytecode(); | |
9dae56ea A |
839 | |
840 | Register* oldEnd = m_registerFile.end(); | |
841 | Register* newEnd = oldEnd + codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize + codeBlock->m_numCalleeRegisters; | |
14957cd0 A |
842 | if (!m_registerFile.grow(newEnd)) |
843 | return checkedReturn(throwStackOverflowError(callFrame)); | |
9dae56ea A |
844 | |
845 | JSGlobalObject* lastGlobalObject = m_registerFile.globalObject(); | |
846 | JSGlobalObject* globalObject = callFrame->dynamicGlobalObject(); | |
847 | globalObject->copyGlobalsTo(m_registerFile); | |
848 | ||
849 | CallFrame* newCallFrame = CallFrame::create(oldEnd + codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize); | |
14957cd0 A |
850 | ASSERT(codeBlock->m_numParameters == 1); // 1 parameter for 'this'. |
851 | newCallFrame->init(codeBlock, 0, scopeChain, CallFrame::noCaller(), codeBlock->m_numParameters, 0); | |
852 | newCallFrame->uncheckedR(newCallFrame->hostThisRegister()) = JSValue(thisObj); | |
9dae56ea A |
853 | |
854 | Profiler** profiler = Profiler::enabledProfilerReference(); | |
855 | if (*profiler) | |
14957cd0 | 856 | (*profiler)->willExecute(callFrame, program->sourceURL(), program->lineNo()); |
9dae56ea | 857 | |
ba379fdc | 858 | JSValue result; |
9dae56ea | 859 | { |
f9bf01c6 | 860 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
9dae56ea | 861 | |
4e4e5a6f | 862 | m_reentryDepth++; |
9dae56ea | 863 | #if ENABLE(JIT) |
4e4e5a6f | 864 | if (callFrame->globalData().canUseJIT()) |
14957cd0 | 865 | result = program->generatedJITCode().execute(&m_registerFile, newCallFrame, scopeChain->globalData); |
4e4e5a6f A |
866 | else |
867 | #endif | |
14957cd0 | 868 | result = privateExecute(Normal, &m_registerFile, newCallFrame); |
4e4e5a6f | 869 | |
9dae56ea A |
870 | m_reentryDepth--; |
871 | } | |
872 | ||
873 | if (*profiler) | |
f9bf01c6 | 874 | (*profiler)->didExecute(callFrame, program->sourceURL(), program->lineNo()); |
9dae56ea A |
875 | |
876 | if (m_reentryDepth && lastGlobalObject && globalObject != lastGlobalObject) | |
877 | lastGlobalObject->copyGlobalsTo(m_registerFile); | |
878 | ||
879 | m_registerFile.shrink(oldEnd); | |
880 | ||
14957cd0 | 881 | return checkedReturn(result); |
9dae56ea A |
882 | } |
883 | ||
14957cd0 | 884 | JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) |
9dae56ea | 885 | { |
14957cd0 A |
886 | ASSERT(!callFrame->hadException()); |
887 | ASSERT(!callFrame->globalData().isCollectorBusy()); | |
888 | if (callFrame->globalData().isCollectorBusy()) | |
889 | return jsNull(); | |
9dae56ea | 890 | |
14957cd0 A |
891 | if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) |
892 | return checkedReturn(throwStackOverflowError(callFrame)); | |
9dae56ea A |
893 | |
894 | Register* oldEnd = m_registerFile.end(); | |
14957cd0 A |
895 | int argCount = 1 + args.size(); // implicit "this" parameter |
896 | size_t registerOffset = argCount + RegisterFile::CallFrameHeaderSize; | |
9dae56ea | 897 | |
14957cd0 A |
898 | if (!m_registerFile.grow(oldEnd + registerOffset)) |
899 | return checkedReturn(throwStackOverflowError(callFrame)); | |
9dae56ea A |
900 | |
901 | CallFrame* newCallFrame = CallFrame::create(oldEnd); | |
902 | size_t dst = 0; | |
14957cd0 | 903 | newCallFrame->uncheckedR(0) = thisValue; |
9dae56ea A |
904 | ArgList::const_iterator end = args.end(); |
905 | for (ArgList::const_iterator it = args.begin(); it != end; ++it) | |
14957cd0 A |
906 | newCallFrame->uncheckedR(++dst) = *it; |
907 | ||
908 | if (callType == CallTypeJS) { | |
909 | ScopeChainNode* callDataScopeChain = callData.js.scopeChain; | |
910 | ||
911 | DynamicGlobalObjectScope globalObjectScope(*callDataScopeChain->globalData, callDataScopeChain->globalObject.get()); | |
912 | ||
913 | JSObject* compileError = callData.js.functionExecutable->compileForCall(callFrame, callDataScopeChain); | |
914 | if (UNLIKELY(!!compileError)) { | |
915 | m_registerFile.shrink(oldEnd); | |
916 | return checkedReturn(throwError(callFrame, compileError)); | |
917 | } | |
918 | ||
919 | CodeBlock* newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); | |
920 | newCallFrame = slideRegisterWindowForCall(newCodeBlock, &m_registerFile, newCallFrame, registerOffset, argCount); | |
921 | if (UNLIKELY(!newCallFrame)) { | |
922 | m_registerFile.shrink(oldEnd); | |
923 | return checkedReturn(throwStackOverflowError(callFrame)); | |
924 | } | |
925 | ||
926 | newCallFrame->init(newCodeBlock, 0, callDataScopeChain, callFrame->addHostCallFrameFlag(), argCount, function); | |
927 | ||
928 | Profiler** profiler = Profiler::enabledProfilerReference(); | |
929 | if (*profiler) | |
930 | (*profiler)->willExecute(callFrame, function); | |
931 | ||
932 | JSValue result; | |
933 | { | |
934 | SamplingTool::CallRecord callRecord(m_sampler.get()); | |
935 | ||
936 | m_reentryDepth++; | |
937 | #if ENABLE(JIT) | |
938 | if (callFrame->globalData().canUseJIT()) | |
939 | result = callData.js.functionExecutable->generatedJITCodeForCall().execute(&m_registerFile, newCallFrame, callDataScopeChain->globalData); | |
940 | else | |
941 | #endif | |
942 | result = privateExecute(Normal, &m_registerFile, newCallFrame); | |
943 | m_reentryDepth--; | |
944 | } | |
945 | ||
946 | if (*profiler) | |
947 | (*profiler)->didExecute(callFrame, function); | |
9dae56ea | 948 | |
9dae56ea | 949 | m_registerFile.shrink(oldEnd); |
14957cd0 | 950 | return checkedReturn(result); |
9dae56ea | 951 | } |
14957cd0 A |
952 | |
953 | ASSERT(callType == CallTypeHost); | |
954 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
955 | newCallFrame = CallFrame::create(newCallFrame->registers() + registerOffset); | |
956 | newCallFrame->init(0, 0, scopeChain, callFrame->addHostCallFrameFlag(), argCount, function); | |
957 | ||
958 | DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); | |
9dae56ea A |
959 | |
960 | Profiler** profiler = Profiler::enabledProfilerReference(); | |
961 | if (*profiler) | |
962 | (*profiler)->willExecute(callFrame, function); | |
963 | ||
ba379fdc | 964 | JSValue result; |
9dae56ea | 965 | { |
14957cd0 A |
966 | SamplingTool::HostCallRecord callRecord(m_sampler.get()); |
967 | result = JSValue::decode(callData.native.function(newCallFrame)); | |
968 | } | |
9dae56ea | 969 | |
14957cd0 A |
970 | if (*profiler) |
971 | (*profiler)->didExecute(callFrame, function); | |
972 | ||
973 | m_registerFile.shrink(oldEnd); | |
974 | return checkedReturn(result); | |
975 | } | |
976 | ||
977 | JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) | |
978 | { | |
979 | ASSERT(!callFrame->hadException()); | |
980 | ASSERT(!callFrame->globalData().isCollectorBusy()); | |
981 | // We throw in this case because we have to return something "valid" but we're | |
982 | // already in an invalid state. | |
983 | if (callFrame->globalData().isCollectorBusy()) | |
984 | return checkedReturn(throwStackOverflowError(callFrame)); | |
985 | ||
986 | if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) | |
987 | return checkedReturn(throwStackOverflowError(callFrame)); | |
988 | ||
989 | Register* oldEnd = m_registerFile.end(); | |
990 | int argCount = 1 + args.size(); // implicit "this" parameter | |
991 | size_t registerOffset = argCount + RegisterFile::CallFrameHeaderSize; | |
992 | ||
993 | if (!m_registerFile.grow(oldEnd + registerOffset)) | |
994 | return checkedReturn(throwStackOverflowError(callFrame)); | |
995 | ||
996 | CallFrame* newCallFrame = CallFrame::create(oldEnd); | |
997 | size_t dst = 0; | |
998 | ArgList::const_iterator end = args.end(); | |
999 | for (ArgList::const_iterator it = args.begin(); it != end; ++it) | |
1000 | newCallFrame->uncheckedR(++dst) = *it; | |
1001 | ||
1002 | if (constructType == ConstructTypeJS) { | |
1003 | ScopeChainNode* constructDataScopeChain = constructData.js.scopeChain; | |
1004 | ||
1005 | DynamicGlobalObjectScope globalObjectScope(*constructDataScopeChain->globalData, constructDataScopeChain->globalObject.get()); | |
1006 | ||
1007 | JSObject* compileError = constructData.js.functionExecutable->compileForConstruct(callFrame, constructDataScopeChain); | |
1008 | if (UNLIKELY(!!compileError)) { | |
1009 | m_registerFile.shrink(oldEnd); | |
1010 | return checkedReturn(throwError(callFrame, compileError)); | |
1011 | } | |
1012 | ||
1013 | CodeBlock* newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); | |
1014 | newCallFrame = slideRegisterWindowForCall(newCodeBlock, &m_registerFile, newCallFrame, registerOffset, argCount); | |
1015 | if (UNLIKELY(!newCallFrame)) { | |
1016 | m_registerFile.shrink(oldEnd); | |
1017 | return checkedReturn(throwStackOverflowError(callFrame)); | |
1018 | } | |
1019 | ||
1020 | newCallFrame->init(newCodeBlock, 0, constructDataScopeChain, callFrame->addHostCallFrameFlag(), argCount, constructor); | |
1021 | ||
1022 | Profiler** profiler = Profiler::enabledProfilerReference(); | |
1023 | if (*profiler) | |
1024 | (*profiler)->willExecute(callFrame, constructor); | |
1025 | ||
1026 | JSValue result; | |
1027 | { | |
1028 | SamplingTool::CallRecord callRecord(m_sampler.get()); | |
1029 | ||
1030 | m_reentryDepth++; | |
9dae56ea | 1031 | #if ENABLE(JIT) |
14957cd0 A |
1032 | if (callFrame->globalData().canUseJIT()) |
1033 | result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_registerFile, newCallFrame, constructDataScopeChain->globalData); | |
1034 | else | |
9dae56ea | 1035 | #endif |
14957cd0 A |
1036 | result = privateExecute(Normal, &m_registerFile, newCallFrame); |
1037 | m_reentryDepth--; | |
1038 | } | |
1039 | ||
1040 | if (*profiler) | |
1041 | (*profiler)->didExecute(callFrame, constructor); | |
1042 | ||
1043 | m_registerFile.shrink(oldEnd); | |
1044 | if (callFrame->hadException()) | |
1045 | return 0; | |
1046 | ASSERT(result.isObject()); | |
1047 | return checkedReturn(asObject(result)); | |
9dae56ea A |
1048 | } |
1049 | ||
14957cd0 A |
1050 | ASSERT(constructType == ConstructTypeHost); |
1051 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
1052 | newCallFrame = CallFrame::create(newCallFrame->registers() + registerOffset); | |
1053 | newCallFrame->init(0, 0, scopeChain, callFrame->addHostCallFrameFlag(), argCount, constructor); | |
1054 | ||
1055 | DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); | |
1056 | ||
1057 | Profiler** profiler = Profiler::enabledProfilerReference(); | |
9dae56ea | 1058 | if (*profiler) |
14957cd0 A |
1059 | (*profiler)->willExecute(callFrame, constructor); |
1060 | ||
1061 | JSValue result; | |
1062 | { | |
1063 | SamplingTool::HostCallRecord callRecord(m_sampler.get()); | |
1064 | result = JSValue::decode(constructData.native.function(newCallFrame)); | |
1065 | } | |
1066 | ||
1067 | if (*profiler) | |
1068 | (*profiler)->didExecute(callFrame, constructor); | |
9dae56ea A |
1069 | |
1070 | m_registerFile.shrink(oldEnd); | |
14957cd0 A |
1071 | if (callFrame->hadException()) |
1072 | return 0; | |
1073 | ASSERT(result.isObject()); | |
1074 | return checkedReturn(asObject(result)); | |
9dae56ea A |
1075 | } |
1076 | ||
14957cd0 | 1077 | CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* FunctionExecutable, CallFrame* callFrame, JSFunction* function, int argCount, ScopeChainNode* scopeChain) |
ba379fdc A |
1078 | { |
1079 | ASSERT(!scopeChain->globalData->exception); | |
1080 | ||
4e4e5a6f A |
1081 | if (m_reentryDepth >= MaxSmallThreadReentryDepth) { |
1082 | if (m_reentryDepth >= callFrame->globalData().maxReentryDepth) { | |
14957cd0 | 1083 | throwStackOverflowError(callFrame); |
ba379fdc A |
1084 | return CallFrameClosure(); |
1085 | } | |
1086 | } | |
1087 | ||
1088 | Register* oldEnd = m_registerFile.end(); | |
1089 | int argc = 1 + argCount; // implicit "this" parameter | |
1090 | ||
1091 | if (!m_registerFile.grow(oldEnd + argc)) { | |
14957cd0 | 1092 | throwStackOverflowError(callFrame); |
ba379fdc A |
1093 | return CallFrameClosure(); |
1094 | } | |
1095 | ||
1096 | CallFrame* newCallFrame = CallFrame::create(oldEnd); | |
14957cd0 | 1097 | // We initialise |this| unnecessarily here for the sake of code clarity |
ba379fdc A |
1098 | size_t dst = 0; |
1099 | for (int i = 0; i < argc; ++i) | |
14957cd0 | 1100 | newCallFrame->uncheckedR(dst++) = jsUndefined(); |
ba379fdc | 1101 | |
14957cd0 A |
1102 | JSObject* error = FunctionExecutable->compileForCall(callFrame, scopeChain); |
1103 | if (error) { | |
1104 | throwError(callFrame, error); | |
1105 | m_registerFile.shrink(oldEnd); | |
1106 | return CallFrameClosure(); | |
1107 | } | |
1108 | CodeBlock* codeBlock = &FunctionExecutable->generatedBytecodeForCall(); | |
1109 | ||
ba379fdc A |
1110 | newCallFrame = slideRegisterWindowForCall(codeBlock, &m_registerFile, newCallFrame, argc + RegisterFile::CallFrameHeaderSize, argc); |
1111 | if (UNLIKELY(!newCallFrame)) { | |
14957cd0 | 1112 | throwStackOverflowError(callFrame); |
ba379fdc A |
1113 | m_registerFile.shrink(oldEnd); |
1114 | return CallFrameClosure(); | |
1115 | } | |
14957cd0 | 1116 | newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), argc, function); |
f9bf01c6 | 1117 | CallFrameClosure result = { callFrame, newCallFrame, function, FunctionExecutable, scopeChain->globalData, oldEnd, scopeChain, codeBlock->m_numParameters, argc }; |
ba379fdc A |
1118 | return result; |
1119 | } | |
1120 | ||
14957cd0 | 1121 | JSValue Interpreter::execute(CallFrameClosure& closure) |
ba379fdc | 1122 | { |
14957cd0 A |
1123 | ASSERT(!closure.oldCallFrame->globalData().isCollectorBusy()); |
1124 | if (closure.oldCallFrame->globalData().isCollectorBusy()) | |
1125 | return jsNull(); | |
ba379fdc A |
1126 | closure.resetCallFrame(); |
1127 | Profiler** profiler = Profiler::enabledProfilerReference(); | |
1128 | if (*profiler) | |
1129 | (*profiler)->willExecute(closure.oldCallFrame, closure.function); | |
1130 | ||
1131 | JSValue result; | |
1132 | { | |
f9bf01c6 | 1133 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
ba379fdc | 1134 | |
4e4e5a6f | 1135 | m_reentryDepth++; |
ba379fdc | 1136 | #if ENABLE(JIT) |
4e4e5a6f A |
1137 | #if ENABLE(INTERPRETER) |
1138 | if (closure.newCallFrame->globalData().canUseJIT()) | |
1139 | #endif | |
14957cd0 | 1140 | result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_registerFile, closure.newCallFrame, closure.globalData); |
4e4e5a6f A |
1141 | #if ENABLE(INTERPRETER) |
1142 | else | |
1143 | #endif | |
1144 | #endif | |
1145 | #if ENABLE(INTERPRETER) | |
14957cd0 | 1146 | result = privateExecute(Normal, &m_registerFile, closure.newCallFrame); |
ba379fdc A |
1147 | #endif |
1148 | m_reentryDepth--; | |
1149 | } | |
1150 | ||
1151 | if (*profiler) | |
1152 | (*profiler)->didExecute(closure.oldCallFrame, closure.function); | |
14957cd0 | 1153 | return checkedReturn(result); |
ba379fdc A |
1154 | } |
1155 | ||
1156 | void Interpreter::endRepeatCall(CallFrameClosure& closure) | |
1157 | { | |
1158 | m_registerFile.shrink(closure.oldEnd); | |
1159 | } | |
1160 | ||
14957cd0 | 1161 | JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObject* thisObj, ScopeChainNode* scopeChain) |
9dae56ea | 1162 | { |
14957cd0 A |
1163 | JSObject* compileError = eval->compile(callFrame, scopeChain); |
1164 | if (UNLIKELY(!!compileError)) | |
1165 | return checkedReturn(throwError(callFrame, compileError)); | |
1166 | return execute(eval, callFrame, thisObj, m_registerFile.size() + eval->generatedBytecode().m_numParameters + RegisterFile::CallFrameHeaderSize, scopeChain); | |
9dae56ea A |
1167 | } |
1168 | ||
14957cd0 | 1169 | JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObject* thisObj, int globalRegisterOffset, ScopeChainNode* scopeChain) |
9dae56ea A |
1170 | { |
1171 | ASSERT(!scopeChain->globalData->exception); | |
14957cd0 A |
1172 | ASSERT(!callFrame->globalData().isCollectorBusy()); |
1173 | if (callFrame->globalData().isCollectorBusy()) | |
1174 | return jsNull(); | |
9dae56ea | 1175 | |
14957cd0 | 1176 | DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); |
9dae56ea | 1177 | |
14957cd0 A |
1178 | if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) |
1179 | return checkedReturn(throwStackOverflowError(callFrame)); | |
9dae56ea | 1180 | |
14957cd0 A |
1181 | JSObject* compileError = eval->compile(callFrame, scopeChain); |
1182 | if (UNLIKELY(!!compileError)) | |
1183 | return checkedReturn(throwError(callFrame, compileError)); | |
1184 | EvalCodeBlock* codeBlock = &eval->generatedBytecode(); | |
9dae56ea | 1185 | |
14957cd0 A |
1186 | JSObject* variableObject; |
1187 | for (ScopeChainNode* node = scopeChain; ; node = node->next.get()) { | |
9dae56ea A |
1188 | ASSERT(node); |
1189 | if (node->object->isVariableObject()) { | |
14957cd0 | 1190 | variableObject = static_cast<JSVariableObject*>(node->object.get()); |
9dae56ea A |
1191 | break; |
1192 | } | |
1193 | } | |
1194 | ||
4e4e5a6f A |
1195 | unsigned numVariables = codeBlock->numVariables(); |
1196 | int numFunctions = codeBlock->numberOfFunctionDecls(); | |
14957cd0 | 1197 | bool pushedScope = false; |
4e4e5a6f | 1198 | if (numVariables || numFunctions) { |
14957cd0 A |
1199 | if (codeBlock->isStrictMode()) { |
1200 | variableObject = new (callFrame) StrictEvalActivation(callFrame); | |
1201 | scopeChain = scopeChain->push(variableObject); | |
1202 | pushedScope = true; | |
1203 | } | |
4e4e5a6f | 1204 | // Scope for BatchedTransitionOptimizer |
14957cd0 | 1205 | BatchedTransitionOptimizer optimizer(callFrame->globalData(), variableObject); |
9dae56ea | 1206 | |
f9bf01c6 A |
1207 | for (unsigned i = 0; i < numVariables; ++i) { |
1208 | const Identifier& ident = codeBlock->variable(i); | |
9dae56ea A |
1209 | if (!variableObject->hasProperty(callFrame, ident)) { |
1210 | PutPropertySlot slot; | |
1211 | variableObject->put(callFrame, ident, jsUndefined(), slot); | |
1212 | } | |
1213 | } | |
1214 | ||
f9bf01c6 A |
1215 | for (int i = 0; i < numFunctions; ++i) { |
1216 | FunctionExecutable* function = codeBlock->functionDecl(i); | |
9dae56ea | 1217 | PutPropertySlot slot; |
f9bf01c6 | 1218 | variableObject->put(callFrame, function->name(), function->make(callFrame, scopeChain), slot); |
9dae56ea | 1219 | } |
9dae56ea A |
1220 | } |
1221 | ||
1222 | Register* oldEnd = m_registerFile.end(); | |
1223 | Register* newEnd = m_registerFile.start() + globalRegisterOffset + codeBlock->m_numCalleeRegisters; | |
1224 | if (!m_registerFile.grow(newEnd)) { | |
14957cd0 A |
1225 | if (pushedScope) |
1226 | scopeChain->pop(); | |
1227 | return checkedReturn(throwStackOverflowError(callFrame)); | |
9dae56ea A |
1228 | } |
1229 | ||
1230 | CallFrame* newCallFrame = CallFrame::create(m_registerFile.start() + globalRegisterOffset); | |
1231 | ||
14957cd0 A |
1232 | ASSERT(codeBlock->m_numParameters == 1); // 1 parameter for 'this'. |
1233 | newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), codeBlock->m_numParameters, 0); | |
1234 | newCallFrame->uncheckedR(newCallFrame->hostThisRegister()) = JSValue(thisObj); | |
9dae56ea A |
1235 | |
1236 | Profiler** profiler = Profiler::enabledProfilerReference(); | |
1237 | if (*profiler) | |
14957cd0 | 1238 | (*profiler)->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); |
9dae56ea | 1239 | |
ba379fdc | 1240 | JSValue result; |
9dae56ea | 1241 | { |
f9bf01c6 | 1242 | SamplingTool::CallRecord callRecord(m_sampler.get()); |
9dae56ea A |
1243 | |
1244 | m_reentryDepth++; | |
4e4e5a6f | 1245 | |
9dae56ea | 1246 | #if ENABLE(JIT) |
4e4e5a6f A |
1247 | #if ENABLE(INTERPRETER) |
1248 | if (callFrame->globalData().canUseJIT()) | |
1249 | #endif | |
14957cd0 | 1250 | result = eval->generatedJITCode().execute(&m_registerFile, newCallFrame, scopeChain->globalData); |
4e4e5a6f A |
1251 | #if ENABLE(INTERPRETER) |
1252 | else | |
1253 | #endif | |
1254 | #endif | |
1255 | #if ENABLE(INTERPRETER) | |
14957cd0 | 1256 | result = privateExecute(Normal, &m_registerFile, newCallFrame); |
9dae56ea A |
1257 | #endif |
1258 | m_reentryDepth--; | |
1259 | } | |
1260 | ||
1261 | if (*profiler) | |
f9bf01c6 | 1262 | (*profiler)->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); |
9dae56ea A |
1263 | |
1264 | m_registerFile.shrink(oldEnd); | |
14957cd0 A |
1265 | if (pushedScope) |
1266 | scopeChain->pop(); | |
1267 | return checkedReturn(result); | |
9dae56ea A |
1268 | } |
1269 | ||
1270 | NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID, int firstLine, int lastLine) | |
1271 | { | |
1272 | Debugger* debugger = callFrame->dynamicGlobalObject()->debugger(); | |
1273 | if (!debugger) | |
1274 | return; | |
1275 | ||
1276 | switch (debugHookID) { | |
1277 | case DidEnterCallFrame: | |
f9bf01c6 | 1278 | debugger->callEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine); |
9dae56ea A |
1279 | return; |
1280 | case WillLeaveCallFrame: | |
f9bf01c6 | 1281 | debugger->returnEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine); |
9dae56ea A |
1282 | return; |
1283 | case WillExecuteStatement: | |
f9bf01c6 | 1284 | debugger->atStatement(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine); |
9dae56ea A |
1285 | return; |
1286 | case WillExecuteProgram: | |
f9bf01c6 | 1287 | debugger->willExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine); |
9dae56ea A |
1288 | return; |
1289 | case DidExecuteProgram: | |
f9bf01c6 | 1290 | debugger->didExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine); |
9dae56ea A |
1291 | return; |
1292 | case DidReachBreakpoint: | |
f9bf01c6 | 1293 | debugger->didReachBreakpoint(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine); |
9dae56ea A |
1294 | return; |
1295 | } | |
1296 | } | |
9dae56ea | 1297 | |
4e4e5a6f | 1298 | #if ENABLE(INTERPRETER) |
9dae56ea A |
1299 | NEVER_INLINE ScopeChainNode* Interpreter::createExceptionScope(CallFrame* callFrame, const Instruction* vPC) |
1300 | { | |
f9bf01c6 | 1301 | int dst = vPC[1].u.operand; |
9dae56ea | 1302 | CodeBlock* codeBlock = callFrame->codeBlock(); |
f9bf01c6 A |
1303 | Identifier& property = codeBlock->identifier(vPC[2].u.operand); |
1304 | JSValue value = callFrame->r(vPC[3].u.operand).jsValue(); | |
9dae56ea | 1305 | JSObject* scope = new (callFrame) JSStaticScopeObject(callFrame, property, value, DontDelete); |
14957cd0 | 1306 | callFrame->uncheckedR(dst) = JSValue(scope); |
9dae56ea A |
1307 | |
1308 | return callFrame->scopeChain()->push(scope); | |
1309 | } | |
1310 | ||
ba379fdc | 1311 | NEVER_INLINE void Interpreter::tryCachePutByID(CallFrame* callFrame, CodeBlock* codeBlock, Instruction* vPC, JSValue baseValue, const PutPropertySlot& slot) |
9dae56ea A |
1312 | { |
1313 | // Recursive invocation may already have specialized this instruction. | |
1314 | if (vPC[0].u.opcode != getOpcode(op_put_by_id)) | |
1315 | return; | |
1316 | ||
1317 | if (!baseValue.isCell()) | |
1318 | return; | |
1319 | ||
1320 | // Uncacheable: give up. | |
1321 | if (!slot.isCacheable()) { | |
1322 | vPC[0] = getOpcode(op_put_by_id_generic); | |
1323 | return; | |
1324 | } | |
1325 | ||
14957cd0 | 1326 | JSCell* baseCell = baseValue.asCell(); |
9dae56ea A |
1327 | Structure* structure = baseCell->structure(); |
1328 | ||
14957cd0 | 1329 | if (structure->isUncacheableDictionary() || structure->typeInfo().prohibitsPropertyCaching()) { |
9dae56ea A |
1330 | vPC[0] = getOpcode(op_put_by_id_generic); |
1331 | return; | |
1332 | } | |
1333 | ||
1334 | // Cache miss: record Structure to compare against next time. | |
14957cd0 | 1335 | Structure* lastStructure = vPC[4].u.structure.get(); |
9dae56ea A |
1336 | if (structure != lastStructure) { |
1337 | // First miss: record Structure to compare against next time. | |
1338 | if (!lastStructure) { | |
14957cd0 | 1339 | vPC[4].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure); |
9dae56ea A |
1340 | return; |
1341 | } | |
1342 | ||
1343 | // Second miss: give up. | |
1344 | vPC[0] = getOpcode(op_put_by_id_generic); | |
1345 | return; | |
1346 | } | |
1347 | ||
1348 | // Cache hit: Specialize instruction and ref Structures. | |
1349 | ||
1350 | // If baseCell != slot.base(), then baseCell must be a proxy for another object. | |
1351 | if (baseCell != slot.base()) { | |
1352 | vPC[0] = getOpcode(op_put_by_id_generic); | |
1353 | return; | |
1354 | } | |
1355 | ||
1356 | // Structure transition, cache transition info | |
1357 | if (slot.type() == PutPropertySlot::NewProperty) { | |
ba379fdc A |
1358 | if (structure->isDictionary()) { |
1359 | vPC[0] = getOpcode(op_put_by_id_generic); | |
1360 | return; | |
1361 | } | |
f9bf01c6 A |
1362 | |
1363 | // put_by_id_transition checks the prototype chain for setters. | |
1364 | normalizePrototypeChain(callFrame, baseCell); | |
14957cd0 A |
1365 | JSCell* owner = codeBlock->ownerExecutable(); |
1366 | JSGlobalData& globalData = callFrame->globalData(); | |
1367 | // Get the prototype here because the call to prototypeChain could cause a | |
1368 | // GC allocation, which we don't want to happen while we're in the middle of | |
1369 | // initializing the union. | |
1370 | StructureChain* prototypeChain = structure->prototypeChain(callFrame); | |
9dae56ea | 1371 | vPC[0] = getOpcode(op_put_by_id_transition); |
14957cd0 A |
1372 | vPC[4].u.structure.set(globalData, owner, structure->previousID()); |
1373 | vPC[5].u.structure.set(globalData, owner, structure); | |
1374 | vPC[6].u.structureChain.set(callFrame->globalData(), codeBlock->ownerExecutable(), prototypeChain); | |
1375 | ASSERT(vPC[6].u.structureChain); | |
9dae56ea | 1376 | vPC[7] = slot.cachedOffset(); |
9dae56ea A |
1377 | return; |
1378 | } | |
1379 | ||
1380 | vPC[0] = getOpcode(op_put_by_id_replace); | |
1381 | vPC[5] = slot.cachedOffset(); | |
9dae56ea A |
1382 | } |
1383 | ||
14957cd0 | 1384 | NEVER_INLINE void Interpreter::uncachePutByID(CodeBlock*, Instruction* vPC) |
9dae56ea | 1385 | { |
9dae56ea A |
1386 | vPC[0] = getOpcode(op_put_by_id); |
1387 | vPC[4] = 0; | |
1388 | } | |
1389 | ||
ba379fdc | 1390 | NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, Instruction* vPC, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot) |
9dae56ea A |
1391 | { |
1392 | // Recursive invocation may already have specialized this instruction. | |
1393 | if (vPC[0].u.opcode != getOpcode(op_get_by_id)) | |
1394 | return; | |
1395 | ||
1396 | // FIXME: Cache property access for immediates. | |
1397 | if (!baseValue.isCell()) { | |
1398 | vPC[0] = getOpcode(op_get_by_id_generic); | |
1399 | return; | |
1400 | } | |
1401 | ||
ba379fdc A |
1402 | JSGlobalData* globalData = &callFrame->globalData(); |
1403 | if (isJSArray(globalData, baseValue) && propertyName == callFrame->propertyNames().length) { | |
9dae56ea A |
1404 | vPC[0] = getOpcode(op_get_array_length); |
1405 | return; | |
1406 | } | |
1407 | ||
ba379fdc | 1408 | if (isJSString(globalData, baseValue) && propertyName == callFrame->propertyNames().length) { |
9dae56ea A |
1409 | vPC[0] = getOpcode(op_get_string_length); |
1410 | return; | |
1411 | } | |
1412 | ||
1413 | // Uncacheable: give up. | |
1414 | if (!slot.isCacheable()) { | |
1415 | vPC[0] = getOpcode(op_get_by_id_generic); | |
1416 | return; | |
1417 | } | |
1418 | ||
14957cd0 | 1419 | Structure* structure = baseValue.asCell()->structure(); |
9dae56ea | 1420 | |
14957cd0 | 1421 | if (structure->isUncacheableDictionary() || structure->typeInfo().prohibitsPropertyCaching()) { |
9dae56ea A |
1422 | vPC[0] = getOpcode(op_get_by_id_generic); |
1423 | return; | |
1424 | } | |
1425 | ||
1426 | // Cache miss | |
14957cd0 | 1427 | Structure* lastStructure = vPC[4].u.structure.get(); |
9dae56ea A |
1428 | if (structure != lastStructure) { |
1429 | // First miss: record Structure to compare against next time. | |
1430 | if (!lastStructure) { | |
14957cd0 | 1431 | vPC[4].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure); |
9dae56ea A |
1432 | return; |
1433 | } | |
1434 | ||
1435 | // Second miss: give up. | |
1436 | vPC[0] = getOpcode(op_get_by_id_generic); | |
1437 | return; | |
1438 | } | |
1439 | ||
1440 | // Cache hit: Specialize instruction and ref Structures. | |
1441 | ||
1442 | if (slot.slotBase() == baseValue) { | |
4e4e5a6f A |
1443 | switch (slot.cachedPropertyType()) { |
1444 | case PropertySlot::Getter: | |
1445 | vPC[0] = getOpcode(op_get_by_id_getter_self); | |
1446 | vPC[5] = slot.cachedOffset(); | |
1447 | break; | |
1448 | case PropertySlot::Custom: | |
1449 | vPC[0] = getOpcode(op_get_by_id_custom_self); | |
1450 | vPC[5] = slot.customGetter(); | |
1451 | break; | |
1452 | default: | |
1453 | vPC[0] = getOpcode(op_get_by_id_self); | |
1454 | vPC[5] = slot.cachedOffset(); | |
1455 | break; | |
1456 | } | |
9dae56ea A |
1457 | return; |
1458 | } | |
1459 | ||
ba379fdc A |
1460 | if (structure->isDictionary()) { |
1461 | vPC[0] = getOpcode(op_get_by_id_generic); | |
1462 | return; | |
1463 | } | |
1464 | ||
9dae56ea A |
1465 | if (slot.slotBase() == structure->prototypeForLookup(callFrame)) { |
1466 | ASSERT(slot.slotBase().isObject()); | |
1467 | ||
1468 | JSObject* baseObject = asObject(slot.slotBase()); | |
ba379fdc | 1469 | size_t offset = slot.cachedOffset(); |
9dae56ea A |
1470 | |
1471 | // Since we're accessing a prototype in a loop, it's a good bet that it | |
1472 | // should not be treated as a dictionary. | |
ba379fdc | 1473 | if (baseObject->structure()->isDictionary()) { |
14957cd0 A |
1474 | baseObject->flattenDictionaryObject(callFrame->globalData()); |
1475 | offset = baseObject->structure()->get(callFrame->globalData(), propertyName); | |
ba379fdc A |
1476 | } |
1477 | ||
1478 | ASSERT(!baseObject->structure()->isUncacheableDictionary()); | |
4e4e5a6f A |
1479 | |
1480 | switch (slot.cachedPropertyType()) { | |
1481 | case PropertySlot::Getter: | |
1482 | vPC[0] = getOpcode(op_get_by_id_getter_proto); | |
1483 | vPC[6] = offset; | |
1484 | break; | |
1485 | case PropertySlot::Custom: | |
1486 | vPC[0] = getOpcode(op_get_by_id_custom_proto); | |
1487 | vPC[6] = slot.customGetter(); | |
1488 | break; | |
1489 | default: | |
1490 | vPC[0] = getOpcode(op_get_by_id_proto); | |
1491 | vPC[6] = offset; | |
1492 | break; | |
1493 | } | |
14957cd0 | 1494 | vPC[5].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), baseObject->structure()); |
9dae56ea A |
1495 | return; |
1496 | } | |
1497 | ||
ba379fdc A |
1498 | size_t offset = slot.cachedOffset(); |
1499 | size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase(), propertyName, offset); | |
9dae56ea A |
1500 | if (!count) { |
1501 | vPC[0] = getOpcode(op_get_by_id_generic); | |
1502 | return; | |
1503 | } | |
1504 | ||
4e4e5a6f A |
1505 | |
1506 | switch (slot.cachedPropertyType()) { | |
1507 | case PropertySlot::Getter: | |
1508 | vPC[0] = getOpcode(op_get_by_id_getter_chain); | |
1509 | vPC[7] = offset; | |
1510 | break; | |
1511 | case PropertySlot::Custom: | |
1512 | vPC[0] = getOpcode(op_get_by_id_custom_chain); | |
1513 | vPC[7] = slot.customGetter(); | |
1514 | break; | |
1515 | default: | |
1516 | vPC[0] = getOpcode(op_get_by_id_chain); | |
1517 | vPC[7] = offset; | |
1518 | break; | |
1519 | } | |
14957cd0 A |
1520 | vPC[4].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure); |
1521 | vPC[5].u.structureChain.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure->prototypeChain(callFrame)); | |
9dae56ea | 1522 | vPC[6] = count; |
9dae56ea A |
1523 | } |
1524 | ||
14957cd0 | 1525 | NEVER_INLINE void Interpreter::uncacheGetByID(CodeBlock*, Instruction* vPC) |
9dae56ea | 1526 | { |
9dae56ea A |
1527 | vPC[0] = getOpcode(op_get_by_id); |
1528 | vPC[4] = 0; | |
1529 | } | |
1530 | ||
4e4e5a6f | 1531 | #endif // ENABLE(INTERPRETER) |
ba379fdc | 1532 | |
14957cd0 | 1533 | JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFile, CallFrame* callFrame) |
9dae56ea A |
1534 | { |
1535 | // One-time initialization of our address tables. We have to put this code | |
1536 | // here because our labels are only in scope inside this function. | |
ba379fdc | 1537 | if (UNLIKELY(flag == InitializeAndReturn)) { |
4e4e5a6f | 1538 | #if ENABLE(COMPUTED_GOTO_INTERPRETER) |
ba379fdc A |
1539 | #define LIST_OPCODE_LABEL(id, length) &&id, |
1540 | static Opcode labels[] = { FOR_EACH_OPCODE_ID(LIST_OPCODE_LABEL) }; | |
14957cd0 | 1541 | for (size_t i = 0; i < WTF_ARRAY_LENGTH(labels); ++i) |
ba379fdc A |
1542 | m_opcodeTable[i] = labels[i]; |
1543 | #undef LIST_OPCODE_LABEL | |
4e4e5a6f | 1544 | #endif // ENABLE(COMPUTED_GOTO_INTERPRETER) |
ba379fdc | 1545 | return JSValue(); |
9dae56ea | 1546 | } |
4e4e5a6f | 1547 | |
9dae56ea | 1548 | #if ENABLE(JIT) |
4e4e5a6f | 1549 | #if ENABLE(INTERPRETER) |
ba379fdc | 1550 | // Mixing Interpreter + JIT is not supported. |
4e4e5a6f A |
1551 | if (callFrame->globalData().canUseJIT()) |
1552 | #endif | |
1553 | ASSERT_NOT_REACHED(); | |
9dae56ea | 1554 | #endif |
4e4e5a6f A |
1555 | |
1556 | #if !ENABLE(INTERPRETER) | |
ba379fdc A |
1557 | UNUSED_PARAM(registerFile); |
1558 | UNUSED_PARAM(callFrame); | |
ba379fdc A |
1559 | return JSValue(); |
1560 | #else | |
9dae56ea A |
1561 | |
1562 | JSGlobalData* globalData = &callFrame->globalData(); | |
ba379fdc | 1563 | JSValue exceptionValue; |
9dae56ea A |
1564 | HandlerInfo* handler = 0; |
1565 | ||
14957cd0 A |
1566 | CodeBlock* codeBlock = callFrame->codeBlock(); |
1567 | Instruction* vPC = codeBlock->instructions().begin(); | |
9dae56ea | 1568 | Profiler** enabledProfilerReference = Profiler::enabledProfilerReference(); |
ba379fdc | 1569 | unsigned tickCount = globalData->timeoutChecker.ticksUntilNextCheck(); |
14957cd0 | 1570 | JSValue functionReturnValue; |
9dae56ea A |
1571 | |
1572 | #define CHECK_FOR_EXCEPTION() \ | |
1573 | do { \ | |
ba379fdc | 1574 | if (UNLIKELY(globalData->exception != JSValue())) { \ |
9dae56ea A |
1575 | exceptionValue = globalData->exception; \ |
1576 | goto vm_throw; \ | |
1577 | } \ | |
1578 | } while (0) | |
1579 | ||
1580 | #if ENABLE(OPCODE_STATS) | |
1581 | OpcodeStats::resetLastInstruction(); | |
1582 | #endif | |
1583 | ||
1584 | #define CHECK_FOR_TIMEOUT() \ | |
1585 | if (!--tickCount) { \ | |
4e4e5a6f | 1586 | if (globalData->terminator.shouldTerminate() || globalData->timeoutChecker.didTimeOut(callFrame)) { \ |
9dae56ea A |
1587 | exceptionValue = jsNull(); \ |
1588 | goto vm_throw; \ | |
1589 | } \ | |
ba379fdc | 1590 | tickCount = globalData->timeoutChecker.ticksUntilNextCheck(); \ |
9dae56ea A |
1591 | } |
1592 | ||
1593 | #if ENABLE(OPCODE_SAMPLING) | |
1594 | #define SAMPLE(codeBlock, vPC) m_sampler->sample(codeBlock, vPC) | |
9dae56ea A |
1595 | #else |
1596 | #define SAMPLE(codeBlock, vPC) | |
9dae56ea A |
1597 | #endif |
1598 | ||
4e4e5a6f | 1599 | #if ENABLE(COMPUTED_GOTO_INTERPRETER) |
14957cd0 | 1600 | #define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto *vPC->u.opcode |
9dae56ea A |
1601 | #if ENABLE(OPCODE_STATS) |
1602 | #define DEFINE_OPCODE(opcode) opcode: OpcodeStats::recordInstruction(opcode); | |
1603 | #else | |
1604 | #define DEFINE_OPCODE(opcode) opcode: | |
1605 | #endif | |
1606 | NEXT_INSTRUCTION(); | |
1607 | #else | |
14957cd0 | 1608 | #define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto interpreterLoopStart |
9dae56ea A |
1609 | #if ENABLE(OPCODE_STATS) |
1610 | #define DEFINE_OPCODE(opcode) case opcode: OpcodeStats::recordInstruction(opcode); | |
1611 | #else | |
1612 | #define DEFINE_OPCODE(opcode) case opcode: | |
1613 | #endif | |
1614 | while (1) { // iterator loop begins | |
1615 | interpreterLoopStart:; | |
1616 | switch (vPC->u.opcode) | |
1617 | #endif | |
1618 | { | |
1619 | DEFINE_OPCODE(op_new_object) { | |
1620 | /* new_object dst(r) | |
1621 | ||
1622 | Constructs a new empty Object instance using the original | |
1623 | constructor, and puts the result in register dst. | |
1624 | */ | |
f9bf01c6 | 1625 | int dst = vPC[1].u.operand; |
14957cd0 | 1626 | callFrame->uncheckedR(dst) = JSValue(constructEmptyObject(callFrame)); |
9dae56ea | 1627 | |
f9bf01c6 | 1628 | vPC += OPCODE_LENGTH(op_new_object); |
9dae56ea A |
1629 | NEXT_INSTRUCTION(); |
1630 | } | |
1631 | DEFINE_OPCODE(op_new_array) { | |
1632 | /* new_array dst(r) firstArg(r) argCount(n) | |
1633 | ||
1634 | Constructs a new Array instance using the original | |
1635 | constructor, and puts the result in register dst. | |
1636 | The array will contain argCount elements with values | |
1637 | taken from registers starting at register firstArg. | |
1638 | */ | |
f9bf01c6 A |
1639 | int dst = vPC[1].u.operand; |
1640 | int firstArg = vPC[2].u.operand; | |
1641 | int argCount = vPC[3].u.operand; | |
9dae56ea | 1642 | ArgList args(callFrame->registers() + firstArg, argCount); |
14957cd0 | 1643 | callFrame->uncheckedR(dst) = JSValue(constructArray(callFrame, args)); |
9dae56ea | 1644 | |
f9bf01c6 | 1645 | vPC += OPCODE_LENGTH(op_new_array); |
9dae56ea A |
1646 | NEXT_INSTRUCTION(); |
1647 | } | |
14957cd0 A |
1648 | DEFINE_OPCODE(op_new_array_buffer) { |
1649 | /* new_array_buffer dst(r) index(n) argCount(n) | |
1650 | ||
1651 | Constructs a new Array instance using the original | |
1652 | constructor, and puts the result in register dst. | |
1653 | The array be initialized with the values from constantBuffer[index] | |
1654 | */ | |
1655 | int dst = vPC[1].u.operand; | |
1656 | int firstArg = vPC[2].u.operand; | |
1657 | int argCount = vPC[3].u.operand; | |
1658 | ArgList args(codeBlock->constantBuffer(firstArg), argCount); | |
1659 | callFrame->uncheckedR(dst) = JSValue(constructArray(callFrame, args)); | |
1660 | ||
1661 | vPC += OPCODE_LENGTH(op_new_array); | |
1662 | NEXT_INSTRUCTION(); | |
1663 | } | |
9dae56ea A |
1664 | DEFINE_OPCODE(op_new_regexp) { |
1665 | /* new_regexp dst(r) regExp(re) | |
1666 | ||
1667 | Constructs a new RegExp instance using the original | |
1668 | constructor from regexp regExp, and puts the result in | |
1669 | register dst. | |
1670 | */ | |
f9bf01c6 | 1671 | int dst = vPC[1].u.operand; |
14957cd0 A |
1672 | RegExp* regExp = codeBlock->regexp(vPC[2].u.operand); |
1673 | if (!regExp->isValid()) { | |
1674 | exceptionValue = createSyntaxError(callFrame, "Invalid flags supplied to RegExp constructor."); | |
1675 | goto vm_throw; | |
1676 | } | |
1677 | callFrame->uncheckedR(dst) = JSValue(new (globalData) RegExpObject(callFrame->lexicalGlobalObject(), callFrame->scopeChain()->globalObject->regExpStructure(), regExp)); | |
9dae56ea | 1678 | |
f9bf01c6 | 1679 | vPC += OPCODE_LENGTH(op_new_regexp); |
9dae56ea A |
1680 | NEXT_INSTRUCTION(); |
1681 | } | |
1682 | DEFINE_OPCODE(op_mov) { | |
1683 | /* mov dst(r) src(r) | |
1684 | ||
1685 | Copies register src to register dst. | |
1686 | */ | |
f9bf01c6 A |
1687 | int dst = vPC[1].u.operand; |
1688 | int src = vPC[2].u.operand; | |
14957cd0 A |
1689 | |
1690 | callFrame->uncheckedR(dst) = callFrame->r(src); | |
9dae56ea | 1691 | |
f9bf01c6 | 1692 | vPC += OPCODE_LENGTH(op_mov); |
9dae56ea A |
1693 | NEXT_INSTRUCTION(); |
1694 | } | |
1695 | DEFINE_OPCODE(op_eq) { | |
1696 | /* eq dst(r) src1(r) src2(r) | |
1697 | ||
1698 | Checks whether register src1 and register src2 are equal, | |
1699 | as with the ECMAScript '==' operator, and puts the result | |
1700 | as a boolean in register dst. | |
1701 | */ | |
f9bf01c6 A |
1702 | int dst = vPC[1].u.operand; |
1703 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1704 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 1705 | if (src1.isInt32() && src2.isInt32()) |
14957cd0 | 1706 | callFrame->uncheckedR(dst) = jsBoolean(src1.asInt32() == src2.asInt32()); |
9dae56ea | 1707 | else { |
ba379fdc | 1708 | JSValue result = jsBoolean(JSValue::equalSlowCase(callFrame, src1, src2)); |
9dae56ea | 1709 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1710 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
1711 | } |
1712 | ||
f9bf01c6 | 1713 | vPC += OPCODE_LENGTH(op_eq); |
9dae56ea A |
1714 | NEXT_INSTRUCTION(); |
1715 | } | |
1716 | DEFINE_OPCODE(op_eq_null) { | |
1717 | /* eq_null dst(r) src(r) | |
1718 | ||
1719 | Checks whether register src is null, as with the ECMAScript '!=' | |
1720 | operator, and puts the result as a boolean in register dst. | |
1721 | */ | |
f9bf01c6 A |
1722 | int dst = vPC[1].u.operand; |
1723 | JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); | |
9dae56ea A |
1724 | |
1725 | if (src.isUndefinedOrNull()) { | |
14957cd0 | 1726 | callFrame->uncheckedR(dst) = jsBoolean(true); |
f9bf01c6 | 1727 | vPC += OPCODE_LENGTH(op_eq_null); |
9dae56ea A |
1728 | NEXT_INSTRUCTION(); |
1729 | } | |
1730 | ||
14957cd0 | 1731 | callFrame->uncheckedR(dst) = jsBoolean(src.isCell() && src.asCell()->structure()->typeInfo().masqueradesAsUndefined()); |
f9bf01c6 | 1732 | vPC += OPCODE_LENGTH(op_eq_null); |
9dae56ea A |
1733 | NEXT_INSTRUCTION(); |
1734 | } | |
1735 | DEFINE_OPCODE(op_neq) { | |
1736 | /* neq dst(r) src1(r) src2(r) | |
1737 | ||
1738 | Checks whether register src1 and register src2 are not | |
1739 | equal, as with the ECMAScript '!=' operator, and puts the | |
1740 | result as a boolean in register dst. | |
1741 | */ | |
f9bf01c6 A |
1742 | int dst = vPC[1].u.operand; |
1743 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1744 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 1745 | if (src1.isInt32() && src2.isInt32()) |
14957cd0 | 1746 | callFrame->uncheckedR(dst) = jsBoolean(src1.asInt32() != src2.asInt32()); |
9dae56ea | 1747 | else { |
ba379fdc | 1748 | JSValue result = jsBoolean(!JSValue::equalSlowCase(callFrame, src1, src2)); |
9dae56ea | 1749 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1750 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
1751 | } |
1752 | ||
f9bf01c6 | 1753 | vPC += OPCODE_LENGTH(op_neq); |
9dae56ea A |
1754 | NEXT_INSTRUCTION(); |
1755 | } | |
1756 | DEFINE_OPCODE(op_neq_null) { | |
1757 | /* neq_null dst(r) src(r) | |
1758 | ||
1759 | Checks whether register src is not null, as with the ECMAScript '!=' | |
1760 | operator, and puts the result as a boolean in register dst. | |
1761 | */ | |
f9bf01c6 A |
1762 | int dst = vPC[1].u.operand; |
1763 | JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); | |
9dae56ea A |
1764 | |
1765 | if (src.isUndefinedOrNull()) { | |
14957cd0 | 1766 | callFrame->uncheckedR(dst) = jsBoolean(false); |
f9bf01c6 | 1767 | vPC += OPCODE_LENGTH(op_neq_null); |
9dae56ea A |
1768 | NEXT_INSTRUCTION(); |
1769 | } | |
1770 | ||
14957cd0 | 1771 | callFrame->uncheckedR(dst) = jsBoolean(!src.isCell() || !src.asCell()->structure()->typeInfo().masqueradesAsUndefined()); |
f9bf01c6 | 1772 | vPC += OPCODE_LENGTH(op_neq_null); |
9dae56ea A |
1773 | NEXT_INSTRUCTION(); |
1774 | } | |
1775 | DEFINE_OPCODE(op_stricteq) { | |
1776 | /* stricteq dst(r) src1(r) src2(r) | |
1777 | ||
1778 | Checks whether register src1 and register src2 are strictly | |
1779 | equal, as with the ECMAScript '===' operator, and puts the | |
1780 | result as a boolean in register dst. | |
1781 | */ | |
f9bf01c6 A |
1782 | int dst = vPC[1].u.operand; |
1783 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1784 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
4e4e5a6f A |
1785 | bool result = JSValue::strictEqual(callFrame, src1, src2); |
1786 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 1787 | callFrame->uncheckedR(dst) = jsBoolean(result); |
9dae56ea | 1788 | |
f9bf01c6 | 1789 | vPC += OPCODE_LENGTH(op_stricteq); |
9dae56ea A |
1790 | NEXT_INSTRUCTION(); |
1791 | } | |
1792 | DEFINE_OPCODE(op_nstricteq) { | |
1793 | /* nstricteq dst(r) src1(r) src2(r) | |
1794 | ||
1795 | Checks whether register src1 and register src2 are not | |
1796 | strictly equal, as with the ECMAScript '!==' operator, and | |
1797 | puts the result as a boolean in register dst. | |
1798 | */ | |
f9bf01c6 A |
1799 | int dst = vPC[1].u.operand; |
1800 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1801 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
4e4e5a6f A |
1802 | bool result = !JSValue::strictEqual(callFrame, src1, src2); |
1803 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 1804 | callFrame->uncheckedR(dst) = jsBoolean(result); |
9dae56ea | 1805 | |
f9bf01c6 | 1806 | vPC += OPCODE_LENGTH(op_nstricteq); |
9dae56ea A |
1807 | NEXT_INSTRUCTION(); |
1808 | } | |
1809 | DEFINE_OPCODE(op_less) { | |
1810 | /* less dst(r) src1(r) src2(r) | |
1811 | ||
1812 | Checks whether register src1 is less than register src2, as | |
1813 | with the ECMAScript '<' operator, and puts the result as | |
1814 | a boolean in register dst. | |
1815 | */ | |
f9bf01c6 A |
1816 | int dst = vPC[1].u.operand; |
1817 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1818 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 1819 | JSValue result = jsBoolean(jsLess(callFrame, src1, src2)); |
9dae56ea | 1820 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1821 | callFrame->uncheckedR(dst) = result; |
9dae56ea | 1822 | |
f9bf01c6 | 1823 | vPC += OPCODE_LENGTH(op_less); |
9dae56ea A |
1824 | NEXT_INSTRUCTION(); |
1825 | } | |
1826 | DEFINE_OPCODE(op_lesseq) { | |
1827 | /* lesseq dst(r) src1(r) src2(r) | |
1828 | ||
1829 | Checks whether register src1 is less than or equal to | |
1830 | register src2, as with the ECMAScript '<=' operator, and | |
1831 | puts the result as a boolean in register dst. | |
1832 | */ | |
f9bf01c6 A |
1833 | int dst = vPC[1].u.operand; |
1834 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1835 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 1836 | JSValue result = jsBoolean(jsLessEq(callFrame, src1, src2)); |
9dae56ea | 1837 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1838 | callFrame->uncheckedR(dst) = result; |
9dae56ea | 1839 | |
f9bf01c6 | 1840 | vPC += OPCODE_LENGTH(op_lesseq); |
9dae56ea A |
1841 | NEXT_INSTRUCTION(); |
1842 | } | |
1843 | DEFINE_OPCODE(op_pre_inc) { | |
1844 | /* pre_inc srcDst(r) | |
1845 | ||
1846 | Converts register srcDst to number, adds one, and puts the result | |
1847 | back in register srcDst. | |
1848 | */ | |
f9bf01c6 | 1849 | int srcDst = vPC[1].u.operand; |
ba379fdc A |
1850 | JSValue v = callFrame->r(srcDst).jsValue(); |
1851 | if (v.isInt32() && v.asInt32() < INT_MAX) | |
14957cd0 | 1852 | callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() + 1); |
9dae56ea | 1853 | else { |
14957cd0 | 1854 | JSValue result = jsNumber(v.toNumber(callFrame) + 1); |
9dae56ea | 1855 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1856 | callFrame->uncheckedR(srcDst) = result; |
9dae56ea A |
1857 | } |
1858 | ||
f9bf01c6 | 1859 | vPC += OPCODE_LENGTH(op_pre_inc); |
9dae56ea A |
1860 | NEXT_INSTRUCTION(); |
1861 | } | |
1862 | DEFINE_OPCODE(op_pre_dec) { | |
1863 | /* pre_dec srcDst(r) | |
1864 | ||
1865 | Converts register srcDst to number, subtracts one, and puts the result | |
1866 | back in register srcDst. | |
1867 | */ | |
f9bf01c6 | 1868 | int srcDst = vPC[1].u.operand; |
ba379fdc A |
1869 | JSValue v = callFrame->r(srcDst).jsValue(); |
1870 | if (v.isInt32() && v.asInt32() > INT_MIN) | |
14957cd0 | 1871 | callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() - 1); |
9dae56ea | 1872 | else { |
14957cd0 | 1873 | JSValue result = jsNumber(v.toNumber(callFrame) - 1); |
9dae56ea | 1874 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1875 | callFrame->uncheckedR(srcDst) = result; |
9dae56ea A |
1876 | } |
1877 | ||
f9bf01c6 | 1878 | vPC += OPCODE_LENGTH(op_pre_dec); |
9dae56ea A |
1879 | NEXT_INSTRUCTION(); |
1880 | } | |
1881 | DEFINE_OPCODE(op_post_inc) { | |
1882 | /* post_inc dst(r) srcDst(r) | |
1883 | ||
1884 | Converts register srcDst to number. The number itself is | |
1885 | written to register dst, and the number plus one is written | |
1886 | back to register srcDst. | |
1887 | */ | |
f9bf01c6 A |
1888 | int dst = vPC[1].u.operand; |
1889 | int srcDst = vPC[2].u.operand; | |
ba379fdc A |
1890 | JSValue v = callFrame->r(srcDst).jsValue(); |
1891 | if (v.isInt32() && v.asInt32() < INT_MAX) { | |
14957cd0 A |
1892 | callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() + 1); |
1893 | callFrame->uncheckedR(dst) = v; | |
9dae56ea | 1894 | } else { |
ba379fdc | 1895 | JSValue number = callFrame->r(srcDst).jsValue().toJSNumber(callFrame); |
9dae56ea | 1896 | CHECK_FOR_EXCEPTION(); |
14957cd0 A |
1897 | callFrame->uncheckedR(srcDst) = jsNumber(number.uncheckedGetNumber() + 1); |
1898 | callFrame->uncheckedR(dst) = number; | |
9dae56ea A |
1899 | } |
1900 | ||
f9bf01c6 | 1901 | vPC += OPCODE_LENGTH(op_post_inc); |
9dae56ea A |
1902 | NEXT_INSTRUCTION(); |
1903 | } | |
1904 | DEFINE_OPCODE(op_post_dec) { | |
1905 | /* post_dec dst(r) srcDst(r) | |
1906 | ||
1907 | Converts register srcDst to number. The number itself is | |
1908 | written to register dst, and the number minus one is written | |
1909 | back to register srcDst. | |
1910 | */ | |
f9bf01c6 A |
1911 | int dst = vPC[1].u.operand; |
1912 | int srcDst = vPC[2].u.operand; | |
ba379fdc A |
1913 | JSValue v = callFrame->r(srcDst).jsValue(); |
1914 | if (v.isInt32() && v.asInt32() > INT_MIN) { | |
14957cd0 A |
1915 | callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() - 1); |
1916 | callFrame->uncheckedR(dst) = v; | |
9dae56ea | 1917 | } else { |
ba379fdc | 1918 | JSValue number = callFrame->r(srcDst).jsValue().toJSNumber(callFrame); |
9dae56ea | 1919 | CHECK_FOR_EXCEPTION(); |
14957cd0 A |
1920 | callFrame->uncheckedR(srcDst) = jsNumber(number.uncheckedGetNumber() - 1); |
1921 | callFrame->uncheckedR(dst) = number; | |
9dae56ea A |
1922 | } |
1923 | ||
f9bf01c6 | 1924 | vPC += OPCODE_LENGTH(op_post_dec); |
9dae56ea A |
1925 | NEXT_INSTRUCTION(); |
1926 | } | |
1927 | DEFINE_OPCODE(op_to_jsnumber) { | |
1928 | /* to_jsnumber dst(r) src(r) | |
1929 | ||
1930 | Converts register src to number, and puts the result | |
1931 | in register dst. | |
1932 | */ | |
f9bf01c6 A |
1933 | int dst = vPC[1].u.operand; |
1934 | int src = vPC[2].u.operand; | |
9dae56ea | 1935 | |
ba379fdc | 1936 | JSValue srcVal = callFrame->r(src).jsValue(); |
9dae56ea A |
1937 | |
1938 | if (LIKELY(srcVal.isNumber())) | |
14957cd0 | 1939 | callFrame->uncheckedR(dst) = callFrame->r(src); |
9dae56ea | 1940 | else { |
ba379fdc | 1941 | JSValue result = srcVal.toJSNumber(callFrame); |
9dae56ea | 1942 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1943 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
1944 | } |
1945 | ||
f9bf01c6 | 1946 | vPC += OPCODE_LENGTH(op_to_jsnumber); |
9dae56ea A |
1947 | NEXT_INSTRUCTION(); |
1948 | } | |
1949 | DEFINE_OPCODE(op_negate) { | |
1950 | /* negate dst(r) src(r) | |
1951 | ||
1952 | Converts register src to number, negates it, and puts the | |
1953 | result in register dst. | |
1954 | */ | |
f9bf01c6 A |
1955 | int dst = vPC[1].u.operand; |
1956 | JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); | |
4e4e5a6f | 1957 | if (src.isInt32() && (src.asInt32() & 0x7fffffff)) // non-zero and no overflow |
14957cd0 | 1958 | callFrame->uncheckedR(dst) = jsNumber(-src.asInt32()); |
9dae56ea | 1959 | else { |
14957cd0 | 1960 | JSValue result = jsNumber(-src.toNumber(callFrame)); |
9dae56ea | 1961 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1962 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
1963 | } |
1964 | ||
f9bf01c6 | 1965 | vPC += OPCODE_LENGTH(op_negate); |
9dae56ea A |
1966 | NEXT_INSTRUCTION(); |
1967 | } | |
1968 | DEFINE_OPCODE(op_add) { | |
1969 | /* add dst(r) src1(r) src2(r) | |
1970 | ||
1971 | Adds register src1 and register src2, and puts the result | |
1972 | in register dst. (JS add may be string concatenation or | |
1973 | numeric add, depending on the types of the operands.) | |
1974 | */ | |
f9bf01c6 A |
1975 | int dst = vPC[1].u.operand; |
1976 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1977 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
1978 | if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | (src2.asInt32() & 0xc0000000))) // no overflow | |
14957cd0 | 1979 | callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() + src2.asInt32()); |
9dae56ea | 1980 | else { |
ba379fdc | 1981 | JSValue result = jsAdd(callFrame, src1, src2); |
9dae56ea | 1982 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 1983 | callFrame->uncheckedR(dst) = result; |
9dae56ea | 1984 | } |
f9bf01c6 | 1985 | vPC += OPCODE_LENGTH(op_add); |
9dae56ea A |
1986 | NEXT_INSTRUCTION(); |
1987 | } | |
1988 | DEFINE_OPCODE(op_mul) { | |
1989 | /* mul dst(r) src1(r) src2(r) | |
1990 | ||
1991 | Multiplies register src1 and register src2 (converted to | |
1992 | numbers), and puts the product in register dst. | |
1993 | */ | |
f9bf01c6 A |
1994 | int dst = vPC[1].u.operand; |
1995 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
1996 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 1997 | if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | src2.asInt32() >> 15)) // no overflow |
14957cd0 | 1998 | callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() * src2.asInt32()); |
9dae56ea | 1999 | else { |
14957cd0 | 2000 | JSValue result = jsNumber(src1.toNumber(callFrame) * src2.toNumber(callFrame)); |
9dae56ea | 2001 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2002 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
2003 | } |
2004 | ||
f9bf01c6 | 2005 | vPC += OPCODE_LENGTH(op_mul); |
9dae56ea A |
2006 | NEXT_INSTRUCTION(); |
2007 | } | |
2008 | DEFINE_OPCODE(op_div) { | |
2009 | /* div dst(r) dividend(r) divisor(r) | |
2010 | ||
2011 | Divides register dividend (converted to number) by the | |
2012 | register divisor (converted to number), and puts the | |
2013 | quotient in register dst. | |
2014 | */ | |
f9bf01c6 A |
2015 | int dst = vPC[1].u.operand; |
2016 | JSValue dividend = callFrame->r(vPC[2].u.operand).jsValue(); | |
2017 | JSValue divisor = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 2018 | |
14957cd0 | 2019 | JSValue result = jsNumber(dividend.toNumber(callFrame) / divisor.toNumber(callFrame)); |
ba379fdc | 2020 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2021 | callFrame->uncheckedR(dst) = result; |
ba379fdc | 2022 | |
f9bf01c6 | 2023 | vPC += OPCODE_LENGTH(op_div); |
9dae56ea A |
2024 | NEXT_INSTRUCTION(); |
2025 | } | |
2026 | DEFINE_OPCODE(op_mod) { | |
2027 | /* mod dst(r) dividend(r) divisor(r) | |
2028 | ||
2029 | Divides register dividend (converted to number) by | |
2030 | register divisor (converted to number), and puts the | |
2031 | remainder in register dst. | |
2032 | */ | |
f9bf01c6 A |
2033 | int dst = vPC[1].u.operand; |
2034 | JSValue dividend = callFrame->r(vPC[2].u.operand).jsValue(); | |
2035 | JSValue divisor = callFrame->r(vPC[3].u.operand).jsValue(); | |
9dae56ea | 2036 | |
ba379fdc | 2037 | if (dividend.isInt32() && divisor.isInt32() && divisor.asInt32() != 0) { |
14957cd0 | 2038 | JSValue result = jsNumber(dividend.asInt32() % divisor.asInt32()); |
9dae56ea | 2039 | ASSERT(result); |
14957cd0 | 2040 | callFrame->uncheckedR(dst) = result; |
f9bf01c6 | 2041 | vPC += OPCODE_LENGTH(op_mod); |
9dae56ea A |
2042 | NEXT_INSTRUCTION(); |
2043 | } | |
2044 | ||
ba379fdc A |
2045 | // Conversion to double must happen outside the call to fmod since the |
2046 | // order of argument evaluation is not guaranteed. | |
2047 | double d1 = dividend.toNumber(callFrame); | |
2048 | double d2 = divisor.toNumber(callFrame); | |
14957cd0 | 2049 | JSValue result = jsNumber(fmod(d1, d2)); |
9dae56ea | 2050 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2051 | callFrame->uncheckedR(dst) = result; |
f9bf01c6 | 2052 | vPC += OPCODE_LENGTH(op_mod); |
9dae56ea A |
2053 | NEXT_INSTRUCTION(); |
2054 | } | |
2055 | DEFINE_OPCODE(op_sub) { | |
2056 | /* sub dst(r) src1(r) src2(r) | |
2057 | ||
2058 | Subtracts register src2 (converted to number) from register | |
2059 | src1 (converted to number), and puts the difference in | |
2060 | register dst. | |
2061 | */ | |
f9bf01c6 A |
2062 | int dst = vPC[1].u.operand; |
2063 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
2064 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
2065 | if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | (src2.asInt32() & 0xc0000000))) // no overflow | |
14957cd0 | 2066 | callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() - src2.asInt32()); |
9dae56ea | 2067 | else { |
14957cd0 | 2068 | JSValue result = jsNumber(src1.toNumber(callFrame) - src2.toNumber(callFrame)); |
9dae56ea | 2069 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2070 | callFrame->uncheckedR(dst) = result; |
9dae56ea | 2071 | } |
f9bf01c6 | 2072 | vPC += OPCODE_LENGTH(op_sub); |
9dae56ea A |
2073 | NEXT_INSTRUCTION(); |
2074 | } | |
2075 | DEFINE_OPCODE(op_lshift) { | |
2076 | /* lshift dst(r) val(r) shift(r) | |
2077 | ||
2078 | Performs left shift of register val (converted to int32) by | |
2079 | register shift (converted to uint32), and puts the result | |
2080 | in register dst. | |
2081 | */ | |
f9bf01c6 A |
2082 | int dst = vPC[1].u.operand; |
2083 | JSValue val = callFrame->r(vPC[2].u.operand).jsValue(); | |
2084 | JSValue shift = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc A |
2085 | |
2086 | if (val.isInt32() && shift.isInt32()) | |
14957cd0 | 2087 | callFrame->uncheckedR(dst) = jsNumber(val.asInt32() << (shift.asInt32() & 0x1f)); |
9dae56ea | 2088 | else { |
14957cd0 | 2089 | JSValue result = jsNumber((val.toInt32(callFrame)) << (shift.toUInt32(callFrame) & 0x1f)); |
9dae56ea | 2090 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2091 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
2092 | } |
2093 | ||
f9bf01c6 | 2094 | vPC += OPCODE_LENGTH(op_lshift); |
9dae56ea A |
2095 | NEXT_INSTRUCTION(); |
2096 | } | |
2097 | DEFINE_OPCODE(op_rshift) { | |
2098 | /* rshift dst(r) val(r) shift(r) | |
2099 | ||
2100 | Performs arithmetic right shift of register val (converted | |
2101 | to int32) by register shift (converted to | |
2102 | uint32), and puts the result in register dst. | |
2103 | */ | |
f9bf01c6 A |
2104 | int dst = vPC[1].u.operand; |
2105 | JSValue val = callFrame->r(vPC[2].u.operand).jsValue(); | |
2106 | JSValue shift = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc A |
2107 | |
2108 | if (val.isInt32() && shift.isInt32()) | |
14957cd0 | 2109 | callFrame->uncheckedR(dst) = jsNumber(val.asInt32() >> (shift.asInt32() & 0x1f)); |
9dae56ea | 2110 | else { |
14957cd0 | 2111 | JSValue result = jsNumber((val.toInt32(callFrame)) >> (shift.toUInt32(callFrame) & 0x1f)); |
9dae56ea | 2112 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2113 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
2114 | } |
2115 | ||
f9bf01c6 | 2116 | vPC += OPCODE_LENGTH(op_rshift); |
9dae56ea A |
2117 | NEXT_INSTRUCTION(); |
2118 | } | |
2119 | DEFINE_OPCODE(op_urshift) { | |
2120 | /* rshift dst(r) val(r) shift(r) | |
2121 | ||
2122 | Performs logical right shift of register val (converted | |
2123 | to uint32) by register shift (converted to | |
2124 | uint32), and puts the result in register dst. | |
2125 | */ | |
f9bf01c6 A |
2126 | int dst = vPC[1].u.operand; |
2127 | JSValue val = callFrame->r(vPC[2].u.operand).jsValue(); | |
2128 | JSValue shift = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 2129 | if (val.isUInt32() && shift.isInt32()) |
14957cd0 | 2130 | callFrame->uncheckedR(dst) = jsNumber(val.asInt32() >> (shift.asInt32() & 0x1f)); |
9dae56ea | 2131 | else { |
14957cd0 | 2132 | JSValue result = jsNumber((val.toUInt32(callFrame)) >> (shift.toUInt32(callFrame) & 0x1f)); |
9dae56ea | 2133 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2134 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
2135 | } |
2136 | ||
f9bf01c6 | 2137 | vPC += OPCODE_LENGTH(op_urshift); |
9dae56ea A |
2138 | NEXT_INSTRUCTION(); |
2139 | } | |
2140 | DEFINE_OPCODE(op_bitand) { | |
2141 | /* bitand dst(r) src1(r) src2(r) | |
2142 | ||
2143 | Computes bitwise AND of register src1 (converted to int32) | |
2144 | and register src2 (converted to int32), and puts the result | |
2145 | in register dst. | |
2146 | */ | |
f9bf01c6 A |
2147 | int dst = vPC[1].u.operand; |
2148 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
2149 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 2150 | if (src1.isInt32() && src2.isInt32()) |
14957cd0 | 2151 | callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() & src2.asInt32()); |
9dae56ea | 2152 | else { |
14957cd0 | 2153 | JSValue result = jsNumber(src1.toInt32(callFrame) & src2.toInt32(callFrame)); |
9dae56ea | 2154 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2155 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
2156 | } |
2157 | ||
f9bf01c6 | 2158 | vPC += OPCODE_LENGTH(op_bitand); |
9dae56ea A |
2159 | NEXT_INSTRUCTION(); |
2160 | } | |
2161 | DEFINE_OPCODE(op_bitxor) { | |
2162 | /* bitxor dst(r) src1(r) src2(r) | |
2163 | ||
2164 | Computes bitwise XOR of register src1 (converted to int32) | |
2165 | and register src2 (converted to int32), and puts the result | |
2166 | in register dst. | |
2167 | */ | |
f9bf01c6 A |
2168 | int dst = vPC[1].u.operand; |
2169 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
2170 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 2171 | if (src1.isInt32() && src2.isInt32()) |
14957cd0 | 2172 | callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() ^ src2.asInt32()); |
9dae56ea | 2173 | else { |
14957cd0 | 2174 | JSValue result = jsNumber(src1.toInt32(callFrame) ^ src2.toInt32(callFrame)); |
9dae56ea | 2175 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2176 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
2177 | } |
2178 | ||
f9bf01c6 | 2179 | vPC += OPCODE_LENGTH(op_bitxor); |
9dae56ea A |
2180 | NEXT_INSTRUCTION(); |
2181 | } | |
2182 | DEFINE_OPCODE(op_bitor) { | |
2183 | /* bitor dst(r) src1(r) src2(r) | |
2184 | ||
2185 | Computes bitwise OR of register src1 (converted to int32) | |
2186 | and register src2 (converted to int32), and puts the | |
2187 | result in register dst. | |
2188 | */ | |
f9bf01c6 A |
2189 | int dst = vPC[1].u.operand; |
2190 | JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); | |
2191 | JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 2192 | if (src1.isInt32() && src2.isInt32()) |
14957cd0 | 2193 | callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() | src2.asInt32()); |
9dae56ea | 2194 | else { |
14957cd0 | 2195 | JSValue result = jsNumber(src1.toInt32(callFrame) | src2.toInt32(callFrame)); |
9dae56ea | 2196 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2197 | callFrame->uncheckedR(dst) = result; |
9dae56ea A |
2198 | } |
2199 | ||
f9bf01c6 | 2200 | vPC += OPCODE_LENGTH(op_bitor); |
9dae56ea A |
2201 | NEXT_INSTRUCTION(); |
2202 | } | |
2203 | DEFINE_OPCODE(op_bitnot) { | |
2204 | /* bitnot dst(r) src(r) | |
2205 | ||
2206 | Computes bitwise NOT of register src1 (converted to int32), | |
2207 | and puts the result in register dst. | |
2208 | */ | |
f9bf01c6 A |
2209 | int dst = vPC[1].u.operand; |
2210 | JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); | |
ba379fdc | 2211 | if (src.isInt32()) |
14957cd0 | 2212 | callFrame->uncheckedR(dst) = jsNumber(~src.asInt32()); |
9dae56ea | 2213 | else { |
14957cd0 | 2214 | JSValue result = jsNumber(~src.toInt32(callFrame)); |
9dae56ea | 2215 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2216 | callFrame->uncheckedR(dst) = result; |
9dae56ea | 2217 | } |
f9bf01c6 | 2218 | vPC += OPCODE_LENGTH(op_bitnot); |
9dae56ea A |
2219 | NEXT_INSTRUCTION(); |
2220 | } | |
2221 | DEFINE_OPCODE(op_not) { | |
2222 | /* not dst(r) src(r) | |
2223 | ||
2224 | Computes logical NOT of register src (converted to | |
2225 | boolean), and puts the result in register dst. | |
2226 | */ | |
f9bf01c6 A |
2227 | int dst = vPC[1].u.operand; |
2228 | int src = vPC[2].u.operand; | |
ba379fdc | 2229 | JSValue result = jsBoolean(!callFrame->r(src).jsValue().toBoolean(callFrame)); |
9dae56ea | 2230 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 2231 | callFrame->uncheckedR(dst) = result; |
9dae56ea | 2232 | |
f9bf01c6 | 2233 | vPC += OPCODE_LENGTH(op_not); |
9dae56ea A |
2234 | NEXT_INSTRUCTION(); |
2235 | } | |
14957cd0 A |
2236 | DEFINE_OPCODE(op_check_has_instance) { |
2237 | /* check_has_instance constructor(r) | |
2238 | ||
2239 | Check 'constructor' is an object with the internal property | |
2240 | [HasInstance] (i.e. is a function ... *shakes head sadly at | |
2241 | JSC API*). Raises an exception if register constructor is not | |
2242 | an valid parameter for instanceof. | |
2243 | */ | |
2244 | int base = vPC[1].u.operand; | |
2245 | JSValue baseVal = callFrame->r(base).jsValue(); | |
2246 | ||
2247 | if (isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue)) | |
2248 | goto vm_throw; | |
2249 | ||
2250 | vPC += OPCODE_LENGTH(op_check_has_instance); | |
2251 | NEXT_INSTRUCTION(); | |
2252 | } | |
9dae56ea A |
2253 | DEFINE_OPCODE(op_instanceof) { |
2254 | /* instanceof dst(r) value(r) constructor(r) constructorProto(r) | |
2255 | ||
2256 | Tests whether register value is an instance of register | |
2257 | constructor, and puts the boolean result in register | |
2258 | dst. Register constructorProto must contain the "prototype" | |
2259 | property (not the actual prototype) of the object in | |
2260 | register constructor. This lookup is separated so that | |
2261 | polymorphic inline caching can apply. | |
2262 | ||
2263 | Raises an exception if register constructor is not an | |
2264 | object. | |
2265 | */ | |
2266 | int dst = vPC[1].u.operand; | |
2267 | int value = vPC[2].u.operand; | |
2268 | int base = vPC[3].u.operand; | |
2269 | int baseProto = vPC[4].u.operand; | |
2270 | ||
ba379fdc | 2271 | JSValue baseVal = callFrame->r(base).jsValue(); |
9dae56ea | 2272 | |
14957cd0 | 2273 | ASSERT(!isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue)); |
9dae56ea | 2274 | |
ba379fdc A |
2275 | bool result = asObject(baseVal)->hasInstance(callFrame, callFrame->r(value).jsValue(), callFrame->r(baseProto).jsValue()); |
2276 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 2277 | callFrame->uncheckedR(dst) = jsBoolean(result); |
9dae56ea | 2278 | |
f9bf01c6 | 2279 | vPC += OPCODE_LENGTH(op_instanceof); |
9dae56ea A |
2280 | NEXT_INSTRUCTION(); |
2281 | } | |
2282 | DEFINE_OPCODE(op_typeof) { | |
2283 | /* typeof dst(r) src(r) | |
2284 | ||
2285 | Determines the type string for src according to ECMAScript | |
2286 | rules, and puts the result in register dst. | |
2287 | */ | |
f9bf01c6 A |
2288 | int dst = vPC[1].u.operand; |
2289 | int src = vPC[2].u.operand; | |
14957cd0 | 2290 | callFrame->uncheckedR(dst) = JSValue(jsTypeStringForValue(callFrame, callFrame->r(src).jsValue())); |
9dae56ea | 2291 | |
f9bf01c6 | 2292 | vPC += OPCODE_LENGTH(op_typeof); |
9dae56ea A |
2293 | NEXT_INSTRUCTION(); |
2294 | } | |
2295 | DEFINE_OPCODE(op_is_undefined) { | |
2296 | /* is_undefined dst(r) src(r) | |
2297 | ||
2298 | Determines whether the type string for src according to | |
2299 | the ECMAScript rules is "undefined", and puts the result | |
2300 | in register dst. | |
2301 | */ | |
f9bf01c6 A |
2302 | int dst = vPC[1].u.operand; |
2303 | int src = vPC[2].u.operand; | |
ba379fdc | 2304 | JSValue v = callFrame->r(src).jsValue(); |
14957cd0 | 2305 | callFrame->uncheckedR(dst) = jsBoolean(v.isCell() ? v.asCell()->structure()->typeInfo().masqueradesAsUndefined() : v.isUndefined()); |
9dae56ea | 2306 | |
f9bf01c6 | 2307 | vPC += OPCODE_LENGTH(op_is_undefined); |
9dae56ea A |
2308 | NEXT_INSTRUCTION(); |
2309 | } | |
2310 | DEFINE_OPCODE(op_is_boolean) { | |
2311 | /* is_boolean dst(r) src(r) | |
2312 | ||
2313 | Determines whether the type string for src according to | |
2314 | the ECMAScript rules is "boolean", and puts the result | |
2315 | in register dst. | |
2316 | */ | |
f9bf01c6 A |
2317 | int dst = vPC[1].u.operand; |
2318 | int src = vPC[2].u.operand; | |
14957cd0 | 2319 | callFrame->uncheckedR(dst) = jsBoolean(callFrame->r(src).jsValue().isBoolean()); |
9dae56ea | 2320 | |
f9bf01c6 | 2321 | vPC += OPCODE_LENGTH(op_is_boolean); |
9dae56ea A |
2322 | NEXT_INSTRUCTION(); |
2323 | } | |
2324 | DEFINE_OPCODE(op_is_number) { | |
2325 | /* is_number dst(r) src(r) | |
2326 | ||
2327 | Determines whether the type string for src according to | |
2328 | the ECMAScript rules is "number", and puts the result | |
2329 | in register dst. | |
2330 | */ | |
f9bf01c6 A |
2331 | int dst = vPC[1].u.operand; |
2332 | int src = vPC[2].u.operand; | |
14957cd0 | 2333 | callFrame->uncheckedR(dst) = jsBoolean(callFrame->r(src).jsValue().isNumber()); |
9dae56ea | 2334 | |
f9bf01c6 | 2335 | vPC += OPCODE_LENGTH(op_is_number); |
9dae56ea A |
2336 | NEXT_INSTRUCTION(); |
2337 | } | |
2338 | DEFINE_OPCODE(op_is_string) { | |
2339 | /* is_string dst(r) src(r) | |
2340 | ||
2341 | Determines whether the type string for src according to | |
2342 | the ECMAScript rules is "string", and puts the result | |
2343 | in register dst. | |
2344 | */ | |
f9bf01c6 A |
2345 | int dst = vPC[1].u.operand; |
2346 | int src = vPC[2].u.operand; | |
14957cd0 | 2347 | callFrame->uncheckedR(dst) = jsBoolean(callFrame->r(src).jsValue().isString()); |
9dae56ea | 2348 | |
f9bf01c6 | 2349 | vPC += OPCODE_LENGTH(op_is_string); |
9dae56ea A |
2350 | NEXT_INSTRUCTION(); |
2351 | } | |
2352 | DEFINE_OPCODE(op_is_object) { | |
2353 | /* is_object dst(r) src(r) | |
2354 | ||
2355 | Determines whether the type string for src according to | |
2356 | the ECMAScript rules is "object", and puts the result | |
2357 | in register dst. | |
2358 | */ | |
f9bf01c6 A |
2359 | int dst = vPC[1].u.operand; |
2360 | int src = vPC[2].u.operand; | |
14957cd0 | 2361 | callFrame->uncheckedR(dst) = jsBoolean(jsIsObjectType(callFrame->r(src).jsValue())); |
9dae56ea | 2362 | |
f9bf01c6 | 2363 | vPC += OPCODE_LENGTH(op_is_object); |
9dae56ea A |
2364 | NEXT_INSTRUCTION(); |
2365 | } | |
2366 | DEFINE_OPCODE(op_is_function) { | |
2367 | /* is_function dst(r) src(r) | |
2368 | ||
2369 | Determines whether the type string for src according to | |
2370 | the ECMAScript rules is "function", and puts the result | |
2371 | in register dst. | |
2372 | */ | |
f9bf01c6 A |
2373 | int dst = vPC[1].u.operand; |
2374 | int src = vPC[2].u.operand; | |
14957cd0 | 2375 | callFrame->uncheckedR(dst) = jsBoolean(jsIsFunctionType(callFrame->r(src).jsValue())); |
9dae56ea | 2376 | |
f9bf01c6 | 2377 | vPC += OPCODE_LENGTH(op_is_function); |
9dae56ea A |
2378 | NEXT_INSTRUCTION(); |
2379 | } | |
2380 | DEFINE_OPCODE(op_in) { | |
2381 | /* in dst(r) property(r) base(r) | |
2382 | ||
2383 | Tests whether register base has a property named register | |
2384 | property, and puts the boolean result in register dst. | |
2385 | ||
2386 | Raises an exception if register constructor is not an | |
2387 | object. | |
2388 | */ | |
f9bf01c6 A |
2389 | int dst = vPC[1].u.operand; |
2390 | int property = vPC[2].u.operand; | |
2391 | int base = vPC[3].u.operand; | |
9dae56ea | 2392 | |
ba379fdc | 2393 | JSValue baseVal = callFrame->r(base).jsValue(); |
14957cd0 | 2394 | if (isInvalidParamForIn(callFrame, baseVal, exceptionValue)) |
9dae56ea A |
2395 | goto vm_throw; |
2396 | ||
2397 | JSObject* baseObj = asObject(baseVal); | |
2398 | ||
ba379fdc | 2399 | JSValue propName = callFrame->r(property).jsValue(); |
9dae56ea A |
2400 | |
2401 | uint32_t i; | |
2402 | if (propName.getUInt32(i)) | |
14957cd0 | 2403 | callFrame->uncheckedR(dst) = jsBoolean(baseObj->hasProperty(callFrame, i)); |
9dae56ea A |
2404 | else { |
2405 | Identifier property(callFrame, propName.toString(callFrame)); | |
2406 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 2407 | callFrame->uncheckedR(dst) = jsBoolean(baseObj->hasProperty(callFrame, property)); |
9dae56ea A |
2408 | } |
2409 | ||
f9bf01c6 | 2410 | vPC += OPCODE_LENGTH(op_in); |
9dae56ea A |
2411 | NEXT_INSTRUCTION(); |
2412 | } | |
2413 | DEFINE_OPCODE(op_resolve) { | |
2414 | /* resolve dst(r) property(id) | |
2415 | ||
2416 | Looks up the property named by identifier property in the | |
2417 | scope chain, and writes the resulting value to register | |
2418 | dst. If the property is not found, raises an exception. | |
2419 | */ | |
2420 | if (UNLIKELY(!resolve(callFrame, vPC, exceptionValue))) | |
2421 | goto vm_throw; | |
2422 | ||
f9bf01c6 | 2423 | vPC += OPCODE_LENGTH(op_resolve); |
9dae56ea A |
2424 | NEXT_INSTRUCTION(); |
2425 | } | |
2426 | DEFINE_OPCODE(op_resolve_skip) { | |
2427 | /* resolve_skip dst(r) property(id) skip(n) | |
2428 | ||
2429 | Looks up the property named by identifier property in the | |
2430 | scope chain skipping the top 'skip' levels, and writes the resulting | |
2431 | value to register dst. If the property is not found, raises an exception. | |
2432 | */ | |
2433 | if (UNLIKELY(!resolveSkip(callFrame, vPC, exceptionValue))) | |
2434 | goto vm_throw; | |
2435 | ||
f9bf01c6 | 2436 | vPC += OPCODE_LENGTH(op_resolve_skip); |
9dae56ea A |
2437 | |
2438 | NEXT_INSTRUCTION(); | |
2439 | } | |
2440 | DEFINE_OPCODE(op_resolve_global) { | |
2441 | /* resolve_skip dst(r) globalObject(c) property(id) structure(sID) offset(n) | |
2442 | ||
2443 | Performs a dynamic property lookup for the given property, on the provided | |
2444 | global object. If structure matches the Structure of the global then perform | |
2445 | a fast lookup using the case offset, otherwise fall back to a full resolve and | |
2446 | cache the new structure and offset | |
2447 | */ | |
2448 | if (UNLIKELY(!resolveGlobal(callFrame, vPC, exceptionValue))) | |
2449 | goto vm_throw; | |
2450 | ||
f9bf01c6 | 2451 | vPC += OPCODE_LENGTH(op_resolve_global); |
9dae56ea A |
2452 | |
2453 | NEXT_INSTRUCTION(); | |
2454 | } | |
4e4e5a6f A |
2455 | DEFINE_OPCODE(op_resolve_global_dynamic) { |
2456 | /* resolve_skip dst(r) globalObject(c) property(id) structure(sID) offset(n), depth(n) | |
2457 | ||
2458 | Performs a dynamic property lookup for the given property, on the provided | |
2459 | global object. If structure matches the Structure of the global then perform | |
2460 | a fast lookup using the case offset, otherwise fall back to a full resolve and | |
2461 | cache the new structure and offset. | |
2462 | ||
2463 | This walks through n levels of the scope chain to verify that none of those levels | |
2464 | in the scope chain include dynamically added properties. | |
2465 | */ | |
2466 | if (UNLIKELY(!resolveGlobalDynamic(callFrame, vPC, exceptionValue))) | |
2467 | goto vm_throw; | |
2468 | ||
2469 | vPC += OPCODE_LENGTH(op_resolve_global_dynamic); | |
2470 | ||
2471 | NEXT_INSTRUCTION(); | |
2472 | } | |
9dae56ea A |
2473 | DEFINE_OPCODE(op_get_global_var) { |
2474 | /* get_global_var dst(r) globalObject(c) index(n) | |
2475 | ||
2476 | Gets the global var at global slot index and places it in register dst. | |
2477 | */ | |
f9bf01c6 | 2478 | int dst = vPC[1].u.operand; |
14957cd0 | 2479 | JSGlobalObject* scope = codeBlock->globalObject(); |
9dae56ea | 2480 | ASSERT(scope->isGlobalObject()); |
14957cd0 | 2481 | int index = vPC[2].u.operand; |
9dae56ea | 2482 | |
14957cd0 | 2483 | callFrame->uncheckedR(dst) = scope->registerAt(index).get(); |
f9bf01c6 | 2484 | vPC += OPCODE_LENGTH(op_get_global_var); |
9dae56ea A |
2485 | NEXT_INSTRUCTION(); |
2486 | } | |
2487 | DEFINE_OPCODE(op_put_global_var) { | |
2488 | /* put_global_var globalObject(c) index(n) value(r) | |
2489 | ||
2490 | Puts value into global slot index. | |
2491 | */ | |
14957cd0 | 2492 | JSGlobalObject* scope = codeBlock->globalObject(); |
9dae56ea | 2493 | ASSERT(scope->isGlobalObject()); |
14957cd0 A |
2494 | int index = vPC[1].u.operand; |
2495 | int value = vPC[2].u.operand; | |
9dae56ea | 2496 | |
14957cd0 | 2497 | scope->registerAt(index).set(*globalData, scope, callFrame->r(value).jsValue()); |
f9bf01c6 | 2498 | vPC += OPCODE_LENGTH(op_put_global_var); |
9dae56ea | 2499 | NEXT_INSTRUCTION(); |
4e4e5a6f | 2500 | } |
9dae56ea A |
2501 | DEFINE_OPCODE(op_get_scoped_var) { |
2502 | /* get_scoped_var dst(r) index(n) skip(n) | |
2503 | ||
2504 | Loads the contents of the index-th local from the scope skip nodes from | |
14957cd0 | 2505 | the top of the scope chain, and places it in register dst. |
9dae56ea | 2506 | */ |
f9bf01c6 A |
2507 | int dst = vPC[1].u.operand; |
2508 | int index = vPC[2].u.operand; | |
14957cd0 | 2509 | int skip = vPC[3].u.operand; |
9dae56ea A |
2510 | |
2511 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
2512 | ScopeChainIterator iter = scopeChain->begin(); | |
2513 | ScopeChainIterator end = scopeChain->end(); | |
2514 | ASSERT(iter != end); | |
14957cd0 A |
2515 | ASSERT(codeBlock == callFrame->codeBlock()); |
2516 | bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); | |
2517 | ASSERT(skip || !checkTopLevel); | |
2518 | if (checkTopLevel && skip--) { | |
2519 | if (callFrame->r(codeBlock->activationRegister()).jsValue()) | |
2520 | ++iter; | |
2521 | } | |
9dae56ea A |
2522 | while (skip--) { |
2523 | ++iter; | |
2524 | ASSERT(iter != end); | |
2525 | } | |
9dae56ea | 2526 | ASSERT((*iter)->isVariableObject()); |
14957cd0 A |
2527 | JSVariableObject* scope = static_cast<JSVariableObject*>(iter->get()); |
2528 | callFrame->uncheckedR(dst) = scope->registerAt(index).get(); | |
2529 | ASSERT(callFrame->r(dst).jsValue()); | |
f9bf01c6 | 2530 | vPC += OPCODE_LENGTH(op_get_scoped_var); |
9dae56ea A |
2531 | NEXT_INSTRUCTION(); |
2532 | } | |
2533 | DEFINE_OPCODE(op_put_scoped_var) { | |
2534 | /* put_scoped_var index(n) skip(n) value(r) | |
2535 | ||
2536 | */ | |
f9bf01c6 | 2537 | int index = vPC[1].u.operand; |
14957cd0 | 2538 | int skip = vPC[2].u.operand; |
f9bf01c6 | 2539 | int value = vPC[3].u.operand; |
9dae56ea A |
2540 | |
2541 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
2542 | ScopeChainIterator iter = scopeChain->begin(); | |
2543 | ScopeChainIterator end = scopeChain->end(); | |
14957cd0 | 2544 | ASSERT(codeBlock == callFrame->codeBlock()); |
9dae56ea | 2545 | ASSERT(iter != end); |
14957cd0 A |
2546 | bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); |
2547 | ASSERT(skip || !checkTopLevel); | |
2548 | if (checkTopLevel && skip--) { | |
2549 | if (callFrame->r(codeBlock->activationRegister()).jsValue()) | |
2550 | ++iter; | |
2551 | } | |
9dae56ea A |
2552 | while (skip--) { |
2553 | ++iter; | |
2554 | ASSERT(iter != end); | |
2555 | } | |
2556 | ||
2557 | ASSERT((*iter)->isVariableObject()); | |
14957cd0 A |
2558 | JSVariableObject* scope = static_cast<JSVariableObject*>(iter->get()); |
2559 | ASSERT(callFrame->r(value).jsValue()); | |
2560 | scope->registerAt(index).set(*globalData, scope, callFrame->r(value).jsValue()); | |
f9bf01c6 | 2561 | vPC += OPCODE_LENGTH(op_put_scoped_var); |
9dae56ea A |
2562 | NEXT_INSTRUCTION(); |
2563 | } | |
2564 | DEFINE_OPCODE(op_resolve_base) { | |
14957cd0 | 2565 | /* resolve_base dst(r) property(id) isStrict(bool) |
9dae56ea A |
2566 | |
2567 | Searches the scope chain for an object containing | |
2568 | identifier property, and if one is found, writes it to | |
14957cd0 A |
2569 | register dst. If none is found and isStrict is false, the |
2570 | outermost scope (which will be the global object) is | |
2571 | stored in register dst. | |
9dae56ea A |
2572 | */ |
2573 | resolveBase(callFrame, vPC); | |
14957cd0 | 2574 | CHECK_FOR_EXCEPTION(); |
9dae56ea | 2575 | |
f9bf01c6 | 2576 | vPC += OPCODE_LENGTH(op_resolve_base); |
9dae56ea A |
2577 | NEXT_INSTRUCTION(); |
2578 | } | |
14957cd0 A |
2579 | DEFINE_OPCODE(op_ensure_property_exists) { |
2580 | /* ensure_property_exists base(r) property(id) | |
2581 | ||
2582 | Throws an exception if property does not exist on base | |
2583 | */ | |
2584 | int base = vPC[1].u.operand; | |
2585 | int property = vPC[2].u.operand; | |
2586 | Identifier& ident = codeBlock->identifier(property); | |
2587 | ||
2588 | JSValue baseVal = callFrame->r(base).jsValue(); | |
2589 | JSObject* baseObject = asObject(baseVal); | |
2590 | PropertySlot slot(baseVal); | |
2591 | if (!baseObject->getPropertySlot(callFrame, ident, slot)) { | |
2592 | exceptionValue = createErrorForInvalidGlobalAssignment(callFrame, ident.ustring()); | |
2593 | goto vm_throw; | |
2594 | } | |
2595 | ||
2596 | vPC += OPCODE_LENGTH(op_ensure_property_exists); | |
2597 | NEXT_INSTRUCTION(); | |
2598 | } | |
9dae56ea A |
2599 | DEFINE_OPCODE(op_resolve_with_base) { |
2600 | /* resolve_with_base baseDst(r) propDst(r) property(id) | |
2601 | ||
2602 | Searches the scope chain for an object containing | |
2603 | identifier property, and if one is found, writes it to | |
2604 | register srcDst, and the retrieved property value to register | |
2605 | propDst. If the property is not found, raises an exception. | |
2606 | ||
2607 | This is more efficient than doing resolve_base followed by | |
2608 | resolve, or resolve_base followed by get_by_id, as it | |
2609 | avoids duplicate hash lookups. | |
2610 | */ | |
2611 | if (UNLIKELY(!resolveBaseAndProperty(callFrame, vPC, exceptionValue))) | |
2612 | goto vm_throw; | |
2613 | ||
f9bf01c6 | 2614 | vPC += OPCODE_LENGTH(op_resolve_with_base); |
9dae56ea A |
2615 | NEXT_INSTRUCTION(); |
2616 | } | |
9dae56ea A |
2617 | DEFINE_OPCODE(op_get_by_id) { |
2618 | /* get_by_id dst(r) base(r) property(id) structure(sID) nop(n) nop(n) nop(n) | |
2619 | ||
2620 | Generic property access: Gets the property named by identifier | |
2621 | property from the value base, and puts the result in register dst. | |
2622 | */ | |
2623 | int dst = vPC[1].u.operand; | |
2624 | int base = vPC[2].u.operand; | |
2625 | int property = vPC[3].u.operand; | |
2626 | ||
9dae56ea | 2627 | Identifier& ident = codeBlock->identifier(property); |
ba379fdc | 2628 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea | 2629 | PropertySlot slot(baseValue); |
ba379fdc | 2630 | JSValue result = baseValue.get(callFrame, ident, slot); |
9dae56ea A |
2631 | CHECK_FOR_EXCEPTION(); |
2632 | ||
2633 | tryCacheGetByID(callFrame, codeBlock, vPC, baseValue, ident, slot); | |
2634 | ||
14957cd0 | 2635 | callFrame->uncheckedR(dst) = result; |
f9bf01c6 | 2636 | vPC += OPCODE_LENGTH(op_get_by_id); |
9dae56ea A |
2637 | NEXT_INSTRUCTION(); |
2638 | } | |
2639 | DEFINE_OPCODE(op_get_by_id_self) { | |
2640 | /* op_get_by_id_self dst(r) base(r) property(id) structure(sID) offset(n) nop(n) nop(n) | |
2641 | ||
2642 | Cached property access: Attempts to get a cached property from the | |
2643 | value base. If the cache misses, op_get_by_id_self reverts to | |
2644 | op_get_by_id. | |
2645 | */ | |
2646 | int base = vPC[2].u.operand; | |
ba379fdc | 2647 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea A |
2648 | |
2649 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2650 | JSCell* baseCell = baseValue.asCell(); |
2651 | Structure* structure = vPC[4].u.structure.get(); | |
9dae56ea A |
2652 | |
2653 | if (LIKELY(baseCell->structure() == structure)) { | |
2654 | ASSERT(baseCell->isObject()); | |
2655 | JSObject* baseObject = asObject(baseCell); | |
2656 | int dst = vPC[1].u.operand; | |
2657 | int offset = vPC[5].u.operand; | |
2658 | ||
14957cd0 A |
2659 | ASSERT(baseObject->get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); |
2660 | callFrame->uncheckedR(dst) = JSValue(baseObject->getDirectOffset(offset)); | |
9dae56ea | 2661 | |
f9bf01c6 | 2662 | vPC += OPCODE_LENGTH(op_get_by_id_self); |
9dae56ea A |
2663 | NEXT_INSTRUCTION(); |
2664 | } | |
2665 | } | |
2666 | ||
14957cd0 | 2667 | uncacheGetByID(codeBlock, vPC); |
9dae56ea A |
2668 | NEXT_INSTRUCTION(); |
2669 | } | |
2670 | DEFINE_OPCODE(op_get_by_id_proto) { | |
2671 | /* op_get_by_id_proto dst(r) base(r) property(id) structure(sID) prototypeStructure(sID) offset(n) nop(n) | |
2672 | ||
2673 | Cached property access: Attempts to get a cached property from the | |
2674 | value base's prototype. If the cache misses, op_get_by_id_proto | |
2675 | reverts to op_get_by_id. | |
2676 | */ | |
2677 | int base = vPC[2].u.operand; | |
ba379fdc | 2678 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea A |
2679 | |
2680 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2681 | JSCell* baseCell = baseValue.asCell(); |
2682 | Structure* structure = vPC[4].u.structure.get(); | |
9dae56ea A |
2683 | |
2684 | if (LIKELY(baseCell->structure() == structure)) { | |
2685 | ASSERT(structure->prototypeForLookup(callFrame).isObject()); | |
2686 | JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); | |
14957cd0 | 2687 | Structure* prototypeStructure = vPC[5].u.structure.get(); |
9dae56ea A |
2688 | |
2689 | if (LIKELY(protoObject->structure() == prototypeStructure)) { | |
2690 | int dst = vPC[1].u.operand; | |
2691 | int offset = vPC[6].u.operand; | |
2692 | ||
14957cd0 A |
2693 | ASSERT(protoObject->get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == protoObject->getDirectOffset(offset)); |
2694 | ASSERT(baseValue.get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == protoObject->getDirectOffset(offset)); | |
2695 | callFrame->uncheckedR(dst) = JSValue(protoObject->getDirectOffset(offset)); | |
9dae56ea | 2696 | |
f9bf01c6 | 2697 | vPC += OPCODE_LENGTH(op_get_by_id_proto); |
9dae56ea A |
2698 | NEXT_INSTRUCTION(); |
2699 | } | |
2700 | } | |
2701 | } | |
2702 | ||
14957cd0 | 2703 | uncacheGetByID(codeBlock, vPC); |
9dae56ea A |
2704 | NEXT_INSTRUCTION(); |
2705 | } | |
4e4e5a6f A |
2706 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) |
2707 | goto *(&&skip_id_getter_proto); | |
2708 | #endif | |
2709 | DEFINE_OPCODE(op_get_by_id_getter_proto) { | |
2710 | /* op_get_by_id_getter_proto dst(r) base(r) property(id) structure(sID) prototypeStructure(sID) offset(n) nop(n) | |
2711 | ||
2712 | Cached property access: Attempts to get a cached getter property from the | |
2713 | value base's prototype. If the cache misses, op_get_by_id_getter_proto | |
2714 | reverts to op_get_by_id. | |
2715 | */ | |
2716 | int base = vPC[2].u.operand; | |
2717 | JSValue baseValue = callFrame->r(base).jsValue(); | |
2718 | ||
2719 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2720 | JSCell* baseCell = baseValue.asCell(); |
2721 | Structure* structure = vPC[4].u.structure.get(); | |
4e4e5a6f A |
2722 | |
2723 | if (LIKELY(baseCell->structure() == structure)) { | |
2724 | ASSERT(structure->prototypeForLookup(callFrame).isObject()); | |
2725 | JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); | |
14957cd0 | 2726 | Structure* prototypeStructure = vPC[5].u.structure.get(); |
4e4e5a6f A |
2727 | |
2728 | if (LIKELY(protoObject->structure() == prototypeStructure)) { | |
2729 | int dst = vPC[1].u.operand; | |
2730 | int offset = vPC[6].u.operand; | |
2731 | if (GetterSetter* getterSetter = asGetterSetter(protoObject->getDirectOffset(offset).asCell())) { | |
2732 | JSObject* getter = getterSetter->getter(); | |
2733 | CallData callData; | |
2734 | CallType callType = getter->getCallData(callData); | |
2735 | JSValue result = call(callFrame, getter, callType, callData, asObject(baseCell), ArgList()); | |
2736 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 2737 | callFrame->uncheckedR(dst) = result; |
4e4e5a6f | 2738 | } else |
14957cd0 | 2739 | callFrame->uncheckedR(dst) = jsUndefined(); |
4e4e5a6f A |
2740 | vPC += OPCODE_LENGTH(op_get_by_id_getter_proto); |
2741 | NEXT_INSTRUCTION(); | |
2742 | } | |
2743 | } | |
2744 | } | |
14957cd0 | 2745 | uncacheGetByID(codeBlock, vPC); |
4e4e5a6f A |
2746 | NEXT_INSTRUCTION(); |
2747 | } | |
2748 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
2749 | skip_id_getter_proto: | |
2750 | #endif | |
2751 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
2752 | goto *(&&skip_id_custom_proto); | |
2753 | #endif | |
2754 | DEFINE_OPCODE(op_get_by_id_custom_proto) { | |
2755 | /* op_get_by_id_custom_proto dst(r) base(r) property(id) structure(sID) prototypeStructure(sID) offset(n) nop(n) | |
2756 | ||
2757 | Cached property access: Attempts to use a cached named property getter | |
2758 | from the value base's prototype. If the cache misses, op_get_by_id_custom_proto | |
2759 | reverts to op_get_by_id. | |
2760 | */ | |
2761 | int base = vPC[2].u.operand; | |
2762 | JSValue baseValue = callFrame->r(base).jsValue(); | |
2763 | ||
2764 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2765 | JSCell* baseCell = baseValue.asCell(); |
2766 | Structure* structure = vPC[4].u.structure.get(); | |
4e4e5a6f A |
2767 | |
2768 | if (LIKELY(baseCell->structure() == structure)) { | |
2769 | ASSERT(structure->prototypeForLookup(callFrame).isObject()); | |
2770 | JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); | |
14957cd0 | 2771 | Structure* prototypeStructure = vPC[5].u.structure.get(); |
4e4e5a6f A |
2772 | |
2773 | if (LIKELY(protoObject->structure() == prototypeStructure)) { | |
2774 | int dst = vPC[1].u.operand; | |
2775 | int property = vPC[3].u.operand; | |
14957cd0 | 2776 | Identifier& ident = codeBlock->identifier(property); |
4e4e5a6f A |
2777 | |
2778 | PropertySlot::GetValueFunc getter = vPC[6].u.getterFunc; | |
2779 | JSValue result = getter(callFrame, protoObject, ident); | |
2780 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 2781 | callFrame->uncheckedR(dst) = result; |
4e4e5a6f A |
2782 | vPC += OPCODE_LENGTH(op_get_by_id_custom_proto); |
2783 | NEXT_INSTRUCTION(); | |
2784 | } | |
2785 | } | |
2786 | } | |
14957cd0 | 2787 | uncacheGetByID(codeBlock, vPC); |
4e4e5a6f A |
2788 | NEXT_INSTRUCTION(); |
2789 | } | |
2790 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
2791 | skip_id_custom_proto: | |
2792 | #endif | |
9dae56ea A |
2793 | DEFINE_OPCODE(op_get_by_id_self_list) { |
2794 | // Polymorphic self access caching currently only supported when JITting. | |
2795 | ASSERT_NOT_REACHED(); | |
2796 | // This case of the switch must not be empty, else (op_get_by_id_self_list == op_get_by_id_chain)! | |
f9bf01c6 | 2797 | vPC += OPCODE_LENGTH(op_get_by_id_self_list); |
9dae56ea A |
2798 | NEXT_INSTRUCTION(); |
2799 | } | |
2800 | DEFINE_OPCODE(op_get_by_id_proto_list) { | |
2801 | // Polymorphic prototype access caching currently only supported when JITting. | |
2802 | ASSERT_NOT_REACHED(); | |
2803 | // This case of the switch must not be empty, else (op_get_by_id_proto_list == op_get_by_id_chain)! | |
f9bf01c6 | 2804 | vPC += OPCODE_LENGTH(op_get_by_id_proto_list); |
9dae56ea A |
2805 | NEXT_INSTRUCTION(); |
2806 | } | |
4e4e5a6f A |
2807 | DEFINE_OPCODE(op_get_by_id_getter_self_list) { |
2808 | // Polymorphic self access caching currently only supported when JITting. | |
2809 | ASSERT_NOT_REACHED(); | |
2810 | // This case of the switch must not be empty, else (op_get_by_id_self_list == op_get_by_id_chain)! | |
2811 | vPC += OPCODE_LENGTH(op_get_by_id_self_list); | |
2812 | NEXT_INSTRUCTION(); | |
2813 | } | |
2814 | DEFINE_OPCODE(op_get_by_id_getter_proto_list) { | |
2815 | // Polymorphic prototype access caching currently only supported when JITting. | |
2816 | ASSERT_NOT_REACHED(); | |
2817 | // This case of the switch must not be empty, else (op_get_by_id_proto_list == op_get_by_id_chain)! | |
2818 | vPC += OPCODE_LENGTH(op_get_by_id_proto_list); | |
2819 | NEXT_INSTRUCTION(); | |
2820 | } | |
2821 | DEFINE_OPCODE(op_get_by_id_custom_self_list) { | |
2822 | // Polymorphic self access caching currently only supported when JITting. | |
2823 | ASSERT_NOT_REACHED(); | |
2824 | // This case of the switch must not be empty, else (op_get_by_id_self_list == op_get_by_id_chain)! | |
2825 | vPC += OPCODE_LENGTH(op_get_by_id_custom_self_list); | |
2826 | NEXT_INSTRUCTION(); | |
2827 | } | |
2828 | DEFINE_OPCODE(op_get_by_id_custom_proto_list) { | |
2829 | // Polymorphic prototype access caching currently only supported when JITting. | |
2830 | ASSERT_NOT_REACHED(); | |
2831 | // This case of the switch must not be empty, else (op_get_by_id_proto_list == op_get_by_id_chain)! | |
2832 | vPC += OPCODE_LENGTH(op_get_by_id_proto_list); | |
2833 | NEXT_INSTRUCTION(); | |
2834 | } | |
14957cd0 A |
2835 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) |
2836 | goto *(&&skip_get_by_id_chain); | |
2837 | #endif | |
9dae56ea A |
2838 | DEFINE_OPCODE(op_get_by_id_chain) { |
2839 | /* op_get_by_id_chain dst(r) base(r) property(id) structure(sID) structureChain(chain) count(n) offset(n) | |
2840 | ||
2841 | Cached property access: Attempts to get a cached property from the | |
2842 | value base's prototype chain. If the cache misses, op_get_by_id_chain | |
2843 | reverts to op_get_by_id. | |
2844 | */ | |
2845 | int base = vPC[2].u.operand; | |
ba379fdc | 2846 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea A |
2847 | |
2848 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2849 | JSCell* baseCell = baseValue.asCell(); |
2850 | Structure* structure = vPC[4].u.structure.get(); | |
9dae56ea A |
2851 | |
2852 | if (LIKELY(baseCell->structure() == structure)) { | |
14957cd0 | 2853 | WriteBarrier<Structure>* it = vPC[5].u.structureChain->head(); |
9dae56ea | 2854 | size_t count = vPC[6].u.operand; |
14957cd0 | 2855 | WriteBarrier<Structure>* end = it + count; |
9dae56ea A |
2856 | |
2857 | while (true) { | |
2858 | JSObject* baseObject = asObject(baseCell->structure()->prototypeForLookup(callFrame)); | |
2859 | ||
2860 | if (UNLIKELY(baseObject->structure() != (*it).get())) | |
2861 | break; | |
2862 | ||
2863 | if (++it == end) { | |
2864 | int dst = vPC[1].u.operand; | |
2865 | int offset = vPC[7].u.operand; | |
2866 | ||
14957cd0 A |
2867 | ASSERT(baseObject->get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); |
2868 | ASSERT(baseValue.get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); | |
2869 | callFrame->uncheckedR(dst) = JSValue(baseObject->getDirectOffset(offset)); | |
9dae56ea | 2870 | |
f9bf01c6 | 2871 | vPC += OPCODE_LENGTH(op_get_by_id_chain); |
9dae56ea A |
2872 | NEXT_INSTRUCTION(); |
2873 | } | |
2874 | ||
2875 | // Update baseCell, so that next time around the loop we'll pick up the prototype's prototype. | |
2876 | baseCell = baseObject; | |
2877 | } | |
2878 | } | |
2879 | } | |
2880 | ||
14957cd0 | 2881 | uncacheGetByID(codeBlock, vPC); |
9dae56ea A |
2882 | NEXT_INSTRUCTION(); |
2883 | } | |
4e4e5a6f | 2884 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) |
14957cd0 | 2885 | skip_get_by_id_chain: |
4e4e5a6f A |
2886 | goto *(&&skip_id_getter_self); |
2887 | #endif | |
2888 | DEFINE_OPCODE(op_get_by_id_getter_self) { | |
2889 | /* op_get_by_id_self dst(r) base(r) property(id) structure(sID) offset(n) nop(n) nop(n) | |
2890 | ||
2891 | Cached property access: Attempts to get a cached property from the | |
2892 | value base. If the cache misses, op_get_by_id_getter_self reverts to | |
2893 | op_get_by_id. | |
2894 | */ | |
2895 | int base = vPC[2].u.operand; | |
2896 | JSValue baseValue = callFrame->r(base).jsValue(); | |
2897 | ||
2898 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2899 | JSCell* baseCell = baseValue.asCell(); |
2900 | Structure* structure = vPC[4].u.structure.get(); | |
4e4e5a6f A |
2901 | |
2902 | if (LIKELY(baseCell->structure() == structure)) { | |
2903 | ASSERT(baseCell->isObject()); | |
2904 | JSObject* baseObject = asObject(baseCell); | |
2905 | int dst = vPC[1].u.operand; | |
2906 | int offset = vPC[5].u.operand; | |
2907 | ||
2908 | if (GetterSetter* getterSetter = asGetterSetter(baseObject->getDirectOffset(offset).asCell())) { | |
2909 | JSObject* getter = getterSetter->getter(); | |
2910 | CallData callData; | |
2911 | CallType callType = getter->getCallData(callData); | |
2912 | JSValue result = call(callFrame, getter, callType, callData, baseObject, ArgList()); | |
2913 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 2914 | callFrame->uncheckedR(dst) = result; |
4e4e5a6f | 2915 | } else |
14957cd0 | 2916 | callFrame->uncheckedR(dst) = jsUndefined(); |
4e4e5a6f A |
2917 | |
2918 | vPC += OPCODE_LENGTH(op_get_by_id_getter_self); | |
2919 | NEXT_INSTRUCTION(); | |
2920 | } | |
2921 | } | |
14957cd0 | 2922 | uncacheGetByID(codeBlock, vPC); |
4e4e5a6f A |
2923 | NEXT_INSTRUCTION(); |
2924 | } | |
2925 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
2926 | skip_id_getter_self: | |
2927 | #endif | |
2928 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
2929 | goto *(&&skip_id_custom_self); | |
2930 | #endif | |
2931 | DEFINE_OPCODE(op_get_by_id_custom_self) { | |
2932 | /* op_get_by_id_custom_self dst(r) base(r) property(id) structure(sID) offset(n) nop(n) nop(n) | |
2933 | ||
2934 | Cached property access: Attempts to use a cached named property getter | |
2935 | from the value base. If the cache misses, op_get_by_id_custom_self reverts to | |
2936 | op_get_by_id. | |
2937 | */ | |
2938 | int base = vPC[2].u.operand; | |
2939 | JSValue baseValue = callFrame->r(base).jsValue(); | |
2940 | ||
2941 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2942 | JSCell* baseCell = baseValue.asCell(); |
2943 | Structure* structure = vPC[4].u.structure.get(); | |
4e4e5a6f A |
2944 | |
2945 | if (LIKELY(baseCell->structure() == structure)) { | |
2946 | ASSERT(baseCell->isObject()); | |
2947 | int dst = vPC[1].u.operand; | |
2948 | int property = vPC[3].u.operand; | |
14957cd0 | 2949 | Identifier& ident = codeBlock->identifier(property); |
4e4e5a6f A |
2950 | |
2951 | PropertySlot::GetValueFunc getter = vPC[5].u.getterFunc; | |
2952 | JSValue result = getter(callFrame, baseValue, ident); | |
2953 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 2954 | callFrame->uncheckedR(dst) = result; |
4e4e5a6f A |
2955 | vPC += OPCODE_LENGTH(op_get_by_id_custom_self); |
2956 | NEXT_INSTRUCTION(); | |
2957 | } | |
2958 | } | |
14957cd0 | 2959 | uncacheGetByID(codeBlock, vPC); |
4e4e5a6f A |
2960 | NEXT_INSTRUCTION(); |
2961 | } | |
2962 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
2963 | skip_id_custom_self: | |
2964 | #endif | |
9dae56ea A |
2965 | DEFINE_OPCODE(op_get_by_id_generic) { |
2966 | /* op_get_by_id_generic dst(r) base(r) property(id) nop(sID) nop(n) nop(n) nop(n) | |
2967 | ||
2968 | Generic property access: Gets the property named by identifier | |
2969 | property from the value base, and puts the result in register dst. | |
2970 | */ | |
2971 | int dst = vPC[1].u.operand; | |
2972 | int base = vPC[2].u.operand; | |
2973 | int property = vPC[3].u.operand; | |
2974 | ||
14957cd0 | 2975 | Identifier& ident = codeBlock->identifier(property); |
ba379fdc | 2976 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea | 2977 | PropertySlot slot(baseValue); |
ba379fdc | 2978 | JSValue result = baseValue.get(callFrame, ident, slot); |
9dae56ea A |
2979 | CHECK_FOR_EXCEPTION(); |
2980 | ||
14957cd0 | 2981 | callFrame->uncheckedR(dst) = result; |
f9bf01c6 | 2982 | vPC += OPCODE_LENGTH(op_get_by_id_generic); |
9dae56ea A |
2983 | NEXT_INSTRUCTION(); |
2984 | } | |
4e4e5a6f A |
2985 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) |
2986 | goto *(&&skip_id_getter_chain); | |
2987 | #endif | |
2988 | DEFINE_OPCODE(op_get_by_id_getter_chain) { | |
2989 | /* op_get_by_id_getter_chain dst(r) base(r) property(id) structure(sID) structureChain(chain) count(n) offset(n) | |
2990 | ||
2991 | Cached property access: Attempts to get a cached property from the | |
2992 | value base's prototype chain. If the cache misses, op_get_by_id_getter_chain | |
2993 | reverts to op_get_by_id. | |
2994 | */ | |
2995 | int base = vPC[2].u.operand; | |
2996 | JSValue baseValue = callFrame->r(base).jsValue(); | |
2997 | ||
2998 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
2999 | JSCell* baseCell = baseValue.asCell(); |
3000 | Structure* structure = vPC[4].u.structure.get(); | |
4e4e5a6f A |
3001 | |
3002 | if (LIKELY(baseCell->structure() == structure)) { | |
14957cd0 | 3003 | WriteBarrier<Structure>* it = vPC[5].u.structureChain->head(); |
4e4e5a6f | 3004 | size_t count = vPC[6].u.operand; |
14957cd0 | 3005 | WriteBarrier<Structure>* end = it + count; |
4e4e5a6f A |
3006 | |
3007 | while (true) { | |
3008 | JSObject* baseObject = asObject(baseCell->structure()->prototypeForLookup(callFrame)); | |
3009 | ||
3010 | if (UNLIKELY(baseObject->structure() != (*it).get())) | |
3011 | break; | |
3012 | ||
3013 | if (++it == end) { | |
3014 | int dst = vPC[1].u.operand; | |
3015 | int offset = vPC[7].u.operand; | |
3016 | if (GetterSetter* getterSetter = asGetterSetter(baseObject->getDirectOffset(offset).asCell())) { | |
3017 | JSObject* getter = getterSetter->getter(); | |
3018 | CallData callData; | |
3019 | CallType callType = getter->getCallData(callData); | |
3020 | JSValue result = call(callFrame, getter, callType, callData, baseValue, ArgList()); | |
3021 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 3022 | callFrame->uncheckedR(dst) = result; |
4e4e5a6f | 3023 | } else |
14957cd0 | 3024 | callFrame->uncheckedR(dst) = jsUndefined(); |
4e4e5a6f A |
3025 | vPC += OPCODE_LENGTH(op_get_by_id_getter_chain); |
3026 | NEXT_INSTRUCTION(); | |
3027 | } | |
3028 | ||
3029 | // Update baseCell, so that next time around the loop we'll pick up the prototype's prototype. | |
3030 | baseCell = baseObject; | |
3031 | } | |
3032 | } | |
3033 | } | |
14957cd0 | 3034 | uncacheGetByID(codeBlock, vPC); |
4e4e5a6f A |
3035 | NEXT_INSTRUCTION(); |
3036 | } | |
3037 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
3038 | skip_id_getter_chain: | |
3039 | #endif | |
3040 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
3041 | goto *(&&skip_id_custom_chain); | |
3042 | #endif | |
3043 | DEFINE_OPCODE(op_get_by_id_custom_chain) { | |
3044 | /* op_get_by_id_custom_chain dst(r) base(r) property(id) structure(sID) structureChain(chain) count(n) offset(n) | |
3045 | ||
3046 | Cached property access: Attempts to use a cached named property getter on the | |
3047 | value base's prototype chain. If the cache misses, op_get_by_id_custom_chain | |
3048 | reverts to op_get_by_id. | |
3049 | */ | |
3050 | int base = vPC[2].u.operand; | |
3051 | JSValue baseValue = callFrame->r(base).jsValue(); | |
3052 | ||
3053 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
3054 | JSCell* baseCell = baseValue.asCell(); |
3055 | Structure* structure = vPC[4].u.structure.get(); | |
4e4e5a6f A |
3056 | |
3057 | if (LIKELY(baseCell->structure() == structure)) { | |
14957cd0 | 3058 | WriteBarrier<Structure>* it = vPC[5].u.structureChain->head(); |
4e4e5a6f | 3059 | size_t count = vPC[6].u.operand; |
14957cd0 | 3060 | WriteBarrier<Structure>* end = it + count; |
4e4e5a6f A |
3061 | |
3062 | while (true) { | |
3063 | JSObject* baseObject = asObject(baseCell->structure()->prototypeForLookup(callFrame)); | |
3064 | ||
3065 | if (UNLIKELY(baseObject->structure() != (*it).get())) | |
3066 | break; | |
3067 | ||
3068 | if (++it == end) { | |
3069 | int dst = vPC[1].u.operand; | |
3070 | int property = vPC[3].u.operand; | |
14957cd0 | 3071 | Identifier& ident = codeBlock->identifier(property); |
4e4e5a6f A |
3072 | |
3073 | PropertySlot::GetValueFunc getter = vPC[7].u.getterFunc; | |
3074 | JSValue result = getter(callFrame, baseObject, ident); | |
3075 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 3076 | callFrame->uncheckedR(dst) = result; |
4e4e5a6f A |
3077 | vPC += OPCODE_LENGTH(op_get_by_id_custom_chain); |
3078 | NEXT_INSTRUCTION(); | |
3079 | } | |
3080 | ||
3081 | // Update baseCell, so that next time around the loop we'll pick up the prototype's prototype. | |
3082 | baseCell = baseObject; | |
3083 | } | |
3084 | } | |
3085 | } | |
14957cd0 | 3086 | uncacheGetByID(codeBlock, vPC); |
4e4e5a6f A |
3087 | NEXT_INSTRUCTION(); |
3088 | } | |
3089 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) | |
3090 | skip_id_custom_chain: | |
14957cd0 | 3091 | goto *(&&skip_get_array_length); |
4e4e5a6f | 3092 | #endif |
9dae56ea A |
3093 | DEFINE_OPCODE(op_get_array_length) { |
3094 | /* op_get_array_length dst(r) base(r) property(id) nop(sID) nop(n) nop(n) nop(n) | |
3095 | ||
3096 | Cached property access: Gets the length of the array in register base, | |
3097 | and puts the result in register dst. If register base does not hold | |
3098 | an array, op_get_array_length reverts to op_get_by_id. | |
3099 | */ | |
3100 | ||
3101 | int base = vPC[2].u.operand; | |
ba379fdc A |
3102 | JSValue baseValue = callFrame->r(base).jsValue(); |
3103 | if (LIKELY(isJSArray(globalData, baseValue))) { | |
9dae56ea | 3104 | int dst = vPC[1].u.operand; |
14957cd0 | 3105 | callFrame->uncheckedR(dst) = jsNumber(asArray(baseValue)->length()); |
f9bf01c6 | 3106 | vPC += OPCODE_LENGTH(op_get_array_length); |
9dae56ea A |
3107 | NEXT_INSTRUCTION(); |
3108 | } | |
3109 | ||
14957cd0 | 3110 | uncacheGetByID(codeBlock, vPC); |
9dae56ea A |
3111 | NEXT_INSTRUCTION(); |
3112 | } | |
14957cd0 A |
3113 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) |
3114 | skip_get_array_length: | |
3115 | goto *(&&skip_get_string_length); | |
3116 | #endif | |
9dae56ea A |
3117 | DEFINE_OPCODE(op_get_string_length) { |
3118 | /* op_get_string_length dst(r) base(r) property(id) nop(sID) nop(n) nop(n) nop(n) | |
3119 | ||
3120 | Cached property access: Gets the length of the string in register base, | |
3121 | and puts the result in register dst. If register base does not hold | |
3122 | a string, op_get_string_length reverts to op_get_by_id. | |
3123 | */ | |
3124 | ||
3125 | int base = vPC[2].u.operand; | |
ba379fdc A |
3126 | JSValue baseValue = callFrame->r(base).jsValue(); |
3127 | if (LIKELY(isJSString(globalData, baseValue))) { | |
9dae56ea | 3128 | int dst = vPC[1].u.operand; |
14957cd0 | 3129 | callFrame->uncheckedR(dst) = jsNumber(asString(baseValue)->length()); |
f9bf01c6 | 3130 | vPC += OPCODE_LENGTH(op_get_string_length); |
9dae56ea A |
3131 | NEXT_INSTRUCTION(); |
3132 | } | |
3133 | ||
14957cd0 | 3134 | uncacheGetByID(codeBlock, vPC); |
9dae56ea A |
3135 | NEXT_INSTRUCTION(); |
3136 | } | |
14957cd0 A |
3137 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) |
3138 | skip_get_string_length: | |
3139 | goto *(&&skip_put_by_id); | |
3140 | #endif | |
9dae56ea | 3141 | DEFINE_OPCODE(op_put_by_id) { |
4e4e5a6f | 3142 | /* put_by_id base(r) property(id) value(r) nop(n) nop(n) nop(n) nop(n) direct(b) |
9dae56ea A |
3143 | |
3144 | Generic property access: Sets the property named by identifier | |
3145 | property, belonging to register base, to register value. | |
3146 | ||
3147 | Unlike many opcodes, this one does not write any output to | |
3148 | the register file. | |
14957cd0 | 3149 | |
4e4e5a6f A |
3150 | The "direct" flag should only be set this put_by_id is to initialize |
3151 | an object literal. | |
9dae56ea A |
3152 | */ |
3153 | ||
3154 | int base = vPC[1].u.operand; | |
3155 | int property = vPC[2].u.operand; | |
3156 | int value = vPC[3].u.operand; | |
4e4e5a6f | 3157 | int direct = vPC[8].u.operand; |
9dae56ea | 3158 | |
ba379fdc | 3159 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea | 3160 | Identifier& ident = codeBlock->identifier(property); |
14957cd0 | 3161 | PutPropertySlot slot(codeBlock->isStrictMode()); |
4e4e5a6f A |
3162 | if (direct) { |
3163 | baseValue.putDirect(callFrame, ident, callFrame->r(value).jsValue(), slot); | |
3164 | ASSERT(slot.base() == baseValue); | |
3165 | } else | |
3166 | baseValue.put(callFrame, ident, callFrame->r(value).jsValue(), slot); | |
9dae56ea A |
3167 | CHECK_FOR_EXCEPTION(); |
3168 | ||
3169 | tryCachePutByID(callFrame, codeBlock, vPC, baseValue, slot); | |
3170 | ||
f9bf01c6 | 3171 | vPC += OPCODE_LENGTH(op_put_by_id); |
9dae56ea A |
3172 | NEXT_INSTRUCTION(); |
3173 | } | |
14957cd0 A |
3174 | #if USE(GCC_COMPUTED_GOTO_WORKAROUND) |
3175 | skip_put_by_id: | |
3176 | #endif | |
9dae56ea | 3177 | DEFINE_OPCODE(op_put_by_id_transition) { |
4e4e5a6f | 3178 | /* op_put_by_id_transition base(r) property(id) value(r) oldStructure(sID) newStructure(sID) structureChain(chain) offset(n) direct(b) |
9dae56ea A |
3179 | |
3180 | Cached property access: Attempts to set a new property with a cached transition | |
3181 | property named by identifier property, belonging to register base, | |
3182 | to register value. If the cache misses, op_put_by_id_transition | |
3183 | reverts to op_put_by_id_generic. | |
3184 | ||
3185 | Unlike many opcodes, this one does not write any output to | |
3186 | the register file. | |
3187 | */ | |
3188 | int base = vPC[1].u.operand; | |
ba379fdc | 3189 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea A |
3190 | |
3191 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
3192 | JSCell* baseCell = baseValue.asCell(); |
3193 | Structure* oldStructure = vPC[4].u.structure.get(); | |
3194 | Structure* newStructure = vPC[5].u.structure.get(); | |
9dae56ea A |
3195 | |
3196 | if (LIKELY(baseCell->structure() == oldStructure)) { | |
3197 | ASSERT(baseCell->isObject()); | |
3198 | JSObject* baseObject = asObject(baseCell); | |
4e4e5a6f | 3199 | int direct = vPC[8].u.operand; |
14957cd0 | 3200 | |
4e4e5a6f | 3201 | if (!direct) { |
14957cd0 | 3202 | WriteBarrier<Structure>* it = vPC[6].u.structureChain->head(); |
4e4e5a6f A |
3203 | |
3204 | JSValue proto = baseObject->structure()->prototypeForLookup(callFrame); | |
3205 | while (!proto.isNull()) { | |
3206 | if (UNLIKELY(asObject(proto)->structure() != (*it).get())) { | |
14957cd0 | 3207 | uncachePutByID(codeBlock, vPC); |
4e4e5a6f A |
3208 | NEXT_INSTRUCTION(); |
3209 | } | |
3210 | ++it; | |
3211 | proto = asObject(proto)->structure()->prototypeForLookup(callFrame); | |
9dae56ea | 3212 | } |
9dae56ea | 3213 | } |
14957cd0 | 3214 | baseObject->transitionTo(*globalData, newStructure); |
9dae56ea A |
3215 | |
3216 | int value = vPC[3].u.operand; | |
3217 | unsigned offset = vPC[7].u.operand; | |
14957cd0 A |
3218 | ASSERT(baseObject->offsetForLocation(baseObject->getDirectLocation(*globalData, codeBlock->identifier(vPC[2].u.operand))) == offset); |
3219 | baseObject->putDirectOffset(callFrame->globalData(), offset, callFrame->r(value).jsValue()); | |
9dae56ea | 3220 | |
f9bf01c6 | 3221 | vPC += OPCODE_LENGTH(op_put_by_id_transition); |
9dae56ea A |
3222 | NEXT_INSTRUCTION(); |
3223 | } | |
3224 | } | |
3225 | ||
14957cd0 | 3226 | uncachePutByID(codeBlock, vPC); |
9dae56ea A |
3227 | NEXT_INSTRUCTION(); |
3228 | } | |
3229 | DEFINE_OPCODE(op_put_by_id_replace) { | |
4e4e5a6f | 3230 | /* op_put_by_id_replace base(r) property(id) value(r) structure(sID) offset(n) nop(n) nop(n) direct(b) |
9dae56ea A |
3231 | |
3232 | Cached property access: Attempts to set a pre-existing, cached | |
3233 | property named by identifier property, belonging to register base, | |
3234 | to register value. If the cache misses, op_put_by_id_replace | |
3235 | reverts to op_put_by_id. | |
3236 | ||
3237 | Unlike many opcodes, this one does not write any output to | |
3238 | the register file. | |
3239 | */ | |
3240 | int base = vPC[1].u.operand; | |
ba379fdc | 3241 | JSValue baseValue = callFrame->r(base).jsValue(); |
9dae56ea A |
3242 | |
3243 | if (LIKELY(baseValue.isCell())) { | |
14957cd0 A |
3244 | JSCell* baseCell = baseValue.asCell(); |
3245 | Structure* structure = vPC[4].u.structure.get(); | |
9dae56ea A |
3246 | |
3247 | if (LIKELY(baseCell->structure() == structure)) { | |
3248 | ASSERT(baseCell->isObject()); | |
3249 | JSObject* baseObject = asObject(baseCell); | |
3250 | int value = vPC[3].u.operand; | |
3251 | unsigned offset = vPC[5].u.operand; | |
3252 | ||
14957cd0 A |
3253 | ASSERT(baseObject->offsetForLocation(baseObject->getDirectLocation(*globalData, codeBlock->identifier(vPC[2].u.operand))) == offset); |
3254 | baseObject->putDirectOffset(callFrame->globalData(), offset, callFrame->r(value).jsValue()); | |
9dae56ea | 3255 | |
f9bf01c6 | 3256 | vPC += OPCODE_LENGTH(op_put_by_id_replace); |
9dae56ea A |
3257 | NEXT_INSTRUCTION(); |
3258 | } | |
3259 | } | |
3260 | ||
14957cd0 | 3261 | uncachePutByID(codeBlock, vPC); |
9dae56ea A |
3262 | NEXT_INSTRUCTION(); |
3263 | } | |
3264 | DEFINE_OPCODE(op_put_by_id_generic) { | |
4e4e5a6f | 3265 | /* op_put_by_id_generic base(r) property(id) value(r) nop(n) nop(n) nop(n) nop(n) direct(b) |
9dae56ea A |
3266 | |
3267 | Generic property access: Sets the property named by identifier | |
3268 | property, belonging to register base, to register value. | |
3269 | ||
3270 | Unlike many opcodes, this one does not write any output to | |
3271 | the register file. | |
3272 | */ | |
3273 | int base = vPC[1].u.operand; | |
3274 | int property = vPC[2].u.operand; | |
3275 | int value = vPC[3].u.operand; | |
4e4e5a6f | 3276 | int direct = vPC[8].u.operand; |
9dae56ea | 3277 | |
ba379fdc | 3278 | JSValue baseValue = callFrame->r(base).jsValue(); |
14957cd0 A |
3279 | Identifier& ident = codeBlock->identifier(property); |
3280 | PutPropertySlot slot(codeBlock->isStrictMode()); | |
4e4e5a6f A |
3281 | if (direct) { |
3282 | baseValue.putDirect(callFrame, ident, callFrame->r(value).jsValue(), slot); | |
3283 | ASSERT(slot.base() == baseValue); | |
3284 | } else | |
3285 | baseValue.put(callFrame, ident, callFrame->r(value).jsValue(), slot); | |
9dae56ea A |
3286 | CHECK_FOR_EXCEPTION(); |
3287 | ||
f9bf01c6 | 3288 | vPC += OPCODE_LENGTH(op_put_by_id_generic); |
9dae56ea A |
3289 | NEXT_INSTRUCTION(); |
3290 | } | |
3291 | DEFINE_OPCODE(op_del_by_id) { | |
3292 | /* del_by_id dst(r) base(r) property(id) | |
3293 | ||
3294 | Converts register base to Object, deletes the property | |
3295 | named by identifier property from the object, and writes a | |
3296 | boolean indicating success (if true) or failure (if false) | |
3297 | to register dst. | |
3298 | */ | |
f9bf01c6 A |
3299 | int dst = vPC[1].u.operand; |
3300 | int base = vPC[2].u.operand; | |
3301 | int property = vPC[3].u.operand; | |
9dae56ea | 3302 | |
ba379fdc | 3303 | JSObject* baseObj = callFrame->r(base).jsValue().toObject(callFrame); |
14957cd0 A |
3304 | Identifier& ident = codeBlock->identifier(property); |
3305 | bool result = baseObj->deleteProperty(callFrame, ident); | |
3306 | if (!result && codeBlock->isStrictMode()) { | |
3307 | exceptionValue = createTypeError(callFrame, "Unable to delete property."); | |
3308 | goto vm_throw; | |
3309 | } | |
9dae56ea | 3310 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 3311 | callFrame->uncheckedR(dst) = jsBoolean(result); |
f9bf01c6 A |
3312 | vPC += OPCODE_LENGTH(op_del_by_id); |
3313 | NEXT_INSTRUCTION(); | |
3314 | } | |
3315 | DEFINE_OPCODE(op_get_by_pname) { | |
3316 | int dst = vPC[1].u.operand; | |
3317 | int base = vPC[2].u.operand; | |
3318 | int property = vPC[3].u.operand; | |
3319 | int expected = vPC[4].u.operand; | |
3320 | int iter = vPC[5].u.operand; | |
3321 | int i = vPC[6].u.operand; | |
3322 | ||
3323 | JSValue baseValue = callFrame->r(base).jsValue(); | |
3324 | JSPropertyNameIterator* it = callFrame->r(iter).propertyNameIterator(); | |
3325 | JSValue subscript = callFrame->r(property).jsValue(); | |
3326 | JSValue expectedSubscript = callFrame->r(expected).jsValue(); | |
3327 | int index = callFrame->r(i).i() - 1; | |
3328 | JSValue result; | |
3329 | int offset = 0; | |
3330 | if (subscript == expectedSubscript && baseValue.isCell() && (baseValue.asCell()->structure() == it->cachedStructure()) && it->getOffset(index, offset)) { | |
14957cd0 | 3331 | callFrame->uncheckedR(dst) = JSValue(asObject(baseValue)->getDirectOffset(offset)); |
f9bf01c6 A |
3332 | vPC += OPCODE_LENGTH(op_get_by_pname); |
3333 | NEXT_INSTRUCTION(); | |
3334 | } | |
14957cd0 A |
3335 | { |
3336 | Identifier propertyName(callFrame, subscript.toString(callFrame)); | |
3337 | result = baseValue.get(callFrame, propertyName); | |
3338 | } | |
f9bf01c6 | 3339 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 3340 | callFrame->uncheckedR(dst) = result; |
f9bf01c6 | 3341 | vPC += OPCODE_LENGTH(op_get_by_pname); |
9dae56ea A |
3342 | NEXT_INSTRUCTION(); |
3343 | } | |
14957cd0 A |
3344 | DEFINE_OPCODE(op_get_arguments_length) { |
3345 | int dst = vPC[1].u.operand; | |
3346 | int argumentsRegister = vPC[2].u.operand; | |
3347 | int property = vPC[3].u.operand; | |
3348 | JSValue arguments = callFrame->r(argumentsRegister).jsValue(); | |
3349 | if (arguments) { | |
3350 | Identifier& ident = codeBlock->identifier(property); | |
3351 | PropertySlot slot(arguments); | |
3352 | JSValue result = arguments.get(callFrame, ident, slot); | |
3353 | CHECK_FOR_EXCEPTION(); | |
3354 | callFrame->uncheckedR(dst) = result; | |
3355 | } else | |
3356 | callFrame->uncheckedR(dst) = jsNumber(callFrame->argumentCount()); | |
3357 | ||
3358 | vPC += OPCODE_LENGTH(op_get_arguments_length); | |
3359 | NEXT_INSTRUCTION(); | |
3360 | } | |
3361 | DEFINE_OPCODE(op_get_argument_by_val) { | |
3362 | int dst = vPC[1].u.operand; | |
3363 | int argumentsRegister = vPC[2].u.operand; | |
3364 | int property = vPC[3].u.operand; | |
3365 | JSValue arguments = callFrame->r(argumentsRegister).jsValue(); | |
3366 | JSValue subscript = callFrame->r(property).jsValue(); | |
3367 | if (!arguments && subscript.isUInt32() && subscript.asUInt32() < callFrame->argumentCount()) { | |
3368 | unsigned arg = subscript.asUInt32() + 1; | |
3369 | unsigned numParameters = callFrame->codeBlock()->m_numParameters; | |
3370 | if (arg < numParameters) | |
3371 | callFrame->uncheckedR(dst) = callFrame->r(arg - RegisterFile::CallFrameHeaderSize - numParameters); | |
3372 | else | |
3373 | callFrame->uncheckedR(dst) = callFrame->r(arg - RegisterFile::CallFrameHeaderSize - numParameters - callFrame->argumentCount() - 1); | |
3374 | vPC += OPCODE_LENGTH(op_get_argument_by_val); | |
3375 | NEXT_INSTRUCTION(); | |
3376 | } | |
3377 | if (!arguments) { | |
3378 | Arguments* arguments = new (globalData) Arguments(callFrame); | |
3379 | callFrame->uncheckedR(argumentsRegister) = JSValue(arguments); | |
3380 | callFrame->uncheckedR(unmodifiedArgumentsRegister(argumentsRegister)) = JSValue(arguments); | |
3381 | } | |
3382 | // fallthrough | |
3383 | } | |
9dae56ea A |
3384 | DEFINE_OPCODE(op_get_by_val) { |
3385 | /* get_by_val dst(r) base(r) property(r) | |
3386 | ||
3387 | Converts register base to Object, gets the property named | |
3388 | by register property from the object, and puts the result | |
3389 | in register dst. property is nominally converted to string | |
3390 | but numbers are treated more efficiently. | |
3391 | */ | |
f9bf01c6 A |
3392 | int dst = vPC[1].u.operand; |
3393 | int base = vPC[2].u.operand; | |
3394 | int property = vPC[3].u.operand; | |
9dae56ea | 3395 | |
ba379fdc A |
3396 | JSValue baseValue = callFrame->r(base).jsValue(); |
3397 | JSValue subscript = callFrame->r(property).jsValue(); | |
9dae56ea | 3398 | |
ba379fdc | 3399 | JSValue result; |
9dae56ea | 3400 | |
ba379fdc A |
3401 | if (LIKELY(subscript.isUInt32())) { |
3402 | uint32_t i = subscript.asUInt32(); | |
3403 | if (isJSArray(globalData, baseValue)) { | |
9dae56ea A |
3404 | JSArray* jsArray = asArray(baseValue); |
3405 | if (jsArray->canGetIndex(i)) | |
3406 | result = jsArray->getIndex(i); | |
3407 | else | |
3408 | result = jsArray->JSArray::get(callFrame, i); | |
ba379fdc | 3409 | } else if (isJSString(globalData, baseValue) && asString(baseValue)->canGetIndex(i)) |
f9bf01c6 | 3410 | result = asString(baseValue)->getIndex(callFrame, i); |
ba379fdc | 3411 | else if (isJSByteArray(globalData, baseValue) && asByteArray(baseValue)->canAccessIndex(i)) |
9dae56ea A |
3412 | result = asByteArray(baseValue)->getIndex(callFrame, i); |
3413 | else | |
3414 | result = baseValue.get(callFrame, i); | |
3415 | } else { | |
3416 | Identifier property(callFrame, subscript.toString(callFrame)); | |
3417 | result = baseValue.get(callFrame, property); | |
3418 | } | |
3419 | ||
3420 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 3421 | callFrame->uncheckedR(dst) = result; |
f9bf01c6 | 3422 | vPC += OPCODE_LENGTH(op_get_by_val); |
9dae56ea A |
3423 | NEXT_INSTRUCTION(); |
3424 | } | |
3425 | DEFINE_OPCODE(op_put_by_val) { | |
3426 | /* put_by_val base(r) property(r) value(r) | |
3427 | ||
3428 | Sets register value on register base as the property named | |
3429 | by register property. Base is converted to object | |
3430 | first. register property is nominally converted to string | |
3431 | but numbers are treated more efficiently. | |
3432 | ||
3433 | Unlike many opcodes, this one does not write any output to | |
3434 | the register file. | |
3435 | */ | |
f9bf01c6 A |
3436 | int base = vPC[1].u.operand; |
3437 | int property = vPC[2].u.operand; | |
3438 | int value = vPC[3].u.operand; | |
9dae56ea | 3439 | |
ba379fdc A |
3440 | JSValue baseValue = callFrame->r(base).jsValue(); |
3441 | JSValue subscript = callFrame->r(property).jsValue(); | |
9dae56ea | 3442 | |
ba379fdc A |
3443 | if (LIKELY(subscript.isUInt32())) { |
3444 | uint32_t i = subscript.asUInt32(); | |
3445 | if (isJSArray(globalData, baseValue)) { | |
9dae56ea A |
3446 | JSArray* jsArray = asArray(baseValue); |
3447 | if (jsArray->canSetIndex(i)) | |
14957cd0 | 3448 | jsArray->setIndex(*globalData, i, callFrame->r(value).jsValue()); |
9dae56ea | 3449 | else |
ba379fdc A |
3450 | jsArray->JSArray::put(callFrame, i, callFrame->r(value).jsValue()); |
3451 | } else if (isJSByteArray(globalData, baseValue) && asByteArray(baseValue)->canAccessIndex(i)) { | |
9dae56ea A |
3452 | JSByteArray* jsByteArray = asByteArray(baseValue); |
3453 | double dValue = 0; | |
ba379fdc A |
3454 | JSValue jsValue = callFrame->r(value).jsValue(); |
3455 | if (jsValue.isInt32()) | |
3456 | jsByteArray->setIndex(i, jsValue.asInt32()); | |
9dae56ea A |
3457 | else if (jsValue.getNumber(dValue)) |
3458 | jsByteArray->setIndex(i, dValue); | |
3459 | else | |
3460 | baseValue.put(callFrame, i, jsValue); | |
3461 | } else | |
ba379fdc | 3462 | baseValue.put(callFrame, i, callFrame->r(value).jsValue()); |
9dae56ea A |
3463 | } else { |
3464 | Identifier property(callFrame, subscript.toString(callFrame)); | |
3465 | if (!globalData->exception) { // Don't put to an object if toString threw an exception. | |
14957cd0 | 3466 | PutPropertySlot slot(codeBlock->isStrictMode()); |
ba379fdc | 3467 | baseValue.put(callFrame, property, callFrame->r(value).jsValue(), slot); |
9dae56ea A |
3468 | } |
3469 | } | |
3470 | ||
3471 | CHECK_FOR_EXCEPTION(); | |
f9bf01c6 | 3472 | vPC += OPCODE_LENGTH(op_put_by_val); |
9dae56ea A |
3473 | NEXT_INSTRUCTION(); |
3474 | } | |
3475 | DEFINE_OPCODE(op_del_by_val) { | |
3476 | /* del_by_val dst(r) base(r) property(r) | |
3477 | ||
3478 | Converts register base to Object, deletes the property | |
3479 | named by register property from the object, and writes a | |
3480 | boolean indicating success (if true) or failure (if false) | |
3481 | to register dst. | |
3482 | */ | |
f9bf01c6 A |
3483 | int dst = vPC[1].u.operand; |
3484 | int base = vPC[2].u.operand; | |
3485 | int property = vPC[3].u.operand; | |
9dae56ea | 3486 | |
ba379fdc | 3487 | JSObject* baseObj = callFrame->r(base).jsValue().toObject(callFrame); // may throw |
9dae56ea | 3488 | |
ba379fdc | 3489 | JSValue subscript = callFrame->r(property).jsValue(); |
14957cd0 | 3490 | bool result; |
9dae56ea A |
3491 | uint32_t i; |
3492 | if (subscript.getUInt32(i)) | |
14957cd0 | 3493 | result = baseObj->deleteProperty(callFrame, i); |
9dae56ea A |
3494 | else { |
3495 | CHECK_FOR_EXCEPTION(); | |
3496 | Identifier property(callFrame, subscript.toString(callFrame)); | |
3497 | CHECK_FOR_EXCEPTION(); | |
14957cd0 A |
3498 | result = baseObj->deleteProperty(callFrame, property); |
3499 | } | |
3500 | if (!result && codeBlock->isStrictMode()) { | |
3501 | exceptionValue = createTypeError(callFrame, "Unable to delete property."); | |
3502 | goto vm_throw; | |
9dae56ea | 3503 | } |
9dae56ea | 3504 | CHECK_FOR_EXCEPTION(); |
14957cd0 | 3505 | callFrame->uncheckedR(dst) = jsBoolean(result); |
f9bf01c6 | 3506 | vPC += OPCODE_LENGTH(op_del_by_val); |
9dae56ea A |
3507 | NEXT_INSTRUCTION(); |
3508 | } | |
3509 | DEFINE_OPCODE(op_put_by_index) { | |
3510 | /* put_by_index base(r) property(n) value(r) | |
3511 | ||
3512 | Sets register value on register base as the property named | |
3513 | by the immediate number property. Base is converted to | |
3514 | object first. | |
3515 | ||
3516 | Unlike many opcodes, this one does not write any output to | |
3517 | the register file. | |
3518 | ||
3519 | This opcode is mainly used to initialize array literals. | |
3520 | */ | |
f9bf01c6 A |
3521 | int base = vPC[1].u.operand; |
3522 | unsigned property = vPC[2].u.operand; | |
3523 | int value = vPC[3].u.operand; | |
9dae56ea | 3524 | |
ba379fdc | 3525 | callFrame->r(base).jsValue().put(callFrame, property, callFrame->r(value).jsValue()); |
9dae56ea | 3526 | |
f9bf01c6 | 3527 | vPC += OPCODE_LENGTH(op_put_by_index); |
9dae56ea A |
3528 | NEXT_INSTRUCTION(); |
3529 | } | |
3530 | DEFINE_OPCODE(op_loop) { | |
3531 | /* loop target(offset) | |
3532 | ||
3533 | Jumps unconditionally to offset target from the current | |
3534 | instruction. | |
3535 | ||
3536 | Additionally this loop instruction may terminate JS execution is | |
3537 | the JS timeout is reached. | |
3538 | */ | |
3539 | #if ENABLE(OPCODE_STATS) | |
3540 | OpcodeStats::resetLastInstruction(); | |
3541 | #endif | |
f9bf01c6 | 3542 | int target = vPC[1].u.operand; |
9dae56ea A |
3543 | CHECK_FOR_TIMEOUT(); |
3544 | vPC += target; | |
3545 | NEXT_INSTRUCTION(); | |
3546 | } | |
3547 | DEFINE_OPCODE(op_jmp) { | |
3548 | /* jmp target(offset) | |
3549 | ||
3550 | Jumps unconditionally to offset target from the current | |
3551 | instruction. | |
3552 | */ | |
3553 | #if ENABLE(OPCODE_STATS) | |
3554 | OpcodeStats::resetLastInstruction(); | |
3555 | #endif | |
f9bf01c6 | 3556 | int target = vPC[1].u.operand; |
9dae56ea A |
3557 | |
3558 | vPC += target; | |
3559 | NEXT_INSTRUCTION(); | |
3560 | } | |
3561 | DEFINE_OPCODE(op_loop_if_true) { | |
3562 | /* loop_if_true cond(r) target(offset) | |
3563 | ||
3564 | Jumps to offset target from the current instruction, if and | |
3565 | only if register cond converts to boolean as true. | |
3566 | ||
3567 | Additionally this loop instruction may terminate JS execution is | |
3568 | the JS timeout is reached. | |
3569 | */ | |
f9bf01c6 A |
3570 | int cond = vPC[1].u.operand; |
3571 | int target = vPC[2].u.operand; | |
ba379fdc | 3572 | if (callFrame->r(cond).jsValue().toBoolean(callFrame)) { |
9dae56ea A |
3573 | vPC += target; |
3574 | CHECK_FOR_TIMEOUT(); | |
3575 | NEXT_INSTRUCTION(); | |
3576 | } | |
3577 | ||
f9bf01c6 A |
3578 | vPC += OPCODE_LENGTH(op_loop_if_true); |
3579 | NEXT_INSTRUCTION(); | |
3580 | } | |
3581 | DEFINE_OPCODE(op_loop_if_false) { | |
3582 | /* loop_if_true cond(r) target(offset) | |
3583 | ||
3584 | Jumps to offset target from the current instruction, if and | |
3585 | only if register cond converts to boolean as false. | |
3586 | ||
3587 | Additionally this loop instruction may terminate JS execution is | |
3588 | the JS timeout is reached. | |
3589 | */ | |
3590 | int cond = vPC[1].u.operand; | |
3591 | int target = vPC[2].u.operand; | |
3592 | if (!callFrame->r(cond).jsValue().toBoolean(callFrame)) { | |
3593 | vPC += target; | |
3594 | CHECK_FOR_TIMEOUT(); | |
3595 | NEXT_INSTRUCTION(); | |
3596 | } | |
3597 | ||
3598 | vPC += OPCODE_LENGTH(op_loop_if_true); | |
9dae56ea A |
3599 | NEXT_INSTRUCTION(); |
3600 | } | |
3601 | DEFINE_OPCODE(op_jtrue) { | |
3602 | /* jtrue cond(r) target(offset) | |
3603 | ||
3604 | Jumps to offset target from the current instruction, if and | |
3605 | only if register cond converts to boolean as true. | |
3606 | */ | |
f9bf01c6 A |
3607 | int cond = vPC[1].u.operand; |
3608 | int target = vPC[2].u.operand; | |
ba379fdc | 3609 | if (callFrame->r(cond).jsValue().toBoolean(callFrame)) { |
9dae56ea A |
3610 | vPC += target; |
3611 | NEXT_INSTRUCTION(); | |
3612 | } | |
3613 | ||
f9bf01c6 | 3614 | vPC += OPCODE_LENGTH(op_jtrue); |
9dae56ea A |
3615 | NEXT_INSTRUCTION(); |
3616 | } | |
3617 | DEFINE_OPCODE(op_jfalse) { | |
3618 | /* jfalse cond(r) target(offset) | |
3619 | ||
3620 | Jumps to offset target from the current instruction, if and | |
3621 | only if register cond converts to boolean as false. | |
3622 | */ | |
f9bf01c6 A |
3623 | int cond = vPC[1].u.operand; |
3624 | int target = vPC[2].u.operand; | |
ba379fdc | 3625 | if (!callFrame->r(cond).jsValue().toBoolean(callFrame)) { |
9dae56ea A |
3626 | vPC += target; |
3627 | NEXT_INSTRUCTION(); | |
3628 | } | |
3629 | ||
f9bf01c6 | 3630 | vPC += OPCODE_LENGTH(op_jfalse); |
9dae56ea A |
3631 | NEXT_INSTRUCTION(); |
3632 | } | |
3633 | DEFINE_OPCODE(op_jeq_null) { | |
3634 | /* jeq_null src(r) target(offset) | |
3635 | ||
3636 | Jumps to offset target from the current instruction, if and | |
3637 | only if register src is null. | |
3638 | */ | |
f9bf01c6 A |
3639 | int src = vPC[1].u.operand; |
3640 | int target = vPC[2].u.operand; | |
ba379fdc | 3641 | JSValue srcValue = callFrame->r(src).jsValue(); |
9dae56ea A |
3642 | |
3643 | if (srcValue.isUndefinedOrNull() || (srcValue.isCell() && srcValue.asCell()->structure()->typeInfo().masqueradesAsUndefined())) { | |
3644 | vPC += target; | |
3645 | NEXT_INSTRUCTION(); | |
3646 | } | |
3647 | ||
f9bf01c6 | 3648 | vPC += OPCODE_LENGTH(op_jeq_null); |
9dae56ea A |
3649 | NEXT_INSTRUCTION(); |
3650 | } | |
3651 | DEFINE_OPCODE(op_jneq_null) { | |
3652 | /* jneq_null src(r) target(offset) | |
3653 | ||
3654 | Jumps to offset target from the current instruction, if and | |
3655 | only if register src is not null. | |
3656 | */ | |
f9bf01c6 A |
3657 | int src = vPC[1].u.operand; |
3658 | int target = vPC[2].u.operand; | |
ba379fdc | 3659 | JSValue srcValue = callFrame->r(src).jsValue(); |
9dae56ea | 3660 | |
f9bf01c6 | 3661 | if (!srcValue.isUndefinedOrNull() && (!srcValue.isCell() || !srcValue.asCell()->structure()->typeInfo().masqueradesAsUndefined())) { |
9dae56ea A |
3662 | vPC += target; |
3663 | NEXT_INSTRUCTION(); | |
3664 | } | |
3665 | ||
f9bf01c6 | 3666 | vPC += OPCODE_LENGTH(op_jneq_null); |
9dae56ea A |
3667 | NEXT_INSTRUCTION(); |
3668 | } | |
ba379fdc A |
3669 | DEFINE_OPCODE(op_jneq_ptr) { |
3670 | /* jneq_ptr src(r) ptr(jsCell) target(offset) | |
3671 | ||
3672 | Jumps to offset target from the current instruction, if the value r is equal | |
3673 | to ptr, using pointer equality. | |
3674 | */ | |
f9bf01c6 | 3675 | int src = vPC[1].u.operand; |
f9bf01c6 | 3676 | int target = vPC[3].u.operand; |
ba379fdc | 3677 | JSValue srcValue = callFrame->r(src).jsValue(); |
14957cd0 | 3678 | if (srcValue != vPC[2].u.jsCell.get()) { |
ba379fdc A |
3679 | vPC += target; |
3680 | NEXT_INSTRUCTION(); | |
3681 | } | |
3682 | ||
f9bf01c6 | 3683 | vPC += OPCODE_LENGTH(op_jneq_ptr); |
ba379fdc A |
3684 | NEXT_INSTRUCTION(); |
3685 | } | |
9dae56ea A |
3686 | DEFINE_OPCODE(op_loop_if_less) { |
3687 | /* loop_if_less src1(r) src2(r) target(offset) | |
3688 | ||
3689 | Checks whether register src1 is less than register src2, as | |
3690 | with the ECMAScript '<' operator, and then jumps to offset | |
3691 | target from the current instruction, if and only if the | |
3692 | result of the comparison is true. | |
3693 | ||
3694 | Additionally this loop instruction may terminate JS execution is | |
3695 | the JS timeout is reached. | |
3696 | */ | |
f9bf01c6 A |
3697 | JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); |
3698 | JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); | |
3699 | int target = vPC[3].u.operand; | |
9dae56ea A |
3700 | |
3701 | bool result = jsLess(callFrame, src1, src2); | |
3702 | CHECK_FOR_EXCEPTION(); | |
3703 | ||
3704 | if (result) { | |
3705 | vPC += target; | |
3706 | CHECK_FOR_TIMEOUT(); | |
3707 | NEXT_INSTRUCTION(); | |
3708 | } | |
3709 | ||
f9bf01c6 | 3710 | vPC += OPCODE_LENGTH(op_loop_if_less); |
9dae56ea A |
3711 | NEXT_INSTRUCTION(); |
3712 | } | |
3713 | DEFINE_OPCODE(op_loop_if_lesseq) { | |
3714 | /* loop_if_lesseq src1(r) src2(r) target(offset) | |
3715 | ||
3716 | Checks whether register src1 is less than or equal to register | |
3717 | src2, as with the ECMAScript '<=' operator, and then jumps to | |
3718 | offset target from the current instruction, if and only if the | |
3719 | result of the comparison is true. | |
3720 | ||
3721 | Additionally this loop instruction may terminate JS execution is | |
3722 | the JS timeout is reached. | |
3723 | */ | |
f9bf01c6 A |
3724 | JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); |
3725 | JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); | |
3726 | int target = vPC[3].u.operand; | |
9dae56ea A |
3727 | |
3728 | bool result = jsLessEq(callFrame, src1, src2); | |
3729 | CHECK_FOR_EXCEPTION(); | |
3730 | ||
3731 | if (result) { | |
3732 | vPC += target; | |
3733 | CHECK_FOR_TIMEOUT(); | |
3734 | NEXT_INSTRUCTION(); | |
3735 | } | |
3736 | ||
f9bf01c6 | 3737 | vPC += OPCODE_LENGTH(op_loop_if_lesseq); |
9dae56ea A |
3738 | NEXT_INSTRUCTION(); |
3739 | } | |
3740 | DEFINE_OPCODE(op_jnless) { | |
3741 | /* jnless src1(r) src2(r) target(offset) | |
3742 | ||
3743 | Checks whether register src1 is less than register src2, as | |
3744 | with the ECMAScript '<' operator, and then jumps to offset | |
3745 | target from the current instruction, if and only if the | |
3746 | result of the comparison is false. | |
3747 | */ | |
f9bf01c6 A |
3748 | JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); |
3749 | JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); | |
3750 | int target = vPC[3].u.operand; | |
9dae56ea A |
3751 | |
3752 | bool result = jsLess(callFrame, src1, src2); | |
3753 | CHECK_FOR_EXCEPTION(); | |
3754 | ||
3755 | if (!result) { | |
3756 | vPC += target; | |
3757 | NEXT_INSTRUCTION(); | |
3758 | } | |
3759 | ||
f9bf01c6 A |
3760 | vPC += OPCODE_LENGTH(op_jnless); |
3761 | NEXT_INSTRUCTION(); | |
3762 | } | |
3763 | DEFINE_OPCODE(op_jless) { | |
3764 | /* jless src1(r) src2(r) target(offset) | |
3765 | ||
3766 | Checks whether register src1 is less than register src2, as | |
3767 | with the ECMAScript '<' operator, and then jumps to offset | |
3768 | target from the current instruction, if and only if the | |
3769 | result of the comparison is true. | |
3770 | */ | |
3771 | JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); | |
3772 | JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); | |
3773 | int target = vPC[3].u.operand; | |
3774 | ||
3775 | bool result = jsLess(callFrame, src1, src2); | |
3776 | CHECK_FOR_EXCEPTION(); | |
3777 | ||
3778 | if (result) { | |
3779 | vPC += target; | |
3780 | NEXT_INSTRUCTION(); | |
3781 | } | |
3782 | ||
3783 | vPC += OPCODE_LENGTH(op_jless); | |
9dae56ea A |
3784 | NEXT_INSTRUCTION(); |
3785 | } | |
ba379fdc A |
3786 | DEFINE_OPCODE(op_jnlesseq) { |
3787 | /* jnlesseq src1(r) src2(r) target(offset) | |
3788 | ||
3789 | Checks whether register src1 is less than or equal to | |
3790 | register src2, as with the ECMAScript '<=' operator, | |
3791 | and then jumps to offset target from the current instruction, | |
3792 | if and only if theresult of the comparison is false. | |
3793 | */ | |
f9bf01c6 A |
3794 | JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); |
3795 | JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); | |
3796 | int target = vPC[3].u.operand; | |
ba379fdc A |
3797 | |
3798 | bool result = jsLessEq(callFrame, src1, src2); | |
3799 | CHECK_FOR_EXCEPTION(); | |
3800 | ||
3801 | if (!result) { | |
3802 | vPC += target; | |
3803 | NEXT_INSTRUCTION(); | |
3804 | } | |
3805 | ||
f9bf01c6 | 3806 | vPC += OPCODE_LENGTH(op_jnlesseq); |
ba379fdc A |
3807 | NEXT_INSTRUCTION(); |
3808 | } | |
4e4e5a6f A |
3809 | DEFINE_OPCODE(op_jlesseq) { |
3810 | /* jlesseq src1(r) src2(r) target(offset) | |
3811 | ||
3812 | Checks whether register src1 is less than or equal to | |
3813 | register src2, as with the ECMAScript '<=' operator, | |
3814 | and then jumps to offset target from the current instruction, | |
3815 | if and only if the result of the comparison is true. | |
3816 | */ | |
3817 | JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); | |
3818 | JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); | |
3819 | int target = vPC[3].u.operand; | |
3820 | ||
3821 | bool result = jsLessEq(callFrame, src1, src2); | |
3822 | CHECK_FOR_EXCEPTION(); | |
3823 | ||
3824 | if (result) { | |
3825 | vPC += target; | |
3826 | NEXT_INSTRUCTION(); | |
3827 | } | |
3828 | ||
3829 | vPC += OPCODE_LENGTH(op_jlesseq); | |
3830 | NEXT_INSTRUCTION(); | |
3831 | } | |
9dae56ea A |
3832 | DEFINE_OPCODE(op_switch_imm) { |
3833 | /* switch_imm tableIndex(n) defaultOffset(offset) scrutinee(r) | |
3834 | ||
3835 | Performs a range checked switch on the scrutinee value, using | |
3836 | the tableIndex-th immediate switch jump table. If the scrutinee value | |
3837 | is an immediate number in the range covered by the referenced jump | |
3838 | table, and the value at jumpTable[scrutinee value] is non-zero, then | |
3839 | that value is used as the jump offset, otherwise defaultOffset is used. | |
3840 | */ | |
f9bf01c6 A |
3841 | int tableIndex = vPC[1].u.operand; |
3842 | int defaultOffset = vPC[2].u.operand; | |
3843 | JSValue scrutinee = callFrame->r(vPC[3].u.operand).jsValue(); | |
ba379fdc | 3844 | if (scrutinee.isInt32()) |
14957cd0 | 3845 | vPC += codeBlock->immediateSwitchJumpTable(tableIndex).offsetForValue(scrutinee.asInt32(), defaultOffset); |
9dae56ea | 3846 | else { |
ba379fdc A |
3847 | double value; |
3848 | int32_t intValue; | |
3849 | if (scrutinee.getNumber(value) && ((intValue = static_cast<int32_t>(value)) == value)) | |
14957cd0 | 3850 | vPC += codeBlock->immediateSwitchJumpTable(tableIndex).offsetForValue(intValue, defaultOffset); |
9dae56ea A |
3851 | else |
3852 | vPC += defaultOffset; | |
3853 | } | |
3854 | NEXT_INSTRUCTION(); | |
3855 | } | |
3856 | DEFINE_OPCODE(op_switch_char) { | |
3857 | /* switch_char tableIndex(n) defaultOffset(offset) scrutinee(r) | |
3858 | ||
3859 | Performs a range checked switch on the scrutinee value, using | |
3860 | the tableIndex-th character switch jump table. If the scrutinee value | |
3861 | is a single character string in the range covered by the referenced jump | |
3862 | table, and the value at jumpTable[scrutinee value] is non-zero, then | |
3863 | that value is used as the jump offset, otherwise defaultOffset is used. | |
3864 | */ | |
f9bf01c6 A |
3865 | int tableIndex = vPC[1].u.operand; |
3866 | int defaultOffset = vPC[2].u.operand; | |
3867 | JSValue scrutinee = callFrame->r(vPC[3].u.operand).jsValue(); | |
9dae56ea A |
3868 | if (!scrutinee.isString()) |
3869 | vPC += defaultOffset; | |
3870 | else { | |
14957cd0 | 3871 | StringImpl* value = asString(scrutinee)->value(callFrame).impl(); |
4e4e5a6f | 3872 | if (value->length() != 1) |
9dae56ea A |
3873 | vPC += defaultOffset; |
3874 | else | |
14957cd0 | 3875 | vPC += codeBlock->characterSwitchJumpTable(tableIndex).offsetForValue(value->characters()[0], defaultOffset); |
9dae56ea A |
3876 | } |
3877 | NEXT_INSTRUCTION(); | |
3878 | } | |
3879 | DEFINE_OPCODE(op_switch_string) { | |
3880 | /* switch_string tableIndex(n) defaultOffset(offset) scrutinee(r) | |
3881 | ||
3882 | Performs a sparse hashmap based switch on the value in the scrutinee | |
3883 | register, using the tableIndex-th string switch jump table. If the | |
3884 | scrutinee value is a string that exists as a key in the referenced | |
3885 | jump table, then the value associated with the string is used as the | |
3886 | jump offset, otherwise defaultOffset is used. | |
3887 | */ | |
f9bf01c6 A |
3888 | int tableIndex = vPC[1].u.operand; |
3889 | int defaultOffset = vPC[2].u.operand; | |
3890 | JSValue scrutinee = callFrame->r(vPC[3].u.operand).jsValue(); | |
9dae56ea A |
3891 | if (!scrutinee.isString()) |
3892 | vPC += defaultOffset; | |
3893 | else | |
14957cd0 | 3894 | vPC += codeBlock->stringSwitchJumpTable(tableIndex).offsetForValue(asString(scrutinee)->value(callFrame).impl(), defaultOffset); |
9dae56ea A |
3895 | NEXT_INSTRUCTION(); |
3896 | } | |
3897 | DEFINE_OPCODE(op_new_func) { | |
3898 | /* new_func dst(r) func(f) | |
3899 | ||
3900 | Constructs a new Function instance from function func and | |
3901 | the current scope chain using the original Function | |
3902 | constructor, using the rules for function declarations, and | |
3903 | puts the result in register dst. | |
3904 | */ | |
f9bf01c6 A |
3905 | int dst = vPC[1].u.operand; |
3906 | int func = vPC[2].u.operand; | |
14957cd0 A |
3907 | int shouldCheck = vPC[3].u.operand; |
3908 | ASSERT(codeBlock->codeType() != FunctionCode || !codeBlock->needsFullScopeChain() || callFrame->r(codeBlock->activationRegister()).jsValue()); | |
3909 | if (!shouldCheck || !callFrame->r(dst).jsValue()) | |
3910 | callFrame->uncheckedR(dst) = JSValue(codeBlock->functionDecl(func)->make(callFrame, callFrame->scopeChain())); | |
9dae56ea | 3911 | |
f9bf01c6 | 3912 | vPC += OPCODE_LENGTH(op_new_func); |
9dae56ea A |
3913 | NEXT_INSTRUCTION(); |
3914 | } | |
3915 | DEFINE_OPCODE(op_new_func_exp) { | |
3916 | /* new_func_exp dst(r) func(f) | |
3917 | ||
3918 | Constructs a new Function instance from function func and | |
3919 | the current scope chain using the original Function | |
3920 | constructor, using the rules for function expressions, and | |
3921 | puts the result in register dst. | |
3922 | */ | |
f9bf01c6 A |
3923 | int dst = vPC[1].u.operand; |
3924 | int funcIndex = vPC[2].u.operand; | |
14957cd0 A |
3925 | |
3926 | ASSERT(codeBlock->codeType() != FunctionCode || !codeBlock->needsFullScopeChain() || callFrame->r(codeBlock->activationRegister()).jsValue()); | |
3927 | FunctionExecutable* function = codeBlock->functionExpr(funcIndex); | |
f9bf01c6 A |
3928 | JSFunction* func = function->make(callFrame, callFrame->scopeChain()); |
3929 | ||
3930 | /* | |
3931 | The Identifier in a FunctionExpression can be referenced from inside | |
3932 | the FunctionExpression's FunctionBody to allow the function to call | |
3933 | itself recursively. However, unlike in a FunctionDeclaration, the | |
3934 | Identifier in a FunctionExpression cannot be referenced from and | |
3935 | does not affect the scope enclosing the FunctionExpression. | |
3936 | */ | |
3937 | if (!function->name().isNull()) { | |
3938 | JSStaticScopeObject* functionScopeObject = new (callFrame) JSStaticScopeObject(callFrame, function->name(), func, ReadOnly | DontDelete); | |
14957cd0 | 3939 | func->setScope(*globalData, func->scope()->push(functionScopeObject)); |
f9bf01c6 | 3940 | } |
9dae56ea | 3941 | |
14957cd0 | 3942 | callFrame->uncheckedR(dst) = JSValue(func); |
f9bf01c6 A |
3943 | |
3944 | vPC += OPCODE_LENGTH(op_new_func_exp); | |
9dae56ea A |
3945 | NEXT_INSTRUCTION(); |
3946 | } | |
3947 | DEFINE_OPCODE(op_call_eval) { | |
14957cd0 | 3948 | /* call_eval func(r) argCount(n) registerOffset(n) |
9dae56ea A |
3949 | |
3950 | Call a function named "eval" with no explicit "this" value | |
3951 | (which may therefore be the eval operator). If register | |
3952 | thisVal is the global object, and register func contains | |
3953 | that global object's original global eval function, then | |
3954 | perform the eval operator in local scope (interpreting | |
3955 | the argument registers as for the "call" | |
3956 | opcode). Otherwise, act exactly as the "call" opcode would. | |
3957 | */ | |
3958 | ||
14957cd0 A |
3959 | int func = vPC[1].u.operand; |
3960 | int argCount = vPC[2].u.operand; | |
3961 | int registerOffset = vPC[3].u.operand; | |
3962 | ||
3963 | ASSERT(codeBlock->codeType() != FunctionCode || !codeBlock->needsFullScopeChain() || callFrame->r(codeBlock->activationRegister()).jsValue()); | |
ba379fdc | 3964 | JSValue funcVal = callFrame->r(func).jsValue(); |
9dae56ea A |
3965 | |
3966 | Register* newCallFrame = callFrame->registers() + registerOffset; | |
3967 | Register* argv = newCallFrame - RegisterFile::CallFrameHeaderSize - argCount; | |
ba379fdc | 3968 | JSValue thisValue = argv[0].jsValue(); |
14957cd0 | 3969 | JSGlobalObject* globalObject = callFrame->scopeChain()->globalObject.get(); |
9dae56ea A |
3970 | |
3971 | if (thisValue == globalObject && funcVal == globalObject->evalFunction()) { | |
14957cd0 A |
3972 | JSValue result = callEval(callFrame, registerFile, argv, argCount, registerOffset); |
3973 | if ((exceptionValue = globalData->exception)) | |
9dae56ea | 3974 | goto vm_throw; |
14957cd0 | 3975 | functionReturnValue = result; |
9dae56ea | 3976 | |
f9bf01c6 | 3977 | vPC += OPCODE_LENGTH(op_call_eval); |
9dae56ea A |
3978 | NEXT_INSTRUCTION(); |
3979 | } | |
3980 | ||
3981 | // We didn't find the blessed version of eval, so process this | |
3982 | // instruction as a normal function call. | |
3983 | // fall through to op_call | |
3984 | } | |
3985 | DEFINE_OPCODE(op_call) { | |
14957cd0 | 3986 | /* call func(r) argCount(n) registerOffset(n) |
9dae56ea A |
3987 | |
3988 | Perform a function call. | |
3989 | ||
3990 | registerOffset is the distance the callFrame pointer should move | |
3991 | before the VM initializes the new call frame's header. | |
3992 | ||
3993 | dst is where op_ret should store its result. | |
3994 | */ | |
3995 | ||
14957cd0 A |
3996 | int func = vPC[1].u.operand; |
3997 | int argCount = vPC[2].u.operand; | |
3998 | int registerOffset = vPC[3].u.operand; | |
9dae56ea | 3999 | |
ba379fdc | 4000 | JSValue v = callFrame->r(func).jsValue(); |
9dae56ea A |
4001 | |
4002 | CallData callData; | |
14957cd0 | 4003 | CallType callType = getCallData(v, callData); |
9dae56ea A |
4004 | |
4005 | if (callType == CallTypeJS) { | |
4006 | ScopeChainNode* callDataScopeChain = callData.js.scopeChain; | |
9dae56ea | 4007 | |
14957cd0 A |
4008 | JSObject* error = callData.js.functionExecutable->compileForCall(callFrame, callDataScopeChain); |
4009 | if (UNLIKELY(!!error)) { | |
4010 | exceptionValue = error; | |
4011 | goto vm_throw; | |
4012 | } | |
9dae56ea | 4013 | |
14957cd0 A |
4014 | CallFrame* previousCallFrame = callFrame; |
4015 | CodeBlock* newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); | |
9dae56ea A |
4016 | callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount); |
4017 | if (UNLIKELY(!callFrame)) { | |
4018 | callFrame = previousCallFrame; | |
4019 | exceptionValue = createStackOverflowError(callFrame); | |
4020 | goto vm_throw; | |
4021 | } | |
4022 | ||
14957cd0 A |
4023 | callFrame->init(newCodeBlock, vPC + OPCODE_LENGTH(op_call), callDataScopeChain, previousCallFrame, argCount, asFunction(v)); |
4024 | codeBlock = newCodeBlock; | |
4025 | ASSERT(codeBlock == callFrame->codeBlock()); | |
9dae56ea A |
4026 | vPC = newCodeBlock->instructions().begin(); |
4027 | ||
4028 | #if ENABLE(OPCODE_STATS) | |
4029 | OpcodeStats::resetLastInstruction(); | |
4030 | #endif | |
4031 | ||
4032 | NEXT_INSTRUCTION(); | |
4033 | } | |
4034 | ||
4035 | if (callType == CallTypeHost) { | |
4036 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
4037 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); | |
14957cd0 A |
4038 | if (!registerFile->grow(newCallFrame->registers())) { |
4039 | exceptionValue = createStackOverflowError(callFrame); | |
4040 | goto vm_throw; | |
4041 | } | |
9dae56ea | 4042 | |
14957cd0 | 4043 | newCallFrame->init(0, vPC + OPCODE_LENGTH(op_call), scopeChain, callFrame, argCount, asObject(v)); |
9dae56ea | 4044 | |
ba379fdc | 4045 | JSValue returnValue; |
9dae56ea | 4046 | { |
f9bf01c6 | 4047 | SamplingTool::HostCallRecord callRecord(m_sampler.get()); |
14957cd0 | 4048 | returnValue = JSValue::decode(callData.native.function(newCallFrame)); |
9dae56ea A |
4049 | } |
4050 | CHECK_FOR_EXCEPTION(); | |
4051 | ||
14957cd0 | 4052 | functionReturnValue = returnValue; |
9dae56ea | 4053 | |
f9bf01c6 | 4054 | vPC += OPCODE_LENGTH(op_call); |
9dae56ea A |
4055 | NEXT_INSTRUCTION(); |
4056 | } | |
4057 | ||
4058 | ASSERT(callType == CallTypeNone); | |
4059 | ||
14957cd0 | 4060 | exceptionValue = createNotAFunctionError(callFrame, v); |
9dae56ea A |
4061 | goto vm_throw; |
4062 | } | |
ba379fdc | 4063 | DEFINE_OPCODE(op_load_varargs) { |
f9bf01c6 A |
4064 | int argCountDst = vPC[1].u.operand; |
4065 | int argsOffset = vPC[2].u.operand; | |
ba379fdc A |
4066 | |
4067 | JSValue arguments = callFrame->r(argsOffset).jsValue(); | |
fb8617cd | 4068 | uint32_t argCount = 0; |
ba379fdc | 4069 | if (!arguments) { |
14957cd0 | 4070 | argCount = (uint32_t)(callFrame->argumentCount()); |
fb8617cd | 4071 | argCount = min<uint32_t>(argCount, Arguments::MaxArguments); |
ba379fdc A |
4072 | int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; |
4073 | Register* newEnd = callFrame->registers() + sizeDelta; | |
4074 | if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { | |
4075 | exceptionValue = createStackOverflowError(callFrame); | |
4076 | goto vm_throw; | |
4077 | } | |
14957cd0 A |
4078 | ASSERT(!asFunction(callFrame->callee())->isHostFunction()); |
4079 | int32_t expectedParams = asFunction(callFrame->callee())->jsExecutable()->parameterCount(); | |
4080 | int32_t inplaceArgs = min(static_cast<int32_t>(argCount), expectedParams); | |
4081 | int32_t i = 0; | |
ba379fdc A |
4082 | Register* argStore = callFrame->registers() + argsOffset; |
4083 | ||
4084 | // First step is to copy the "expected" parameters from their normal location relative to the callframe | |
4085 | for (; i < inplaceArgs; i++) | |
4086 | argStore[i] = callFrame->registers()[i - RegisterFile::CallFrameHeaderSize - expectedParams]; | |
4087 | // Then we copy any additional arguments that may be further up the stack ('-1' to account for 'this') | |
14957cd0 A |
4088 | for (; i < static_cast<int32_t>(argCount); i++) |
4089 | argStore[i] = callFrame->registers()[i - RegisterFile::CallFrameHeaderSize - expectedParams - static_cast<int32_t>(argCount) - 1]; | |
ba379fdc A |
4090 | } else if (!arguments.isUndefinedOrNull()) { |
4091 | if (!arguments.isObject()) { | |
14957cd0 | 4092 | exceptionValue = createInvalidParamError(callFrame, "Function.prototype.apply", arguments); |
ba379fdc A |
4093 | goto vm_throw; |
4094 | } | |
14957cd0 | 4095 | if (asObject(arguments)->classInfo() == &Arguments::s_info) { |
ba379fdc A |
4096 | Arguments* args = asArguments(arguments); |
4097 | argCount = args->numProvidedArguments(callFrame); | |
fb8617cd | 4098 | argCount = min<uint32_t>(argCount, Arguments::MaxArguments); |
ba379fdc A |
4099 | int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; |
4100 | Register* newEnd = callFrame->registers() + sizeDelta; | |
4101 | if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { | |
4102 | exceptionValue = createStackOverflowError(callFrame); | |
4103 | goto vm_throw; | |
4104 | } | |
4105 | args->copyToRegisters(callFrame, callFrame->registers() + argsOffset, argCount); | |
4106 | } else if (isJSArray(&callFrame->globalData(), arguments)) { | |
4107 | JSArray* array = asArray(arguments); | |
4108 | argCount = array->length(); | |
fb8617cd | 4109 | argCount = min<uint32_t>(argCount, Arguments::MaxArguments); |
ba379fdc A |
4110 | int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; |
4111 | Register* newEnd = callFrame->registers() + sizeDelta; | |
4112 | if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { | |
4113 | exceptionValue = createStackOverflowError(callFrame); | |
4114 | goto vm_throw; | |
4115 | } | |
4116 | array->copyToRegisters(callFrame, callFrame->registers() + argsOffset, argCount); | |
14957cd0 | 4117 | } else if (asObject(arguments)->inherits(&JSArray::s_info)) { |
ba379fdc A |
4118 | JSObject* argObject = asObject(arguments); |
4119 | argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); | |
fb8617cd | 4120 | argCount = min<uint32_t>(argCount, Arguments::MaxArguments); |
ba379fdc A |
4121 | int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; |
4122 | Register* newEnd = callFrame->registers() + sizeDelta; | |
4123 | if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { | |
4124 | exceptionValue = createStackOverflowError(callFrame); | |
4125 | goto vm_throw; | |
4126 | } | |
4127 | Register* argsBuffer = callFrame->registers() + argsOffset; | |
fb8617cd | 4128 | for (uint32_t i = 0; i < argCount; ++i) { |
ba379fdc A |
4129 | argsBuffer[i] = asObject(arguments)->get(callFrame, i); |
4130 | CHECK_FOR_EXCEPTION(); | |
4131 | } | |
4132 | } else { | |
14957cd0 | 4133 | exceptionValue = createInvalidParamError(callFrame, "Function.prototype.apply", arguments); |
4e4e5a6f | 4134 | goto vm_throw; |
ba379fdc A |
4135 | } |
4136 | } | |
4137 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 4138 | callFrame->uncheckedR(argCountDst) = Register::withInt(argCount + 1); |
f9bf01c6 | 4139 | vPC += OPCODE_LENGTH(op_load_varargs); |
ba379fdc A |
4140 | NEXT_INSTRUCTION(); |
4141 | } | |
4142 | DEFINE_OPCODE(op_call_varargs) { | |
14957cd0 | 4143 | /* call_varargs func(r) argCountReg(r) baseRegisterOffset(n) |
ba379fdc A |
4144 | |
4145 | Perform a function call with a dynamic set of arguments. | |
4146 | ||
4147 | registerOffset is the distance the callFrame pointer should move | |
4148 | before the VM initializes the new call frame's header, excluding | |
4149 | space for arguments. | |
4150 | ||
4151 | dst is where op_ret should store its result. | |
4152 | */ | |
4153 | ||
14957cd0 A |
4154 | int func = vPC[1].u.operand; |
4155 | int argCountReg = vPC[2].u.operand; | |
4156 | int registerOffset = vPC[3].u.operand; | |
ba379fdc A |
4157 | |
4158 | JSValue v = callFrame->r(func).jsValue(); | |
4159 | int argCount = callFrame->r(argCountReg).i(); | |
4160 | registerOffset += argCount; | |
4161 | CallData callData; | |
14957cd0 | 4162 | CallType callType = getCallData(v, callData); |
ba379fdc A |
4163 | |
4164 | if (callType == CallTypeJS) { | |
4165 | ScopeChainNode* callDataScopeChain = callData.js.scopeChain; | |
14957cd0 A |
4166 | |
4167 | JSObject* error = callData.js.functionExecutable->compileForCall(callFrame, callDataScopeChain); | |
4168 | if (UNLIKELY(!!error)) { | |
4169 | exceptionValue = error; | |
4170 | goto vm_throw; | |
4171 | } | |
4172 | ||
ba379fdc | 4173 | CallFrame* previousCallFrame = callFrame; |
14957cd0 | 4174 | CodeBlock* newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); |
ba379fdc A |
4175 | callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount); |
4176 | if (UNLIKELY(!callFrame)) { | |
4177 | callFrame = previousCallFrame; | |
4178 | exceptionValue = createStackOverflowError(callFrame); | |
4179 | goto vm_throw; | |
4180 | } | |
14957cd0 A |
4181 | |
4182 | callFrame->init(newCodeBlock, vPC + OPCODE_LENGTH(op_call_varargs), callDataScopeChain, previousCallFrame, argCount, asFunction(v)); | |
4183 | codeBlock = newCodeBlock; | |
4184 | ASSERT(codeBlock == callFrame->codeBlock()); | |
ba379fdc A |
4185 | vPC = newCodeBlock->instructions().begin(); |
4186 | ||
4187 | #if ENABLE(OPCODE_STATS) | |
4188 | OpcodeStats::resetLastInstruction(); | |
4189 | #endif | |
4190 | ||
4191 | NEXT_INSTRUCTION(); | |
4192 | } | |
4193 | ||
4194 | if (callType == CallTypeHost) { | |
4195 | ScopeChainNode* scopeChain = callFrame->scopeChain(); | |
4196 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); | |
14957cd0 A |
4197 | if (!registerFile->grow(newCallFrame->registers())) { |
4198 | exceptionValue = createStackOverflowError(callFrame); | |
4199 | goto vm_throw; | |
4200 | } | |
4201 | newCallFrame->init(0, vPC + OPCODE_LENGTH(op_call_varargs), scopeChain, callFrame, argCount, asObject(v)); | |
ba379fdc A |
4202 | |
4203 | JSValue returnValue; | |
4204 | { | |
f9bf01c6 | 4205 | SamplingTool::HostCallRecord callRecord(m_sampler.get()); |
14957cd0 | 4206 | returnValue = JSValue::decode(callData.native.function(newCallFrame)); |
ba379fdc A |
4207 | } |
4208 | CHECK_FOR_EXCEPTION(); | |
4209 | ||
14957cd0 | 4210 | functionReturnValue = returnValue; |
ba379fdc | 4211 | |
f9bf01c6 | 4212 | vPC += OPCODE_LENGTH(op_call_varargs); |
ba379fdc A |
4213 | NEXT_INSTRUCTION(); |
4214 | } | |
4215 | ||
4216 | ASSERT(callType == CallTypeNone); | |
4217 | ||
14957cd0 | 4218 | exceptionValue = createNotAFunctionError(callFrame, v); |
ba379fdc A |
4219 | goto vm_throw; |
4220 | } | |
9dae56ea | 4221 | DEFINE_OPCODE(op_tear_off_activation) { |
14957cd0 | 4222 | /* tear_off_activation activation(r) arguments(r) |
9dae56ea | 4223 | |
14957cd0 A |
4224 | Copy locals and named parameters from the register file to the heap. |
4225 | Point the bindings in 'activation' and 'arguments' to this new backing | |
4226 | store. (Note that 'arguments' may not have been created. If created, | |
4227 | 'arguments' already holds a copy of any extra / unnamed parameters.) | |
9dae56ea | 4228 | |
14957cd0 | 4229 | This opcode appears before op_ret in functions that require full scope chains. |
9dae56ea A |
4230 | */ |
4231 | ||
14957cd0 A |
4232 | int activation = vPC[1].u.operand; |
4233 | int arguments = vPC[2].u.operand; | |
4234 | ASSERT(codeBlock->needsFullScopeChain()); | |
4235 | JSValue activationValue = callFrame->r(activation).jsValue(); | |
4236 | if (activationValue) { | |
4237 | asActivation(activationValue)->copyRegisters(*globalData); | |
9dae56ea | 4238 | |
14957cd0 A |
4239 | if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) { |
4240 | if (!codeBlock->isStrictMode()) | |
4241 | asArguments(argumentsValue)->setActivation(*globalData, asActivation(activationValue)); | |
4242 | } | |
4243 | } else if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) { | |
4244 | if (!codeBlock->isStrictMode()) | |
4245 | asArguments(argumentsValue)->copyRegisters(*globalData); | |
4246 | } | |
9dae56ea | 4247 | |
f9bf01c6 | 4248 | vPC += OPCODE_LENGTH(op_tear_off_activation); |
9dae56ea A |
4249 | NEXT_INSTRUCTION(); |
4250 | } | |
4251 | DEFINE_OPCODE(op_tear_off_arguments) { | |
14957cd0 | 4252 | /* tear_off_arguments arguments(r) |
9dae56ea | 4253 | |
14957cd0 A |
4254 | Copy named parameters from the register file to the heap. Point the |
4255 | bindings in 'arguments' to this new backing store. (Note that | |
4256 | 'arguments' may not have been created. If created, 'arguments' already | |
4257 | holds a copy of any extra / unnamed parameters.) | |
9dae56ea | 4258 | |
14957cd0 A |
4259 | This opcode appears before op_ret in functions that don't require full |
4260 | scope chains, but do use 'arguments'. | |
9dae56ea A |
4261 | */ |
4262 | ||
14957cd0 A |
4263 | int src1 = vPC[1].u.operand; |
4264 | ASSERT(!codeBlock->needsFullScopeChain() && codeBlock->ownerExecutable()->usesArguments()); | |
9dae56ea | 4265 | |
14957cd0 A |
4266 | if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(src1)).jsValue()) |
4267 | asArguments(arguments)->copyRegisters(*globalData); | |
9dae56ea | 4268 | |
f9bf01c6 | 4269 | vPC += OPCODE_LENGTH(op_tear_off_arguments); |
9dae56ea A |
4270 | NEXT_INSTRUCTION(); |
4271 | } | |
4272 | DEFINE_OPCODE(op_ret) { | |
14957cd0 A |
4273 | /* ret result(r) |
4274 | ||
4275 | Return register result as the return value of the current | |
4276 | function call, writing it into functionReturnValue. | |
4277 | In addition, unwind one call frame and restore the scope | |
4278 | chain, code block instruction pointer and register base | |
4279 | to those of the calling function. | |
4280 | */ | |
4281 | ||
4282 | int result = vPC[1].u.operand; | |
4283 | ||
4284 | JSValue returnValue = callFrame->r(result).jsValue(); | |
4285 | ||
4286 | vPC = callFrame->returnVPC(); | |
4287 | callFrame = callFrame->callerFrame(); | |
4288 | ||
4289 | if (callFrame->hasHostCallFrameFlag()) | |
4290 | return returnValue; | |
4291 | ||
4292 | functionReturnValue = returnValue; | |
4293 | codeBlock = callFrame->codeBlock(); | |
4294 | ASSERT(codeBlock == callFrame->codeBlock()); | |
4295 | ||
4296 | NEXT_INSTRUCTION(); | |
4297 | } | |
4298 | DEFINE_OPCODE(op_call_put_result) { | |
4299 | /* op_call_put_result result(r) | |
4300 | ||
4301 | Move call result from functionReturnValue to caller's | |
4302 | expected return value register. | |
4303 | */ | |
4304 | ||
4305 | callFrame->uncheckedR(vPC[1].u.operand) = functionReturnValue; | |
4306 | ||
4307 | vPC += OPCODE_LENGTH(op_call_put_result); | |
4308 | NEXT_INSTRUCTION(); | |
4309 | } | |
4310 | DEFINE_OPCODE(op_ret_object_or_this) { | |
9dae56ea A |
4311 | /* ret result(r) |
4312 | ||
4313 | Return register result as the return value of the current | |
4314 | function call, writing it into the caller's expected return | |
4315 | value register. In addition, unwind one call frame and | |
4316 | restore the scope chain, code block instruction pointer and | |
4317 | register base to those of the calling function. | |
4318 | */ | |
4319 | ||
f9bf01c6 | 4320 | int result = vPC[1].u.operand; |
9dae56ea | 4321 | |
ba379fdc | 4322 | JSValue returnValue = callFrame->r(result).jsValue(); |
9dae56ea | 4323 | |
14957cd0 A |
4324 | if (UNLIKELY(!returnValue.isObject())) |
4325 | returnValue = callFrame->r(vPC[2].u.operand).jsValue(); | |
4326 | ||
4e4e5a6f | 4327 | vPC = callFrame->returnVPC(); |
9dae56ea | 4328 | callFrame = callFrame->callerFrame(); |
14957cd0 | 4329 | |
9dae56ea A |
4330 | if (callFrame->hasHostCallFrameFlag()) |
4331 | return returnValue; | |
4332 | ||
14957cd0 A |
4333 | functionReturnValue = returnValue; |
4334 | codeBlock = callFrame->codeBlock(); | |
4335 | ASSERT(codeBlock == callFrame->codeBlock()); | |
9dae56ea A |
4336 | |
4337 | NEXT_INSTRUCTION(); | |
4338 | } | |
4339 | DEFINE_OPCODE(op_enter) { | |
4340 | /* enter | |
4341 | ||
14957cd0 A |
4342 | Initializes local variables to undefined. If the code block requires |
4343 | an activation, enter_with_activation is used instead. | |
9dae56ea | 4344 | |
14957cd0 | 4345 | This opcode appears only at the beginning of a code block. |
9dae56ea A |
4346 | */ |
4347 | ||
4348 | size_t i = 0; | |
9dae56ea | 4349 | for (size_t count = codeBlock->m_numVars; i < count; ++i) |
14957cd0 | 4350 | callFrame->uncheckedR(i) = jsUndefined(); |
9dae56ea | 4351 | |
f9bf01c6 | 4352 | vPC += OPCODE_LENGTH(op_enter); |
9dae56ea A |
4353 | NEXT_INSTRUCTION(); |
4354 | } | |
14957cd0 A |
4355 | DEFINE_OPCODE(op_create_activation) { |
4356 | /* create_activation dst(r) | |
9dae56ea | 4357 | |
14957cd0 A |
4358 | If the activation object for this callframe has not yet been created, |
4359 | this creates it and writes it back to dst. | |
4360 | */ | |
4361 | ||
4362 | int activationReg = vPC[1].u.operand; | |
4363 | if (!callFrame->r(activationReg).jsValue()) { | |
4364 | JSActivation* activation = new (globalData) JSActivation(callFrame, static_cast<FunctionExecutable*>(codeBlock->ownerExecutable())); | |
4365 | callFrame->r(activationReg) = JSValue(activation); | |
4366 | callFrame->setScopeChain(callFrame->scopeChain()->push(activation)); | |
4367 | } | |
4368 | vPC += OPCODE_LENGTH(op_create_activation); | |
4369 | NEXT_INSTRUCTION(); | |
4370 | } | |
4371 | DEFINE_OPCODE(op_get_callee) { | |
4372 | /* op_get_callee callee(r) | |
4373 | ||
4374 | Move callee into a register. | |
4375 | */ | |
4376 | ||
4377 | callFrame->uncheckedR(vPC[1].u.operand) = JSValue(callFrame->callee()); | |
4378 | ||
4379 | vPC += OPCODE_LENGTH(op_get_callee); | |
4380 | NEXT_INSTRUCTION(); | |
4381 | } | |
4382 | DEFINE_OPCODE(op_create_this) { | |
4383 | /* op_create_this this(r) proto(r) | |
4384 | ||
4385 | Allocate an object as 'this', fr use in construction. | |
9dae56ea A |
4386 | |
4387 | This opcode should only be used at the beginning of a code | |
4388 | block. | |
4389 | */ | |
4390 | ||
14957cd0 A |
4391 | int thisRegister = vPC[1].u.operand; |
4392 | int protoRegister = vPC[2].u.operand; | |
9dae56ea | 4393 | |
14957cd0 A |
4394 | JSFunction* constructor = asFunction(callFrame->callee()); |
4395 | #if !ASSERT_DISABLED | |
4396 | ConstructData constructData; | |
4397 | ASSERT(constructor->getConstructData(constructData) == ConstructTypeJS); | |
4398 | #endif | |
9dae56ea | 4399 | |
14957cd0 A |
4400 | Structure* structure; |
4401 | JSValue proto = callFrame->r(protoRegister).jsValue(); | |
4402 | if (proto.isObject()) | |
4403 | structure = asObject(proto)->inheritorID(callFrame->globalData()); | |
4404 | else | |
4405 | structure = constructor->scope()->globalObject->emptyObjectStructure(); | |
4406 | callFrame->uncheckedR(thisRegister) = constructEmptyObject(callFrame, structure); | |
9dae56ea | 4407 | |
14957cd0 | 4408 | vPC += OPCODE_LENGTH(op_create_this); |
9dae56ea A |
4409 | NEXT_INSTRUCTION(); |
4410 | } | |
4411 | DEFINE_OPCODE(op_convert_this) { | |
4412 | /* convert_this this(r) | |
4413 | ||
4414 | Takes the value in the 'this' register, converts it to a | |
4415 | value that is suitable for use as the 'this' value, and | |
4416 | stores it in the 'this' register. This opcode is emitted | |
4417 | to avoid doing the conversion in the caller unnecessarily. | |
4418 | ||
4419 | This opcode should only be used at the beginning of a code | |
4420 | block. | |
4421 | */ | |
4422 | ||
f9bf01c6 | 4423 | int thisRegister = vPC[1].u.operand; |
ba379fdc | 4424 | JSValue thisVal = callFrame->r(thisRegister).jsValue(); |
9dae56ea | 4425 | if (thisVal.needsThisConversion()) |
14957cd0 | 4426 | callFrame->uncheckedR(thisRegister) = JSValue(thisVal.toThisObject(callFrame)); |
9dae56ea | 4427 | |
f9bf01c6 | 4428 | vPC += OPCODE_LENGTH(op_convert_this); |
9dae56ea A |
4429 | NEXT_INSTRUCTION(); |
4430 | } | |
14957cd0 A |
4431 | DEFINE_OPCODE(op_convert_this_strict) { |
4432 | /* convert_this_strict this(r) | |
4433 | ||
4434 | Takes the value in the 'this' register, and converts it to | |
4435 | its "this" form if (and only if) "this" is an object with a | |
4436 | custom this conversion | |
4437 | ||
4438 | This opcode should only be used at the beginning of a code | |
4439 | block. | |
4440 | */ | |
4441 | ||
4442 | int thisRegister = vPC[1].u.operand; | |
4443 | JSValue thisVal = callFrame->r(thisRegister).jsValue(); | |
4444 | if (thisVal.isObject() && thisVal.needsThisConversion()) | |
4445 | callFrame->uncheckedR(thisRegister) = JSValue(thisVal.toStrictThisObject(callFrame)); | |
4446 | ||
4447 | vPC += OPCODE_LENGTH(op_convert_this_strict); | |
4448 | NEXT_INSTRUCTION(); | |
4449 | } | |
4450 | DEFINE_OPCODE(op_init_lazy_reg) { | |
4451 | /* init_lazy_reg dst(r) | |
9dae56ea | 4452 | |
14957cd0 | 4453 | Initialises dst(r) to JSValue(). |
9dae56ea | 4454 | |
14957cd0 | 4455 | This opcode appears only at the beginning of a code block. |
ba379fdc | 4456 | */ |
14957cd0 A |
4457 | int dst = vPC[1].u.operand; |
4458 | ||
4459 | callFrame->uncheckedR(dst) = JSValue(); | |
4460 | vPC += OPCODE_LENGTH(op_init_lazy_reg); | |
ba379fdc A |
4461 | NEXT_INSTRUCTION(); |
4462 | } | |
4463 | DEFINE_OPCODE(op_create_arguments) { | |
14957cd0 | 4464 | /* create_arguments dst(r) |
9dae56ea | 4465 | |
ba379fdc A |
4466 | Creates the 'arguments' object and places it in both the |
4467 | 'arguments' call frame slot and the local 'arguments' | |
4468 | register, if it has not already been initialised. | |
4469 | */ | |
9dae56ea | 4470 | |
14957cd0 A |
4471 | int dst = vPC[1].u.operand; |
4472 | ||
4473 | if (!callFrame->r(dst).jsValue()) { | |
4474 | Arguments* arguments = new (globalData) Arguments(callFrame); | |
4475 | callFrame->uncheckedR(dst) = JSValue(arguments); | |
4476 | callFrame->uncheckedR(unmodifiedArgumentsRegister(dst)) = JSValue(arguments); | |
4477 | } | |
f9bf01c6 | 4478 | vPC += OPCODE_LENGTH(op_create_arguments); |
9dae56ea A |
4479 | NEXT_INSTRUCTION(); |
4480 | } | |
4481 | DEFINE_OPCODE(op_construct) { | |
14957cd0 | 4482 | /* construct func(r) argCount(n) registerOffset(n) proto(r) thisRegister(r) |
9dae56ea A |
4483 | |
4484 | Invoke register "func" as a constructor. For JS | |
4485 | functions, the calling convention is exactly as for the | |
4486 | "call" opcode, except that the "this" value is a newly | |
4487 | created Object. For native constructors, no "this" | |
4488 | value is passed. In either case, the argCount and registerOffset | |
4489 | registers are interpreted as for the "call" opcode. | |
4490 | ||
4491 | Register proto must contain the prototype property of | |
4492 | register func. This is to enable polymorphic inline | |
4493 | caching of this lookup. | |
4494 | */ | |
4495 | ||
14957cd0 A |
4496 | int func = vPC[1].u.operand; |
4497 | int argCount = vPC[2].u.operand; | |
4498 | int registerOffset = vPC[3].u.operand; | |
9dae56ea | 4499 | |
ba379fdc | 4500 | JSValue v = callFrame->r(func).jsValue(); |
9dae56ea A |
4501 | |
4502 | ConstructData constructData; | |
14957cd0 | 4503 | ConstructType constructType = getConstructData(v, constructData); |
9dae56ea A |
4504 | |
4505 | if (constructType == ConstructTypeJS) { | |
4506 | ScopeChainNode* callDataScopeChain = constructData.js.scopeChain; | |
9dae56ea | 4507 | |
14957cd0 A |
4508 | JSObject* error = constructData.js.functionExecutable->compileForConstruct(callFrame, callDataScopeChain); |
4509 | if (UNLIKELY(!!error)) { | |
4510 | exceptionValue = error; | |
4511 | goto vm_throw; | |
4512 | } | |
9dae56ea A |
4513 | |
4514 | CallFrame* previousCallFrame = callFrame; | |
14957cd0 | 4515 | CodeBlock* newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); |
9dae56ea A |
4516 | callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount); |
4517 | if (UNLIKELY(!callFrame)) { | |
4518 | callFrame = previousCallFrame; | |
4519 | exceptionValue = createStackOverflowError(callFrame); | |
4520 | goto vm_throw; | |
4521 | } | |
4522 | ||
14957cd0 A |
4523 | callFrame->init(newCodeBlock, vPC + OPCODE_LENGTH(op_construct), callDataScopeChain, previousCallFrame, argCount, asFunction(v)); |
4524 | codeBlock = newCodeBlock; | |
9dae56ea | 4525 | vPC = newCodeBlock->instructions().begin(); |
9dae56ea A |
4526 | #if ENABLE(OPCODE_STATS) |
4527 | OpcodeStats::resetLastInstruction(); | |
4528 | #endif | |
4529 | ||
4530 | NEXT_INSTRUCTION(); | |
4531 | } | |
4532 | ||
4533 | if (constructType == ConstructTypeHost) { | |
9dae56ea A |
4534 | ScopeChainNode* scopeChain = callFrame->scopeChain(); |
4535 | CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); | |
14957cd0 A |
4536 | if (!registerFile->grow(newCallFrame->registers())) { |
4537 | exceptionValue = createStackOverflowError(callFrame); | |
4538 | goto vm_throw; | |
4539 | } | |
4540 | newCallFrame->init(0, vPC + OPCODE_LENGTH(op_construct), scopeChain, callFrame, argCount, asObject(v)); | |
9dae56ea | 4541 | |
ba379fdc | 4542 | JSValue returnValue; |
9dae56ea | 4543 | { |
f9bf01c6 | 4544 | SamplingTool::HostCallRecord callRecord(m_sampler.get()); |
14957cd0 | 4545 | returnValue = JSValue::decode(constructData.native.function(newCallFrame)); |
9dae56ea A |
4546 | } |
4547 | CHECK_FOR_EXCEPTION(); | |
14957cd0 | 4548 | functionReturnValue = returnValue; |
9dae56ea | 4549 | |
f9bf01c6 | 4550 | vPC += OPCODE_LENGTH(op_construct); |
9dae56ea A |
4551 | NEXT_INSTRUCTION(); |
4552 | } | |
4553 | ||
4554 | ASSERT(constructType == ConstructTypeNone); | |
4555 | ||
14957cd0 | 4556 | exceptionValue = createNotAConstructorError(callFrame, v); |
9dae56ea A |
4557 | goto vm_throw; |
4558 | } | |
14957cd0 A |
4559 | DEFINE_OPCODE(op_strcat) { |
4560 | /* strcat dst(r) src(r) count(n) | |
9dae56ea | 4561 | |
14957cd0 A |
4562 | Construct a new String instance using the original |
4563 | constructor, and puts the result in register dst. | |
4564 | The string will be the result of concatenating count | |
4565 | strings with values taken from registers starting at | |
4566 | register src. | |
9dae56ea | 4567 | */ |
f9bf01c6 A |
4568 | int dst = vPC[1].u.operand; |
4569 | int src = vPC[2].u.operand; | |
4570 | int count = vPC[3].u.operand; | |
ba379fdc | 4571 | |
14957cd0 | 4572 | callFrame->uncheckedR(dst) = concatenateStrings(callFrame, &callFrame->registers()[src], count); |
f9bf01c6 A |
4573 | CHECK_FOR_EXCEPTION(); |
4574 | vPC += OPCODE_LENGTH(op_strcat); | |
ba379fdc A |
4575 | |
4576 | NEXT_INSTRUCTION(); | |
4577 | } | |
4578 | DEFINE_OPCODE(op_to_primitive) { | |
f9bf01c6 A |
4579 | int dst = vPC[1].u.operand; |
4580 | int src = vPC[2].u.operand; | |
ba379fdc | 4581 | |
14957cd0 | 4582 | callFrame->uncheckedR(dst) = callFrame->r(src).jsValue().toPrimitive(callFrame); |
f9bf01c6 | 4583 | vPC += OPCODE_LENGTH(op_to_primitive); |
ba379fdc A |
4584 | |
4585 | NEXT_INSTRUCTION(); | |
4586 | } | |
9dae56ea A |
4587 | DEFINE_OPCODE(op_push_scope) { |
4588 | /* push_scope scope(r) | |
4589 | ||
4590 | Converts register scope to object, and pushes it onto the top | |
4591 | of the current scope chain. The contents of the register scope | |
4592 | are replaced by the result of toObject conversion of the scope. | |
4593 | */ | |
f9bf01c6 | 4594 | int scope = vPC[1].u.operand; |
ba379fdc | 4595 | JSValue v = callFrame->r(scope).jsValue(); |
9dae56ea A |
4596 | JSObject* o = v.toObject(callFrame); |
4597 | CHECK_FOR_EXCEPTION(); | |
4598 | ||
14957cd0 | 4599 | callFrame->uncheckedR(scope) = JSValue(o); |
9dae56ea A |
4600 | callFrame->setScopeChain(callFrame->scopeChain()->push(o)); |
4601 | ||
f9bf01c6 | 4602 | vPC += OPCODE_LENGTH(op_push_scope); |
9dae56ea A |
4603 | NEXT_INSTRUCTION(); |
4604 | } | |
4605 | DEFINE_OPCODE(op_pop_scope) { | |
4606 | /* pop_scope | |
4607 | ||
4608 | Removes the top item from the current scope chain. | |
4609 | */ | |
4610 | callFrame->setScopeChain(callFrame->scopeChain()->pop()); | |
4611 | ||
f9bf01c6 | 4612 | vPC += OPCODE_LENGTH(op_pop_scope); |
9dae56ea A |
4613 | NEXT_INSTRUCTION(); |
4614 | } | |
4615 | DEFINE_OPCODE(op_get_pnames) { | |
f9bf01c6 | 4616 | /* get_pnames dst(r) base(r) i(n) size(n) breakTarget(offset) |
9dae56ea A |
4617 | |
4618 | Creates a property name list for register base and puts it | |
f9bf01c6 A |
4619 | in register dst, initializing i and size for iteration. If |
4620 | base is undefined or null, jumps to breakTarget. | |
9dae56ea | 4621 | */ |
f9bf01c6 A |
4622 | int dst = vPC[1].u.operand; |
4623 | int base = vPC[2].u.operand; | |
4624 | int i = vPC[3].u.operand; | |
4625 | int size = vPC[4].u.operand; | |
4626 | int breakTarget = vPC[5].u.operand; | |
4627 | ||
4628 | JSValue v = callFrame->r(base).jsValue(); | |
4629 | if (v.isUndefinedOrNull()) { | |
4630 | vPC += breakTarget; | |
4631 | NEXT_INSTRUCTION(); | |
4632 | } | |
4633 | ||
4634 | JSObject* o = v.toObject(callFrame); | |
4635 | Structure* structure = o->structure(); | |
4636 | JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache(); | |
4637 | if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(callFrame)) | |
4638 | jsPropertyNameIterator = JSPropertyNameIterator::create(callFrame, o); | |
9dae56ea | 4639 | |
14957cd0 A |
4640 | callFrame->uncheckedR(dst) = jsPropertyNameIterator; |
4641 | callFrame->uncheckedR(base) = JSValue(o); | |
4642 | callFrame->uncheckedR(i) = Register::withInt(0); | |
4643 | callFrame->uncheckedR(size) = Register::withInt(jsPropertyNameIterator->size()); | |
f9bf01c6 | 4644 | vPC += OPCODE_LENGTH(op_get_pnames); |
9dae56ea A |
4645 | NEXT_INSTRUCTION(); |
4646 | } | |
4647 | DEFINE_OPCODE(op_next_pname) { | |
f9bf01c6 | 4648 | /* next_pname dst(r) base(r) i(n) size(n) iter(r) target(offset) |
9dae56ea | 4649 | |
f9bf01c6 A |
4650 | Copies the next name from the property name list in |
4651 | register iter to dst, then jumps to offset target. If there are no | |
4652 | names left, invalidates the iterator and continues to the next | |
9dae56ea A |
4653 | instruction. |
4654 | */ | |
f9bf01c6 A |
4655 | int dst = vPC[1].u.operand; |
4656 | int base = vPC[2].u.operand; | |
4657 | int i = vPC[3].u.operand; | |
4658 | int size = vPC[4].u.operand; | |
4659 | int iter = vPC[5].u.operand; | |
4660 | int target = vPC[6].u.operand; | |
9dae56ea | 4661 | |
ba379fdc | 4662 | JSPropertyNameIterator* it = callFrame->r(iter).propertyNameIterator(); |
f9bf01c6 A |
4663 | while (callFrame->r(i).i() != callFrame->r(size).i()) { |
4664 | JSValue key = it->get(callFrame, asObject(callFrame->r(base).jsValue()), callFrame->r(i).i()); | |
14957cd0 A |
4665 | CHECK_FOR_EXCEPTION(); |
4666 | callFrame->uncheckedR(i) = Register::withInt(callFrame->r(i).i() + 1); | |
f9bf01c6 A |
4667 | if (key) { |
4668 | CHECK_FOR_TIMEOUT(); | |
14957cd0 | 4669 | callFrame->uncheckedR(dst) = key; |
f9bf01c6 A |
4670 | vPC += target; |
4671 | NEXT_INSTRUCTION(); | |
4672 | } | |
9dae56ea | 4673 | } |
9dae56ea | 4674 | |
f9bf01c6 | 4675 | vPC += OPCODE_LENGTH(op_next_pname); |
9dae56ea A |
4676 | NEXT_INSTRUCTION(); |
4677 | } | |
4678 | DEFINE_OPCODE(op_jmp_scopes) { | |
4679 | /* jmp_scopes count(n) target(offset) | |
4680 | ||
4681 | Removes the a number of items from the current scope chain | |
4682 | specified by immediate number count, then jumps to offset | |
4683 | target. | |
4684 | */ | |
f9bf01c6 A |
4685 | int count = vPC[1].u.operand; |
4686 | int target = vPC[2].u.operand; | |
9dae56ea A |
4687 | |
4688 | ScopeChainNode* tmp = callFrame->scopeChain(); | |
4689 | while (count--) | |
4690 | tmp = tmp->pop(); | |
4691 | callFrame->setScopeChain(tmp); | |
4692 | ||
4693 | vPC += target; | |
4694 | NEXT_INSTRUCTION(); | |
4695 | } | |
4e4e5a6f | 4696 | #if ENABLE(COMPUTED_GOTO_INTERPRETER) |
9dae56ea A |
4697 | // Appease GCC |
4698 | goto *(&&skip_new_scope); | |
4699 | #endif | |
4700 | DEFINE_OPCODE(op_push_new_scope) { | |
4701 | /* new_scope dst(r) property(id) value(r) | |
4702 | ||
4703 | Constructs a new StaticScopeObject with property set to value. That scope | |
4704 | object is then pushed onto the ScopeChain. The scope object is then stored | |
4705 | in dst for GC. | |
4706 | */ | |
4707 | callFrame->setScopeChain(createExceptionScope(callFrame, vPC)); | |
4708 | ||
f9bf01c6 | 4709 | vPC += OPCODE_LENGTH(op_push_new_scope); |
9dae56ea A |
4710 | NEXT_INSTRUCTION(); |
4711 | } | |
4e4e5a6f | 4712 | #if ENABLE(COMPUTED_GOTO_INTERPRETER) |
9dae56ea A |
4713 | skip_new_scope: |
4714 | #endif | |
4715 | DEFINE_OPCODE(op_catch) { | |
4716 | /* catch ex(r) | |
4717 | ||
ba379fdc | 4718 | Retrieves the VM's current exception and puts it in register |
9dae56ea A |
4719 | ex. This is only valid after an exception has been raised, |
4720 | and usually forms the beginning of an exception handler. | |
4721 | */ | |
4722 | ASSERT(exceptionValue); | |
4723 | ASSERT(!globalData->exception); | |
f9bf01c6 | 4724 | int ex = vPC[1].u.operand; |
14957cd0 | 4725 | callFrame->uncheckedR(ex) = exceptionValue; |
ba379fdc | 4726 | exceptionValue = JSValue(); |
9dae56ea | 4727 | |
f9bf01c6 | 4728 | vPC += OPCODE_LENGTH(op_catch); |
9dae56ea A |
4729 | NEXT_INSTRUCTION(); |
4730 | } | |
4731 | DEFINE_OPCODE(op_throw) { | |
4732 | /* throw ex(r) | |
4733 | ||
4734 | Throws register ex as an exception. This involves three | |
4735 | steps: first, it is set as the current exception in the | |
4736 | VM's internal state, then the stack is unwound until an | |
4737 | exception handler or a native code boundary is found, and | |
4738 | then control resumes at the exception handler if any or | |
4739 | else the script returns control to the nearest native caller. | |
4740 | */ | |
4741 | ||
f9bf01c6 | 4742 | int ex = vPC[1].u.operand; |
ba379fdc | 4743 | exceptionValue = callFrame->r(ex).jsValue(); |
9dae56ea | 4744 | |
14957cd0 A |
4745 | handler = throwException(callFrame, exceptionValue, vPC - codeBlock->instructions().begin()); |
4746 | if (!handler) | |
4747 | return throwError(callFrame, exceptionValue); | |
9dae56ea | 4748 | |
14957cd0 A |
4749 | codeBlock = callFrame->codeBlock(); |
4750 | vPC = codeBlock->instructions().begin() + handler->target; | |
9dae56ea A |
4751 | NEXT_INSTRUCTION(); |
4752 | } | |
14957cd0 A |
4753 | DEFINE_OPCODE(op_throw_reference_error) { |
4754 | /* op_throw_reference_error message(k) | |
9dae56ea | 4755 | |
14957cd0 A |
4756 | Constructs a new reference Error instance using the |
4757 | original constructor, using constant message as the | |
4758 | message string. The result is thrown. | |
9dae56ea | 4759 | */ |
14957cd0 A |
4760 | UString message = callFrame->r(vPC[1].u.operand).jsValue().toString(callFrame); |
4761 | exceptionValue = JSValue(createReferenceError(callFrame, message)); | |
4762 | goto vm_throw; | |
9dae56ea A |
4763 | } |
4764 | DEFINE_OPCODE(op_end) { | |
4765 | /* end result(r) | |
4766 | ||
4767 | Return register result as the value of a global or eval | |
4768 | program. Return control to the calling native code. | |
4769 | */ | |
4770 | ||
f9bf01c6 | 4771 | int result = vPC[1].u.operand; |
ba379fdc | 4772 | return callFrame->r(result).jsValue(); |
9dae56ea A |
4773 | } |
4774 | DEFINE_OPCODE(op_put_getter) { | |
4775 | /* put_getter base(r) property(id) function(r) | |
4776 | ||
4777 | Sets register function on register base as the getter named | |
4778 | by identifier property. Base and function are assumed to be | |
4779 | objects as this op should only be used for getters defined | |
4780 | in object literal form. | |
4781 | ||
4782 | Unlike many opcodes, this one does not write any output to | |
4783 | the register file. | |
4784 | */ | |
f9bf01c6 A |
4785 | int base = vPC[1].u.operand; |
4786 | int property = vPC[2].u.operand; | |
4787 | int function = vPC[3].u.operand; | |
9dae56ea | 4788 | |
ba379fdc A |
4789 | ASSERT(callFrame->r(base).jsValue().isObject()); |
4790 | JSObject* baseObj = asObject(callFrame->r(base).jsValue()); | |
14957cd0 | 4791 | Identifier& ident = codeBlock->identifier(property); |
ba379fdc A |
4792 | ASSERT(callFrame->r(function).jsValue().isObject()); |
4793 | baseObj->defineGetter(callFrame, ident, asObject(callFrame->r(function).jsValue())); | |
9dae56ea | 4794 | |
f9bf01c6 | 4795 | vPC += OPCODE_LENGTH(op_put_getter); |
9dae56ea A |
4796 | NEXT_INSTRUCTION(); |
4797 | } | |
4798 | DEFINE_OPCODE(op_put_setter) { | |
4799 | /* put_setter base(r) property(id) function(r) | |
4800 | ||
4801 | Sets register function on register base as the setter named | |
4802 | by identifier property. Base and function are assumed to be | |
4803 | objects as this op should only be used for setters defined | |
4804 | in object literal form. | |
4805 | ||
4806 | Unlike many opcodes, this one does not write any output to | |
4807 | the register file. | |
4808 | */ | |
f9bf01c6 A |
4809 | int base = vPC[1].u.operand; |
4810 | int property = vPC[2].u.operand; | |
4811 | int function = vPC[3].u.operand; | |
9dae56ea | 4812 | |
ba379fdc A |
4813 | ASSERT(callFrame->r(base).jsValue().isObject()); |
4814 | JSObject* baseObj = asObject(callFrame->r(base).jsValue()); | |
14957cd0 | 4815 | Identifier& ident = codeBlock->identifier(property); |
ba379fdc | 4816 | ASSERT(callFrame->r(function).jsValue().isObject()); |
f9bf01c6 | 4817 | baseObj->defineSetter(callFrame, ident, asObject(callFrame->r(function).jsValue()), 0); |
9dae56ea | 4818 | |
f9bf01c6 | 4819 | vPC += OPCODE_LENGTH(op_put_setter); |
9dae56ea A |
4820 | NEXT_INSTRUCTION(); |
4821 | } | |
ba379fdc A |
4822 | DEFINE_OPCODE(op_method_check) { |
4823 | vPC++; | |
4824 | NEXT_INSTRUCTION(); | |
4825 | } | |
9dae56ea A |
4826 | DEFINE_OPCODE(op_jsr) { |
4827 | /* jsr retAddrDst(r) target(offset) | |
4828 | ||
4829 | Places the address of the next instruction into the retAddrDst | |
4830 | register and jumps to offset target from the current instruction. | |
4831 | */ | |
f9bf01c6 A |
4832 | int retAddrDst = vPC[1].u.operand; |
4833 | int target = vPC[2].u.operand; | |
4834 | callFrame->r(retAddrDst) = vPC + OPCODE_LENGTH(op_jsr); | |
9dae56ea A |
4835 | |
4836 | vPC += target; | |
4837 | NEXT_INSTRUCTION(); | |
4838 | } | |
4839 | DEFINE_OPCODE(op_sret) { | |
4840 | /* sret retAddrSrc(r) | |
4841 | ||
4842 | Jumps to the address stored in the retAddrSrc register. This | |
4843 | differs from op_jmp because the target address is stored in a | |
4844 | register, not as an immediate. | |
4845 | */ | |
f9bf01c6 | 4846 | int retAddrSrc = vPC[1].u.operand; |
ba379fdc | 4847 | vPC = callFrame->r(retAddrSrc).vPC(); |
9dae56ea A |
4848 | NEXT_INSTRUCTION(); |
4849 | } | |
4850 | DEFINE_OPCODE(op_debug) { | |
4851 | /* debug debugHookID(n) firstLine(n) lastLine(n) | |
4852 | ||
4853 | Notifies the debugger of the current state of execution. This opcode | |
4854 | is only generated while the debugger is attached. | |
4855 | */ | |
f9bf01c6 A |
4856 | int debugHookID = vPC[1].u.operand; |
4857 | int firstLine = vPC[2].u.operand; | |
4858 | int lastLine = vPC[3].u.operand; | |
9dae56ea A |
4859 | |
4860 | debug(callFrame, static_cast<DebugHookID>(debugHookID), firstLine, lastLine); | |
4861 | ||
f9bf01c6 | 4862 | vPC += OPCODE_LENGTH(op_debug); |
9dae56ea A |
4863 | NEXT_INSTRUCTION(); |
4864 | } | |
4865 | DEFINE_OPCODE(op_profile_will_call) { | |
4866 | /* op_profile_will_call function(r) | |
4867 | ||
4868 | Notifies the profiler of the beginning of a function call. This opcode | |
4869 | is only generated if developer tools are enabled. | |
4870 | */ | |
4871 | int function = vPC[1].u.operand; | |
4872 | ||
4873 | if (*enabledProfilerReference) | |
ba379fdc | 4874 | (*enabledProfilerReference)->willExecute(callFrame, callFrame->r(function).jsValue()); |
9dae56ea | 4875 | |
f9bf01c6 | 4876 | vPC += OPCODE_LENGTH(op_profile_will_call); |
9dae56ea A |
4877 | NEXT_INSTRUCTION(); |
4878 | } | |
4879 | DEFINE_OPCODE(op_profile_did_call) { | |
4880 | /* op_profile_did_call function(r) | |
4881 | ||
4882 | Notifies the profiler of the end of a function call. This opcode | |
4883 | is only generated if developer tools are enabled. | |
4884 | */ | |
4885 | int function = vPC[1].u.operand; | |
4886 | ||
4887 | if (*enabledProfilerReference) | |
ba379fdc | 4888 | (*enabledProfilerReference)->didExecute(callFrame, callFrame->r(function).jsValue()); |
9dae56ea | 4889 | |
f9bf01c6 | 4890 | vPC += OPCODE_LENGTH(op_profile_did_call); |
9dae56ea A |
4891 | NEXT_INSTRUCTION(); |
4892 | } | |
4893 | vm_throw: { | |
ba379fdc | 4894 | globalData->exception = JSValue(); |
9dae56ea A |
4895 | if (!tickCount) { |
4896 | // The exceptionValue is a lie! (GCC produces bad code for reasons I | |
4897 | // cannot fathom if we don't assign to the exceptionValue before branching) | |
4898 | exceptionValue = createInterruptedExecutionException(globalData); | |
4899 | } | |
14957cd0 A |
4900 | JSGlobalObject* globalObject = callFrame->lexicalGlobalObject(); |
4901 | handler = throwException(callFrame, exceptionValue, vPC - codeBlock->instructions().begin()); | |
9dae56ea | 4902 | if (!handler) { |
14957cd0 A |
4903 | // Can't use the callframe at this point as the scopechain, etc have |
4904 | // been released. | |
4905 | return throwError(globalObject->globalExec(), exceptionValue); | |
9dae56ea A |
4906 | } |
4907 | ||
14957cd0 A |
4908 | codeBlock = callFrame->codeBlock(); |
4909 | vPC = codeBlock->instructions().begin() + handler->target; | |
9dae56ea A |
4910 | NEXT_INSTRUCTION(); |
4911 | } | |
4912 | } | |
4e4e5a6f | 4913 | #if !ENABLE(COMPUTED_GOTO_INTERPRETER) |
9dae56ea A |
4914 | } // iterator loop ends |
4915 | #endif | |
4916 | #undef NEXT_INSTRUCTION | |
4917 | #undef DEFINE_OPCODE | |
4918 | #undef CHECK_FOR_EXCEPTION | |
4919 | #undef CHECK_FOR_TIMEOUT | |
4e4e5a6f | 4920 | #endif // ENABLE(INTERPRETER) |
9dae56ea A |
4921 | } |
4922 | ||
ba379fdc | 4923 | JSValue Interpreter::retrieveArguments(CallFrame* callFrame, JSFunction* function) const |
9dae56ea A |
4924 | { |
4925 | CallFrame* functionCallFrame = findFunctionCallFrame(callFrame, function); | |
4926 | if (!functionCallFrame) | |
4927 | return jsNull(); | |
4928 | ||
4929 | CodeBlock* codeBlock = functionCallFrame->codeBlock(); | |
4930 | if (codeBlock->usesArguments()) { | |
4931 | ASSERT(codeBlock->codeType() == FunctionCode); | |
14957cd0 A |
4932 | int argumentsRegister = codeBlock->argumentsRegister(); |
4933 | int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister); | |
4934 | if (JSValue arguments = functionCallFrame->uncheckedR(argumentsRegister).jsValue()) | |
4935 | return arguments; | |
4936 | JSValue arguments = JSValue(new (callFrame) Arguments(functionCallFrame)); | |
4937 | functionCallFrame->r(argumentsRegister) = arguments; | |
4938 | functionCallFrame->r(realArgumentsRegister) = arguments; | |
4939 | return arguments; | |
4940 | } | |
4941 | ||
4942 | Arguments* arguments = new (functionCallFrame) Arguments(functionCallFrame); | |
4943 | arguments->copyRegisters(functionCallFrame->globalData()); | |
9dae56ea A |
4944 | return arguments; |
4945 | } | |
4946 | ||
14957cd0 | 4947 | JSValue Interpreter::retrieveCaller(CallFrame* callFrame, JSFunction* function) const |
9dae56ea A |
4948 | { |
4949 | CallFrame* functionCallFrame = findFunctionCallFrame(callFrame, function); | |
4950 | if (!functionCallFrame) | |
4951 | return jsNull(); | |
4952 | ||
4953 | CallFrame* callerFrame = functionCallFrame->callerFrame(); | |
4954 | if (callerFrame->hasHostCallFrameFlag()) | |
4955 | return jsNull(); | |
4956 | ||
ba379fdc | 4957 | JSValue caller = callerFrame->callee(); |
9dae56ea A |
4958 | if (!caller) |
4959 | return jsNull(); | |
4960 | ||
4961 | return caller; | |
4962 | } | |
4963 | ||
ba379fdc | 4964 | void Interpreter::retrieveLastCaller(CallFrame* callFrame, int& lineNumber, intptr_t& sourceID, UString& sourceURL, JSValue& function) const |
9dae56ea | 4965 | { |
ba379fdc | 4966 | function = JSValue(); |
9dae56ea A |
4967 | lineNumber = -1; |
4968 | sourceURL = UString(); | |
4969 | ||
4970 | CallFrame* callerFrame = callFrame->callerFrame(); | |
4971 | if (callerFrame->hasHostCallFrameFlag()) | |
4972 | return; | |
4973 | ||
4974 | CodeBlock* callerCodeBlock = callerFrame->codeBlock(); | |
4975 | if (!callerCodeBlock) | |
4976 | return; | |
4e4e5a6f A |
4977 | unsigned bytecodeOffset = 0; |
4978 | #if ENABLE(INTERPRETER) | |
4979 | if (!callerFrame->globalData().canUseJIT()) | |
14957cd0 | 4980 | bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnVPC()); |
4e4e5a6f A |
4981 | #if ENABLE(JIT) |
4982 | else | |
14957cd0 | 4983 | bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC()); |
4e4e5a6f A |
4984 | #endif |
4985 | #else | |
14957cd0 | 4986 | bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC()); |
4e4e5a6f | 4987 | #endif |
14957cd0 | 4988 | lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1); |
f9bf01c6 A |
4989 | sourceID = callerCodeBlock->ownerExecutable()->sourceID(); |
4990 | sourceURL = callerCodeBlock->ownerExecutable()->sourceURL(); | |
9dae56ea A |
4991 | function = callerFrame->callee(); |
4992 | } | |
4993 | ||
14957cd0 | 4994 | CallFrame* Interpreter::findFunctionCallFrame(CallFrame* callFrame, JSFunction* function) |
9dae56ea A |
4995 | { |
4996 | for (CallFrame* candidate = callFrame; candidate; candidate = candidate->callerFrame()->removeHostCallFrameFlag()) { | |
4997 | if (candidate->callee() == function) | |
4998 | return candidate; | |
4999 | } | |
5000 | return 0; | |
5001 | } | |
5002 | ||
f9bf01c6 A |
5003 | void Interpreter::enableSampler() |
5004 | { | |
5005 | #if ENABLE(OPCODE_SAMPLING) | |
5006 | if (!m_sampler) { | |
5007 | m_sampler.set(new SamplingTool(this)); | |
5008 | m_sampler->setup(); | |
5009 | } | |
5010 | #endif | |
5011 | } | |
5012 | void Interpreter::dumpSampleData(ExecState* exec) | |
5013 | { | |
5014 | #if ENABLE(OPCODE_SAMPLING) | |
5015 | if (m_sampler) | |
5016 | m_sampler->dump(exec); | |
5017 | #else | |
5018 | UNUSED_PARAM(exec); | |
5019 | #endif | |
5020 | } | |
5021 | void Interpreter::startSampling() | |
5022 | { | |
5023 | #if ENABLE(SAMPLING_THREAD) | |
5024 | if (!m_sampleEntryDepth) | |
5025 | SamplingThread::start(); | |
5026 | ||
5027 | m_sampleEntryDepth++; | |
5028 | #endif | |
5029 | } | |
5030 | void Interpreter::stopSampling() | |
5031 | { | |
5032 | #if ENABLE(SAMPLING_THREAD) | |
5033 | m_sampleEntryDepth--; | |
5034 | if (!m_sampleEntryDepth) | |
5035 | SamplingThread::stop(); | |
5036 | #endif | |
5037 | } | |
5038 | ||
9dae56ea | 5039 | } // namespace JSC |