1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JavaScript bytecode interpreter.
43 : */
44 : #include <stdio.h>
45 : #include <string.h>
46 : #include <math.h>
47 : #include "jstypes.h"
48 : #include "jsutil.h"
49 : #include "jsprf.h"
50 : #include "jsapi.h"
51 : #include "jsarray.h"
52 : #include "jsatom.h"
53 : #include "jsbool.h"
54 : #include "jscntxt.h"
55 : #include "jsdate.h"
56 : #include "jsversion.h"
57 : #include "jsdbgapi.h"
58 : #include "jsfun.h"
59 : #include "jsgc.h"
60 : #include "jsgcmark.h"
61 : #include "jsinterp.h"
62 : #include "jsiter.h"
63 : #include "jslock.h"
64 : #include "jsnum.h"
65 : #include "jsobj.h"
66 : #include "jsopcode.h"
67 : #include "jspropertycache.h"
68 : #include "jsscope.h"
69 : #include "jsscript.h"
70 : #include "jsstr.h"
71 : #include "jslibmath.h"
72 :
73 : #include "frontend/BytecodeEmitter.h"
74 : #ifdef JS_METHODJIT
75 : #include "methodjit/MethodJIT.h"
76 : #include "methodjit/Logging.h"
77 : #endif
78 : #include "vm/Debugger.h"
79 :
80 : #include "jsatominlines.h"
81 : #include "jsinferinlines.h"
82 : #include "jsinterpinlines.h"
83 : #include "jsobjinlines.h"
84 : #include "jsopcodeinlines.h"
85 : #include "jsprobes.h"
86 : #include "jspropertycacheinlines.h"
87 : #include "jsscopeinlines.h"
88 : #include "jsscriptinlines.h"
89 : #include "jstypedarrayinlines.h"
90 :
91 : #include "vm/Stack-inl.h"
92 : #include "vm/String-inl.h"
93 :
94 : #if JS_HAS_XML_SUPPORT
95 : #include "jsxml.h"
96 : #endif
97 :
98 : #include "jsautooplen.h"
99 :
100 : #if defined(JS_METHODJIT) && defined(JS_MONOIC)
101 : #include "methodjit/MonoIC.h"
102 : #endif
103 :
104 : using namespace js;
105 : using namespace js::gc;
106 : using namespace js::types;
107 :
108 : /*
109 : * We can't determine in advance which local variables can live on the stack and
110 : * be freed when their dynamic scope ends, and which will be closed over and
111 : * need to live in the heap. So we place variables on the stack initially, note
112 : * when they are closed over, and copy those that are out to the heap when we
113 : * leave their dynamic scope.
114 : *
115 : * The bytecode compiler produces a tree of block objects accompanying each
116 : * JSScript representing those lexical blocks in the script that have let-bound
117 : * variables associated with them. These block objects are never modified, and
118 : * never become part of any function's scope chain. Their parent slots point to
119 : * the innermost block that encloses them, or are NULL in the outermost blocks
120 : * within a function or in eval or global code.
121 : *
122 : * When we are in the static scope of such a block, blockChain points to its
123 : * compiler-allocated block object; otherwise, it is NULL.
124 : *
125 : * scopeChain is the current scope chain, including 'call' and 'block' objects
126 : * for those function calls and lexical blocks whose static scope we are
127 : * currently executing in, and 'with' objects for with statements; the chain is
128 : * typically terminated by a global object. However, as an optimization, the
129 : * young end of the chain omits block objects we have not yet cloned. To create
130 : * a closure, we clone the missing blocks from blockChain (which is always
131 : * current), place them at the head of scopeChain, and use that for the
132 : * closure's scope chain. If we never close over a lexical block, we never
133 : * place a mutable clone of it on scopeChain.
134 : *
135 : * This lazy cloning is implemented in GetScopeChain, which is also used in
136 : * some other cases --- entering 'with' blocks, for example.
137 : */
138 : JSObject *
139 876394 : js::GetScopeChain(JSContext *cx, StackFrame *fp)
140 : {
141 876394 : StaticBlockObject *sharedBlock = fp->maybeBlockChain();
142 :
143 876394 : if (!sharedBlock) {
144 : /*
145 : * Don't force a call object for a lightweight function call, but do
146 : * insist that there is a call object for a heavyweight function call.
147 : */
148 2386458 : JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(),
149 2386458 : fp->hasCallObj());
150 847227 : return &fp->scopeChain();
151 : }
152 :
153 : /*
154 : * We have one or more lexical scopes to reflect into fp->scopeChain, so
155 : * make sure there's a call object at the current head of the scope chain,
156 : * if this frame is a call frame.
157 : *
158 : * Also, identify the innermost compiler-allocated block we needn't clone.
159 : */
160 : JSObject *limitBlock, *limitClone;
161 29167 : if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj()) {
162 1 : JS_ASSERT_IF(fp->scopeChain().isClonedBlock(), fp->scopeChain().getPrivate() != fp);
163 1 : if (!CallObject::createForFunction(cx, fp))
164 0 : return NULL;
165 :
166 : /* We know we must clone everything on blockChain. */
167 1 : limitBlock = limitClone = NULL;
168 : } else {
169 : /*
170 : * scopeChain includes all blocks whose static scope we're within that
171 : * have already been cloned. Find the innermost such block. Its
172 : * prototype should appear on blockChain; we'll clone blockChain up
173 : * to, but not including, that prototype.
174 : */
175 29166 : limitClone = &fp->scopeChain();
176 58404 : while (limitClone->isWith())
177 72 : limitClone = &limitClone->asWith().enclosingScope();
178 29166 : JS_ASSERT(limitClone);
179 :
180 : /*
181 : * It may seem like we don't know enough about limitClone to be able
182 : * to just grab its prototype as we do here, but it's actually okay.
183 : *
184 : * If limitClone is a block object belonging to this frame, then its
185 : * prototype is the innermost entry in blockChain that we have already
186 : * cloned, and is thus the place to stop when we clone below.
187 : *
188 : * Otherwise, there are no blocks for this frame on scopeChain, and we
189 : * need to clone the whole blockChain. In this case, limitBlock can
190 : * point to any object known not to be on blockChain, since we simply
191 : * loop until we hit limitBlock or NULL. If limitClone is a block, it
192 : * isn't a block from this function, since blocks can't be nested
193 : * within themselves on scopeChain (recursion is dynamic nesting, not
194 : * static nesting). If limitClone isn't a block, its prototype won't
195 : * be a block either. So we can just grab limitClone's prototype here
196 : * regardless of its type or which frame it belongs to.
197 : */
198 29166 : limitBlock = limitClone->getProto();
199 :
200 : /* If the innermost block has already been cloned, we are done. */
201 29166 : if (limitBlock == sharedBlock)
202 11430 : return &fp->scopeChain();
203 : }
204 :
205 : /*
206 : * Special-case cloning the innermost block; this doesn't have enough in
207 : * common with subsequent steps to include in the loop.
208 : *
209 : * create() leaves the clone's enclosingScope unset. We set it below.
210 : */
211 17737 : ClonedBlockObject *innermostNewChild = ClonedBlockObject::create(cx, *sharedBlock, fp);
212 17737 : if (!innermostNewChild)
213 0 : return NULL;
214 :
215 : /*
216 : * Clone our way towards outer scopes until we reach the innermost
217 : * enclosing function, or the innermost block we've already cloned.
218 : */
219 17737 : ClonedBlockObject *newChild = innermostNewChild;
220 901 : for (;;) {
221 18638 : JS_ASSERT(newChild->getProto() == sharedBlock);
222 18638 : sharedBlock = sharedBlock->enclosingBlock();
223 :
224 : /* Sometimes limitBlock will be NULL, so check that first. */
225 18638 : if (sharedBlock == limitBlock || !sharedBlock)
226 : break;
227 :
228 : /* As in the call above, we don't know the real parent yet. */
229 901 : ClonedBlockObject *clone = ClonedBlockObject::create(cx, *sharedBlock, fp);
230 901 : if (!clone)
231 0 : return NULL;
232 :
233 901 : if (!newChild->setEnclosingScope(cx, *clone))
234 0 : return NULL;
235 901 : newChild = clone;
236 : }
237 17737 : if (!newChild->setEnclosingScope(cx, fp->scopeChain()))
238 0 : return NULL;
239 :
240 :
241 : /*
242 : * If we found a limit block belonging to this frame, then we should have
243 : * found it in blockChain.
244 : */
245 5890 : JS_ASSERT_IF(limitBlock &&
246 : limitBlock->isClonedBlock() &&
247 : limitClone->getPrivate() == js_FloatingFrameIfGenerator(cx, fp),
248 23627 : sharedBlock);
249 :
250 : /* Place our newly cloned blocks at the head of the scope chain. */
251 17737 : fp->setScopeChainNoCallObj(*innermostNewChild);
252 17737 : return innermostNewChild;
253 : }
254 :
255 : JSObject *
256 0 : js::GetScopeChain(JSContext *cx)
257 : {
258 : /*
259 : * Note: we don't need to expand inline frames here, because frames are
260 : * only inlined when the caller and callee share the same scope chain.
261 : */
262 0 : StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
263 0 : if (!fp) {
264 : /*
265 : * There is no code active on this context. In place of an actual
266 : * scope chain, use the context's global object, which is set in
267 : * js_InitFunctionAndObjectClasses, and which represents the default
268 : * scope chain for the embedding. See also js_FindClassObject.
269 : *
270 : * For embeddings that use the inner and outer object hooks, the inner
271 : * object represents the ultimate global object, with the outer object
272 : * acting as a stand-in.
273 : */
274 0 : JSObject *obj = cx->globalObject;
275 0 : if (!obj) {
276 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
277 0 : return NULL;
278 : }
279 :
280 0 : OBJ_TO_INNER_OBJECT(cx, obj);
281 0 : return obj;
282 : }
283 0 : return GetScopeChain(cx, fp);
284 : }
285 :
286 : /* Some objects (e.g., With) delegate 'this' to another object. */
287 : static inline JSObject *
288 : CallThisObjectHook(JSContext *cx, JSObject *obj, Value *argv)
289 : {
290 : JSObject *thisp = obj->thisObject(cx);
291 : if (!thisp)
292 : return NULL;
293 : argv[-1].setObject(*thisp);
294 : return thisp;
295 : }
296 :
297 : /*
298 : * ECMA requires "the global object", but in embeddings such as the browser,
299 : * which have multiple top-level objects (windows, frames, etc. in the DOM),
300 : * we prefer fun's parent. An example that causes this code to run:
301 : *
302 : * // in window w1
303 : * function f() { return this }
304 : * function g() { return f }
305 : *
306 : * // in window w2
307 : * var h = w1.g()
308 : * alert(h() == w1)
309 : *
310 : * The alert should display "true".
311 : */
312 : bool
313 361902 : js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call)
314 : {
315 : /*
316 : * Check for SynthesizeFrame poisoning and fast constructors which
317 : * didn't check their callee properly.
318 : */
319 361902 : Value &thisv = call.thisv();
320 361902 : JS_ASSERT(!thisv.isMagic());
321 :
322 : #ifdef DEBUG
323 361902 : JSFunction *fun = call.callee().isFunction() ? call.callee().toFunction() : NULL;
324 361902 : JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode());
325 : #endif
326 :
327 361902 : if (thisv.isNullOrUndefined()) {
328 64909 : JSObject *thisp = call.callee().global().thisObject(cx);
329 64909 : if (!thisp)
330 0 : return false;
331 64909 : call.thisv().setObject(*thisp);
332 64909 : return true;
333 : }
334 :
335 296993 : if (!thisv.isObject())
336 479 : return !!js_PrimitiveToObject(cx, &thisv);
337 :
338 296514 : return true;
339 : }
340 :
341 : #if JS_HAS_NO_SUCH_METHOD
342 :
343 : const uint32_t JSSLOT_FOUND_FUNCTION = 0;
344 : const uint32_t JSSLOT_SAVED_ID = 1;
345 :
346 : Class js_NoSuchMethodClass = {
347 : "NoSuchMethod",
348 : JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
349 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
350 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
351 : };
352 :
353 : /*
354 : * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
355 : * the base object, we search for the __noSuchMethod__ method in the base.
356 : * If it exists, we store the method and the property's id into an object of
357 : * NoSuchMethod class and store this object into the callee's stack slot.
358 : * Later, Invoke will recognise such an object and transfer control to
359 : * NoSuchMethod that invokes the method like:
360 : *
361 : * this.__noSuchMethod__(id, args)
362 : *
363 : * where id is the name of the method that this invocation attempted to
364 : * call by name, and args is an Array containing this invocation's actual
365 : * parameters.
366 : */
367 : bool
368 1227 : js::OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp)
369 : {
370 1227 : jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
371 2454 : AutoValueRooter tvr(cx);
372 1227 : if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, tvr.addr()))
373 0 : return false;
374 1227 : TypeScript::MonitorUnknown(cx, cx->fp()->script(), cx->regs().pc);
375 :
376 1227 : if (tvr.value().isPrimitive()) {
377 498 : *vp = tvr.value();
378 : } else {
379 : #if JS_HAS_XML_SUPPORT
380 : /* Extract the function name from function::name qname. */
381 729 : if (idval.isObject()) {
382 0 : obj = &idval.toObject();
383 0 : if (js_GetLocalNameFromFunctionQName(obj, &id, cx))
384 0 : idval = IdToValue(id);
385 : }
386 : #endif
387 :
388 729 : obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, NULL, NULL);
389 729 : if (!obj)
390 0 : return false;
391 :
392 729 : obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
393 729 : obj->setSlot(JSSLOT_SAVED_ID, idval);
394 729 : vp->setObject(*obj);
395 : }
396 1227 : return true;
397 : }
398 :
399 : static JSBool
400 729 : NoSuchMethod(JSContext *cx, unsigned argc, Value *vp)
401 : {
402 1458 : InvokeArgsGuard args;
403 729 : if (!cx->stack.pushInvokeArgs(cx, 2, &args))
404 0 : return JS_FALSE;
405 :
406 729 : JS_ASSERT(vp[0].isObject());
407 729 : JS_ASSERT(vp[1].isObject());
408 729 : JSObject *obj = &vp[0].toObject();
409 729 : JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass);
410 :
411 729 : args.calleev() = obj->getSlot(JSSLOT_FOUND_FUNCTION);
412 729 : args.thisv() = vp[1];
413 729 : args[0] = obj->getSlot(JSSLOT_SAVED_ID);
414 729 : JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2);
415 729 : if (!argsobj)
416 0 : return JS_FALSE;
417 729 : args[1].setObject(*argsobj);
418 729 : JSBool ok = Invoke(cx, args);
419 729 : vp[0] = args.rval();
420 729 : return ok;
421 : }
422 :
423 : #endif /* JS_HAS_NO_SUCH_METHOD */
424 :
425 : bool
426 5428662 : js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp)
427 : {
428 5428662 : JS_ASSERT(script);
429 5428662 : JS_ASSERT(fp == cx->fp());
430 5428662 : JS_ASSERT(fp->script() == script);
431 : #ifdef JS_METHODJIT_SPEW
432 5428662 : JMCheckLogging();
433 : #endif
434 :
435 : /* FIXME: Once bug 470510 is fixed, make this an assert. */
436 5428662 : if (script->compileAndGo) {
437 1536069 : if (fp->scopeChain().global().isCleared()) {
438 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
439 0 : return false;
440 : }
441 : }
442 :
443 : #ifdef DEBUG
444 : struct CheckStackBalance {
445 : JSContext *cx;
446 : StackFrame *fp;
447 : JSObject *enumerators;
448 5428662 : CheckStackBalance(JSContext *cx)
449 5428662 : : cx(cx), fp(cx->fp()), enumerators(cx->enumerators)
450 5428662 : {}
451 5428662 : ~CheckStackBalance() {
452 5428662 : JS_ASSERT(fp == cx->fp());
453 5428662 : JS_ASSERT_IF(!fp->isGeneratorFrame(), enumerators == cx->enumerators);
454 5428662 : }
455 10857324 : } check(cx);
456 : #endif
457 :
458 : #ifdef JS_METHODJIT
459 : mjit::CompileStatus status;
460 5428662 : status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(),
461 5428662 : mjit::CompileRequest_Interpreter);
462 5428662 : if (status == mjit::Compile_Error)
463 0 : return false;
464 :
465 5428662 : if (status == mjit::Compile_Okay)
466 3563828 : return mjit::JaegerStatusToSuccess(mjit::JaegerShot(cx, false));
467 : #endif
468 :
469 1864834 : return Interpret(cx, fp);
470 : }
471 :
472 : /*
473 : * Find a function reference and its 'this' value implicit first parameter
474 : * under argc arguments on cx's stack, and call the function. Push missing
475 : * required arguments, allocate declared local variables, and pop everything
476 : * when done. Then push the return value.
477 : */
478 : bool
479 20074332 : js::InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct)
480 : {
481 20074332 : JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX);
482 :
483 20074332 : JS_ASSERT(!cx->compartment->activeAnalysis);
484 :
485 : /* MaybeConstruct is a subset of InitialFrameFlags */
486 20074332 : InitialFrameFlags initial = (InitialFrameFlags) construct;
487 :
488 20074332 : if (args.calleev().isPrimitive()) {
489 786 : js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(initial));
490 786 : return false;
491 : }
492 :
493 20073546 : JSObject &callee = args.callee();
494 20073546 : Class *clasp = callee.getClass();
495 :
496 : /* Invoke non-functions. */
497 20073546 : if (JS_UNLIKELY(clasp != &FunctionClass)) {
498 : #if JS_HAS_NO_SUCH_METHOD
499 40431 : if (JS_UNLIKELY(clasp == &js_NoSuchMethodClass))
500 729 : return NoSuchMethod(cx, args.length(), args.base());
501 : #endif
502 39702 : JS_ASSERT_IF(construct, !clasp->construct);
503 39702 : if (!clasp->call) {
504 91 : js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(initial));
505 91 : return false;
506 : }
507 39611 : return CallJSNative(cx, clasp->call, args);
508 : }
509 :
510 : /* Invoke native functions. */
511 20033115 : JSFunction *fun = callee.toFunction();
512 20033115 : JS_ASSERT_IF(construct, !fun->isNativeConstructor());
513 20033115 : if (fun->isNative())
514 14814852 : return CallJSNative(cx, fun->u.n.native, args);
515 :
516 5218263 : TypeMonitorCall(cx, args, construct);
517 :
518 : /* Get pointer to new frame/slots, prepare arguments. */
519 10436526 : InvokeFrameGuard ifg;
520 5218263 : if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
521 28 : return false;
522 :
523 : /* Now that the new frame is rooted, maybe create a call object. */
524 5218235 : StackFrame *fp = ifg.fp();
525 5218235 : if (!fp->functionPrologue(cx))
526 0 : return false;
527 :
528 : /* Run function until JSOP_STOP, JSOP_RETURN or error. */
529 5218235 : JSBool ok = RunScript(cx, fun->script(), fp);
530 :
531 : /* Propagate the return value out. */
532 5218235 : args.rval() = fp->returnValue();
533 5218235 : JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive());
534 5218235 : return ok;
535 : }
536 :
537 : bool
538 5223509 : js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv,
539 : Value *rval)
540 : {
541 10447018 : InvokeArgsGuard args;
542 5223509 : if (!cx->stack.pushInvokeArgs(cx, argc, &args))
543 0 : return false;
544 :
545 5223509 : args.calleev() = fval;
546 5223509 : args.thisv() = thisv;
547 5223509 : PodCopy(args.array(), argv, argc);
548 :
549 5223509 : if (args.thisv().isObject()) {
550 : /*
551 : * We must call the thisObject hook in case we are not called from the
552 : * interpreter, where a prior bytecode has computed an appropriate
553 : * |this| already.
554 : */
555 5221520 : JSObject *thisp = args.thisv().toObject().thisObject(cx);
556 5221520 : if (!thisp)
557 0 : return false;
558 5221520 : args.thisv().setObject(*thisp);
559 : }
560 :
561 5223509 : if (!Invoke(cx, args))
562 89664 : return false;
563 :
564 5133845 : *rval = args.rval();
565 5133845 : return true;
566 : }
567 :
568 : bool
569 266525 : js::InvokeConstructorKernel(JSContext *cx, const CallArgs &argsRef)
570 : {
571 266525 : JS_ASSERT(!FunctionClass.construct);
572 266525 : CallArgs args = argsRef;
573 :
574 266525 : args.thisv().setMagic(JS_IS_CONSTRUCTING);
575 :
576 266525 : if (args.calleev().isObject()) {
577 266498 : JSObject *callee = &args.callee();
578 266498 : Class *clasp = callee->getClass();
579 266498 : if (clasp == &FunctionClass) {
580 218571 : JSFunction *fun = callee->toFunction();
581 :
582 218571 : if (fun->isNativeConstructor()) {
583 218004 : Probes::calloutBegin(cx, fun);
584 218004 : bool ok = CallJSNativeConstructor(cx, fun->u.n.native, args);
585 218004 : Probes::calloutEnd(cx, fun);
586 218004 : return ok;
587 : }
588 :
589 567 : if (!fun->isInterpretedConstructor())
590 252 : goto error;
591 :
592 315 : if (!InvokeKernel(cx, args, CONSTRUCT))
593 63 : return false;
594 :
595 252 : JS_ASSERT(args.rval().isObject());
596 252 : return true;
597 : }
598 47927 : if (clasp->construct)
599 47918 : return CallJSNativeConstructor(cx, clasp->construct, args);
600 : }
601 :
602 : error:
603 288 : js_ReportIsNotFunction(cx, &args.calleev(), JSV2F_CONSTRUCT);
604 288 : return false;
605 : }
606 :
607 : bool
608 8017 : js::InvokeConstructor(JSContext *cx, const Value &fval, unsigned argc, Value *argv, Value *rval)
609 : {
610 16034 : InvokeArgsGuard args;
611 8017 : if (!cx->stack.pushInvokeArgs(cx, argc, &args))
612 0 : return false;
613 :
614 8017 : args.calleev() = fval;
615 8017 : args.thisv().setMagic(JS_THIS_POISON);
616 8017 : PodCopy(args.array(), argv, argc);
617 :
618 8017 : if (!InvokeConstructor(cx, args))
619 72 : return false;
620 :
621 7945 : *rval = args.rval();
622 7945 : return true;
623 : }
624 :
625 : bool
626 3365844 : js::InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, unsigned argc, Value *argv,
627 : Value *rval)
628 : {
629 : /*
630 : * Invoke could result in another try to get or set the same id again, see
631 : * bug 355497.
632 : */
633 3365844 : JS_CHECK_RECURSION(cx, return false);
634 :
635 3365838 : return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
636 : }
637 :
638 : bool
639 180093 : js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv,
640 : ExecuteType type, StackFrame *evalInFrame, Value *result)
641 : {
642 180093 : JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
643 :
644 360186 : Root<JSScript*> scriptRoot(cx, &script);
645 :
646 180093 : if (script->isEmpty()) {
647 500 : if (result)
648 490 : result->setUndefined();
649 500 : return true;
650 : }
651 :
652 359186 : ExecuteFrameGuard efg;
653 179593 : if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
654 0 : return false;
655 :
656 179593 : if (!script->ensureRanAnalysis(cx, &scopeChain))
657 0 : return false;
658 :
659 : /* Give strict mode eval its own fresh lexical environment. */
660 179593 : StackFrame *fp = efg.fp();
661 179593 : if (fp->isStrictEvalFrame() && !CallObject::createForStrictEval(cx, fp))
662 0 : return false;
663 :
664 179593 : Probes::startExecution(cx, script);
665 :
666 179593 : TypeScript::SetThis(cx, script, fp->thisValue());
667 :
668 179593 : bool ok = RunScript(cx, script, fp);
669 :
670 179593 : if (fp->isStrictEvalFrame())
671 1908 : js_PutCallObject(fp);
672 :
673 179593 : Probes::stopExecution(cx, script);
674 :
675 : /* Propgate the return value out. */
676 179593 : if (result)
677 130137 : *result = fp->returnValue();
678 179593 : return ok;
679 : }
680 :
681 : bool
682 84280 : js::Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval)
683 : {
684 : /* The scope chain could be anything, so innerize just in case. */
685 84280 : JSObject *scopeChain = &scopeChainArg;
686 84280 : OBJ_TO_INNER_OBJECT(cx, scopeChain);
687 84280 : if (!scopeChain)
688 0 : return false;
689 :
690 : /* If we were handed a non-native object, complain bitterly. */
691 84280 : if (!scopeChain->isNative()) {
692 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_NATIVE_SCOPE);
693 0 : return false;
694 : }
695 84280 : JS_ASSERT(!scopeChain->getOps()->defineProperty);
696 :
697 : /* The VAROBJFIX option makes varObj == globalObj in global code. */
698 84280 : if (!cx->hasRunOption(JSOPTION_VAROBJFIX)) {
699 84050 : if (!scopeChain->setVarObj(cx))
700 0 : return false;
701 : }
702 :
703 : /* Use the scope chain as 'this', modulo outerization. */
704 84280 : JSObject *thisObj = scopeChain->thisObject(cx);
705 84280 : if (!thisObj)
706 0 : return false;
707 84280 : Value thisv = ObjectValue(*thisObj);
708 :
709 : return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
710 84280 : NULL /* evalInFrame */, rval);
711 : }
712 :
713 : JSBool
714 1228738 : js::HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
715 : {
716 1228738 : Class *clasp = obj->getClass();
717 1228738 : if (clasp->hasInstance)
718 1228720 : return clasp->hasInstance(cx, obj, v, bp);
719 18 : js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
720 18 : JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL);
721 18 : return JS_FALSE;
722 : }
723 :
724 : bool
725 5265784 : js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *result)
726 : {
727 : #if JS_HAS_XML_SUPPORT
728 10619061 : if (JS_UNLIKELY(lval.isObject() && lval.toObject().isXML()) ||
729 5353277 : (rval.isObject() && rval.toObject().isXML())) {
730 : JSBool res;
731 9 : if (!js_TestXMLEquality(cx, lval, rval, &res))
732 0 : return false;
733 9 : *result = !!res;
734 9 : return true;
735 : }
736 : #endif
737 :
738 5265775 : if (SameType(lval, rval)) {
739 4975314 : if (lval.isString()) {
740 961545 : JSString *l = lval.toString();
741 961545 : JSString *r = rval.toString();
742 961545 : return EqualStrings(cx, l, r, result);
743 : }
744 :
745 4013769 : if (lval.isDouble()) {
746 45988 : double l = lval.toDouble(), r = rval.toDouble();
747 45988 : *result = (l == r);
748 45988 : return true;
749 : }
750 :
751 3967781 : if (lval.isObject()) {
752 79129 : JSObject *l = &lval.toObject();
753 79129 : JSObject *r = &rval.toObject();
754 :
755 79129 : if (JSEqualityOp eq = l->getClass()->ext.equality) {
756 : JSBool res;
757 1441 : if (!eq(cx, l, &rval, &res))
758 0 : return false;
759 1441 : *result = !!res;
760 1441 : return true;
761 : }
762 :
763 77688 : *result = l == r;
764 77688 : return true;
765 : }
766 :
767 3888652 : *result = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
768 3888652 : return true;
769 : }
770 :
771 290461 : if (lval.isNullOrUndefined()) {
772 22549 : *result = rval.isNullOrUndefined();
773 22549 : return true;
774 : }
775 :
776 267912 : if (rval.isNullOrUndefined()) {
777 243576 : *result = false;
778 243576 : return true;
779 : }
780 :
781 24336 : Value lvalue = lval;
782 24336 : Value rvalue = rval;
783 :
784 24336 : if (!ToPrimitive(cx, &lvalue))
785 5 : return false;
786 24331 : if (!ToPrimitive(cx, &rvalue))
787 0 : return false;
788 :
789 24331 : if (lvalue.isString() && rvalue.isString()) {
790 5032 : JSString *l = lvalue.toString();
791 5032 : JSString *r = rvalue.toString();
792 5032 : return EqualStrings(cx, l, r, result);
793 : }
794 :
795 : double l, r;
796 19299 : if (!ToNumber(cx, lvalue, &l) || !ToNumber(cx, rvalue, &r))
797 0 : return false;
798 19299 : *result = (l == r);
799 19299 : return true;
800 : }
801 :
802 : bool
803 12458319 : js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, bool *equal)
804 : {
805 12458319 : Value lval = lref, rval = rref;
806 12458319 : if (SameType(lval, rval)) {
807 11095830 : if (lval.isString())
808 1234982 : return EqualStrings(cx, lval.toString(), rval.toString(), equal);
809 9860848 : if (lval.isDouble()) {
810 1055900 : *equal = (lval.toDouble() == rval.toDouble());
811 1055900 : return true;
812 : }
813 8804948 : if (lval.isObject()) {
814 720818 : *equal = lval.toObject() == rval.toObject();
815 720818 : return true;
816 : }
817 8084130 : if (lval.isUndefined()) {
818 1203851 : *equal = true;
819 1203851 : return true;
820 : }
821 6880279 : *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
822 6880279 : return true;
823 : }
824 :
825 1362489 : if (lval.isDouble() && rval.isInt32()) {
826 7659 : double ld = lval.toDouble();
827 7659 : double rd = rval.toInt32();
828 7659 : *equal = (ld == rd);
829 7659 : return true;
830 : }
831 1354830 : if (lval.isInt32() && rval.isDouble()) {
832 8825 : double ld = lval.toInt32();
833 8825 : double rd = rval.toDouble();
834 8825 : *equal = (ld == rd);
835 8825 : return true;
836 : }
837 :
838 1346005 : *equal = false;
839 1346005 : return true;
840 : }
841 :
842 : static inline bool
843 10613692 : IsNegativeZero(const Value &v)
844 : {
845 10613692 : return v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble());
846 : }
847 :
848 : static inline bool
849 5308800 : IsNaN(const Value &v)
850 : {
851 5308800 : return v.isDouble() && JSDOUBLE_IS_NaN(v.toDouble());
852 : }
853 :
854 : bool
855 5306846 : js::SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same)
856 : {
857 5306846 : if (IsNegativeZero(v1)) {
858 621 : *same = IsNegativeZero(v2);
859 621 : return true;
860 : }
861 5306225 : if (IsNegativeZero(v2)) {
862 1 : *same = false;
863 1 : return true;
864 : }
865 5306224 : if (IsNaN(v1) && IsNaN(v2)) {
866 2576 : *same = true;
867 2576 : return true;
868 : }
869 5303648 : return StrictlyEqual(cx, v1, v2, same);
870 : }
871 :
872 : JSType
873 7243639 : js::TypeOfValue(JSContext *cx, const Value &vref)
874 : {
875 7243639 : Value v = vref;
876 7243639 : if (v.isNumber())
877 40851 : return JSTYPE_NUMBER;
878 7202788 : if (v.isString())
879 152464 : return JSTYPE_STRING;
880 7050324 : if (v.isNull())
881 4434 : return JSTYPE_OBJECT;
882 7045890 : if (v.isUndefined())
883 51901 : return JSTYPE_VOID;
884 6993989 : if (v.isObject())
885 6990067 : return v.toObject().typeOf(cx);
886 3922 : JS_ASSERT(v.isBoolean());
887 3922 : return JSTYPE_BOOLEAN;
888 : }
889 :
890 : bool
891 18682600 : js::ValueToId(JSContext *cx, const Value &v, jsid *idp)
892 : {
893 : int32_t i;
894 18682600 : if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
895 4738850 : *idp = INT_TO_JSID(i);
896 4738850 : return true;
897 : }
898 :
899 : #if JS_HAS_XML_SUPPORT
900 13943750 : if (v.isObject()) {
901 72 : JSObject *obj = &v.toObject();
902 72 : if (obj->isXMLId()) {
903 36 : *idp = OBJECT_TO_JSID(obj);
904 36 : return JS_TRUE;
905 : }
906 : }
907 : #endif
908 :
909 13943714 : return js_ValueToStringId(cx, v, idp);
910 : }
911 :
912 : /*
913 : * Enter the new with scope using an object at sp[-1] and associate the depth
914 : * of the with block with sp + stackIndex.
915 : */
916 : static bool
917 1800 : EnterWith(JSContext *cx, int stackIndex)
918 : {
919 1800 : StackFrame *fp = cx->fp();
920 1800 : Value *sp = cx->regs().sp;
921 1800 : JS_ASSERT(stackIndex < 0);
922 1800 : JS_ASSERT(fp->base() <= sp + stackIndex);
923 :
924 : JSObject *obj;
925 1800 : if (sp[-1].isObject()) {
926 1773 : obj = &sp[-1].toObject();
927 : } else {
928 27 : obj = js_ValueToNonNullObject(cx, sp[-1]);
929 27 : if (!obj)
930 9 : return JS_FALSE;
931 18 : sp[-1].setObject(*obj);
932 : }
933 :
934 1791 : JSObject *parent = GetScopeChain(cx, fp);
935 1791 : if (!parent)
936 0 : return JS_FALSE;
937 :
938 : JSObject *withobj = WithObject::create(cx, fp, *obj, *parent,
939 1791 : sp + stackIndex - fp->base());
940 1791 : if (!withobj)
941 0 : return JS_FALSE;
942 :
943 1791 : fp->setScopeChainNoCallObj(*withobj);
944 1791 : return JS_TRUE;
945 : }
946 :
947 : static void
948 1791 : LeaveWith(JSContext *cx)
949 : {
950 1791 : WithObject &withobj = cx->fp()->scopeChain().asWith();
951 1791 : JS_ASSERT(withobj.maybeStackFrame() == js_FloatingFrameIfGenerator(cx, cx->fp()));
952 1791 : JS_ASSERT(withobj.stackDepth() >= 0);
953 1791 : withobj.setStackFrame(NULL);
954 1791 : cx->fp()->setScopeChainNoCallObj(withobj.enclosingScope());
955 1791 : }
956 :
957 : bool
958 18651217 : js::IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth)
959 : {
960 37302283 : return (obj.isWith() || obj.isBlock()) &&
961 34192 : obj.getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) &&
962 37336475 : obj.asNestedScope().stackDepth() >= stackDepth;
963 : }
964 :
965 : /* Unwind block and scope chains to match the given depth. */
966 : void
967 3401865 : js::UnwindScope(JSContext *cx, uint32_t stackDepth)
968 : {
969 3401865 : JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp);
970 :
971 3401865 : StackFrame *fp = cx->fp();
972 3401865 : StaticBlockObject *block = fp->maybeBlockChain();
973 6807433 : while (block) {
974 4551 : if (block->stackDepth() < stackDepth)
975 848 : break;
976 3703 : block = block->enclosingBlock();
977 : }
978 3401865 : fp->setBlockChain(block);
979 :
980 108 : for (;;) {
981 3401973 : JSObject &scopeChain = fp->scopeChain();
982 3401973 : if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth))
983 : break;
984 108 : if (scopeChain.isClonedBlock())
985 90 : scopeChain.asClonedBlock().put(cx);
986 : else
987 18 : LeaveWith(cx);
988 : }
989 3401865 : }
990 :
991 : void
992 2198 : js::UnwindForUncatchableException(JSContext *cx, const FrameRegs ®s)
993 : {
994 :
995 : /* c.f. the regular (catchable) TryNoteIter loop in Interpret. */
996 2261 : for (TryNoteIter tni(regs); !tni.done(); ++tni) {
997 63 : JSTryNote *tn = *tni;
998 63 : if (tn->kind == JSTRY_ITER) {
999 0 : Value *sp = regs.fp()->base() + tn->stackDepth;
1000 0 : UnwindIteratorForUncatchableException(cx, &sp[-1].toObject());
1001 : }
1002 : }
1003 2198 : }
1004 :
1005 2127709 : TryNoteIter::TryNoteIter(const FrameRegs ®s)
1006 : : regs(regs),
1007 2127709 : script(regs.fp()->script()),
1008 4255418 : pcOffset(regs.pc - script->main())
1009 : {
1010 2127709 : if (JSScript::isValidOffset(script->trynotesOffset)) {
1011 93461 : tn = script->trynotes()->vector;
1012 93461 : tnEnd = tn + script->trynotes()->length;
1013 : } else {
1014 2034248 : tn = tnEnd = NULL;
1015 : }
1016 2127709 : settle();
1017 2127709 : }
1018 :
1019 : void
1020 2906 : TryNoteIter::operator++()
1021 : {
1022 2906 : ++tn;
1023 2906 : settle();
1024 2906 : }
1025 :
1026 : bool
1027 2130615 : TryNoteIter::done() const
1028 : {
1029 2130615 : return tn == tnEnd;
1030 : }
1031 :
1032 : void
1033 2130615 : TryNoteIter::settle()
1034 : {
1035 2244558 : for (; tn != tnEnd; ++tn) {
1036 : /* If pc is out of range, try the next one. */
1037 170354 : if (pcOffset - tn->start >= tn->length)
1038 113943 : continue;
1039 :
1040 : /*
1041 : * We have a note that covers the exception pc but we must check
1042 : * whether the interpreter has already executed the corresponding
1043 : * handler. This is possible when the executed bytecode implements
1044 : * break or return from inside a for-in loop.
1045 : *
1046 : * In this case the emitter generates additional [enditer] and [gosub]
1047 : * opcodes to close all outstanding iterators and execute the finally
1048 : * blocks. If such an [enditer] throws an exception, its pc can still
1049 : * be inside several nested for-in loops and try-finally statements
1050 : * even if we have already closed the corresponding iterators and
1051 : * invoked the finally blocks.
1052 : *
1053 : * To address this, we make [enditer] always decrease the stack even
1054 : * when its implementation throws an exception. Thus already executed
1055 : * [enditer] and [gosub] opcodes will have try notes with the stack
1056 : * depth exceeding the current one and this condition is what we use to
1057 : * filter them out.
1058 : */
1059 56411 : if (tn->stackDepth <= regs.sp - regs.fp()->base())
1060 56411 : break;
1061 : }
1062 2130615 : }
1063 :
1064 : /*
1065 : * Increment/decrement the value 'v'. The resulting value is stored in *slot.
1066 : * The result of the expression (taking into account prefix/postfix) is stored
1067 : * in *expr.
1068 : */
1069 : static bool
1070 304364363 : DoIncDec(JSContext *cx, JSScript *script, jsbytecode *pc, const Value &v, Value *slot, Value *expr)
1071 : {
1072 304364363 : const JSCodeSpec &cs = js_CodeSpec[*pc];
1073 :
1074 304364363 : if (v.isInt32()) {
1075 304362164 : int32_t i = v.toInt32();
1076 304362164 : if (i > JSVAL_INT_MIN && i < JSVAL_INT_MAX) {
1077 304362140 : int32_t sum = i + (cs.format & JOF_INC ? 1 : -1);
1078 304362140 : *slot = Int32Value(sum);
1079 304362140 : *expr = (cs.format & JOF_POST) ? Int32Value(i) : *slot;
1080 304362140 : return true;
1081 : }
1082 : }
1083 :
1084 : double d;
1085 2223 : if (!ToNumber(cx, *slot, &d))
1086 0 : return false;
1087 :
1088 2223 : double sum = d + (cs.format & JOF_INC ? 1 : -1);
1089 2223 : *slot = NumberValue(sum);
1090 2223 : *expr = (cs.format & JOF_POST) ? NumberValue(d) : *slot;
1091 :
1092 2223 : TypeScript::MonitorOverflow(cx, script, pc);
1093 2223 : return true;
1094 : }
1095 :
1096 : const Value &
1097 743992 : js::GetUpvar(JSContext *cx, unsigned closureLevel, UpvarCookie cookie)
1098 : {
1099 743992 : JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
1100 743992 : const unsigned targetLevel = closureLevel - cookie.level();
1101 :
1102 743992 : StackFrame *fp = FindUpvarFrame(cx, targetLevel);
1103 743992 : unsigned slot = cookie.slot();
1104 : const Value *vp;
1105 :
1106 743992 : if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
1107 30 : vp = fp->slots() + fp->numFixed();
1108 743962 : } else if (slot < fp->numFormalArgs()) {
1109 390557 : vp = fp->formalArgs();
1110 353405 : } else if (slot == UpvarCookie::CALLEE_SLOT) {
1111 0 : vp = &fp->calleev();
1112 0 : slot = 0;
1113 : } else {
1114 353405 : slot -= fp->numFormalArgs();
1115 353405 : JS_ASSERT(slot < fp->numSlots());
1116 353405 : vp = fp->slots();
1117 : }
1118 :
1119 743992 : return vp[slot];
1120 : }
1121 :
1122 : extern StackFrame *
1123 743992 : js::FindUpvarFrame(JSContext *cx, unsigned targetLevel)
1124 : {
1125 743992 : StackFrame *fp = cx->fp();
1126 889 : while (true) {
1127 744881 : JS_ASSERT(fp && fp->isScriptFrame());
1128 744881 : if (fp->script()->staticLevel == targetLevel)
1129 : break;
1130 889 : fp = fp->prev();
1131 : }
1132 743992 : return fp;
1133 : }
1134 :
1135 : #define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
1136 : #define PUSH_COPY_SKIP_CHECK(v) *regs.sp++ = v
1137 : #define PUSH_NULL() regs.sp++->setNull()
1138 : #define PUSH_UNDEFINED() regs.sp++->setUndefined()
1139 : #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b)
1140 : #define PUSH_DOUBLE(d) regs.sp++->setDouble(d)
1141 : #define PUSH_INT32(i) regs.sp++->setInt32(i)
1142 : #define PUSH_STRING(s) do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1143 : #define PUSH_OBJECT(obj) do { regs.sp++->setObject(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1144 : #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1145 : #define PUSH_HOLE() regs.sp++->setMagic(JS_ARRAY_HOLE)
1146 : #define POP_COPY_TO(v) v = *--regs.sp
1147 : #define POP_RETURN_VALUE() regs.fp()->setReturnValue(*--regs.sp)
1148 :
1149 : #define VALUE_TO_BOOLEAN(cx, vp, b) \
1150 : JS_BEGIN_MACRO \
1151 : vp = ®s.sp[-1]; \
1152 : if (vp->isNull()) { \
1153 : b = false; \
1154 : } else if (vp->isBoolean()) { \
1155 : b = vp->toBoolean(); \
1156 : } else { \
1157 : b = !!js_ValueToBoolean(*vp); \
1158 : } \
1159 : JS_END_MACRO
1160 :
1161 : #define POP_BOOLEAN(cx, vp, b) do { VALUE_TO_BOOLEAN(cx, vp, b); regs.sp--; } while(0)
1162 :
1163 : #define FETCH_OBJECT(cx, n, obj) \
1164 : JS_BEGIN_MACRO \
1165 : Value *vp_ = ®s.sp[n]; \
1166 : obj = ToObject(cx, (vp_)); \
1167 : if (!obj) \
1168 : goto error; \
1169 : JS_END_MACRO
1170 :
1171 : /*
1172 : * Threaded interpretation via computed goto appears to be well-supported by
1173 : * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
1174 : * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
1175 : * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
1176 : * Add your compiler support macros here.
1177 : */
1178 : #ifndef JS_THREADED_INTERP
1179 : # if JS_VERSION >= 160 && ( \
1180 : __GNUC__ >= 3 || \
1181 : (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
1182 : __SUNPRO_C >= 0x570)
1183 : # define JS_THREADED_INTERP 1
1184 : # else
1185 : # define JS_THREADED_INTERP 0
1186 : # endif
1187 : #endif
1188 :
1189 : template<typename T>
1190 : class GenericInterruptEnabler : public InterpreterFrames::InterruptEnablerBase {
1191 : public:
1192 1977915 : GenericInterruptEnabler(T *variable, T value) : variable(variable), value(value) { }
1193 1534 : void enableInterrupts() const { *variable = value; }
1194 :
1195 : private:
1196 : T *variable;
1197 : T value;
1198 : };
1199 :
1200 1977915 : inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs,
1201 : const InterruptEnablerBase &enabler)
1202 1977915 : : context(cx), regs(regs), enabler(enabler)
1203 : {
1204 1977915 : older = cx->runtime->interpreterFrames;
1205 1977915 : cx->runtime->interpreterFrames = this;
1206 1977915 : }
1207 :
1208 1977915 : inline InterpreterFrames::~InterpreterFrames()
1209 : {
1210 1977915 : context->runtime->interpreterFrames = older;
1211 1977915 : }
1212 :
1213 : #if defined(DEBUG) && !defined(JS_THREADSAFE)
1214 : void
1215 : js::AssertValidPropertyCacheHit(JSContext *cx,
1216 : JSObject *start, JSObject *found,
1217 : PropertyCacheEntry *entry)
1218 : {
1219 : jsbytecode *pc;
1220 : cx->stack.currentScript(&pc);
1221 :
1222 : uint64_t sample = cx->runtime->gcNumber;
1223 : PropertyCacheEntry savedEntry = *entry;
1224 :
1225 : PropertyName *name = GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc]);
1226 :
1227 : JSObject *obj, *pobj;
1228 : JSProperty *prop;
1229 : JSBool ok;
1230 :
1231 : if (JOF_OPMODE(*pc) == JOF_NAME)
1232 : ok = FindProperty(cx, name, start, &obj, &pobj, &prop);
1233 : else
1234 : ok = LookupProperty(cx, start, name, &pobj, &prop);
1235 : JS_ASSERT(ok);
1236 :
1237 : if (cx->runtime->gcNumber != sample)
1238 : JS_PROPERTY_CACHE(cx).restore(&savedEntry);
1239 : JS_ASSERT(prop);
1240 : JS_ASSERT(pobj == found);
1241 :
1242 : const Shape *shape = (Shape *) prop;
1243 : JS_ASSERT(entry->prop == shape);
1244 : }
1245 : #endif /* DEBUG && !JS_THREADSAFE */
1246 :
1247 : /*
1248 : * Ensure that the intrepreter switch can close call-bytecode cases in the
1249 : * same way as non-call bytecodes.
1250 : */
1251 : JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
1252 : JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
1253 : JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
1254 : JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
1255 : JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
1256 :
1257 : /*
1258 : * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
1259 : * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}.
1260 : */
1261 : JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
1262 : JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETMETHOD_LENGTH);
1263 : JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH == JSOP_INITMETHOD_LENGTH);
1264 :
1265 : /* See TRY_BRANCH_AFTER_COND. */
1266 : JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
1267 : JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
1268 :
1269 : /* For the fastest case inder JSOP_INCNAME, etc. */
1270 : JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
1271 : JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
1272 : JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
1273 :
1274 : /*
1275 : * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
1276 : * all cases, but we inline the most frequently taken paths here.
1277 : */
1278 : static inline bool
1279 4620085 : IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
1280 : {
1281 4620085 : if (iterobj->isIterator()) {
1282 4597869 : NativeIterator *ni = iterobj->getNativeIterator();
1283 4597869 : if (ni->isKeyIter()) {
1284 736816 : *cond = (ni->props_cursor < ni->props_end);
1285 736816 : return true;
1286 : }
1287 : }
1288 3883269 : if (!js_IteratorMore(cx, iterobj, rval))
1289 20 : return false;
1290 3883249 : *cond = rval->isTrue();
1291 3883249 : return true;
1292 : }
1293 :
1294 : static inline bool
1295 4422919 : IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
1296 : {
1297 4422919 : if (iterobj->isIterator()) {
1298 4406813 : NativeIterator *ni = iterobj->getNativeIterator();
1299 4406813 : if (ni->isKeyIter()) {
1300 687043 : JS_ASSERT(ni->props_cursor < ni->props_end);
1301 687043 : rval->setString(*ni->current());
1302 687043 : ni->incCursor();
1303 687043 : return true;
1304 : }
1305 : }
1306 3735876 : return js_IteratorNext(cx, iterobj, rval);
1307 : }
1308 :
1309 : /*
1310 : * For bytecodes which push values and then fall through, make sure the
1311 : * types of the pushed values are consistent with type inference information.
1312 : */
1313 : static inline void
1314 1961045842 : TypeCheckNextBytecode(JSContext *cx, JSScript *script, unsigned n, const FrameRegs ®s)
1315 : {
1316 : #ifdef DEBUG
1317 -1471779413 : if (cx->typeInferenceEnabled() &&
1318 862142041 : n == GetBytecodeLength(regs.pc)) {
1319 859440498 : TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp);
1320 : }
1321 : #endif
1322 1961045842 : }
1323 :
1324 : JS_NEVER_INLINE bool
1325 1977915 : js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
1326 : {
1327 3955830 : JSAutoResolveFlags rf(cx, RESOLVE_INFER);
1328 :
1329 1977915 : gc::MaybeVerifyBarriers(cx, true);
1330 :
1331 1977915 : JS_ASSERT(!cx->compartment->activeAnalysis);
1332 :
1333 : #if JS_THREADED_INTERP
1334 : #define CHECK_PCCOUNT_INTERRUPTS() JS_ASSERT_IF(script->pcCounters, jumpTable == interruptJumpTable)
1335 : #else
1336 : #define CHECK_PCCOUNT_INTERRUPTS() JS_ASSERT_IF(script->pcCounters, switchMask == -1)
1337 : #endif
1338 :
1339 : /*
1340 : * Macros for threaded interpreter loop
1341 : */
1342 : #if JS_THREADED_INTERP
1343 : static void *const normalJumpTable[] = {
1344 : # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
1345 : JS_EXTENSION &&L_##op,
1346 : # include "jsopcode.tbl"
1347 : # undef OPDEF
1348 : };
1349 :
1350 : static void *const interruptJumpTable[] = {
1351 : # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
1352 : JS_EXTENSION &&interrupt,
1353 : # include "jsopcode.tbl"
1354 : # undef OPDEF
1355 : };
1356 :
1357 1977915 : register void * const *jumpTable = normalJumpTable;
1358 :
1359 : typedef GenericInterruptEnabler<void * const *> InterruptEnabler;
1360 1977915 : InterruptEnabler interruptEnabler(&jumpTable, interruptJumpTable);
1361 :
1362 : # define DO_OP() JS_BEGIN_MACRO \
1363 : CHECK_PCCOUNT_INTERRUPTS(); \
1364 : js::gc::MaybeVerifyBarriers(cx); \
1365 : JS_EXTENSION_(goto *jumpTable[op]); \
1366 : JS_END_MACRO
1367 : # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
1368 : TypeCheckNextBytecode(cx, script, n, regs); \
1369 : op = (JSOp) *(regs.pc += (n)); \
1370 : DO_OP(); \
1371 : JS_END_MACRO
1372 :
1373 : # define BEGIN_CASE(OP) L_##OP:
1374 : # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
1375 : # define END_VARLEN_CASE DO_NEXT_OP(len);
1376 : # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
1377 : JS_ASSERT(js_CodeSpec[OP].length == 1); \
1378 : op = (JSOp) *++regs.pc; \
1379 : DO_OP();
1380 :
1381 : # define END_EMPTY_CASES
1382 :
1383 : #else /* !JS_THREADED_INTERP */
1384 :
1385 : register int switchMask = 0;
1386 : int switchOp;
1387 : typedef GenericInterruptEnabler<int> InterruptEnabler;
1388 : InterruptEnabler interruptEnabler(&switchMask, -1);
1389 :
1390 : # define DO_OP() goto do_op
1391 : # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
1392 : JS_ASSERT((n) == len); \
1393 : goto advance_pc; \
1394 : JS_END_MACRO
1395 :
1396 : # define BEGIN_CASE(OP) case OP:
1397 : # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
1398 : # define END_CASE_LEN(n) END_CASE_LENX(n)
1399 : # define END_CASE_LENX(n) END_CASE_LEN##n
1400 :
1401 : /*
1402 : * To share the code for all len == 1 cases we use the specialized label with
1403 : * code that falls through to advance_pc: .
1404 : */
1405 : # define END_CASE_LEN1 goto advance_pc_by_one;
1406 : # define END_CASE_LEN2 len = 2; goto advance_pc;
1407 : # define END_CASE_LEN3 len = 3; goto advance_pc;
1408 : # define END_CASE_LEN4 len = 4; goto advance_pc;
1409 : # define END_CASE_LEN5 len = 5; goto advance_pc;
1410 : # define END_CASE_LEN6 len = 6; goto advance_pc;
1411 : # define END_CASE_LEN7 len = 7; goto advance_pc;
1412 : # define END_VARLEN_CASE goto advance_pc;
1413 : # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
1414 : # define END_EMPTY_CASES goto advance_pc_by_one;
1415 :
1416 : #endif /* !JS_THREADED_INTERP */
1417 :
1418 : #define ENABLE_INTERRUPTS() (interruptEnabler.enableInterrupts())
1419 :
1420 : #define LOAD_ATOM(PCOFF, atom) \
1421 : JS_BEGIN_MACRO \
1422 : JS_ASSERT((size_t)(atoms - script->atoms) < \
1423 : (size_t)(script->natoms - GET_UINT32_INDEX(regs.pc + PCOFF)));\
1424 : atom = atoms[GET_UINT32_INDEX(regs.pc + PCOFF)]; \
1425 : JS_END_MACRO
1426 :
1427 : #define LOAD_NAME(PCOFF, name) \
1428 : JS_BEGIN_MACRO \
1429 : JSAtom *atom; \
1430 : LOAD_ATOM((PCOFF), atom); \
1431 : name = atom->asPropertyName(); \
1432 : JS_END_MACRO
1433 :
1434 : #define LOAD_DOUBLE(PCOFF, dbl) \
1435 : (dbl = script->getConst(GET_UINT32_INDEX(regs.pc + (PCOFF))).toDouble())
1436 :
1437 : #if defined(JS_METHODJIT)
1438 1977915 : bool useMethodJIT = false;
1439 : #endif
1440 :
1441 : #ifdef JS_METHODJIT
1442 :
1443 : #define RESET_USE_METHODJIT() \
1444 : JS_BEGIN_MACRO \
1445 : useMethodJIT = cx->methodJitEnabled && \
1446 : (interpMode == JSINTERP_NORMAL || \
1447 : interpMode == JSINTERP_REJOIN || \
1448 : interpMode == JSINTERP_SKIP_TRAP); \
1449 : JS_END_MACRO
1450 :
1451 : #define CHECK_PARTIAL_METHODJIT(status) \
1452 : JS_BEGIN_MACRO \
1453 : switch (status) { \
1454 : case mjit::Jaeger_UnfinishedAtTrap: \
1455 : interpMode = JSINTERP_SKIP_TRAP; \
1456 : /* FALLTHROUGH */ \
1457 : case mjit::Jaeger_Unfinished: \
1458 : op = (JSOp) *regs.pc; \
1459 : RESTORE_INTERP_VARS_CHECK_EXCEPTION(); \
1460 : DO_OP(); \
1461 : default:; \
1462 : } \
1463 : JS_END_MACRO
1464 :
1465 : #else
1466 :
1467 : #define RESET_USE_METHODJIT() ((void) 0)
1468 :
1469 : #endif
1470 :
1471 : #define RESTORE_INTERP_VARS() \
1472 : JS_BEGIN_MACRO \
1473 : SET_SCRIPT(regs.fp()->script()); \
1474 : argv = regs.fp()->maybeFormalArgs(); \
1475 : atoms = FrameAtomBase(cx, regs.fp()); \
1476 : JS_ASSERT(&cx->regs() == ®s); \
1477 : JS_END_MACRO
1478 :
1479 : #define RESTORE_INTERP_VARS_CHECK_EXCEPTION() \
1480 : JS_BEGIN_MACRO \
1481 : RESTORE_INTERP_VARS(); \
1482 : if (cx->isExceptionPending()) \
1483 : goto error; \
1484 : CHECK_INTERRUPT_HANDLER(); \
1485 : JS_END_MACRO
1486 :
1487 : /*
1488 : * Prepare to call a user-supplied branch handler, and abort the script
1489 : * if it returns false.
1490 : */
1491 : #define CHECK_BRANCH() \
1492 : JS_BEGIN_MACRO \
1493 : if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx)) \
1494 : goto error; \
1495 : JS_END_MACRO
1496 :
1497 : #define BRANCH(n) \
1498 : JS_BEGIN_MACRO \
1499 : regs.pc += (n); \
1500 : op = (JSOp) *regs.pc; \
1501 : if ((n) <= 0) \
1502 : goto check_backedge; \
1503 : DO_OP(); \
1504 : JS_END_MACRO
1505 :
1506 : #define SET_SCRIPT(s) \
1507 : JS_BEGIN_MACRO \
1508 : script = (s); \
1509 : if (script->hasAnyBreakpointsOrStepMode()) \
1510 : ENABLE_INTERRUPTS(); \
1511 : if (script->pcCounters) \
1512 : ENABLE_INTERRUPTS(); \
1513 : JS_ASSERT_IF(interpMode == JSINTERP_SKIP_TRAP, \
1514 : script->hasAnyBreakpointsOrStepMode()); \
1515 : JS_END_MACRO
1516 :
1517 : #define CHECK_INTERRUPT_HANDLER() \
1518 : JS_BEGIN_MACRO \
1519 : if (cx->runtime->debugHooks.interruptHook) \
1520 : ENABLE_INTERRUPTS(); \
1521 : JS_END_MACRO
1522 :
1523 : /* Repoint cx->regs to a local variable for faster access. */
1524 1977915 : FrameRegs regs = cx->regs();
1525 3955830 : PreserveRegsGuard interpGuard(cx, regs);
1526 :
1527 : /*
1528 : * Help Debugger find frames running scripts that it has put in
1529 : * single-step mode.
1530 : */
1531 3955830 : InterpreterFrames interpreterFrame(cx, ®s, interruptEnabler);
1532 :
1533 : /* Copy in hot values that change infrequently. */
1534 1977915 : JSRuntime *const rt = cx->runtime;
1535 : JSScript *script;
1536 1977915 : SET_SCRIPT(regs.fp()->script());
1537 1977915 : Value *argv = regs.fp()->maybeFormalArgs();
1538 1977915 : CHECK_INTERRUPT_HANDLER();
1539 :
1540 1977915 : if (rt->profilingScripts)
1541 0 : ENABLE_INTERRUPTS();
1542 :
1543 1977915 : if (!entryFrame)
1544 0 : entryFrame = regs.fp();
1545 :
1546 : /*
1547 : * Initialize the index segment register used by LOAD_ATOM and
1548 : * GET_FULL_INDEX macros below. As a register we use a pointer based on
1549 : * the atom map to turn frequently executed LOAD_ATOM into simple array
1550 : * access. For less frequent object loads we have to recover the segment
1551 : * from atoms pointer first.
1552 : */
1553 1977915 : JSAtom **atoms = script->atoms;
1554 :
1555 : #if JS_HAS_GENERATORS
1556 1977915 : if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
1557 30834 : JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
1558 30834 : JS_ASSERT((size_t) (regs.sp - regs.fp()->base()) <= StackDepth(script));
1559 :
1560 : /*
1561 : * To support generator_throw and to catch ignored exceptions,
1562 : * fail if cx->isExceptionPending() is true.
1563 : */
1564 30834 : if (cx->isExceptionPending())
1565 1505 : goto error;
1566 : }
1567 : #endif
1568 :
1569 : /* State communicated between non-local jumps: */
1570 : bool interpReturnOK;
1571 :
1572 : /* Don't call the script prologue if executing between Method and Trace JIT. */
1573 1976410 : if (interpMode == JSINTERP_NORMAL) {
1574 1961998 : StackFrame *fp = regs.fp();
1575 1961998 : JS_ASSERT_IF(!fp->isGeneratorFrame(), regs.pc == script->code);
1576 1961998 : if (!ScriptPrologueOrGeneratorResume(cx, fp, UseNewTypeAtEntry(cx, fp)))
1577 0 : goto error;
1578 1961998 : if (cx->compartment->debugMode()) {
1579 601784 : JSTrapStatus status = ScriptDebugPrologue(cx, fp);
1580 601784 : switch (status) {
1581 : case JSTRAP_CONTINUE:
1582 : break;
1583 : case JSTRAP_RETURN:
1584 10 : interpReturnOK = true;
1585 10 : goto forced_return;
1586 : case JSTRAP_THROW:
1587 : case JSTRAP_ERROR:
1588 399 : goto error;
1589 : default:
1590 0 : JS_NOT_REACHED("bad ScriptDebugPrologue status");
1591 : }
1592 : }
1593 : }
1594 :
1595 : /* The REJOIN mode acts like the normal mode, except the prologue is skipped. */
1596 1976001 : if (interpMode == JSINTERP_REJOIN)
1597 14406 : interpMode = JSINTERP_NORMAL;
1598 :
1599 1976001 : CHECK_INTERRUPT_HANDLER();
1600 :
1601 1976001 : RESET_USE_METHODJIT();
1602 :
1603 : /*
1604 : * It is important that "op" be initialized before calling DO_OP because
1605 : * it is possible for "op" to be specially assigned during the normal
1606 : * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
1607 : * "op" correctly in all other cases.
1608 : */
1609 : JSOp op;
1610 : int32_t len;
1611 1976001 : len = 0;
1612 :
1613 : /* Check for too deep of a native thread stack. */
1614 1976001 : JS_CHECK_RECURSION(cx, goto error);
1615 :
1616 1975938 : DO_NEXT_OP(len);
1617 :
1618 : #if JS_THREADED_INTERP
1619 : /*
1620 : * This is a loop, but it does not look like a loop. The loop-closing
1621 : * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
1622 : * When interrupts are enabled, jumpTable is set to interruptJumpTable
1623 : * where all jumps point to the interrupt label. The latter, after
1624 : * calling the interrupt handler, dispatches through normalJumpTable to
1625 : * continue the normal bytecode processing.
1626 : */
1627 :
1628 : #else /* !JS_THREADED_INTERP */
1629 : for (;;) {
1630 : advance_pc_by_one:
1631 : JS_ASSERT(js_CodeSpec[op].length == 1);
1632 : len = 1;
1633 : advance_pc:
1634 : regs.pc += len;
1635 : op = (JSOp) *regs.pc;
1636 :
1637 : do_op:
1638 : CHECK_PCCOUNT_INTERRUPTS();
1639 : js::gc::MaybeVerifyBarriers(cx);
1640 : switchOp = int(op) | switchMask;
1641 : do_switch:
1642 : switch (switchOp) {
1643 : #endif
1644 :
1645 : #if JS_THREADED_INTERP
1646 : interrupt:
1647 : #else /* !JS_THREADED_INTERP */
1648 : case -1:
1649 : JS_ASSERT(switchMask == -1);
1650 : #endif /* !JS_THREADED_INTERP */
1651 : {
1652 18252 : bool moreInterrupts = false;
1653 :
1654 18252 : if (cx->runtime->profilingScripts) {
1655 0 : if (!script->pcCounters)
1656 0 : script->initCounts(cx);
1657 0 : moreInterrupts = true;
1658 : }
1659 :
1660 18252 : if (script->pcCounters) {
1661 0 : OpcodeCounts counts = script->getCounts(regs.pc);
1662 0 : counts.get(OpcodeCounts::BASE_INTERP)++;
1663 0 : moreInterrupts = true;
1664 : }
1665 :
1666 18252 : JSInterruptHook hook = cx->runtime->debugHooks.interruptHook;
1667 18252 : if (hook || script->stepModeEnabled()) {
1668 : Value rval;
1669 5486 : JSTrapStatus status = JSTRAP_CONTINUE;
1670 5486 : if (hook)
1671 16 : status = hook(cx, script, regs.pc, &rval, cx->runtime->debugHooks.interruptHookData);
1672 5486 : if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
1673 5486 : status = Debugger::onSingleStep(cx, &rval);
1674 5486 : switch (status) {
1675 : case JSTRAP_ERROR:
1676 18 : goto error;
1677 : case JSTRAP_CONTINUE:
1678 5450 : break;
1679 : case JSTRAP_RETURN:
1680 9 : regs.fp()->setReturnValue(rval);
1681 9 : interpReturnOK = true;
1682 9 : goto forced_return;
1683 : case JSTRAP_THROW:
1684 9 : cx->setPendingException(rval);
1685 9 : goto error;
1686 : default:;
1687 : }
1688 5450 : moreInterrupts = true;
1689 : }
1690 :
1691 18216 : if (script->hasAnyBreakpointsOrStepMode())
1692 17780 : moreInterrupts = true;
1693 :
1694 18216 : if (script->hasBreakpointsAt(regs.pc) && interpMode != JSINTERP_SKIP_TRAP) {
1695 : Value rval;
1696 982 : JSTrapStatus status = Debugger::onTrap(cx, &rval);
1697 982 : switch (status) {
1698 : case JSTRAP_ERROR:
1699 5 : goto error;
1700 : case JSTRAP_RETURN:
1701 30 : regs.fp()->setReturnValue(rval);
1702 30 : interpReturnOK = true;
1703 30 : goto forced_return;
1704 : case JSTRAP_THROW:
1705 9 : cx->setPendingException(rval);
1706 9 : goto error;
1707 : default:
1708 : break;
1709 : }
1710 938 : JS_ASSERT(status == JSTRAP_CONTINUE);
1711 938 : CHECK_INTERRUPT_HANDLER();
1712 938 : JS_ASSERT(rval.isInt32() && rval.toInt32() == op);
1713 : }
1714 :
1715 18172 : interpMode = JSINTERP_NORMAL;
1716 :
1717 : #if JS_THREADED_INTERP
1718 18172 : jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
1719 18172 : JS_EXTENSION_(goto *normalJumpTable[op]);
1720 : #else
1721 : switchMask = moreInterrupts ? -1 : 0;
1722 : switchOp = int(op);
1723 : goto do_switch;
1724 : #endif
1725 : }
1726 :
1727 : /* No-ops for ease of decompilation. */
1728 408268 : ADD_EMPTY_CASE(JSOP_NOP)
1729 0 : ADD_EMPTY_CASE(JSOP_UNUSED0)
1730 0 : ADD_EMPTY_CASE(JSOP_UNUSED1)
1731 0 : ADD_EMPTY_CASE(JSOP_UNUSED2)
1732 0 : ADD_EMPTY_CASE(JSOP_UNUSED3)
1733 0 : ADD_EMPTY_CASE(JSOP_UNUSED4)
1734 0 : ADD_EMPTY_CASE(JSOP_UNUSED5)
1735 0 : ADD_EMPTY_CASE(JSOP_UNUSED6)
1736 0 : ADD_EMPTY_CASE(JSOP_UNUSED7)
1737 0 : ADD_EMPTY_CASE(JSOP_UNUSED8)
1738 0 : ADD_EMPTY_CASE(JSOP_UNUSED9)
1739 0 : ADD_EMPTY_CASE(JSOP_UNUSED10)
1740 0 : ADD_EMPTY_CASE(JSOP_UNUSED11)
1741 0 : ADD_EMPTY_CASE(JSOP_UNUSED12)
1742 0 : ADD_EMPTY_CASE(JSOP_UNUSED13)
1743 0 : ADD_EMPTY_CASE(JSOP_UNUSED14)
1744 0 : ADD_EMPTY_CASE(JSOP_UNUSED15)
1745 0 : ADD_EMPTY_CASE(JSOP_UNUSED16)
1746 0 : ADD_EMPTY_CASE(JSOP_UNUSED17)
1747 0 : ADD_EMPTY_CASE(JSOP_UNUSED18)
1748 0 : ADD_EMPTY_CASE(JSOP_UNUSED19)
1749 0 : ADD_EMPTY_CASE(JSOP_UNUSED20)
1750 0 : ADD_EMPTY_CASE(JSOP_UNUSED21)
1751 0 : ADD_EMPTY_CASE(JSOP_UNUSED22)
1752 0 : ADD_EMPTY_CASE(JSOP_UNUSED23)
1753 1024482 : ADD_EMPTY_CASE(JSOP_CONDSWITCH)
1754 687993 : ADD_EMPTY_CASE(JSOP_TRY)
1755 : #if JS_HAS_XML_SUPPORT
1756 623 : ADD_EMPTY_CASE(JSOP_STARTXML)
1757 0 : ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
1758 : #endif
1759 167272799 : ADD_EMPTY_CASE(JSOP_LOOPHEAD)
1760 169671524 : ADD_EMPTY_CASE(JSOP_LOOPENTRY)
1761 : END_EMPTY_CASES
1762 :
1763 : BEGIN_CASE(JSOP_LABEL)
1764 427 : END_CASE(JSOP_LABEL)
1765 :
1766 : check_backedge:
1767 : {
1768 167285352 : CHECK_BRANCH();
1769 167285352 : if (op != JSOP_LOOPHEAD)
1770 1056 : DO_OP();
1771 :
1772 : #ifdef JS_METHODJIT
1773 167284296 : if (!useMethodJIT)
1774 166611205 : DO_OP();
1775 : mjit::CompileStatus status =
1776 673091 : mjit::CanMethodJIT(cx, script, regs.pc, regs.fp()->isConstructing(),
1777 673091 : mjit::CompileRequest_Interpreter);
1778 673091 : if (status == mjit::Compile_Error)
1779 0 : goto error;
1780 673091 : if (status == mjit::Compile_Okay) {
1781 : void *ncode =
1782 33494 : script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);
1783 33494 : JS_ASSERT(ncode);
1784 33494 : mjit::JaegerStatus status = mjit::JaegerShotAtSafePoint(cx, ncode, true);
1785 33494 : if (status == mjit::Jaeger_ThrowBeforeEnter)
1786 0 : goto error;
1787 33494 : CHECK_PARTIAL_METHODJIT(status);
1788 11060 : interpReturnOK = (status == mjit::Jaeger_Returned);
1789 11060 : if (entryFrame != regs.fp())
1790 10179 : goto jit_return;
1791 881 : regs.fp()->setFinishedInInterpreter();
1792 881 : goto leave_on_safe_point;
1793 : }
1794 639597 : if (status == mjit::Compile_Abort)
1795 146051 : useMethodJIT = false;
1796 : #endif /* JS_METHODJIT */
1797 :
1798 639597 : DO_OP();
1799 : }
1800 :
1801 : /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
1802 : BEGIN_CASE(JSOP_LINENO)
1803 22271 : END_CASE(JSOP_LINENO)
1804 :
1805 : BEGIN_CASE(JSOP_UNDEFINED)
1806 10433067 : PUSH_UNDEFINED();
1807 10433067 : END_CASE(JSOP_UNDEFINED)
1808 :
1809 : BEGIN_CASE(JSOP_POP)
1810 397013325 : regs.sp--;
1811 397013325 : END_CASE(JSOP_POP)
1812 :
1813 : BEGIN_CASE(JSOP_POPN)
1814 : {
1815 101981 : regs.sp -= GET_UINT16(regs.pc);
1816 : #ifdef DEBUG
1817 101981 : JS_ASSERT(regs.fp()->base() <= regs.sp);
1818 101981 : StaticBlockObject *block = regs.fp()->maybeBlockChain();
1819 7156 : JS_ASSERT_IF(block,
1820 : block->stackDepth() + block->slotCount()
1821 109137 : <= (size_t) (regs.sp - regs.fp()->base()));
1822 212836 : for (JSObject *obj = ®s.fp()->scopeChain(); obj; obj = obj->enclosingScope()) {
1823 110855 : if (!obj->isBlock() || !obj->isWith())
1824 110855 : continue;
1825 0 : if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp()))
1826 0 : break;
1827 0 : JS_ASSERT(regs.fp()->base() + obj->asBlock().stackDepth()
1828 : + (obj->isBlock() ? obj->asBlock().slotCount() : 1)
1829 0 : <= regs.sp);
1830 : }
1831 : #endif
1832 : }
1833 101981 : END_CASE(JSOP_POPN)
1834 :
1835 : BEGIN_CASE(JSOP_SETRVAL)
1836 : BEGIN_CASE(JSOP_POPV)
1837 782909 : POP_RETURN_VALUE();
1838 782909 : END_CASE(JSOP_POPV)
1839 :
1840 : BEGIN_CASE(JSOP_ENTERWITH)
1841 1737 : if (!EnterWith(cx, -1))
1842 9 : goto error;
1843 :
1844 : /*
1845 : * We must ensure that different "with" blocks have different stack depth
1846 : * associated with them. This allows the try handler search to properly
1847 : * recover the scope chain. Thus we must keep the stack at least at the
1848 : * current level.
1849 : *
1850 : * We set sp[-1] to the current "with" object to help asserting the
1851 : * enter/leave balance in [leavewith].
1852 : */
1853 1728 : regs.sp[-1].setObject(regs.fp()->scopeChain());
1854 1728 : END_CASE(JSOP_ENTERWITH)
1855 :
1856 : BEGIN_CASE(JSOP_LEAVEWITH)
1857 1710 : JS_ASSERT(regs.sp[-1].toObject() == regs.fp()->scopeChain());
1858 1710 : regs.sp--;
1859 1710 : LeaveWith(cx);
1860 1710 : END_CASE(JSOP_LEAVEWITH)
1861 :
1862 : BEGIN_CASE(JSOP_RETURN)
1863 7736017 : POP_RETURN_VALUE();
1864 : /* FALL THROUGH */
1865 :
1866 : BEGIN_CASE(JSOP_RETRVAL) /* fp return value already set */
1867 : BEGIN_CASE(JSOP_STOP)
1868 : {
1869 : /*
1870 : * When the inlined frame exits with an exception or an error, ok will be
1871 : * false after the inline_return label.
1872 : */
1873 11914778 : CHECK_BRANCH();
1874 :
1875 11914778 : interpReturnOK = true;
1876 11914778 : if (entryFrame != regs.fp())
1877 : inline_return:
1878 : {
1879 12047429 : JS_ASSERT(!regs.fp()->hasBlockChain());
1880 12047429 : JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
1881 :
1882 12047429 : if (cx->compartment->debugMode())
1883 1672977 : interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
1884 :
1885 12047429 : interpReturnOK = ScriptEpilogue(cx, regs.fp(), interpReturnOK);
1886 :
1887 : /* The JIT inlines ScriptEpilogue. */
1888 : #ifdef JS_METHODJIT
1889 : jit_return:
1890 : #endif
1891 :
1892 : /* The results of lowered call/apply frames need to be shifted. */
1893 12677473 : bool shiftResult = regs.fp()->loweredCallOrApply();
1894 :
1895 12677473 : cx->stack.popInlineFrame(regs);
1896 :
1897 12677473 : RESTORE_INTERP_VARS();
1898 :
1899 0 : JS_ASSERT(*regs.pc == JSOP_NEW || *regs.pc == JSOP_CALL ||
1900 12677473 : *regs.pc == JSOP_FUNCALL || *regs.pc == JSOP_FUNAPPLY);
1901 :
1902 : /* Resume execution in the calling frame. */
1903 12677473 : RESET_USE_METHODJIT();
1904 12677473 : if (JS_LIKELY(interpReturnOK)) {
1905 10671147 : TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
1906 :
1907 10671147 : if (shiftResult) {
1908 161 : regs.sp[-2] = regs.sp[-1];
1909 161 : regs.sp--;
1910 : }
1911 :
1912 10671147 : len = JSOP_CALL_LENGTH;
1913 10671147 : DO_NEXT_OP(len);
1914 : }
1915 :
1916 : /* Increment pc so that |sp - fp->slots == ReconstructStackDepth(pc)|. */
1917 2006326 : regs.pc += JSOP_CALL_LENGTH;
1918 2006326 : goto error;
1919 : } else {
1920 1881645 : JS_ASSERT(regs.sp == regs.fp()->base());
1921 : }
1922 1881645 : interpReturnOK = true;
1923 1881645 : goto exit;
1924 : }
1925 :
1926 : BEGIN_CASE(JSOP_DEFAULT)
1927 751 : regs.sp--;
1928 : /* FALL THROUGH */
1929 : BEGIN_CASE(JSOP_GOTO)
1930 : {
1931 6631369 : len = GET_JUMP_OFFSET(regs.pc);
1932 6631369 : BRANCH(len);
1933 : }
1934 : END_CASE(JSOP_GOTO)
1935 :
1936 : BEGIN_CASE(JSOP_IFEQ)
1937 : {
1938 : bool cond;
1939 : Value *_;
1940 9050044 : POP_BOOLEAN(cx, _, cond);
1941 9050044 : if (cond == false) {
1942 5895429 : len = GET_JUMP_OFFSET(regs.pc);
1943 5895429 : BRANCH(len);
1944 : }
1945 : }
1946 3154615 : END_CASE(JSOP_IFEQ)
1947 :
1948 : BEGIN_CASE(JSOP_IFNE)
1949 : {
1950 : bool cond;
1951 : Value *_;
1952 6617179 : POP_BOOLEAN(cx, _, cond);
1953 6617179 : if (cond != false) {
1954 6222831 : len = GET_JUMP_OFFSET(regs.pc);
1955 6222831 : BRANCH(len);
1956 : }
1957 : }
1958 394348 : END_CASE(JSOP_IFNE)
1959 :
1960 : BEGIN_CASE(JSOP_OR)
1961 : {
1962 : bool cond;
1963 : Value *_;
1964 898787 : VALUE_TO_BOOLEAN(cx, _, cond);
1965 898787 : if (cond == true) {
1966 165228 : len = GET_JUMP_OFFSET(regs.pc);
1967 165228 : DO_NEXT_OP(len);
1968 : }
1969 : }
1970 733559 : END_CASE(JSOP_OR)
1971 :
1972 : BEGIN_CASE(JSOP_AND)
1973 : {
1974 : bool cond;
1975 : Value *_;
1976 711448 : VALUE_TO_BOOLEAN(cx, _, cond);
1977 711448 : if (cond == false) {
1978 292061 : len = GET_JUMP_OFFSET(regs.pc);
1979 292061 : DO_NEXT_OP(len);
1980 : }
1981 : }
1982 419387 : END_CASE(JSOP_AND)
1983 :
1984 : /*
1985 : * If the index value at sp[n] is not an int that fits in a jsval, it could
1986 : * be an object (an XML QName, AttributeName, or AnyName), but only if we are
1987 : * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
1988 : * string atom id.
1989 : */
1990 : #define FETCH_ELEMENT_ID(obj, n, id) \
1991 : JS_BEGIN_MACRO \
1992 : const Value &idval_ = regs.sp[n]; \
1993 : int32_t i_; \
1994 : if (ValueFitsInInt32(idval_, &i_) && INT_FITS_IN_JSID(i_)) { \
1995 : id = INT_TO_JSID(i_); \
1996 : } else { \
1997 : if (!js_InternNonIntElementId(cx, obj, idval_, &id, ®s.sp[n])) \
1998 : goto error; \
1999 : } \
2000 : JS_END_MACRO
2001 :
2002 : #define TRY_BRANCH_AFTER_COND(cond,spdec) \
2003 : JS_BEGIN_MACRO \
2004 : JS_ASSERT(js_CodeSpec[op].length == 1); \
2005 : unsigned diff_ = (unsigned) GET_UINT8(regs.pc) - (unsigned) JSOP_IFEQ; \
2006 : if (diff_ <= 1) { \
2007 : regs.sp -= spdec; \
2008 : if (cond == (diff_ != 0)) { \
2009 : ++regs.pc; \
2010 : len = GET_JUMP_OFFSET(regs.pc); \
2011 : BRANCH(len); \
2012 : } \
2013 : len = 1 + JSOP_IFEQ_LENGTH; \
2014 : DO_NEXT_OP(len); \
2015 : } \
2016 : JS_END_MACRO
2017 :
2018 : BEGIN_CASE(JSOP_IN)
2019 : {
2020 433423 : const Value &rref = regs.sp[-1];
2021 433423 : if (!rref.isObject()) {
2022 11 : js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
2023 11 : goto error;
2024 : }
2025 433412 : JSObject *obj = &rref.toObject();
2026 : jsid id;
2027 433412 : FETCH_ELEMENT_ID(obj, -2, id);
2028 : JSObject *obj2;
2029 : JSProperty *prop;
2030 433412 : if (!obj->lookupGeneric(cx, id, &obj2, &prop))
2031 0 : goto error;
2032 433412 : bool cond = prop != NULL;
2033 433412 : TRY_BRANCH_AFTER_COND(cond, 2);
2034 212866 : regs.sp--;
2035 212866 : regs.sp[-1].setBoolean(cond);
2036 : }
2037 212866 : END_CASE(JSOP_IN)
2038 :
2039 : BEGIN_CASE(JSOP_ITER)
2040 : {
2041 333544 : JS_ASSERT(regs.sp > regs.fp()->base());
2042 333544 : uint8_t flags = GET_UINT8(regs.pc);
2043 333544 : if (!ValueToIterator(cx, flags, ®s.sp[-1]))
2044 150 : goto error;
2045 333394 : CHECK_INTERRUPT_HANDLER();
2046 333394 : JS_ASSERT(!regs.sp[-1].isPrimitive());
2047 : }
2048 333394 : END_CASE(JSOP_ITER)
2049 :
2050 : BEGIN_CASE(JSOP_MOREITER)
2051 : {
2052 4620085 : JS_ASSERT(regs.sp - 1 >= regs.fp()->base());
2053 4620085 : JS_ASSERT(regs.sp[-1].isObject());
2054 4620085 : PUSH_NULL();
2055 : bool cond;
2056 4620085 : if (!IteratorMore(cx, ®s.sp[-2].toObject(), &cond, ®s.sp[-1]))
2057 20 : goto error;
2058 4620065 : CHECK_INTERRUPT_HANDLER();
2059 4620065 : regs.sp[-1].setBoolean(cond);
2060 : }
2061 4620065 : END_CASE(JSOP_MOREITER)
2062 :
2063 : BEGIN_CASE(JSOP_ITERNEXT)
2064 : {
2065 4422919 : Value *itervp = regs.sp - GET_INT8(regs.pc);
2066 4422919 : JS_ASSERT(itervp >= regs.fp()->base());
2067 4422919 : JS_ASSERT(itervp->isObject());
2068 4422919 : PUSH_NULL();
2069 4422919 : if (!IteratorNext(cx, &itervp->toObject(), ®s.sp[-1]))
2070 0 : goto error;
2071 : }
2072 4422919 : END_CASE(JSOP_ITERNEXT)
2073 :
2074 : BEGIN_CASE(JSOP_ENDITER)
2075 : {
2076 333967 : JS_ASSERT(regs.sp - 1 >= regs.fp()->base());
2077 333967 : bool ok = CloseIterator(cx, ®s.sp[-1].toObject());
2078 333967 : regs.sp--;
2079 333967 : if (!ok)
2080 0 : goto error;
2081 : }
2082 333967 : END_CASE(JSOP_ENDITER)
2083 :
2084 : BEGIN_CASE(JSOP_DUP)
2085 : {
2086 20885863 : JS_ASSERT(regs.sp > regs.fp()->base());
2087 20885863 : const Value &rref = regs.sp[-1];
2088 20885863 : PUSH_COPY(rref);
2089 : }
2090 20885863 : END_CASE(JSOP_DUP)
2091 :
2092 : BEGIN_CASE(JSOP_DUP2)
2093 : {
2094 8227967 : JS_ASSERT(regs.sp - 2 >= regs.fp()->base());
2095 8227967 : const Value &lref = regs.sp[-2];
2096 8227967 : const Value &rref = regs.sp[-1];
2097 8227967 : PUSH_COPY(lref);
2098 8227967 : PUSH_COPY(rref);
2099 : }
2100 8227967 : END_CASE(JSOP_DUP2)
2101 :
2102 : BEGIN_CASE(JSOP_SWAP)
2103 : {
2104 12900246 : JS_ASSERT(regs.sp - 2 >= regs.fp()->base());
2105 12900246 : Value &lref = regs.sp[-2];
2106 12900246 : Value &rref = regs.sp[-1];
2107 12900246 : lref.swap(rref);
2108 : }
2109 12900246 : END_CASE(JSOP_SWAP)
2110 :
2111 : BEGIN_CASE(JSOP_PICK)
2112 : {
2113 21859667 : unsigned i = GET_UINT8(regs.pc);
2114 21859667 : JS_ASSERT(regs.sp - (i + 1) >= regs.fp()->base());
2115 21859667 : Value lval = regs.sp[-int(i + 1)];
2116 21859667 : memmove(regs.sp - (i + 1), regs.sp - i, sizeof(Value) * i);
2117 21859667 : regs.sp[-1] = lval;
2118 : }
2119 21859667 : END_CASE(JSOP_PICK)
2120 :
2121 : BEGIN_CASE(JSOP_SETCONST)
2122 : {
2123 : PropertyName *name;
2124 166854 : LOAD_NAME(0, name);
2125 166854 : JSObject &obj = regs.fp()->varObj();
2126 166854 : const Value &ref = regs.sp[-1];
2127 166854 : if (!obj.defineProperty(cx, name, ref,
2128 : JS_PropertyStub, JS_StrictPropertyStub,
2129 166854 : JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
2130 0 : goto error;
2131 : }
2132 : }
2133 166854 : END_CASE(JSOP_SETCONST);
2134 :
2135 : #if JS_HAS_DESTRUCTURING
2136 : BEGIN_CASE(JSOP_ENUMCONSTELEM)
2137 : {
2138 4 : const Value &ref = regs.sp[-3];
2139 : JSObject *obj;
2140 4 : FETCH_OBJECT(cx, -2, obj);
2141 : jsid id;
2142 4 : FETCH_ELEMENT_ID(obj, -1, id);
2143 4 : if (!obj->defineGeneric(cx, id, ref,
2144 : JS_PropertyStub, JS_StrictPropertyStub,
2145 4 : JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
2146 0 : goto error;
2147 : }
2148 4 : regs.sp -= 3;
2149 : }
2150 4 : END_CASE(JSOP_ENUMCONSTELEM)
2151 : #endif
2152 :
2153 : BEGIN_CASE(JSOP_BINDGNAME)
2154 11706579 : PUSH_OBJECT(regs.fp()->scopeChain().global());
2155 11706579 : END_CASE(JSOP_BINDGNAME)
2156 :
2157 : BEGIN_CASE(JSOP_BINDNAME)
2158 : {
2159 : JSObject *obj;
2160 : do {
2161 : /*
2162 : * We can skip the property lookup for the global object. If the
2163 : * property does not exist anywhere on the scope chain, JSOP_SETNAME
2164 : * adds the property to the global.
2165 : *
2166 : * As a consequence of this optimization for the global object we run
2167 : * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME,
2168 : * after the interpreter evaluates the right- hand-side of the
2169 : * assignment, and not here.
2170 : *
2171 : * This should be transparent to the hooks because the script, instead
2172 : * of name = rhs, could have used global.name = rhs given a global
2173 : * object reference, which also calls the hooks only after evaluating
2174 : * the rhs. We desire such resolve hook equivalence between the two
2175 : * forms.
2176 : */
2177 244505 : obj = ®s.fp()->scopeChain();
2178 244505 : if (obj->isGlobal())
2179 189158 : break;
2180 :
2181 : PropertyName *name;
2182 55347 : LOAD_NAME(0, name);
2183 :
2184 55347 : obj = FindIdentifierBase(cx, ®s.fp()->scopeChain(), name);
2185 55347 : if (!obj)
2186 0 : goto error;
2187 : } while (0);
2188 244505 : PUSH_OBJECT(*obj);
2189 : }
2190 244505 : END_CASE(JSOP_BINDNAME)
2191 :
2192 : #define BITWISE_OP(OP) \
2193 : JS_BEGIN_MACRO \
2194 : int32_t i, j; \
2195 : if (!ToInt32(cx, regs.sp[-2], &i)) \
2196 : goto error; \
2197 : if (!ToInt32(cx, regs.sp[-1], &j)) \
2198 : goto error; \
2199 : i = i OP j; \
2200 : regs.sp--; \
2201 : regs.sp[-1].setInt32(i); \
2202 : JS_END_MACRO
2203 :
2204 : BEGIN_CASE(JSOP_BITOR)
2205 622276 : BITWISE_OP(|);
2206 622270 : END_CASE(JSOP_BITOR)
2207 :
2208 : BEGIN_CASE(JSOP_BITXOR)
2209 628981 : BITWISE_OP(^);
2210 628981 : END_CASE(JSOP_BITXOR)
2211 :
2212 : BEGIN_CASE(JSOP_BITAND)
2213 20947006 : BITWISE_OP(&);
2214 20947006 : END_CASE(JSOP_BITAND)
2215 :
2216 : #undef BITWISE_OP
2217 :
2218 : #define EQUALITY_OP(OP) \
2219 : JS_BEGIN_MACRO \
2220 : Value rval = regs.sp[-1]; \
2221 : Value lval = regs.sp[-2]; \
2222 : bool cond; \
2223 : if (!LooselyEqual(cx, lval, rval, &cond)) \
2224 : goto error; \
2225 : cond = cond OP JS_TRUE; \
2226 : TRY_BRANCH_AFTER_COND(cond, 2); \
2227 : regs.sp--; \
2228 : regs.sp[-1].setBoolean(cond); \
2229 : JS_END_MACRO
2230 :
2231 : BEGIN_CASE(JSOP_EQ)
2232 4523994 : EQUALITY_OP(==);
2233 869848 : END_CASE(JSOP_EQ)
2234 :
2235 : BEGIN_CASE(JSOP_NE)
2236 741713 : EQUALITY_OP(!=);
2237 152669 : END_CASE(JSOP_NE)
2238 :
2239 : #undef EQUALITY_OP
2240 :
2241 : #define STRICT_EQUALITY_OP(OP, COND) \
2242 : JS_BEGIN_MACRO \
2243 : const Value &rref = regs.sp[-1]; \
2244 : const Value &lref = regs.sp[-2]; \
2245 : bool equal; \
2246 : if (!StrictlyEqual(cx, lref, rref, &equal)) \
2247 : goto error; \
2248 : COND = equal OP JS_TRUE; \
2249 : regs.sp--; \
2250 : JS_END_MACRO
2251 :
2252 : BEGIN_CASE(JSOP_STRICTEQ)
2253 : {
2254 : bool cond;
2255 2019108 : STRICT_EQUALITY_OP(==, cond);
2256 2019108 : regs.sp[-1].setBoolean(cond);
2257 : }
2258 2019108 : END_CASE(JSOP_STRICTEQ)
2259 :
2260 : BEGIN_CASE(JSOP_STRICTNE)
2261 : {
2262 : bool cond;
2263 907937 : STRICT_EQUALITY_OP(!=, cond);
2264 907937 : regs.sp[-1].setBoolean(cond);
2265 : }
2266 907937 : END_CASE(JSOP_STRICTNE)
2267 :
2268 : BEGIN_CASE(JSOP_CASE)
2269 : {
2270 : bool cond;
2271 1049610 : STRICT_EQUALITY_OP(==, cond);
2272 1049610 : if (cond) {
2273 1023759 : regs.sp--;
2274 1023759 : len = GET_JUMP_OFFSET(regs.pc);
2275 1023759 : BRANCH(len);
2276 : }
2277 : }
2278 25851 : END_CASE(JSOP_CASE)
2279 :
2280 : #undef STRICT_EQUALITY_OP
2281 :
2282 : BEGIN_CASE(JSOP_LT)
2283 : {
2284 : bool cond;
2285 157465842 : const Value &lref = regs.sp[-2];
2286 157465842 : const Value &rref = regs.sp[-1];
2287 157465842 : if (!LessThanOperation(cx, lref, rref, &cond))
2288 0 : goto error;
2289 157465842 : TRY_BRANCH_AFTER_COND(cond, 2);
2290 202835 : regs.sp[-2].setBoolean(cond);
2291 202835 : regs.sp--;
2292 : }
2293 202835 : END_CASE(JSOP_LT)
2294 :
2295 : BEGIN_CASE(JSOP_LE)
2296 : {
2297 : bool cond;
2298 1765038 : const Value &lref = regs.sp[-2];
2299 1765038 : const Value &rref = regs.sp[-1];
2300 1765038 : if (!LessThanOrEqualOperation(cx, lref, rref, &cond))
2301 0 : goto error;
2302 1765038 : TRY_BRANCH_AFTER_COND(cond, 2);
2303 56322 : regs.sp[-2].setBoolean(cond);
2304 56322 : regs.sp--;
2305 : }
2306 56322 : END_CASE(JSOP_LE)
2307 :
2308 : BEGIN_CASE(JSOP_GT)
2309 : {
2310 : bool cond;
2311 1724099 : const Value &lref = regs.sp[-2];
2312 1724099 : const Value &rref = regs.sp[-1];
2313 1724099 : if (!GreaterThanOperation(cx, lref, rref, &cond))
2314 7 : goto error;
2315 1724092 : TRY_BRANCH_AFTER_COND(cond, 2);
2316 101780 : regs.sp[-2].setBoolean(cond);
2317 101780 : regs.sp--;
2318 : }
2319 101780 : END_CASE(JSOP_GT)
2320 :
2321 : BEGIN_CASE(JSOP_GE)
2322 : {
2323 : bool cond;
2324 6230173 : const Value &lref = regs.sp[-2];
2325 6230173 : const Value &rref = regs.sp[-1];
2326 6230173 : if (!GreaterThanOrEqualOperation(cx, lref, rref, &cond))
2327 0 : goto error;
2328 6230173 : TRY_BRANCH_AFTER_COND(cond, 2);
2329 82206 : regs.sp[-2].setBoolean(cond);
2330 82206 : regs.sp--;
2331 : }
2332 82206 : END_CASE(JSOP_GE)
2333 :
2334 : #define SIGNED_SHIFT_OP(OP) \
2335 : JS_BEGIN_MACRO \
2336 : int32_t i, j; \
2337 : if (!ToInt32(cx, regs.sp[-2], &i)) \
2338 : goto error; \
2339 : if (!ToInt32(cx, regs.sp[-1], &j)) \
2340 : goto error; \
2341 : i = i OP (j & 31); \
2342 : regs.sp--; \
2343 : regs.sp[-1].setInt32(i); \
2344 : JS_END_MACRO
2345 :
2346 : BEGIN_CASE(JSOP_LSH)
2347 8257786 : SIGNED_SHIFT_OP(<<);
2348 8257786 : END_CASE(JSOP_LSH)
2349 :
2350 : BEGIN_CASE(JSOP_RSH)
2351 18669801 : SIGNED_SHIFT_OP(>>);
2352 18669801 : END_CASE(JSOP_RSH)
2353 :
2354 : #undef SIGNED_SHIFT_OP
2355 :
2356 : BEGIN_CASE(JSOP_URSH)
2357 : {
2358 : uint32_t u;
2359 140311 : if (!ToUint32(cx, regs.sp[-2], &u))
2360 0 : goto error;
2361 : int32_t j;
2362 140311 : if (!ToInt32(cx, regs.sp[-1], &j))
2363 0 : goto error;
2364 :
2365 140311 : u >>= (j & 31);
2366 :
2367 140311 : regs.sp--;
2368 140311 : if (!regs.sp[-1].setNumber(uint32_t(u)))
2369 388 : TypeScript::MonitorOverflow(cx, script, regs.pc);
2370 : }
2371 140311 : END_CASE(JSOP_URSH)
2372 :
2373 : BEGIN_CASE(JSOP_ADD)
2374 : {
2375 59135463 : Value lval = regs.sp[-2];
2376 59135463 : Value rval = regs.sp[-1];
2377 59135463 : if (!AddOperation(cx, lval, rval, ®s.sp[-2]))
2378 40 : goto error;
2379 59135423 : regs.sp--;
2380 : }
2381 59135423 : END_CASE(JSOP_ADD)
2382 :
2383 : BEGIN_CASE(JSOP_SUB)
2384 : {
2385 6633500 : Value lval = regs.sp[-2];
2386 6633500 : Value rval = regs.sp[-1];
2387 6633500 : if (!SubOperation(cx, lval, rval, ®s.sp[-2]))
2388 0 : goto error;
2389 6633500 : regs.sp--;
2390 : }
2391 6633500 : END_CASE(JSOP_SUB)
2392 :
2393 : BEGIN_CASE(JSOP_MUL)
2394 : {
2395 24972227 : Value lval = regs.sp[-2];
2396 24972227 : Value rval = regs.sp[-1];
2397 24972227 : if (!MulOperation(cx, lval, rval, ®s.sp[-2]))
2398 0 : goto error;
2399 24972227 : regs.sp--;
2400 : }
2401 24972227 : END_CASE(JSOP_MUL)
2402 :
2403 : BEGIN_CASE(JSOP_DIV)
2404 : {
2405 1396383 : Value lval = regs.sp[-2];
2406 1396383 : Value rval = regs.sp[-1];
2407 1396383 : if (!DivOperation(cx, lval, rval, ®s.sp[-2]))
2408 0 : goto error;
2409 1396383 : regs.sp--;
2410 : }
2411 1396383 : END_CASE(JSOP_DIV)
2412 :
2413 : BEGIN_CASE(JSOP_MOD)
2414 : {
2415 461322 : Value lval = regs.sp[-2];
2416 461322 : Value rval = regs.sp[-1];
2417 461322 : if (!ModOperation(cx, lval, rval, ®s.sp[-2]))
2418 0 : goto error;
2419 461322 : regs.sp--;
2420 : }
2421 461322 : END_CASE(JSOP_MOD)
2422 :
2423 : BEGIN_CASE(JSOP_NOT)
2424 : {
2425 : Value *_;
2426 : bool cond;
2427 3320629 : POP_BOOLEAN(cx, _, cond);
2428 3320629 : PUSH_BOOLEAN(!cond);
2429 : }
2430 3320629 : END_CASE(JSOP_NOT)
2431 :
2432 : BEGIN_CASE(JSOP_BITNOT)
2433 : {
2434 : int32_t i;
2435 867860 : if (!ToInt32(cx, regs.sp[-1], &i))
2436 0 : goto error;
2437 867860 : i = ~i;
2438 867860 : regs.sp[-1].setInt32(i);
2439 : }
2440 867860 : END_CASE(JSOP_BITNOT)
2441 :
2442 : BEGIN_CASE(JSOP_NEG)
2443 : {
2444 : /*
2445 : * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
2446 : * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
2447 : * results, -0.0 or INT32_MAX + 1, are double values.
2448 : */
2449 745802 : Value ref = regs.sp[-1];
2450 : int32_t i;
2451 745802 : if (ref.isInt32() && (i = ref.toInt32()) != 0 && i != INT32_MIN) {
2452 268205 : i = -i;
2453 268205 : regs.sp[-1].setInt32(i);
2454 : } else {
2455 : double d;
2456 477597 : if (!ToNumber(cx, regs.sp[-1], &d))
2457 0 : goto error;
2458 477597 : d = -d;
2459 477597 : if (!regs.sp[-1].setNumber(d) && !ref.isDouble())
2460 4139 : TypeScript::MonitorOverflow(cx, script, regs.pc);
2461 : }
2462 : }
2463 745802 : END_CASE(JSOP_NEG)
2464 :
2465 : BEGIN_CASE(JSOP_POS)
2466 11004739 : if (!ToNumber(cx, ®s.sp[-1]))
2467 0 : goto error;
2468 11004739 : if (!regs.sp[-1].isInt32())
2469 2573 : TypeScript::MonitorOverflow(cx, script, regs.pc);
2470 11004739 : END_CASE(JSOP_POS)
2471 :
2472 : BEGIN_CASE(JSOP_DELNAME)
2473 : {
2474 : PropertyName *name;
2475 326 : LOAD_NAME(0, name);
2476 : JSObject *obj, *obj2;
2477 : JSProperty *prop;
2478 326 : if (!FindProperty(cx, name, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop))
2479 0 : goto error;
2480 :
2481 : /* Strict mode code should never contain JSOP_DELNAME opcodes. */
2482 326 : JS_ASSERT(!script->strictModeCode);
2483 :
2484 : /* ECMA says to return true if name is undefined or inherited. */
2485 326 : PUSH_BOOLEAN(true);
2486 326 : if (prop) {
2487 243 : if (!obj->deleteProperty(cx, name, ®s.sp[-1], false))
2488 0 : goto error;
2489 : }
2490 : }
2491 326 : END_CASE(JSOP_DELNAME)
2492 :
2493 : BEGIN_CASE(JSOP_DELPROP)
2494 : {
2495 : PropertyName *name;
2496 26674 : LOAD_NAME(0, name);
2497 :
2498 : JSObject *obj;
2499 26674 : FETCH_OBJECT(cx, -1, obj);
2500 :
2501 : Value rval;
2502 26674 : if (!obj->deleteProperty(cx, name, &rval, script->strictModeCode))
2503 0 : goto error;
2504 :
2505 26674 : regs.sp[-1] = rval;
2506 : }
2507 26674 : END_CASE(JSOP_DELPROP)
2508 :
2509 : BEGIN_CASE(JSOP_DELELEM)
2510 : {
2511 : /* Fetch the left part and resolve it to a non-null object. */
2512 : JSObject *obj;
2513 25151 : FETCH_OBJECT(cx, -2, obj);
2514 :
2515 25151 : const Value &propval = regs.sp[-1];
2516 25151 : Value &rval = regs.sp[-2];
2517 :
2518 25151 : if (!obj->deleteByValue(cx, propval, &rval, script->strictModeCode))
2519 7 : goto error;
2520 :
2521 25144 : regs.sp--;
2522 : }
2523 25144 : END_CASE(JSOP_DELELEM)
2524 :
2525 : BEGIN_CASE(JSOP_TOID)
2526 : {
2527 : /*
2528 : * Increment or decrement requires use to lookup the same property twice, but we need to avoid
2529 : * the oberservable stringification the second time.
2530 : * There must be an object value below the id, which will not be popped
2531 : * but is necessary in interning the id for XML.
2532 : */
2533 6947632 : Value objval = regs.sp[-2];
2534 6947632 : Value idval = regs.sp[-1];
2535 6947632 : if (!ToIdOperation(cx, objval, idval, ®s.sp[-1]))
2536 0 : goto error;
2537 : }
2538 6947632 : END_CASE(JSOP_TOID)
2539 :
2540 : BEGIN_CASE(JSOP_TYPEOFEXPR)
2541 : BEGIN_CASE(JSOP_TYPEOF)
2542 : {
2543 233980 : const Value &ref = regs.sp[-1];
2544 233980 : JSType type = JS_TypeOfValue(cx, ref);
2545 233980 : regs.sp[-1].setString(rt->atomState.typeAtoms[type]);
2546 : }
2547 233980 : END_CASE(JSOP_TYPEOF)
2548 :
2549 : BEGIN_CASE(JSOP_VOID)
2550 1222 : regs.sp[-1].setUndefined();
2551 1222 : END_CASE(JSOP_VOID)
2552 :
2553 : BEGIN_CASE(JSOP_INCELEM)
2554 : BEGIN_CASE(JSOP_DECELEM)
2555 : BEGIN_CASE(JSOP_ELEMINC)
2556 : BEGIN_CASE(JSOP_ELEMDEC)
2557 : /* No-op */
2558 6947632 : END_CASE(JSOP_INCELEM)
2559 :
2560 : BEGIN_CASE(JSOP_INCPROP)
2561 : BEGIN_CASE(JSOP_DECPROP)
2562 : BEGIN_CASE(JSOP_PROPINC)
2563 : BEGIN_CASE(JSOP_PROPDEC)
2564 : BEGIN_CASE(JSOP_INCNAME)
2565 : BEGIN_CASE(JSOP_DECNAME)
2566 : BEGIN_CASE(JSOP_NAMEINC)
2567 : BEGIN_CASE(JSOP_NAMEDEC)
2568 : BEGIN_CASE(JSOP_INCGNAME)
2569 : BEGIN_CASE(JSOP_DECGNAME)
2570 : BEGIN_CASE(JSOP_GNAMEINC)
2571 : BEGIN_CASE(JSOP_GNAMEDEC)
2572 : /* No-op */
2573 4053058 : END_CASE(JSOP_INCPROP)
2574 :
2575 : BEGIN_CASE(JSOP_DECARG)
2576 : BEGIN_CASE(JSOP_ARGDEC)
2577 : BEGIN_CASE(JSOP_INCARG)
2578 : BEGIN_CASE(JSOP_ARGINC)
2579 : {
2580 13820265 : Value &arg = regs.fp()->formalArg(GET_ARGNO(regs.pc));
2581 13820265 : if (!DoIncDec(cx, script, regs.pc, arg, &arg, ®s.sp[0]))
2582 0 : goto error;
2583 13820265 : regs.sp++;
2584 : }
2585 13820265 : END_CASE(JSOP_ARGINC);
2586 :
2587 : BEGIN_CASE(JSOP_DECLOCAL)
2588 : BEGIN_CASE(JSOP_LOCALDEC)
2589 : BEGIN_CASE(JSOP_INCLOCAL)
2590 : BEGIN_CASE(JSOP_LOCALINC)
2591 : {
2592 290544098 : Value &local = regs.fp()->localSlot(GET_SLOTNO(regs.pc));
2593 290544098 : if (!DoIncDec(cx, script, regs.pc, local, &local, ®s.sp[0]))
2594 0 : goto error;
2595 290544098 : regs.sp++;
2596 : }
2597 290544098 : END_CASE(JSOP_LOCALINC)
2598 :
2599 : BEGIN_CASE(JSOP_THIS)
2600 12027110 : if (!ComputeThis(cx, regs.fp()))
2601 0 : goto error;
2602 12027110 : PUSH_COPY(regs.fp()->thisValue());
2603 12027110 : END_CASE(JSOP_THIS)
2604 :
2605 : BEGIN_CASE(JSOP_GETPROP)
2606 : BEGIN_CASE(JSOP_GETXPROP)
2607 : BEGIN_CASE(JSOP_LENGTH)
2608 : BEGIN_CASE(JSOP_CALLPROP)
2609 : {
2610 : Value rval;
2611 85388868 : if (!GetPropertyOperation(cx, regs.pc, regs.sp[-1], &rval))
2612 725 : goto error;
2613 :
2614 85388143 : TypeScript::Monitor(cx, script, regs.pc, rval);
2615 :
2616 85388143 : regs.sp[-1] = rval;
2617 85388143 : assertSameCompartment(cx, regs.sp[-1]);
2618 : }
2619 85388143 : END_CASE(JSOP_GETPROP)
2620 :
2621 : BEGIN_CASE(JSOP_SETGNAME)
2622 : BEGIN_CASE(JSOP_SETNAME)
2623 : BEGIN_CASE(JSOP_SETPROP)
2624 : BEGIN_CASE(JSOP_SETMETHOD)
2625 : {
2626 17558796 : const Value &rval = regs.sp[-1];
2627 17558796 : const Value &lval = regs.sp[-2];
2628 :
2629 17558796 : if (!SetPropertyOperation(cx, regs.pc, lval, rval))
2630 378 : goto error;
2631 :
2632 17558418 : regs.sp[-2] = regs.sp[-1];
2633 17558418 : regs.sp--;
2634 : }
2635 17558418 : END_CASE(JSOP_SETPROP)
2636 :
2637 : BEGIN_CASE(JSOP_GETELEM)
2638 : BEGIN_CASE(JSOP_CALLELEM)
2639 : {
2640 35403349 : Value &lref = regs.sp[-2];
2641 35403349 : Value &rref = regs.sp[-1];
2642 35403349 : if (!GetElementOperation(cx, op, lref, rref, ®s.sp[-2]))
2643 371 : goto error;
2644 35402978 : TypeScript::Monitor(cx, script, regs.pc, regs.sp[-2]);
2645 35402978 : regs.sp--;
2646 : }
2647 35402978 : END_CASE(JSOP_GETELEM)
2648 :
2649 : BEGIN_CASE(JSOP_SETELEM)
2650 : {
2651 : JSObject *obj;
2652 20220102 : FETCH_OBJECT(cx, -3, obj);
2653 : jsid id;
2654 20220091 : FETCH_ELEMENT_ID(obj, -2, id);
2655 20220091 : Value &value = regs.sp[-1];
2656 20220091 : if (!SetObjectElementOperation(cx, obj, id, value, script->strictModeCode))
2657 471 : goto error;
2658 20219620 : regs.sp[-3] = value;
2659 20219620 : regs.sp -= 2;
2660 : }
2661 20219620 : END_CASE(JSOP_SETELEM)
2662 :
2663 : BEGIN_CASE(JSOP_ENUMELEM)
2664 : {
2665 : /* Funky: the value to set is under the [obj, id] pair. */
2666 : JSObject *obj;
2667 6167 : FETCH_OBJECT(cx, -2, obj);
2668 : jsid id;
2669 6167 : FETCH_ELEMENT_ID(obj, -1, id);
2670 6167 : Value rval = regs.sp[-3];
2671 6167 : if (!obj->setGeneric(cx, id, &rval, script->strictModeCode))
2672 0 : goto error;
2673 6167 : regs.sp -= 3;
2674 : }
2675 6167 : END_CASE(JSOP_ENUMELEM)
2676 :
2677 : BEGIN_CASE(JSOP_EVAL)
2678 : {
2679 22529 : CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
2680 22529 : if (IsBuiltinEvalForScope(®s.fp()->scopeChain(), args.calleev())) {
2681 22508 : if (!DirectEval(cx, args))
2682 319 : goto error;
2683 : } else {
2684 21 : if (!InvokeKernel(cx, args))
2685 0 : goto error;
2686 : }
2687 22210 : CHECK_INTERRUPT_HANDLER();
2688 22210 : regs.sp = args.spAfterCall();
2689 22210 : TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
2690 : }
2691 22210 : END_CASE(JSOP_EVAL)
2692 :
2693 : BEGIN_CASE(JSOP_NEW)
2694 : BEGIN_CASE(JSOP_CALL)
2695 : BEGIN_CASE(JSOP_FUNCALL)
2696 : BEGIN_CASE(JSOP_FUNAPPLY)
2697 : {
2698 22930517 : CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
2699 22930517 : JS_ASSERT(args.base() >= regs.fp()->base());
2700 :
2701 22930517 : bool construct = (*regs.pc == JSOP_NEW);
2702 :
2703 : JSFunction *fun;
2704 :
2705 : /* Don't bother trying to fast-path calls to scripted non-constructors. */
2706 22930517 : if (!IsFunctionObject(args.calleev(), &fun) || !fun->isInterpretedConstructor()) {
2707 11045280 : if (construct) {
2708 174653 : if (!InvokeConstructorKernel(cx, args))
2709 575 : goto error;
2710 : } else {
2711 10870627 : if (!InvokeKernel(cx, args))
2712 48749 : goto error;
2713 : }
2714 10995956 : Value *newsp = args.spAfterCall();
2715 10995956 : TypeScript::Monitor(cx, script, regs.pc, newsp[-1]);
2716 10995956 : regs.sp = newsp;
2717 10995956 : CHECK_INTERRUPT_HANDLER();
2718 10995956 : len = JSOP_CALL_LENGTH;
2719 10995956 : DO_NEXT_OP(len);
2720 : }
2721 :
2722 11885237 : TypeMonitorCall(cx, args, construct);
2723 :
2724 11885237 : InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
2725 :
2726 11885237 : JSScript *newScript = fun->script();
2727 :
2728 11885237 : if (newScript->compileAndGo && newScript->hasClearedGlobal()) {
2729 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
2730 0 : goto error;
2731 : }
2732 :
2733 11885237 : if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, newScript, initial))
2734 52 : goto error;
2735 :
2736 11885185 : RESTORE_INTERP_VARS();
2737 :
2738 11885185 : if (!regs.fp()->functionPrologue(cx))
2739 0 : goto error;
2740 :
2741 11885185 : RESET_USE_METHODJIT();
2742 :
2743 11885185 : bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
2744 :
2745 : #ifdef JS_METHODJIT
2746 11885185 : if (!newType) {
2747 : /* Try to ensure methods are method JIT'd. */
2748 : mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
2749 : ? mjit::CompileRequest_Interpreter
2750 11885185 : : mjit::CompileRequest_JIT;
2751 : mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, script->code,
2752 11885185 : construct, request);
2753 11885185 : if (status == mjit::Compile_Error)
2754 0 : goto error;
2755 11885185 : if (status == mjit::Compile_Okay) {
2756 632092 : mjit::JaegerStatus status = mjit::JaegerShot(cx, true);
2757 632092 : CHECK_PARTIAL_METHODJIT(status);
2758 619865 : interpReturnOK = mjit::JaegerStatusToSuccess(status);
2759 619865 : CHECK_INTERRUPT_HANDLER();
2760 619865 : goto jit_return;
2761 : }
2762 : }
2763 : #endif
2764 :
2765 11253093 : if (!ScriptPrologue(cx, regs.fp(), newType))
2766 0 : goto error;
2767 :
2768 11253093 : if (cx->compartment->debugMode()) {
2769 1361549 : switch (ScriptDebugPrologue(cx, regs.fp())) {
2770 : case JSTRAP_CONTINUE:
2771 : break;
2772 : case JSTRAP_RETURN:
2773 15 : interpReturnOK = true;
2774 15 : goto forced_return;
2775 : case JSTRAP_THROW:
2776 : case JSTRAP_ERROR:
2777 5 : goto error;
2778 : default:
2779 0 : JS_NOT_REACHED("bad ScriptDebugPrologue status");
2780 : }
2781 : }
2782 :
2783 11253073 : CHECK_INTERRUPT_HANDLER();
2784 :
2785 : /* Load first op and dispatch it (safe since JSOP_STOP). */
2786 11253073 : op = (JSOp) *regs.pc;
2787 11253073 : DO_OP();
2788 : }
2789 :
2790 : BEGIN_CASE(JSOP_SETCALL)
2791 : {
2792 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
2793 0 : goto error;
2794 : }
2795 : END_CASE(JSOP_SETCALL)
2796 :
2797 : BEGIN_CASE(JSOP_IMPLICITTHIS)
2798 : {
2799 : PropertyName *name;
2800 1267469 : LOAD_NAME(0, name);
2801 :
2802 : JSObject *obj, *obj2;
2803 : JSProperty *prop;
2804 1267469 : if (!FindPropertyHelper(cx, name, false, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop))
2805 0 : goto error;
2806 :
2807 : Value v;
2808 1267469 : if (!ComputeImplicitThis(cx, obj, &v))
2809 0 : goto error;
2810 1267469 : PUSH_COPY(v);
2811 : }
2812 1267469 : END_CASE(JSOP_IMPLICITTHIS)
2813 :
2814 : BEGIN_CASE(JSOP_GETGNAME)
2815 : BEGIN_CASE(JSOP_CALLGNAME)
2816 : BEGIN_CASE(JSOP_NAME)
2817 : BEGIN_CASE(JSOP_CALLNAME)
2818 : {
2819 : Value rval;
2820 51720447 : if (!NameOperation(cx, regs.pc, &rval))
2821 1314 : goto error;
2822 :
2823 51719133 : PUSH_COPY(rval);
2824 51719133 : TypeScript::Monitor(cx, script, regs.pc, rval);
2825 : }
2826 51719133 : END_CASE(JSOP_NAME)
2827 :
2828 : BEGIN_CASE(JSOP_UINT16)
2829 27296074 : PUSH_INT32((int32_t) GET_UINT16(regs.pc));
2830 27296074 : END_CASE(JSOP_UINT16)
2831 :
2832 : BEGIN_CASE(JSOP_UINT24)
2833 2691200 : PUSH_INT32((int32_t) GET_UINT24(regs.pc));
2834 2691200 : END_CASE(JSOP_UINT24)
2835 :
2836 : BEGIN_CASE(JSOP_INT8)
2837 40770252 : PUSH_INT32(GET_INT8(regs.pc));
2838 40770252 : END_CASE(JSOP_INT8)
2839 :
2840 : BEGIN_CASE(JSOP_INT32)
2841 4617769 : PUSH_INT32(GET_INT32(regs.pc));
2842 4617769 : END_CASE(JSOP_INT32)
2843 :
2844 : BEGIN_CASE(JSOP_DOUBLE)
2845 : {
2846 : double dbl;
2847 1378550 : LOAD_DOUBLE(0, dbl);
2848 1378550 : PUSH_DOUBLE(dbl);
2849 : }
2850 1378550 : END_CASE(JSOP_DOUBLE)
2851 :
2852 : BEGIN_CASE(JSOP_STRING)
2853 : {
2854 : JSAtom *atom;
2855 13762571 : LOAD_ATOM(0, atom);
2856 13762571 : PUSH_STRING(atom);
2857 : }
2858 13762571 : END_CASE(JSOP_STRING)
2859 :
2860 : BEGIN_CASE(JSOP_OBJECT)
2861 : {
2862 6105 : PUSH_OBJECT(*script->getObject(GET_UINT32_INDEX(regs.pc)));
2863 : }
2864 6105 : END_CASE(JSOP_OBJECT)
2865 :
2866 : BEGIN_CASE(JSOP_REGEXP)
2867 : {
2868 : /*
2869 : * Push a regexp object cloned from the regexp literal object mapped by the
2870 : * bytecode at pc.
2871 : */
2872 203727 : uint32_t index = GET_UINT32_INDEX(regs.pc);
2873 203727 : JSObject *proto = regs.fp()->scopeChain().global().getOrCreateRegExpPrototype(cx);
2874 203727 : if (!proto)
2875 0 : goto error;
2876 203727 : JSObject *obj = CloneRegExpObject(cx, script->getRegExp(index), proto);
2877 203727 : if (!obj)
2878 0 : goto error;
2879 203727 : PUSH_OBJECT(*obj);
2880 : }
2881 203727 : END_CASE(JSOP_REGEXP)
2882 :
2883 : BEGIN_CASE(JSOP_ZERO)
2884 15927047 : PUSH_INT32(0);
2885 15927047 : END_CASE(JSOP_ZERO)
2886 :
2887 : BEGIN_CASE(JSOP_ONE)
2888 23694457 : PUSH_INT32(1);
2889 23694457 : END_CASE(JSOP_ONE)
2890 :
2891 : BEGIN_CASE(JSOP_NULL)
2892 3024210 : PUSH_NULL();
2893 3024210 : END_CASE(JSOP_NULL)
2894 :
2895 : BEGIN_CASE(JSOP_FALSE)
2896 2099463 : PUSH_BOOLEAN(false);
2897 2099463 : END_CASE(JSOP_FALSE)
2898 :
2899 : BEGIN_CASE(JSOP_TRUE)
2900 2163384 : PUSH_BOOLEAN(true);
2901 2163384 : END_CASE(JSOP_TRUE)
2902 :
2903 : {
2904 : BEGIN_CASE(JSOP_TABLESWITCH)
2905 : {
2906 2662 : jsbytecode *pc2 = regs.pc;
2907 2662 : len = GET_JUMP_OFFSET(pc2);
2908 :
2909 : /*
2910 : * ECMAv2+ forbids conversion of discriminant, so we will skip to the
2911 : * default case if the discriminant isn't already an int jsval. (This
2912 : * opcode is emitted only for dense int-domain switches.)
2913 : */
2914 2662 : const Value &rref = *--regs.sp;
2915 : int32_t i;
2916 2662 : if (rref.isInt32()) {
2917 2137 : i = rref.toInt32();
2918 : } else {
2919 : double d;
2920 : /* Don't use JSDOUBLE_IS_INT32; treat -0 (double) as 0. */
2921 525 : if (!rref.isDouble() || (d = rref.toDouble()) != (i = int32_t(rref.toDouble())))
2922 481 : DO_NEXT_OP(len);
2923 : }
2924 :
2925 2181 : pc2 += JUMP_OFFSET_LEN;
2926 2181 : int32_t low = GET_JUMP_OFFSET(pc2);
2927 2181 : pc2 += JUMP_OFFSET_LEN;
2928 2181 : int32_t high = GET_JUMP_OFFSET(pc2);
2929 :
2930 2181 : i -= low;
2931 2181 : if ((uint32_t)i < (uint32_t)(high - low + 1)) {
2932 1579 : pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
2933 1579 : int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
2934 1579 : if (off)
2935 1569 : len = off;
2936 : }
2937 : }
2938 2181 : END_VARLEN_CASE
2939 : }
2940 :
2941 : {
2942 : BEGIN_CASE(JSOP_LOOKUPSWITCH)
2943 : {
2944 : int32_t off;
2945 26932 : off = JUMP_OFFSET_LEN;
2946 :
2947 : /*
2948 : * JSOP_LOOKUPSWITCH are never used if any atom index in it would exceed
2949 : * 64K limit.
2950 : */
2951 26932 : JS_ASSERT(atoms == script->atoms);
2952 26932 : jsbytecode *pc2 = regs.pc;
2953 :
2954 26932 : Value lval = regs.sp[-1];
2955 26932 : regs.sp--;
2956 :
2957 : int npairs;
2958 26932 : if (!lval.isPrimitive())
2959 0 : goto end_lookup_switch;
2960 :
2961 26932 : pc2 += off;
2962 26932 : npairs = GET_UINT16(pc2);
2963 26932 : pc2 += UINT16_LEN;
2964 26932 : JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
2965 :
2966 : bool match;
2967 : #define SEARCH_PAIRS(MATCH_CODE) \
2968 : for (;;) { \
2969 : Value rval = script->getConst(GET_UINT32_INDEX(pc2)); \
2970 : MATCH_CODE \
2971 : pc2 += UINT32_INDEX_LEN; \
2972 : if (match) \
2973 : break; \
2974 : pc2 += off; \
2975 : if (--npairs == 0) { \
2976 : pc2 = regs.pc; \
2977 : break; \
2978 : } \
2979 : }
2980 :
2981 26932 : if (lval.isString()) {
2982 26376 : JSLinearString *str = lval.toString()->ensureLinear(cx);
2983 26376 : if (!str)
2984 0 : goto error;
2985 : JSLinearString *str2;
2986 92875 : SEARCH_PAIRS(
2987 : match = (rval.isString() &&
2988 : ((str2 = &rval.toString()->asLinear()) == str ||
2989 : EqualStrings(str2, str)));
2990 : )
2991 556 : } else if (lval.isNumber()) {
2992 225 : double ldbl = lval.toNumber();
2993 574 : SEARCH_PAIRS(
2994 : match = rval.isNumber() && ldbl == rval.toNumber();
2995 : )
2996 : } else {
2997 655 : SEARCH_PAIRS(
2998 : match = (lval == rval);
2999 : )
3000 : }
3001 : #undef SEARCH_PAIRS
3002 :
3003 : end_lookup_switch:
3004 26932 : len = GET_JUMP_OFFSET(pc2);
3005 : }
3006 26932 : END_VARLEN_CASE
3007 : }
3008 :
3009 : BEGIN_CASE(JSOP_ARGUMENTS)
3010 : {
3011 : Value rval;
3012 267499 : if (cx->typeInferenceEnabled() && !script->strictModeCode) {
3013 117221 : if (!script->ensureRanInference(cx))
3014 0 : goto error;
3015 117221 : if (script->createdArgs) {
3016 90295 : ArgumentsObject *arguments = js_GetArgsObject(cx, regs.fp());
3017 90295 : if (!arguments)
3018 0 : goto error;
3019 90295 : rval = ObjectValue(*arguments);
3020 : } else {
3021 26926 : rval = MagicValue(JS_LAZY_ARGUMENTS);
3022 : }
3023 : } else {
3024 150278 : ArgumentsObject *arguments = js_GetArgsObject(cx, regs.fp());
3025 150278 : if (!arguments)
3026 0 : goto error;
3027 150278 : rval = ObjectValue(*arguments);
3028 : }
3029 267499 : PUSH_COPY(rval);
3030 : }
3031 267499 : END_CASE(JSOP_ARGUMENTS)
3032 :
3033 : BEGIN_CASE(JSOP_GETARG)
3034 : BEGIN_CASE(JSOP_CALLARG)
3035 57892163 : PUSH_COPY(regs.fp()->formalArg(GET_ARGNO(regs.pc)));
3036 57892163 : END_CASE(JSOP_GETARG)
3037 :
3038 : BEGIN_CASE(JSOP_SETARG)
3039 5244700 : regs.fp()->formalArg(GET_ARGNO(regs.pc)) = regs.sp[-1];
3040 5244700 : END_CASE(JSOP_SETARG)
3041 :
3042 : BEGIN_CASE(JSOP_GETLOCAL)
3043 : BEGIN_CASE(JSOP_CALLLOCAL)
3044 452151302 : PUSH_COPY_SKIP_CHECK(regs.fp()->localSlot(GET_SLOTNO(regs.pc)));
3045 :
3046 : /*
3047 : * Skip the same-compartment assertion if the local will be immediately
3048 : * popped. We do not guarantee sync for dead locals when coming in from the
3049 : * method JIT, and a GETLOCAL followed by POP is not considered to be
3050 : * a use of the variable.
3051 : */
3052 452151302 : if (regs.pc[JSOP_GETLOCAL_LENGTH] != JSOP_POP)
3053 447953188 : assertSameCompartment(cx, regs.sp[-1]);
3054 452151302 : END_CASE(JSOP_GETLOCAL)
3055 :
3056 : BEGIN_CASE(JSOP_SETLOCAL)
3057 44349352 : regs.fp()->localSlot(GET_SLOTNO(regs.pc)) = regs.sp[-1];
3058 44349352 : END_CASE(JSOP_SETLOCAL)
3059 :
3060 : BEGIN_CASE(JSOP_GETFCSLOT)
3061 : BEGIN_CASE(JSOP_CALLFCSLOT)
3062 : {
3063 702753 : JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
3064 702753 : unsigned index = GET_UINT16(regs.pc);
3065 702753 : JSObject *obj = &argv[-2].toObject();
3066 :
3067 702753 : PUSH_COPY(obj->toFunction()->getFlatClosureUpvar(index));
3068 702753 : TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
3069 : }
3070 702753 : END_CASE(JSOP_GETFCSLOT)
3071 :
3072 : BEGIN_CASE(JSOP_DEFCONST)
3073 : BEGIN_CASE(JSOP_DEFVAR)
3074 : {
3075 286242 : PropertyName *dn = atoms[GET_UINT32_INDEX(regs.pc)]->asPropertyName();
3076 :
3077 : /* ES5 10.5 step 8 (with subsequent errata). */
3078 286242 : unsigned attrs = JSPROP_ENUMERATE;
3079 286242 : if (!regs.fp()->isEvalFrame())
3080 285105 : attrs |= JSPROP_PERMANENT;
3081 286242 : if (op == JSOP_DEFCONST)
3082 166855 : attrs |= JSPROP_READONLY;
3083 :
3084 : /* Step 8b. */
3085 286242 : JSObject &obj = regs.fp()->varObj();
3086 :
3087 286242 : if (!DefVarOrConstOperation(cx, obj, dn, attrs))
3088 0 : goto error;
3089 : }
3090 286242 : END_CASE(JSOP_DEFVAR)
3091 :
3092 : BEGIN_CASE(JSOP_DEFFUN)
3093 : {
3094 : /*
3095 : * A top-level function defined in Global or Eval code (see ECMA-262
3096 : * Ed. 3), or else a SpiderMonkey extension: a named function statement in
3097 : * a compound statement (not at the top statement level of global code, or
3098 : * at the top level of a function body).
3099 : */
3100 185300 : JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc));
3101 185300 : JSObject *obj = fun;
3102 :
3103 : JSObject *obj2;
3104 185300 : if (fun->isNullClosure()) {
3105 : /*
3106 : * Even a null closure needs a parent for principals finding.
3107 : * FIXME: bug 476950, although debugger users may also demand some kind
3108 : * of scope link for debugger-assisted eval-in-frame.
3109 : */
3110 172941 : obj2 = ®s.fp()->scopeChain();
3111 : } else {
3112 12359 : JS_ASSERT(!fun->isFlatClosure());
3113 :
3114 12359 : obj2 = GetScopeChain(cx, regs.fp());
3115 12359 : if (!obj2)
3116 0 : goto error;
3117 : }
3118 :
3119 : /*
3120 : * If static link is not current scope, clone fun's object to link to the
3121 : * current scope via parent. We do this to enable sharing of compiled
3122 : * functions among multiple equivalent scopes, amortizing the cost of
3123 : * compilation over a number of executions. Examples include XUL scripts
3124 : * and event handlers shared among Firefox or other Mozilla app chrome
3125 : * windows, and user-defined JS functions precompiled and then shared among
3126 : * requests in server-side JS.
3127 : */
3128 185300 : if (obj->toFunction()->environment() != obj2) {
3129 183458 : obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2);
3130 183458 : if (!obj)
3131 0 : goto error;
3132 183458 : JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
3133 : }
3134 :
3135 : /*
3136 : * ECMA requires functions defined when entering Eval code to be
3137 : * impermanent.
3138 : */
3139 185300 : unsigned attrs = regs.fp()->isEvalFrame()
3140 : ? JSPROP_ENUMERATE
3141 185300 : : JSPROP_ENUMERATE | JSPROP_PERMANENT;
3142 :
3143 : /*
3144 : * We define the function as a property of the variable object and not the
3145 : * current scope chain even for the case of function expression statements
3146 : * and functions defined by eval inside let or with blocks.
3147 : */
3148 185300 : JSObject *parent = ®s.fp()->varObj();
3149 :
3150 : /* ES5 10.5 (NB: with subsequent errata). */
3151 185300 : PropertyName *name = fun->atom->asPropertyName();
3152 185300 : JSProperty *prop = NULL;
3153 : JSObject *pobj;
3154 185300 : if (!parent->lookupProperty(cx, name, &pobj, &prop))
3155 0 : goto error;
3156 :
3157 185300 : Value rval = ObjectValue(*obj);
3158 :
3159 : do {
3160 : /* Steps 5d, 5f. */
3161 185300 : if (!prop || pobj != parent) {
3162 184289 : if (!parent->defineProperty(cx, name, rval,
3163 184289 : JS_PropertyStub, JS_StrictPropertyStub, attrs))
3164 : {
3165 0 : goto error;
3166 : }
3167 184289 : break;
3168 : }
3169 :
3170 : /* Step 5e. */
3171 1011 : JS_ASSERT(parent->isNative());
3172 1011 : Shape *shape = reinterpret_cast<Shape *>(prop);
3173 1011 : if (parent->isGlobal()) {
3174 721 : if (shape->configurable()) {
3175 201 : if (!parent->defineProperty(cx, name, rval,
3176 201 : JS_PropertyStub, JS_StrictPropertyStub, attrs))
3177 : {
3178 0 : goto error;
3179 : }
3180 201 : break;
3181 : }
3182 :
3183 520 : if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
3184 10 : JSAutoByteString bytes;
3185 5 : if (js_AtomToPrintableString(cx, name, &bytes)) {
3186 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3187 5 : JSMSG_CANT_REDEFINE_PROP, bytes.ptr());
3188 : }
3189 : goto error;
3190 : }
3191 : }
3192 :
3193 : /*
3194 : * Non-global properties, and global properties which we aren't simply
3195 : * redefining, must be set. First, this preserves their attributes.
3196 : * Second, this will produce warnings and/or errors as necessary if the
3197 : * specified Call object property is not writable (const).
3198 : */
3199 :
3200 : /* Step 5f. */
3201 805 : if (!parent->setProperty(cx, name, &rval, script->strictModeCode))
3202 0 : goto error;
3203 : } while (false);
3204 : }
3205 185295 : END_CASE(JSOP_DEFFUN)
3206 :
3207 : BEGIN_CASE(JSOP_DEFLOCALFUN)
3208 : {
3209 : /*
3210 : * Define a local function (i.e., one nested at the top level of another
3211 : * function), parented by the current scope chain, stored in a local
3212 : * variable slot that the compiler allocated. This is an optimization over
3213 : * JSOP_DEFFUN that avoids requiring a call object for the outer function's
3214 : * activation.
3215 : */
3216 124491 : JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN));
3217 124491 : JS_ASSERT(fun->isInterpreted());
3218 124491 : JS_ASSERT(!fun->isFlatClosure());
3219 :
3220 : JSObject *parent;
3221 124491 : if (fun->isNullClosure()) {
3222 21898 : parent = ®s.fp()->scopeChain();
3223 : } else {
3224 102593 : parent = GetScopeChain(cx, regs.fp());
3225 102593 : if (!parent)
3226 0 : goto error;
3227 : }
3228 124491 : JSObject *obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent);
3229 124491 : if (!obj)
3230 0 : goto error;
3231 :
3232 124491 : JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
3233 :
3234 124491 : regs.fp()->varSlot(GET_SLOTNO(regs.pc)) = ObjectValue(*obj);
3235 : }
3236 124491 : END_CASE(JSOP_DEFLOCALFUN)
3237 :
3238 : BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
3239 : {
3240 8009 : JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN));
3241 :
3242 8009 : JSObject *obj = js_NewFlatClosure(cx, fun);
3243 8009 : if (!obj)
3244 0 : goto error;
3245 :
3246 8009 : regs.fp()->varSlot(GET_SLOTNO(regs.pc)) = ObjectValue(*obj);
3247 : }
3248 8009 : END_CASE(JSOP_DEFLOCALFUN_FC)
3249 :
3250 : BEGIN_CASE(JSOP_LAMBDA)
3251 : {
3252 : /* Load the specified function object literal. */
3253 1007161 : JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc));
3254 1007161 : JSObject *obj = fun;
3255 :
3256 : /* do-while(0) so we can break instead of using a goto. */
3257 : do {
3258 : JSObject *parent;
3259 1007161 : if (fun->isNullClosure()) {
3260 625721 : parent = ®s.fp()->scopeChain();
3261 : } else {
3262 381440 : parent = GetScopeChain(cx, regs.fp());
3263 381440 : if (!parent)
3264 0 : goto error;
3265 : }
3266 :
3267 1007161 : obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent);
3268 1007161 : if (!obj)
3269 0 : goto error;
3270 : } while (0);
3271 :
3272 1007161 : JS_ASSERT(obj->getProto());
3273 1007161 : JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
3274 :
3275 1007161 : PUSH_OBJECT(*obj);
3276 : }
3277 1007161 : END_CASE(JSOP_LAMBDA)
3278 :
3279 : BEGIN_CASE(JSOP_LAMBDA_FC)
3280 : {
3281 312716 : JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc));
3282 :
3283 312716 : JSObject *obj = js_NewFlatClosure(cx, fun);
3284 312716 : if (!obj)
3285 0 : goto error;
3286 312716 : JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
3287 :
3288 312716 : PUSH_OBJECT(*obj);
3289 : }
3290 312716 : END_CASE(JSOP_LAMBDA_FC)
3291 :
3292 : BEGIN_CASE(JSOP_CALLEE)
3293 1307123 : JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
3294 1307123 : PUSH_COPY(argv[-2]);
3295 1307123 : END_CASE(JSOP_CALLEE)
3296 :
3297 : BEGIN_CASE(JSOP_GETTER)
3298 : BEGIN_CASE(JSOP_SETTER)
3299 : {
3300 73705 : JSOp op2 = JSOp(*++regs.pc);
3301 : jsid id;
3302 : Value rval;
3303 : int i;
3304 : JSObject *obj;
3305 73705 : switch (op2) {
3306 : case JSOP_SETNAME:
3307 : case JSOP_SETPROP:
3308 : {
3309 : PropertyName *name;
3310 0 : LOAD_NAME(0, name);
3311 0 : id = ATOM_TO_JSID(name);
3312 0 : rval = regs.sp[-1];
3313 0 : i = -1;
3314 0 : goto gs_pop_lval;
3315 : }
3316 : case JSOP_SETELEM:
3317 0 : rval = regs.sp[-1];
3318 0 : id = JSID_VOID;
3319 0 : i = -2;
3320 : gs_pop_lval:
3321 0 : FETCH_OBJECT(cx, i - 1, obj);
3322 0 : break;
3323 :
3324 : case JSOP_INITPROP:
3325 : {
3326 73705 : JS_ASSERT(regs.sp - regs.fp()->base() >= 2);
3327 73705 : rval = regs.sp[-1];
3328 73705 : i = -1;
3329 : PropertyName *name;
3330 73705 : LOAD_NAME(0, name);
3331 73705 : id = ATOM_TO_JSID(name);
3332 73705 : goto gs_get_lval;
3333 : }
3334 : default:
3335 0 : JS_ASSERT(op2 == JSOP_INITELEM);
3336 :
3337 0 : JS_ASSERT(regs.sp - regs.fp()->base() >= 3);
3338 0 : rval = regs.sp[-1];
3339 0 : id = JSID_VOID;
3340 0 : i = -2;
3341 : gs_get_lval:
3342 : {
3343 73705 : const Value &lref = regs.sp[i-1];
3344 73705 : JS_ASSERT(lref.isObject());
3345 73705 : obj = &lref.toObject();
3346 73705 : break;
3347 : }
3348 : }
3349 :
3350 : /* Ensure that id has a type suitable for use with obj. */
3351 73705 : if (JSID_IS_VOID(id))
3352 0 : FETCH_ELEMENT_ID(obj, i, id);
3353 :
3354 73705 : if (!js_IsCallable(rval)) {
3355 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER,
3356 0 : (op == JSOP_GETTER) ? js_getter_str : js_setter_str);
3357 0 : goto error;
3358 : }
3359 :
3360 : /*
3361 : * Getters and setters are just like watchpoints from an access control
3362 : * point of view.
3363 : */
3364 : Value rtmp;
3365 : unsigned attrs;
3366 73705 : if (!CheckAccess(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))
3367 0 : goto error;
3368 :
3369 : PropertyOp getter;
3370 : StrictPropertyOp setter;
3371 73705 : if (op == JSOP_GETTER) {
3372 60133 : getter = CastAsPropertyOp(&rval.toObject());
3373 60133 : setter = JS_StrictPropertyStub;
3374 60133 : attrs = JSPROP_GETTER;
3375 : } else {
3376 13572 : getter = JS_PropertyStub;
3377 13572 : setter = CastAsStrictPropertyOp(&rval.toObject());
3378 13572 : attrs = JSPROP_SETTER;
3379 : }
3380 73705 : attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
3381 :
3382 73705 : if (!obj->defineGeneric(cx, id, UndefinedValue(), getter, setter, attrs))
3383 0 : goto error;
3384 :
3385 73705 : regs.sp += i;
3386 73705 : if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) {
3387 0 : JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1);
3388 0 : regs.sp[-1] = rval;
3389 0 : assertSameCompartment(cx, regs.sp[-1]);
3390 : }
3391 73705 : len = js_CodeSpec[op2].length;
3392 73705 : DO_NEXT_OP(len);
3393 : }
3394 :
3395 : BEGIN_CASE(JSOP_HOLE)
3396 4772 : PUSH_HOLE();
3397 4772 : END_CASE(JSOP_HOLE)
3398 :
3399 : BEGIN_CASE(JSOP_NEWINIT)
3400 : {
3401 302491 : uint8_t i = GET_UINT8(regs.pc);
3402 302491 : JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
3403 :
3404 : JSObject *obj;
3405 302491 : if (i == JSProto_Array) {
3406 57861 : obj = NewDenseEmptyArray(cx);
3407 : } else {
3408 244630 : gc::AllocKind kind = GuessObjectGCKind(0);
3409 244630 : obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
3410 : }
3411 302491 : if (!obj)
3412 0 : goto error;
3413 :
3414 302491 : TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, (JSProtoKey) i);
3415 302491 : if (!type)
3416 0 : goto error;
3417 302491 : obj->setType(type);
3418 :
3419 302491 : PUSH_OBJECT(*obj);
3420 302491 : TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
3421 :
3422 302491 : CHECK_INTERRUPT_HANDLER();
3423 : }
3424 302491 : END_CASE(JSOP_NEWINIT)
3425 :
3426 : BEGIN_CASE(JSOP_NEWARRAY)
3427 : {
3428 1090952 : unsigned count = GET_UINT24(regs.pc);
3429 1090952 : JSObject *obj = NewDenseAllocatedArray(cx, count);
3430 1090952 : if (!obj)
3431 0 : goto error;
3432 :
3433 1090952 : TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, JSProto_Array);
3434 1090952 : if (!type)
3435 0 : goto error;
3436 1090952 : obj->setType(type);
3437 :
3438 1090952 : PUSH_OBJECT(*obj);
3439 1090952 : TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
3440 :
3441 1090952 : CHECK_INTERRUPT_HANDLER();
3442 : }
3443 1090952 : END_CASE(JSOP_NEWARRAY)
3444 :
3445 : BEGIN_CASE(JSOP_NEWOBJECT)
3446 : {
3447 1058456 : JSObject *baseobj = script->getObject(GET_UINT32_INDEX(regs.pc));
3448 :
3449 1058456 : TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, JSProto_Object);
3450 1058456 : if (!type)
3451 0 : goto error;
3452 :
3453 1058456 : JSObject *obj = CopyInitializerObject(cx, baseobj, type);
3454 1058456 : if (!obj)
3455 1 : goto error;
3456 :
3457 1058455 : PUSH_OBJECT(*obj);
3458 1058455 : TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
3459 :
3460 1058455 : CHECK_INTERRUPT_HANDLER();
3461 : }
3462 1058455 : END_CASE(JSOP_NEWOBJECT)
3463 :
3464 : BEGIN_CASE(JSOP_ENDINIT)
3465 : {
3466 : /* FIXME remove JSOP_ENDINIT bug 588522 */
3467 2388664 : JS_ASSERT(regs.sp - regs.fp()->base() >= 1);
3468 2388664 : JS_ASSERT(regs.sp[-1].isObject());
3469 : }
3470 2388664 : END_CASE(JSOP_ENDINIT)
3471 :
3472 : BEGIN_CASE(JSOP_INITPROP)
3473 : BEGIN_CASE(JSOP_INITMETHOD)
3474 : {
3475 : /* Load the property's initial value into rval. */
3476 2999106 : JS_ASSERT(regs.sp - regs.fp()->base() >= 2);
3477 2999106 : Value rval = regs.sp[-1];
3478 :
3479 : /* Load the object being initialized into lval/obj. */
3480 2999106 : JSObject *obj = ®s.sp[-2].toObject();
3481 2999106 : JS_ASSERT(obj->isObject());
3482 :
3483 : JSAtom *atom;
3484 2999106 : LOAD_ATOM(0, atom);
3485 2999106 : jsid id = ATOM_TO_JSID(atom);
3486 :
3487 2999106 : unsigned defineHow = (op == JSOP_INITMETHOD) ? DNP_SET_METHOD : 0;
3488 5998212 : if (JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
3489 9716 : ? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode)
3490 : : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
3491 2989390 : JSPROP_ENUMERATE, 0, 0, defineHow)) {
3492 0 : goto error;
3493 : }
3494 :
3495 2999106 : regs.sp--;
3496 : }
3497 2999106 : END_CASE(JSOP_INITPROP);
3498 :
3499 : BEGIN_CASE(JSOP_INITELEM)
3500 : {
3501 : /* Pop the element's value into rval. */
3502 11336664 : JS_ASSERT(regs.sp - regs.fp()->base() >= 3);
3503 11336664 : const Value &rref = regs.sp[-1];
3504 :
3505 : /* Find the object being initialized at top of stack. */
3506 11336664 : const Value &lref = regs.sp[-3];
3507 11336664 : JS_ASSERT(lref.isObject());
3508 11336664 : JSObject *obj = &lref.toObject();
3509 :
3510 : /* Fetch id now that we have obj. */
3511 : jsid id;
3512 11336664 : FETCH_ELEMENT_ID(obj, -2, id);
3513 :
3514 : /*
3515 : * If rref is a hole, do not call JSObject::defineProperty. In this case,
3516 : * obj must be an array, so if the current op is the last element
3517 : * initialiser, set the array length to one greater than id.
3518 : */
3519 11336664 : if (rref.isMagic(JS_ARRAY_HOLE)) {
3520 4772 : JS_ASSERT(obj->isArray());
3521 4772 : JS_ASSERT(JSID_IS_INT(id));
3522 4772 : JS_ASSERT(uint32_t(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX);
3523 5175 : if (JSOp(regs.pc[JSOP_INITELEM_LENGTH]) == JSOP_ENDINIT &&
3524 403 : !js_SetLengthProperty(cx, obj, (uint32_t) (JSID_TO_INT(id) + 1))) {
3525 0 : goto error;
3526 : }
3527 : } else {
3528 11331892 : if (!obj->defineGeneric(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
3529 0 : goto error;
3530 : }
3531 11336664 : regs.sp -= 2;
3532 : }
3533 11336664 : END_CASE(JSOP_INITELEM)
3534 :
3535 : {
3536 : BEGIN_CASE(JSOP_GOSUB)
3537 22516 : PUSH_BOOLEAN(false);
3538 22516 : int32_t i = (regs.pc - script->code) + JSOP_GOSUB_LENGTH;
3539 22516 : len = GET_JUMP_OFFSET(regs.pc);
3540 22516 : PUSH_INT32(i);
3541 22516 : END_VARLEN_CASE
3542 : }
3543 :
3544 : {
3545 : BEGIN_CASE(JSOP_RETSUB)
3546 : /* Pop [exception or hole, retsub pc-index]. */
3547 : Value rval, lval;
3548 24292 : POP_COPY_TO(rval);
3549 24292 : POP_COPY_TO(lval);
3550 24292 : JS_ASSERT(lval.isBoolean());
3551 24292 : if (lval.toBoolean()) {
3552 : /*
3553 : * Exception was pending during finally, throw it *before* we adjust
3554 : * pc, because pc indexes into script->trynotes. This turns out not to
3555 : * be necessary, but it seems clearer. And it points out a FIXME:
3556 : * 350509, due to Igor Bukanov.
3557 : */
3558 1795 : cx->setPendingException(rval);
3559 1795 : goto error;
3560 : }
3561 22497 : JS_ASSERT(rval.isInt32());
3562 22497 : len = rval.toInt32();
3563 22497 : regs.pc = script->code;
3564 22497 : END_VARLEN_CASE
3565 : }
3566 :
3567 : BEGIN_CASE(JSOP_EXCEPTION)
3568 46902 : PUSH_COPY(cx->getPendingException());
3569 46902 : cx->clearPendingException();
3570 46902 : CHECK_BRANCH();
3571 46902 : END_CASE(JSOP_EXCEPTION)
3572 :
3573 : BEGIN_CASE(JSOP_FINALLY)
3574 24329 : CHECK_BRANCH();
3575 24329 : END_CASE(JSOP_FINALLY)
3576 :
3577 : BEGIN_CASE(JSOP_THROWING)
3578 : {
3579 54 : JS_ASSERT(!cx->isExceptionPending());
3580 : Value v;
3581 54 : POP_COPY_TO(v);
3582 54 : cx->setPendingException(v);
3583 : }
3584 54 : END_CASE(JSOP_THROWING)
3585 :
3586 : BEGIN_CASE(JSOP_THROW)
3587 : {
3588 59131 : JS_ASSERT(!cx->isExceptionPending());
3589 59131 : CHECK_BRANCH();
3590 : Value v;
3591 59131 : POP_COPY_TO(v);
3592 59131 : cx->setPendingException(v);
3593 : /* let the code at error try to catch the exception. */
3594 59131 : goto error;
3595 : }
3596 : BEGIN_CASE(JSOP_SETLOCALPOP)
3597 : /*
3598 : * The stack must have a block with at least one local slot below the
3599 : * exception object.
3600 : */
3601 294207 : JS_ASSERT((size_t) (regs.sp - regs.fp()->base()) >= 2);
3602 294207 : POP_COPY_TO(regs.fp()->localSlot(GET_UINT16(regs.pc)));
3603 294207 : END_CASE(JSOP_SETLOCALPOP)
3604 :
3605 : BEGIN_CASE(JSOP_INSTANCEOF)
3606 : {
3607 1137037 : const Value &rref = regs.sp[-1];
3608 1137037 : if (rref.isPrimitive()) {
3609 0 : js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, NULL);
3610 0 : goto error;
3611 : }
3612 1137037 : JSObject *obj = &rref.toObject();
3613 1137037 : const Value &lref = regs.sp[-2];
3614 1137037 : JSBool cond = JS_FALSE;
3615 1137037 : if (!HasInstance(cx, obj, &lref, &cond))
3616 19 : goto error;
3617 1137018 : regs.sp--;
3618 1137018 : regs.sp[-1].setBoolean(cond);
3619 : }
3620 1137018 : END_CASE(JSOP_INSTANCEOF)
3621 :
3622 : BEGIN_CASE(JSOP_DEBUGGER)
3623 : {
3624 4874 : JSTrapStatus st = JSTRAP_CONTINUE;
3625 : Value rval;
3626 4874 : if (JSDebuggerHandler handler = cx->runtime->debugHooks.debuggerHandler)
3627 59 : st = handler(cx, script, regs.pc, &rval, cx->runtime->debugHooks.debuggerHandlerData);
3628 4874 : if (st == JSTRAP_CONTINUE)
3629 4869 : st = Debugger::onDebuggerStatement(cx, &rval);
3630 4874 : switch (st) {
3631 : case JSTRAP_ERROR:
3632 118 : goto error;
3633 : case JSTRAP_CONTINUE:
3634 4532 : break;
3635 : case JSTRAP_RETURN:
3636 149 : regs.fp()->setReturnValue(rval);
3637 149 : interpReturnOK = true;
3638 149 : goto forced_return;
3639 : case JSTRAP_THROW:
3640 75 : cx->setPendingException(rval);
3641 75 : goto error;
3642 : default:;
3643 : }
3644 4532 : CHECK_INTERRUPT_HANDLER();
3645 : }
3646 4532 : END_CASE(JSOP_DEBUGGER)
3647 :
3648 : #if JS_HAS_XML_SUPPORT
3649 : BEGIN_CASE(JSOP_DEFXMLNS)
3650 : {
3651 9 : JS_ASSERT(!script->strictModeCode);
3652 :
3653 9 : if (!js_SetDefaultXMLNamespace(cx, regs.sp[-1]))
3654 0 : goto error;
3655 9 : regs.sp--;
3656 : }
3657 9 : END_CASE(JSOP_DEFXMLNS)
3658 :
3659 : BEGIN_CASE(JSOP_ANYNAME)
3660 : {
3661 18 : JS_ASSERT(!script->strictModeCode);
3662 :
3663 : jsid id;
3664 18 : if (!js_GetAnyName(cx, &id))
3665 0 : goto error;
3666 18 : PUSH_COPY(IdToValue(id));
3667 : }
3668 18 : END_CASE(JSOP_ANYNAME)
3669 :
3670 : BEGIN_CASE(JSOP_QNAMEPART)
3671 : {
3672 : /*
3673 : * We do not JS_ASSERT(!script->strictModeCode) here because JSOP_QNAMEPART
3674 : * is used for __proto__ and (in contexts where we favor JSOP_*ELEM instead
3675 : * of JSOP_*PROP) obj.prop compiled as obj['prop'].
3676 : */
3677 :
3678 : JSAtom *atom;
3679 7406 : LOAD_ATOM(0, atom);
3680 7406 : PUSH_STRING(atom);
3681 : }
3682 7406 : END_CASE(JSOP_QNAMEPART)
3683 :
3684 : BEGIN_CASE(JSOP_QNAMECONST)
3685 : {
3686 45 : JS_ASSERT(!script->strictModeCode);
3687 :
3688 : JSAtom *atom;
3689 45 : LOAD_ATOM(0, atom);
3690 45 : Value rval = StringValue(atom);
3691 45 : Value lval = regs.sp[-1];
3692 45 : JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
3693 45 : if (!obj)
3694 0 : goto error;
3695 45 : regs.sp[-1].setObject(*obj);
3696 : }
3697 45 : END_CASE(JSOP_QNAMECONST)
3698 :
3699 : BEGIN_CASE(JSOP_QNAME)
3700 : {
3701 0 : JS_ASSERT(!script->strictModeCode);
3702 :
3703 0 : Value rval = regs.sp[-1];
3704 0 : Value lval = regs.sp[-2];
3705 0 : JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
3706 0 : if (!obj)
3707 0 : goto error;
3708 0 : regs.sp--;
3709 0 : regs.sp[-1].setObject(*obj);
3710 : }
3711 0 : END_CASE(JSOP_QNAME)
3712 :
3713 : BEGIN_CASE(JSOP_TOATTRNAME)
3714 : {
3715 0 : JS_ASSERT(!script->strictModeCode);
3716 :
3717 : Value rval;
3718 0 : rval = regs.sp[-1];
3719 0 : if (!js_ToAttributeName(cx, &rval))
3720 0 : goto error;
3721 0 : regs.sp[-1] = rval;
3722 : }
3723 0 : END_CASE(JSOP_TOATTRNAME)
3724 :
3725 : BEGIN_CASE(JSOP_TOATTRVAL)
3726 : {
3727 0 : JS_ASSERT(!script->strictModeCode);
3728 :
3729 : Value rval;
3730 0 : rval = regs.sp[-1];
3731 0 : JS_ASSERT(rval.isString());
3732 0 : JSString *str = js_EscapeAttributeValue(cx, rval.toString(), JS_FALSE);
3733 0 : if (!str)
3734 0 : goto error;
3735 0 : regs.sp[-1].setString(str);
3736 : }
3737 0 : END_CASE(JSOP_TOATTRVAL)
3738 :
3739 : BEGIN_CASE(JSOP_ADDATTRNAME)
3740 : BEGIN_CASE(JSOP_ADDATTRVAL)
3741 : {
3742 0 : JS_ASSERT(!script->strictModeCode);
3743 :
3744 0 : Value rval = regs.sp[-1];
3745 0 : Value lval = regs.sp[-2];
3746 0 : JSString *str = lval.toString();
3747 0 : JSString *str2 = rval.toString();
3748 0 : str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
3749 0 : if (!str)
3750 0 : goto error;
3751 0 : regs.sp--;
3752 0 : regs.sp[-1].setString(str);
3753 : }
3754 0 : END_CASE(JSOP_ADDATTRNAME)
3755 :
3756 : BEGIN_CASE(JSOP_BINDXMLNAME)
3757 : {
3758 18 : JS_ASSERT(!script->strictModeCode);
3759 :
3760 : Value lval;
3761 18 : lval = regs.sp[-1];
3762 : JSObject *obj;
3763 : jsid id;
3764 18 : if (!js_FindXMLProperty(cx, lval, &obj, &id))
3765 0 : goto error;
3766 18 : regs.sp[-1].setObjectOrNull(obj);
3767 18 : PUSH_COPY(IdToValue(id));
3768 : }
3769 18 : END_CASE(JSOP_BINDXMLNAME)
3770 :
3771 : BEGIN_CASE(JSOP_SETXMLNAME)
3772 : {
3773 18 : JS_ASSERT(!script->strictModeCode);
3774 :
3775 18 : JSObject *obj = ®s.sp[-3].toObject();
3776 18 : Value rval = regs.sp[-1];
3777 : jsid id;
3778 18 : FETCH_ELEMENT_ID(obj, -2, id);
3779 18 : if (!obj->setGeneric(cx, id, &rval, script->strictModeCode))
3780 0 : goto error;
3781 18 : rval = regs.sp[-1];
3782 18 : regs.sp -= 2;
3783 18 : regs.sp[-1] = rval;
3784 : }
3785 18 : END_CASE(JSOP_SETXMLNAME)
3786 :
3787 : BEGIN_CASE(JSOP_CALLXMLNAME)
3788 : BEGIN_CASE(JSOP_XMLNAME)
3789 : {
3790 36 : JS_ASSERT(!script->strictModeCode);
3791 :
3792 36 : Value lval = regs.sp[-1];
3793 : JSObject *obj;
3794 : jsid id;
3795 36 : if (!js_FindXMLProperty(cx, lval, &obj, &id))
3796 27 : goto error;
3797 : Value rval;
3798 9 : if (!obj->getGeneric(cx, id, &rval))
3799 0 : goto error;
3800 9 : regs.sp[-1] = rval;
3801 9 : if (op == JSOP_CALLXMLNAME) {
3802 : Value v;
3803 0 : if (!ComputeImplicitThis(cx, obj, &v))
3804 0 : goto error;
3805 0 : PUSH_COPY(v);
3806 : }
3807 : }
3808 9 : END_CASE(JSOP_XMLNAME)
3809 :
3810 : BEGIN_CASE(JSOP_DESCENDANTS)
3811 : BEGIN_CASE(JSOP_DELDESC)
3812 : {
3813 9 : JS_ASSERT(!script->strictModeCode);
3814 :
3815 : JSObject *obj;
3816 9 : FETCH_OBJECT(cx, -2, obj);
3817 9 : jsval rval = regs.sp[-1];
3818 9 : if (!js_GetXMLDescendants(cx, obj, rval, &rval))
3819 0 : goto error;
3820 :
3821 9 : if (op == JSOP_DELDESC) {
3822 0 : regs.sp[-1] = rval; /* set local root */
3823 0 : if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
3824 0 : goto error;
3825 0 : rval = JSVAL_TRUE; /* always succeed */
3826 : }
3827 :
3828 9 : regs.sp--;
3829 9 : regs.sp[-1] = rval;
3830 : }
3831 9 : END_CASE(JSOP_DESCENDANTS)
3832 :
3833 : BEGIN_CASE(JSOP_FILTER)
3834 : {
3835 63 : JS_ASSERT(!script->strictModeCode);
3836 :
3837 : /*
3838 : * We push the hole value before jumping to [enditer] so we can detect the
3839 : * first iteration and direct js_StepXMLListFilter to initialize filter's
3840 : * state.
3841 : */
3842 63 : PUSH_HOLE();
3843 63 : len = GET_JUMP_OFFSET(regs.pc);
3844 63 : JS_ASSERT(len > 0);
3845 : }
3846 63 : END_VARLEN_CASE
3847 :
3848 : BEGIN_CASE(JSOP_ENDFILTER)
3849 : {
3850 126 : JS_ASSERT(!script->strictModeCode);
3851 :
3852 126 : bool cond = !regs.sp[-1].isMagic();
3853 126 : if (cond) {
3854 : /* Exit the "with" block left from the previous iteration. */
3855 63 : LeaveWith(cx);
3856 : }
3857 126 : if (!js_StepXMLListFilter(cx, cond))
3858 9 : goto error;
3859 117 : if (!regs.sp[-1].isNull()) {
3860 : /*
3861 : * Decrease sp after EnterWith returns as we use sp[-1] there to root
3862 : * temporaries.
3863 : */
3864 63 : JS_ASSERT(IsXML(regs.sp[-1]));
3865 63 : if (!EnterWith(cx, -2))
3866 0 : goto error;
3867 63 : regs.sp--;
3868 63 : len = GET_JUMP_OFFSET(regs.pc);
3869 63 : JS_ASSERT(len < 0);
3870 63 : BRANCH(len);
3871 : }
3872 54 : regs.sp--;
3873 : }
3874 54 : END_CASE(JSOP_ENDFILTER);
3875 :
3876 : BEGIN_CASE(JSOP_TOXML)
3877 : {
3878 596 : JS_ASSERT(!script->strictModeCode);
3879 :
3880 596 : Value rval = regs.sp[-1];
3881 596 : JSObject *obj = js_ValueToXMLObject(cx, rval);
3882 596 : if (!obj)
3883 0 : goto error;
3884 596 : regs.sp[-1].setObject(*obj);
3885 : }
3886 596 : END_CASE(JSOP_TOXML)
3887 :
3888 : BEGIN_CASE(JSOP_TOXMLLIST)
3889 : {
3890 18 : JS_ASSERT(!script->strictModeCode);
3891 :
3892 18 : Value rval = regs.sp[-1];
3893 18 : JSObject *obj = js_ValueToXMLListObject(cx, rval);
3894 18 : if (!obj)
3895 0 : goto error;
3896 18 : regs.sp[-1].setObject(*obj);
3897 : }
3898 18 : END_CASE(JSOP_TOXMLLIST)
3899 :
3900 : BEGIN_CASE(JSOP_XMLTAGEXPR)
3901 : {
3902 0 : JS_ASSERT(!script->strictModeCode);
3903 :
3904 0 : Value rval = regs.sp[-1];
3905 0 : JSString *str = ToString(cx, rval);
3906 0 : if (!str)
3907 0 : goto error;
3908 0 : regs.sp[-1].setString(str);
3909 : }
3910 0 : END_CASE(JSOP_XMLTAGEXPR)
3911 :
3912 : BEGIN_CASE(JSOP_XMLELTEXPR)
3913 : {
3914 0 : JS_ASSERT(!script->strictModeCode);
3915 :
3916 0 : Value rval = regs.sp[-1];
3917 : JSString *str;
3918 0 : if (IsXML(rval)) {
3919 0 : str = js_ValueToXMLString(cx, rval);
3920 : } else {
3921 0 : str = ToString(cx, rval);
3922 0 : if (str)
3923 0 : str = js_EscapeElementValue(cx, str);
3924 : }
3925 0 : if (!str)
3926 0 : goto error;
3927 0 : regs.sp[-1].setString(str);
3928 : }
3929 0 : END_CASE(JSOP_XMLELTEXPR)
3930 :
3931 : BEGIN_CASE(JSOP_XMLCDATA)
3932 : {
3933 0 : JS_ASSERT(!script->strictModeCode);
3934 :
3935 0 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(regs.pc));
3936 0 : JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, atom);
3937 0 : if (!obj)
3938 0 : goto error;
3939 0 : PUSH_OBJECT(*obj);
3940 : }
3941 0 : END_CASE(JSOP_XMLCDATA)
3942 :
3943 : BEGIN_CASE(JSOP_XMLCOMMENT)
3944 : {
3945 0 : JS_ASSERT(!script->strictModeCode);
3946 :
3947 0 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(regs.pc));
3948 0 : JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, atom);
3949 0 : if (!obj)
3950 0 : goto error;
3951 0 : PUSH_OBJECT(*obj);
3952 : }
3953 0 : END_CASE(JSOP_XMLCOMMENT)
3954 :
3955 : BEGIN_CASE(JSOP_XMLPI)
3956 : {
3957 0 : JS_ASSERT(!script->strictModeCode);
3958 :
3959 0 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(regs.pc));
3960 0 : Value rval = regs.sp[-1];
3961 0 : JSString *str2 = rval.toString();
3962 0 : JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, atom, str2);
3963 0 : if (!obj)
3964 0 : goto error;
3965 0 : regs.sp[-1].setObject(*obj);
3966 : }
3967 0 : END_CASE(JSOP_XMLPI)
3968 :
3969 : BEGIN_CASE(JSOP_GETFUNNS)
3970 : {
3971 36 : JS_ASSERT(!script->strictModeCode);
3972 :
3973 : Value rval;
3974 36 : if (!cx->fp()->scopeChain().global().getFunctionNamespace(cx, &rval))
3975 0 : goto error;
3976 36 : PUSH_COPY(rval);
3977 : }
3978 36 : END_CASE(JSOP_GETFUNNS)
3979 : #endif /* JS_HAS_XML_SUPPORT */
3980 :
3981 : BEGIN_CASE(JSOP_ENTERBLOCK)
3982 : BEGIN_CASE(JSOP_ENTERLET0)
3983 : BEGIN_CASE(JSOP_ENTERLET1)
3984 : {
3985 546775 : StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(regs.pc))->asStaticBlock();
3986 546775 : JS_ASSERT(regs.fp()->maybeBlockChain() == blockObj.enclosingBlock());
3987 :
3988 546775 : if (op == JSOP_ENTERBLOCK) {
3989 386667 : JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp);
3990 386667 : Value *vp = regs.sp + blockObj.slotCount();
3991 386667 : JS_ASSERT(regs.sp < vp);
3992 386667 : JS_ASSERT(vp <= regs.fp()->slots() + script->nslots);
3993 386667 : SetValueRangeToUndefined(regs.sp, vp);
3994 386667 : regs.sp = vp;
3995 160108 : } else if (op == JSOP_ENTERLET0) {
3996 47851 : JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() + blockObj.slotCount()
3997 47851 : == regs.sp);
3998 112257 : } else if (op == JSOP_ENTERLET1) {
3999 112257 : JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() + blockObj.slotCount()
4000 112257 : == regs.sp - 1);
4001 : }
4002 :
4003 : #ifdef DEBUG
4004 546775 : JS_ASSERT(regs.fp()->maybeBlockChain() == blockObj.enclosingBlock());
4005 :
4006 : /*
4007 : * The young end of fp->scopeChain may omit blocks if we haven't closed
4008 : * over them, but if there are any closure blocks on fp->scopeChain, they'd
4009 : * better be (clones of) ancestors of the block we're entering now;
4010 : * anything else we should have popped off fp->scopeChain when we left its
4011 : * static scope.
4012 : */
4013 546775 : JSObject *obj2 = ®s.fp()->scopeChain();
4014 1093712 : while (obj2->isWith())
4015 162 : obj2 = &obj2->asWith().enclosingScope();
4016 556001 : if (obj2->isBlock() &&
4017 9226 : obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp()))
4018 : {
4019 7959 : StaticBlockObject &youngestProto = obj2->asClonedBlock().staticBlock();
4020 7959 : StaticBlockObject *parent = &blockObj;
4021 17203 : while ((parent = parent->enclosingBlock()) != &youngestProto)
4022 1285 : JS_ASSERT(parent);
4023 : }
4024 : #endif
4025 :
4026 546775 : regs.fp()->setBlockChain(&blockObj);
4027 : }
4028 546775 : END_CASE(JSOP_ENTERBLOCK)
4029 :
4030 : BEGIN_CASE(JSOP_LEAVEBLOCK)
4031 : BEGIN_CASE(JSOP_LEAVEFORLETIN)
4032 : BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
4033 : {
4034 547207 : StaticBlockObject &blockObj = regs.fp()->blockChain();
4035 547207 : JS_ASSERT(blockObj.stackDepth() <= StackDepth(script));
4036 :
4037 : /*
4038 : * If we're about to leave the dynamic scope of a block that has been
4039 : * cloned onto fp->scopeChain, clear its private data, move its locals from
4040 : * the stack into the clone, and pop it off the chain.
4041 : */
4042 547207 : JSObject &scope = regs.fp()->scopeChain();
4043 547207 : if (scope.getProto() == &blockObj)
4044 17202 : scope.asClonedBlock().put(cx);
4045 :
4046 547207 : regs.fp()->setBlockChain(blockObj.enclosingBlock());
4047 :
4048 547207 : if (op == JSOP_LEAVEBLOCK) {
4049 : /* Pop the block's slots. */
4050 445339 : regs.sp -= GET_UINT16(regs.pc);
4051 445339 : JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp);
4052 101868 : } else if (op == JSOP_LEAVEBLOCKEXPR) {
4053 : /* Pop the block's slots maintaining the topmost expr. */
4054 930 : Value *vp = ®s.sp[-1];
4055 930 : regs.sp -= GET_UINT16(regs.pc);
4056 930 : JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp - 1);
4057 930 : regs.sp[-1] = *vp;
4058 : } else {
4059 : /* Another op will pop; nothing to do here. */
4060 100938 : len = JSOP_LEAVEFORLETIN_LENGTH;
4061 100938 : DO_NEXT_OP(len);
4062 : }
4063 : }
4064 446269 : END_CASE(JSOP_LEAVEBLOCK)
4065 :
4066 : #if JS_HAS_GENERATORS
4067 : BEGIN_CASE(JSOP_GENERATOR)
4068 : {
4069 12263 : JS_ASSERT(!cx->isExceptionPending());
4070 12263 : regs.pc += JSOP_GENERATOR_LENGTH;
4071 12263 : JSObject *obj = js_NewGenerator(cx);
4072 12263 : if (!obj)
4073 0 : goto error;
4074 12263 : JS_ASSERT(!regs.fp()->hasCallObj() && !regs.fp()->hasArgsObj());
4075 12263 : regs.fp()->setReturnValue(ObjectValue(*obj));
4076 12263 : interpReturnOK = true;
4077 12263 : if (entryFrame != regs.fp())
4078 9939 : goto inline_return;
4079 2324 : goto exit;
4080 : }
4081 :
4082 : BEGIN_CASE(JSOP_YIELD)
4083 23131 : JS_ASSERT(!cx->isExceptionPending());
4084 23131 : JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
4085 23131 : if (cx->generatorFor(regs.fp())->state == JSGEN_CLOSING) {
4086 0 : js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
4087 0 : JSDVG_SEARCH_STACK, argv[-2], NULL);
4088 0 : goto error;
4089 : }
4090 23131 : regs.fp()->setReturnValue(regs.sp[-1]);
4091 23131 : regs.fp()->setYielding();
4092 23131 : regs.pc += JSOP_YIELD_LENGTH;
4093 23131 : interpReturnOK = true;
4094 23131 : goto exit;
4095 :
4096 : BEGIN_CASE(JSOP_ARRAYPUSH)
4097 : {
4098 940755 : uint32_t slot = GET_UINT16(regs.pc);
4099 940755 : JS_ASSERT(script->nfixed <= slot);
4100 940755 : JS_ASSERT(slot < script->nslots);
4101 940755 : JSObject *obj = ®s.fp()->slots()[slot].toObject();
4102 940755 : if (!js_NewbornArrayPush(cx, obj, regs.sp[-1]))
4103 0 : goto error;
4104 940755 : regs.sp--;
4105 : }
4106 940755 : END_CASE(JSOP_ARRAYPUSH)
4107 : #endif /* JS_HAS_GENERATORS */
4108 :
4109 : #if JS_THREADED_INTERP
4110 : L_JSOP_BACKPATCH:
4111 : L_JSOP_BACKPATCH_POP:
4112 :
4113 : # if !JS_HAS_GENERATORS
4114 : L_JSOP_GENERATOR:
4115 : L_JSOP_YIELD:
4116 : L_JSOP_ARRAYPUSH:
4117 : # endif
4118 :
4119 : # if !JS_HAS_DESTRUCTURING
4120 : L_JSOP_ENUMCONSTELEM:
4121 : # endif
4122 :
4123 : # if !JS_HAS_XML_SUPPORT
4124 : L_JSOP_CALLXMLNAME:
4125 : L_JSOP_STARTXMLEXPR:
4126 : L_JSOP_STARTXML:
4127 : L_JSOP_DELDESC:
4128 : L_JSOP_GETFUNNS:
4129 : L_JSOP_XMLPI:
4130 : L_JSOP_XMLCOMMENT:
4131 : L_JSOP_XMLCDATA:
4132 : L_JSOP_XMLELTEXPR:
4133 : L_JSOP_XMLTAGEXPR:
4134 : L_JSOP_TOXMLLIST:
4135 : L_JSOP_TOXML:
4136 : L_JSOP_ENDFILTER:
4137 : L_JSOP_FILTER:
4138 : L_JSOP_DESCENDANTS:
4139 : L_JSOP_XMLNAME:
4140 : L_JSOP_SETXMLNAME:
4141 : L_JSOP_BINDXMLNAME:
4142 : L_JSOP_ADDATTRVAL:
4143 : L_JSOP_ADDATTRNAME:
4144 : L_JSOP_TOATTRVAL:
4145 : L_JSOP_TOATTRNAME:
4146 : L_JSOP_QNAME:
4147 : L_JSOP_QNAMECONST:
4148 : L_JSOP_QNAMEPART:
4149 : L_JSOP_ANYNAME:
4150 : L_JSOP_DEFXMLNS:
4151 : # endif
4152 :
4153 : #endif /* !JS_THREADED_INTERP */
4154 : #if !JS_THREADED_INTERP
4155 : default:
4156 : #endif
4157 : {
4158 : char numBuf[12];
4159 0 : JS_snprintf(numBuf, sizeof numBuf, "%d", op);
4160 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4161 0 : JSMSG_BAD_BYTECODE, numBuf);
4162 0 : goto error;
4163 : }
4164 :
4165 : #if !JS_THREADED_INTERP
4166 : } /* switch (op) */
4167 : } /* for (;;) */
4168 : #endif /* !JS_THREADED_INTERP */
4169 :
4170 : error:
4171 2122765 : JS_ASSERT(&cx->regs() == ®s);
4172 2122765 : JS_ASSERT(uint32_t(regs.pc - script->code) < script->length);
4173 :
4174 2122765 : if (cx->isExceptionPending()) {
4175 : /* Restore atoms local in case we will resume. */
4176 2120574 : atoms = script->atoms;
4177 :
4178 : /* Call debugger throw hook if set. */
4179 2120574 : if (cx->runtime->debugHooks.throwHook || !cx->compartment->getDebuggees().empty()) {
4180 : Value rval;
4181 29457 : JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval);
4182 29457 : if (st == JSTRAP_CONTINUE) {
4183 29422 : if (JSThrowHook handler = cx->runtime->debugHooks.throwHook)
4184 28758 : st = handler(cx, script, regs.pc, &rval, cx->runtime->debugHooks.throwHookData);
4185 : }
4186 :
4187 29457 : switch (st) {
4188 : case JSTRAP_ERROR:
4189 20 : cx->clearPendingException();
4190 20 : goto error;
4191 : case JSTRAP_RETURN:
4192 15 : cx->clearPendingException();
4193 15 : regs.fp()->setReturnValue(rval);
4194 15 : interpReturnOK = true;
4195 15 : goto forced_return;
4196 : case JSTRAP_THROW:
4197 5 : cx->setPendingException(rval);
4198 : case JSTRAP_CONTINUE:
4199 : default:;
4200 : }
4201 29422 : CHECK_INTERRUPT_HANDLER();
4202 : }
4203 :
4204 2123354 : for (TryNoteIter tni(regs); !tni.done(); ++tni) {
4205 51482 : JSTryNote *tn = *tni;
4206 :
4207 51482 : UnwindScope(cx, tn->stackDepth);
4208 :
4209 : /*
4210 : * Set pc to the first bytecode after the the try note to point
4211 : * to the beginning of catch or finally or to [enditer] closing
4212 : * the for-in loop.
4213 : */
4214 51482 : regs.pc = (script)->main() + tn->start + tn->length;
4215 51482 : regs.sp = regs.fp()->base() + tn->stackDepth;
4216 :
4217 51482 : switch (tn->kind) {
4218 : case JSTRY_CATCH:
4219 49558 : JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK);
4220 :
4221 : #if JS_HAS_GENERATORS
4222 : /* Catch cannot intercept the closing of a generator. */
4223 49558 : if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
4224 2710 : break;
4225 : #endif
4226 :
4227 : /*
4228 : * Don't clear exceptions to save cx->exception from GC
4229 : * until it is pushed to the stack via [exception] in the
4230 : * catch block.
4231 : */
4232 46848 : len = 0;
4233 46848 : DO_NEXT_OP(len);
4234 :
4235 : case JSTRY_FINALLY:
4236 : /*
4237 : * Push (true, exception) pair for finally to indicate that
4238 : * [retsub] should rethrow the exception.
4239 : */
4240 1813 : PUSH_BOOLEAN(true);
4241 1813 : PUSH_COPY(cx->getPendingException());
4242 1813 : cx->clearPendingException();
4243 1813 : len = 0;
4244 1813 : DO_NEXT_OP(len);
4245 :
4246 : case JSTRY_ITER: {
4247 : /* This is similar to JSOP_ENDITER in the interpreter loop. */
4248 111 : JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
4249 111 : bool ok = UnwindIteratorForException(cx, ®s.sp[-1].toObject());
4250 111 : regs.sp -= 1;
4251 111 : if (!ok)
4252 6 : goto error;
4253 : }
4254 : }
4255 : }
4256 :
4257 : /*
4258 : * Propagate the exception or error to the caller unless the exception
4259 : * is an asynchronous return from a generator.
4260 : */
4261 2071872 : interpReturnOK = false;
4262 : #if JS_HAS_GENERATORS
4263 2071872 : if (JS_UNLIKELY(cx->isExceptionPending() &&
4264 : cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
4265 1487 : cx->clearPendingException();
4266 1487 : interpReturnOK = true;
4267 1487 : regs.fp()->clearReturnValue();
4268 : }
4269 : #endif
4270 : } else {
4271 2191 : UnwindForUncatchableException(cx, regs);
4272 2191 : interpReturnOK = false;
4273 : }
4274 :
4275 : forced_return:
4276 2074291 : UnwindScope(cx, 0);
4277 2074291 : regs.sp = regs.fp()->base();
4278 :
4279 2074291 : if (entryFrame != regs.fp())
4280 2004357 : goto inline_return;
4281 :
4282 : exit:
4283 1977034 : if (cx->compartment->debugMode())
4284 610449 : interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
4285 1977034 : interpReturnOK = ScriptEpilogueOrGeneratorYield(cx, regs.fp(), interpReturnOK);
4286 1977034 : regs.fp()->setFinishedInInterpreter();
4287 :
4288 : /*
4289 : * At this point we are inevitably leaving an interpreted function or a
4290 : * top-level script, and returning to one of:
4291 : * (a) an "out of line" call made through Invoke;
4292 : * (b) a js_Execute activation;
4293 : * (c) a generator (SendToGenerator, jsiter.c).
4294 : *
4295 : * We must not be in an inline frame. The check above ensures that for the
4296 : * error case and for a normal return, the code jumps directly to parent's
4297 : * frame pc.
4298 : */
4299 1977034 : JS_ASSERT(entryFrame == regs.fp());
4300 1977034 : if (!regs.fp()->isGeneratorFrame()) {
4301 1946200 : JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
4302 1946200 : JS_ASSERT(!regs.fp()->hasBlockChain());
4303 : }
4304 :
4305 : #ifdef JS_METHODJIT
4306 : /*
4307 : * This path is used when it's guaranteed the method can be finished
4308 : * inside the JIT.
4309 : */
4310 : leave_on_safe_point:
4311 : #endif
4312 :
4313 1977915 : gc::MaybeVerifyBarriers(cx, true);
4314 1977915 : return interpReturnOK;
4315 : }
|