Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* |
michael@0 | 8 | * JavaScript bytecode interpreter. |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "vm/Interpreter-inl.h" |
michael@0 | 12 | |
michael@0 | 13 | #include "mozilla/DebugOnly.h" |
michael@0 | 14 | #include "mozilla/FloatingPoint.h" |
michael@0 | 15 | #include "mozilla/PodOperations.h" |
michael@0 | 16 | |
michael@0 | 17 | #include <string.h> |
michael@0 | 18 | |
michael@0 | 19 | #include "jsarray.h" |
michael@0 | 20 | #include "jsatom.h" |
michael@0 | 21 | #include "jscntxt.h" |
michael@0 | 22 | #include "jsfun.h" |
michael@0 | 23 | #include "jsgc.h" |
michael@0 | 24 | #include "jsiter.h" |
michael@0 | 25 | #include "jslibmath.h" |
michael@0 | 26 | #include "jsnum.h" |
michael@0 | 27 | #include "jsobj.h" |
michael@0 | 28 | #include "jsopcode.h" |
michael@0 | 29 | #include "jsprf.h" |
michael@0 | 30 | #include "jsscript.h" |
michael@0 | 31 | #include "jsstr.h" |
michael@0 | 32 | |
michael@0 | 33 | #include "builtin/Eval.h" |
michael@0 | 34 | #include "jit/BaselineJIT.h" |
michael@0 | 35 | #include "jit/Ion.h" |
michael@0 | 36 | #include "jit/IonAnalysis.h" |
michael@0 | 37 | #include "js/OldDebugAPI.h" |
michael@0 | 38 | #include "vm/Debugger.h" |
michael@0 | 39 | #include "vm/Opcodes.h" |
michael@0 | 40 | #include "vm/Shape.h" |
michael@0 | 41 | #include "vm/TraceLogging.h" |
michael@0 | 42 | |
michael@0 | 43 | #include "jsatominlines.h" |
michael@0 | 44 | #include "jsboolinlines.h" |
michael@0 | 45 | #include "jsfuninlines.h" |
michael@0 | 46 | #include "jsinferinlines.h" |
michael@0 | 47 | #include "jsscriptinlines.h" |
michael@0 | 48 | |
michael@0 | 49 | #include "jit/IonFrames-inl.h" |
michael@0 | 50 | #include "vm/Probes-inl.h" |
michael@0 | 51 | #include "vm/ScopeObject-inl.h" |
michael@0 | 52 | #include "vm/Stack-inl.h" |
michael@0 | 53 | |
michael@0 | 54 | using namespace js; |
michael@0 | 55 | using namespace js::gc; |
michael@0 | 56 | using namespace js::types; |
michael@0 | 57 | |
michael@0 | 58 | using mozilla::DebugOnly; |
michael@0 | 59 | using mozilla::NumberEqualsInt32; |
michael@0 | 60 | using mozilla::PodCopy; |
michael@0 | 61 | using JS::ForOfIterator; |
michael@0 | 62 | |
michael@0 | 63 | /* |
michael@0 | 64 | * Note: when Clang 3.2 (32-bit) inlines the two functions below in Interpret, |
michael@0 | 65 | * the conservative stack scanner leaks a ton of memory and this negatively |
michael@0 | 66 | * influences performance. The MOZ_NEVER_INLINE is a temporary workaround until |
michael@0 | 67 | * we can remove the conservative scanner. See bug 849526 for more info. |
michael@0 | 68 | */ |
michael@0 | 69 | #if defined(__clang__) && defined(JS_CPU_X86) |
michael@0 | 70 | static MOZ_NEVER_INLINE bool |
michael@0 | 71 | #else |
michael@0 | 72 | static bool |
michael@0 | 73 | #endif |
michael@0 | 74 | ToBooleanOp(const InterpreterRegs ®s) |
michael@0 | 75 | { |
michael@0 | 76 | return ToBoolean(regs.stackHandleAt(-1)); |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | template <bool Eq> |
michael@0 | 80 | #if defined(__clang__) && defined(JS_CPU_X86) |
michael@0 | 81 | static MOZ_NEVER_INLINE bool |
michael@0 | 82 | #else |
michael@0 | 83 | static bool |
michael@0 | 84 | #endif |
michael@0 | 85 | LooseEqualityOp(JSContext *cx, InterpreterRegs ®s) |
michael@0 | 86 | { |
michael@0 | 87 | HandleValue rval = regs.stackHandleAt(-1); |
michael@0 | 88 | HandleValue lval = regs.stackHandleAt(-2); |
michael@0 | 89 | bool cond; |
michael@0 | 90 | if (!LooselyEqual(cx, lval, rval, &cond)) |
michael@0 | 91 | return false; |
michael@0 | 92 | cond = (cond == Eq); |
michael@0 | 93 | regs.sp--; |
michael@0 | 94 | regs.sp[-1].setBoolean(cond); |
michael@0 | 95 | return true; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | JSObject * |
michael@0 | 99 | js::BoxNonStrictThis(JSContext *cx, HandleValue thisv) |
michael@0 | 100 | { |
michael@0 | 101 | /* |
michael@0 | 102 | * Check for SynthesizeFrame poisoning and fast constructors which |
michael@0 | 103 | * didn't check their callee properly. |
michael@0 | 104 | */ |
michael@0 | 105 | JS_ASSERT(!thisv.isMagic()); |
michael@0 | 106 | |
michael@0 | 107 | if (thisv.isNullOrUndefined()) { |
michael@0 | 108 | Rooted<GlobalObject*> global(cx, cx->global()); |
michael@0 | 109 | return JSObject::thisObject(cx, global); |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | if (thisv.isObject()) |
michael@0 | 113 | return &thisv.toObject(); |
michael@0 | 114 | |
michael@0 | 115 | return PrimitiveToObject(cx, thisv); |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | /* |
michael@0 | 119 | * ECMA requires "the global object", but in embeddings such as the browser, |
michael@0 | 120 | * which have multiple top-level objects (windows, frames, etc. in the DOM), |
michael@0 | 121 | * we prefer fun's parent. An example that causes this code to run: |
michael@0 | 122 | * |
michael@0 | 123 | * // in window w1 |
michael@0 | 124 | * function f() { return this } |
michael@0 | 125 | * function g() { return f } |
michael@0 | 126 | * |
michael@0 | 127 | * // in window w2 |
michael@0 | 128 | * var h = w1.g() |
michael@0 | 129 | * alert(h() == w1) |
michael@0 | 130 | * |
michael@0 | 131 | * The alert should display "true". |
michael@0 | 132 | */ |
michael@0 | 133 | bool |
michael@0 | 134 | js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call) |
michael@0 | 135 | { |
michael@0 | 136 | /* |
michael@0 | 137 | * Check for SynthesizeFrame poisoning and fast constructors which |
michael@0 | 138 | * didn't check their callee properly. |
michael@0 | 139 | */ |
michael@0 | 140 | JS_ASSERT(!call.thisv().isMagic()); |
michael@0 | 141 | |
michael@0 | 142 | #ifdef DEBUG |
michael@0 | 143 | JSFunction *fun = call.callee().is<JSFunction>() ? &call.callee().as<JSFunction>() : nullptr; |
michael@0 | 144 | JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->strict()); |
michael@0 | 145 | #endif |
michael@0 | 146 | |
michael@0 | 147 | JSObject *thisObj = BoxNonStrictThis(cx, call.thisv()); |
michael@0 | 148 | if (!thisObj) |
michael@0 | 149 | return false; |
michael@0 | 150 | |
michael@0 | 151 | call.setThis(ObjectValue(*thisObj)); |
michael@0 | 152 | return true; |
michael@0 | 153 | } |
michael@0 | 154 | |
michael@0 | 155 | #if JS_HAS_NO_SUCH_METHOD |
michael@0 | 156 | |
michael@0 | 157 | static const uint32_t JSSLOT_FOUND_FUNCTION = 0; |
michael@0 | 158 | static const uint32_t JSSLOT_SAVED_ID = 1; |
michael@0 | 159 | |
michael@0 | 160 | static const Class js_NoSuchMethodClass = { |
michael@0 | 161 | "NoSuchMethod", |
michael@0 | 162 | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, |
michael@0 | 163 | JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
michael@0 | 164 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, |
michael@0 | 165 | }; |
michael@0 | 166 | |
michael@0 | 167 | /* |
michael@0 | 168 | * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of |
michael@0 | 169 | * the base object, we search for the __noSuchMethod__ method in the base. |
michael@0 | 170 | * If it exists, we store the method and the property's id into an object of |
michael@0 | 171 | * NoSuchMethod class and store this object into the callee's stack slot. |
michael@0 | 172 | * Later, Invoke will recognise such an object and transfer control to |
michael@0 | 173 | * NoSuchMethod that invokes the method like: |
michael@0 | 174 | * |
michael@0 | 175 | * this.__noSuchMethod__(id, args) |
michael@0 | 176 | * |
michael@0 | 177 | * where id is the name of the method that this invocation attempted to |
michael@0 | 178 | * call by name, and args is an Array containing this invocation's actual |
michael@0 | 179 | * parameters. |
michael@0 | 180 | */ |
michael@0 | 181 | bool |
michael@0 | 182 | js::OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval_, MutableHandleValue vp) |
michael@0 | 183 | { |
michael@0 | 184 | RootedValue idval(cx, idval_); |
michael@0 | 185 | |
michael@0 | 186 | RootedValue value(cx); |
michael@0 | 187 | if (!JSObject::getProperty(cx, obj, obj, cx->names().noSuchMethod, &value)) |
michael@0 | 188 | return false; |
michael@0 | 189 | |
michael@0 | 190 | if (value.isObject()) { |
michael@0 | 191 | JSObject *obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, nullptr, nullptr); |
michael@0 | 192 | if (!obj) |
michael@0 | 193 | return false; |
michael@0 | 194 | |
michael@0 | 195 | obj->setSlot(JSSLOT_FOUND_FUNCTION, value); |
michael@0 | 196 | obj->setSlot(JSSLOT_SAVED_ID, idval); |
michael@0 | 197 | vp.setObject(*obj); |
michael@0 | 198 | } |
michael@0 | 199 | return true; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | static bool |
michael@0 | 203 | NoSuchMethod(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 204 | { |
michael@0 | 205 | InvokeArgs args(cx); |
michael@0 | 206 | if (!args.init(2)) |
michael@0 | 207 | return false; |
michael@0 | 208 | |
michael@0 | 209 | JS_ASSERT(vp[0].isObject()); |
michael@0 | 210 | JS_ASSERT(vp[1].isObject()); |
michael@0 | 211 | JSObject *obj = &vp[0].toObject(); |
michael@0 | 212 | JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass); |
michael@0 | 213 | |
michael@0 | 214 | args.setCallee(obj->getReservedSlot(JSSLOT_FOUND_FUNCTION)); |
michael@0 | 215 | args.setThis(vp[1]); |
michael@0 | 216 | args[0].set(obj->getReservedSlot(JSSLOT_SAVED_ID)); |
michael@0 | 217 | JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2); |
michael@0 | 218 | if (!argsobj) |
michael@0 | 219 | return false; |
michael@0 | 220 | args[1].setObject(*argsobj); |
michael@0 | 221 | bool ok = Invoke(cx, args); |
michael@0 | 222 | vp[0] = args.rval(); |
michael@0 | 223 | return ok; |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | #endif /* JS_HAS_NO_SUCH_METHOD */ |
michael@0 | 227 | |
michael@0 | 228 | static inline bool |
michael@0 | 229 | GetPropertyOperation(JSContext *cx, InterpreterFrame *fp, HandleScript script, jsbytecode *pc, |
michael@0 | 230 | MutableHandleValue lval, MutableHandleValue vp) |
michael@0 | 231 | { |
michael@0 | 232 | JSOp op = JSOp(*pc); |
michael@0 | 233 | |
michael@0 | 234 | if (op == JSOP_LENGTH) { |
michael@0 | 235 | if (IsOptimizedArguments(fp, lval.address())) { |
michael@0 | 236 | vp.setInt32(fp->numActualArgs()); |
michael@0 | 237 | return true; |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | if (GetLengthProperty(lval, vp)) |
michael@0 | 241 | return true; |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | Rooted<GlobalObject*> global(cx, &fp->global()); |
michael@0 | 245 | RootedId id(cx, NameToId(script->getName(pc))); |
michael@0 | 246 | RootedObject obj(cx); |
michael@0 | 247 | |
michael@0 | 248 | /* Optimize (.1).toString(). */ |
michael@0 | 249 | if (lval.isNumber() && id == NameToId(cx->names().toString)) { |
michael@0 | 250 | JSObject *proto = GlobalObject::getOrCreateNumberPrototype(cx, global); |
michael@0 | 251 | if (!proto) |
michael@0 | 252 | return false; |
michael@0 | 253 | if (ClassMethodIsNative(cx, proto, &NumberObject::class_, id, js_num_toString)) |
michael@0 | 254 | obj = proto; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | if (!obj) { |
michael@0 | 258 | obj = ToObjectFromStack(cx, lval); |
michael@0 | 259 | if (!obj) |
michael@0 | 260 | return false; |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | bool wasObject = lval.isObject(); |
michael@0 | 264 | |
michael@0 | 265 | if (!JSObject::getGeneric(cx, obj, obj, id, vp)) |
michael@0 | 266 | return false; |
michael@0 | 267 | |
michael@0 | 268 | #if JS_HAS_NO_SUCH_METHOD |
michael@0 | 269 | if (op == JSOP_CALLPROP && |
michael@0 | 270 | MOZ_UNLIKELY(vp.isUndefined()) && |
michael@0 | 271 | wasObject) |
michael@0 | 272 | { |
michael@0 | 273 | if (!OnUnknownMethod(cx, obj, IdToValue(id), vp)) |
michael@0 | 274 | return false; |
michael@0 | 275 | } |
michael@0 | 276 | #endif |
michael@0 | 277 | |
michael@0 | 278 | return true; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | static inline bool |
michael@0 | 282 | NameOperation(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc, MutableHandleValue vp) |
michael@0 | 283 | { |
michael@0 | 284 | JSObject *obj = fp->scopeChain(); |
michael@0 | 285 | PropertyName *name = fp->script()->getName(pc); |
michael@0 | 286 | |
michael@0 | 287 | /* |
michael@0 | 288 | * Skip along the scope chain to the enclosing global object. This is |
michael@0 | 289 | * used for GNAME opcodes where the bytecode emitter has determined a |
michael@0 | 290 | * name access must be on the global. It also insulates us from bugs |
michael@0 | 291 | * in the emitter: type inference will assume that GNAME opcodes are |
michael@0 | 292 | * accessing the global object, and the inferred behavior should match |
michael@0 | 293 | * the actual behavior even if the id could be found on the scope chain |
michael@0 | 294 | * before the global object. |
michael@0 | 295 | */ |
michael@0 | 296 | if (IsGlobalOp(JSOp(*pc))) |
michael@0 | 297 | obj = &obj->global(); |
michael@0 | 298 | |
michael@0 | 299 | Shape *shape = nullptr; |
michael@0 | 300 | JSObject *scope = nullptr, *pobj = nullptr; |
michael@0 | 301 | if (LookupNameNoGC(cx, name, obj, &scope, &pobj, &shape)) { |
michael@0 | 302 | if (FetchNameNoGC(pobj, shape, vp)) |
michael@0 | 303 | return true; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | RootedObject objRoot(cx, obj), scopeRoot(cx), pobjRoot(cx); |
michael@0 | 307 | RootedPropertyName nameRoot(cx, name); |
michael@0 | 308 | RootedShape shapeRoot(cx); |
michael@0 | 309 | |
michael@0 | 310 | if (!LookupName(cx, nameRoot, objRoot, &scopeRoot, &pobjRoot, &shapeRoot)) |
michael@0 | 311 | return false; |
michael@0 | 312 | |
michael@0 | 313 | /* Kludge to allow (typeof foo == "undefined") tests. */ |
michael@0 | 314 | JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]); |
michael@0 | 315 | if (op2 == JSOP_TYPEOF) |
michael@0 | 316 | return FetchName<true>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp); |
michael@0 | 317 | return FetchName<false>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp); |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | static inline bool |
michael@0 | 321 | SetPropertyOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lval, |
michael@0 | 322 | HandleValue rval) |
michael@0 | 323 | { |
michael@0 | 324 | JS_ASSERT(*pc == JSOP_SETPROP); |
michael@0 | 325 | |
michael@0 | 326 | RootedObject obj(cx, ToObjectFromStack(cx, lval)); |
michael@0 | 327 | if (!obj) |
michael@0 | 328 | return false; |
michael@0 | 329 | |
michael@0 | 330 | RootedValue rref(cx, rval); |
michael@0 | 331 | |
michael@0 | 332 | RootedId id(cx, NameToId(script->getName(pc))); |
michael@0 | 333 | if (MOZ_LIKELY(!obj->getOps()->setProperty)) { |
michael@0 | 334 | if (!baseops::SetPropertyHelper<SequentialExecution>(cx, obj, obj, id, baseops::Qualified, |
michael@0 | 335 | &rref, script->strict())) |
michael@0 | 336 | { |
michael@0 | 337 | return false; |
michael@0 | 338 | } |
michael@0 | 339 | } else { |
michael@0 | 340 | if (!JSObject::setGeneric(cx, obj, obj, id, &rref, script->strict())) |
michael@0 | 341 | return false; |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | return true; |
michael@0 | 345 | } |
michael@0 | 346 | |
michael@0 | 347 | bool |
michael@0 | 348 | js::ReportIsNotFunction(JSContext *cx, HandleValue v, int numToSkip, MaybeConstruct construct) |
michael@0 | 349 | { |
michael@0 | 350 | unsigned error = construct ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION; |
michael@0 | 351 | int spIndex = numToSkip >= 0 ? -(numToSkip + 1) : JSDVG_SEARCH_STACK; |
michael@0 | 352 | |
michael@0 | 353 | js_ReportValueError3(cx, error, spIndex, v, NullPtr(), nullptr, nullptr); |
michael@0 | 354 | return false; |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | JSObject * |
michael@0 | 358 | js::ValueToCallable(JSContext *cx, HandleValue v, int numToSkip, MaybeConstruct construct) |
michael@0 | 359 | { |
michael@0 | 360 | if (v.isObject()) { |
michael@0 | 361 | JSObject *callable = &v.toObject(); |
michael@0 | 362 | if (callable->isCallable()) |
michael@0 | 363 | return callable; |
michael@0 | 364 | } |
michael@0 | 365 | |
michael@0 | 366 | ReportIsNotFunction(cx, v, numToSkip, construct); |
michael@0 | 367 | return nullptr; |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | static MOZ_NEVER_INLINE bool |
michael@0 | 371 | Interpret(JSContext *cx, RunState &state); |
michael@0 | 372 | |
michael@0 | 373 | InterpreterFrame * |
michael@0 | 374 | InvokeState::pushInterpreterFrame(JSContext *cx) |
michael@0 | 375 | { |
michael@0 | 376 | return cx->runtime()->interpreterStack().pushInvokeFrame(cx, args_, initial_); |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | InterpreterFrame * |
michael@0 | 380 | ExecuteState::pushInterpreterFrame(JSContext *cx) |
michael@0 | 381 | { |
michael@0 | 382 | return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, scopeChain_, |
michael@0 | 383 | type_, evalInFrame_); |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | bool |
michael@0 | 387 | js::RunScript(JSContext *cx, RunState &state) |
michael@0 | 388 | { |
michael@0 | 389 | JS_CHECK_RECURSION(cx, return false); |
michael@0 | 390 | |
michael@0 | 391 | SPSEntryMarker marker(cx->runtime()); |
michael@0 | 392 | |
michael@0 | 393 | state.script()->ensureNonLazyCanonicalFunction(cx); |
michael@0 | 394 | |
michael@0 | 395 | #ifdef JS_ION |
michael@0 | 396 | if (jit::IsIonEnabled(cx)) { |
michael@0 | 397 | jit::MethodStatus status = jit::CanEnter(cx, state); |
michael@0 | 398 | if (status == jit::Method_Error) |
michael@0 | 399 | return false; |
michael@0 | 400 | if (status == jit::Method_Compiled) { |
michael@0 | 401 | jit::IonExecStatus status = jit::IonCannon(cx, state); |
michael@0 | 402 | return !IsErrorStatus(status); |
michael@0 | 403 | } |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | if (jit::IsBaselineEnabled(cx)) { |
michael@0 | 407 | jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state); |
michael@0 | 408 | if (status == jit::Method_Error) |
michael@0 | 409 | return false; |
michael@0 | 410 | if (status == jit::Method_Compiled) { |
michael@0 | 411 | jit::IonExecStatus status = jit::EnterBaselineMethod(cx, state); |
michael@0 | 412 | return !IsErrorStatus(status); |
michael@0 | 413 | } |
michael@0 | 414 | } |
michael@0 | 415 | #endif |
michael@0 | 416 | |
michael@0 | 417 | if (state.isInvoke()) { |
michael@0 | 418 | InvokeState &invoke = *state.asInvoke(); |
michael@0 | 419 | TypeMonitorCall(cx, invoke.args(), invoke.constructing()); |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | return Interpret(cx, state); |
michael@0 | 423 | } |
michael@0 | 424 | |
michael@0 | 425 | struct AutoGCIfNeeded |
michael@0 | 426 | { |
michael@0 | 427 | JSContext *cx_; |
michael@0 | 428 | AutoGCIfNeeded(JSContext *cx) : cx_(cx) {} |
michael@0 | 429 | ~AutoGCIfNeeded() { js::gc::GCIfNeeded(cx_); } |
michael@0 | 430 | }; |
michael@0 | 431 | |
michael@0 | 432 | /* |
michael@0 | 433 | * Find a function reference and its 'this' value implicit first parameter |
michael@0 | 434 | * under argc arguments on cx's stack, and call the function. Push missing |
michael@0 | 435 | * required arguments, allocate declared local variables, and pop everything |
michael@0 | 436 | * when done. Then push the return value. |
michael@0 | 437 | */ |
michael@0 | 438 | bool |
michael@0 | 439 | js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct) |
michael@0 | 440 | { |
michael@0 | 441 | JS_ASSERT(args.length() <= ARGS_LENGTH_MAX); |
michael@0 | 442 | JS_ASSERT(!cx->compartment()->activeAnalysis); |
michael@0 | 443 | |
michael@0 | 444 | /* We should never enter a new script while cx->iterValue is live. */ |
michael@0 | 445 | JS_ASSERT(cx->iterValue.isMagic(JS_NO_ITER_VALUE)); |
michael@0 | 446 | |
michael@0 | 447 | /* Perform GC if necessary on exit from the function. */ |
michael@0 | 448 | AutoGCIfNeeded gcIfNeeded(cx); |
michael@0 | 449 | |
michael@0 | 450 | /* MaybeConstruct is a subset of InitialFrameFlags */ |
michael@0 | 451 | InitialFrameFlags initial = (InitialFrameFlags) construct; |
michael@0 | 452 | |
michael@0 | 453 | if (args.calleev().isPrimitive()) |
michael@0 | 454 | return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct); |
michael@0 | 455 | |
michael@0 | 456 | JSObject &callee = args.callee(); |
michael@0 | 457 | const Class *clasp = callee.getClass(); |
michael@0 | 458 | |
michael@0 | 459 | /* Invoke non-functions. */ |
michael@0 | 460 | if (MOZ_UNLIKELY(clasp != &JSFunction::class_)) { |
michael@0 | 461 | #if JS_HAS_NO_SUCH_METHOD |
michael@0 | 462 | if (MOZ_UNLIKELY(clasp == &js_NoSuchMethodClass)) |
michael@0 | 463 | return NoSuchMethod(cx, args.length(), args.base()); |
michael@0 | 464 | #endif |
michael@0 | 465 | JS_ASSERT_IF(construct, !clasp->construct); |
michael@0 | 466 | if (!clasp->call) |
michael@0 | 467 | return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct); |
michael@0 | 468 | return CallJSNative(cx, clasp->call, args); |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | /* Invoke native functions. */ |
michael@0 | 472 | JSFunction *fun = &callee.as<JSFunction>(); |
michael@0 | 473 | JS_ASSERT_IF(construct, !fun->isNativeConstructor()); |
michael@0 | 474 | if (fun->isNative()) |
michael@0 | 475 | return CallJSNative(cx, fun->native(), args); |
michael@0 | 476 | |
michael@0 | 477 | if (!fun->getOrCreateScript(cx)) |
michael@0 | 478 | return false; |
michael@0 | 479 | |
michael@0 | 480 | /* Run function until JSOP_RETRVAL, JSOP_RETURN or error. */ |
michael@0 | 481 | InvokeState state(cx, args, initial); |
michael@0 | 482 | |
michael@0 | 483 | // Check to see if useNewType flag should be set for this frame. |
michael@0 | 484 | if (construct) { |
michael@0 | 485 | FrameIter iter(cx); |
michael@0 | 486 | if (!iter.done() && iter.hasScript()) { |
michael@0 | 487 | JSScript *script = iter.script(); |
michael@0 | 488 | jsbytecode *pc = iter.pc(); |
michael@0 | 489 | if (UseNewType(cx, script, pc)) |
michael@0 | 490 | state.setUseNewType(); |
michael@0 | 491 | } |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | bool ok = RunScript(cx, state); |
michael@0 | 495 | |
michael@0 | 496 | JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive()); |
michael@0 | 497 | return ok; |
michael@0 | 498 | } |
michael@0 | 499 | |
michael@0 | 500 | bool |
michael@0 | 501 | js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, const Value *argv, |
michael@0 | 502 | MutableHandleValue rval) |
michael@0 | 503 | { |
michael@0 | 504 | InvokeArgs args(cx); |
michael@0 | 505 | if (!args.init(argc)) |
michael@0 | 506 | return false; |
michael@0 | 507 | |
michael@0 | 508 | args.setCallee(fval); |
michael@0 | 509 | args.setThis(thisv); |
michael@0 | 510 | PodCopy(args.array(), argv, argc); |
michael@0 | 511 | |
michael@0 | 512 | if (args.thisv().isObject()) { |
michael@0 | 513 | /* |
michael@0 | 514 | * We must call the thisObject hook in case we are not called from the |
michael@0 | 515 | * interpreter, where a prior bytecode has computed an appropriate |
michael@0 | 516 | * |this| already. But don't do that if fval is a DOM function. |
michael@0 | 517 | */ |
michael@0 | 518 | if (!fval.isObject() || !fval.toObject().is<JSFunction>() || |
michael@0 | 519 | !fval.toObject().as<JSFunction>().isNative() || |
michael@0 | 520 | !fval.toObject().as<JSFunction>().jitInfo() || |
michael@0 | 521 | fval.toObject().as<JSFunction>().jitInfo()->needsOuterizedThisObject()) |
michael@0 | 522 | { |
michael@0 | 523 | RootedObject thisObj(cx, &args.thisv().toObject()); |
michael@0 | 524 | JSObject *thisp = JSObject::thisObject(cx, thisObj); |
michael@0 | 525 | if (!thisp) |
michael@0 | 526 | return false; |
michael@0 | 527 | args.setThis(ObjectValue(*thisp)); |
michael@0 | 528 | } |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | if (!Invoke(cx, args)) |
michael@0 | 532 | return false; |
michael@0 | 533 | |
michael@0 | 534 | rval.set(args.rval()); |
michael@0 | 535 | return true; |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | bool |
michael@0 | 539 | js::InvokeConstructor(JSContext *cx, CallArgs args) |
michael@0 | 540 | { |
michael@0 | 541 | JS_ASSERT(!JSFunction::class_.construct); |
michael@0 | 542 | |
michael@0 | 543 | args.setThis(MagicValue(JS_IS_CONSTRUCTING)); |
michael@0 | 544 | |
michael@0 | 545 | if (!args.calleev().isObject()) |
michael@0 | 546 | return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT); |
michael@0 | 547 | |
michael@0 | 548 | JSObject &callee = args.callee(); |
michael@0 | 549 | if (callee.is<JSFunction>()) { |
michael@0 | 550 | RootedFunction fun(cx, &callee.as<JSFunction>()); |
michael@0 | 551 | |
michael@0 | 552 | if (fun->isNativeConstructor()) { |
michael@0 | 553 | bool ok = CallJSNativeConstructor(cx, fun->native(), args); |
michael@0 | 554 | return ok; |
michael@0 | 555 | } |
michael@0 | 556 | |
michael@0 | 557 | if (!fun->isInterpretedConstructor()) { |
michael@0 | 558 | RootedValue orig(cx, ObjectValue(*fun->originalFunction())); |
michael@0 | 559 | return ReportIsNotFunction(cx, orig, args.length() + 1, CONSTRUCT); |
michael@0 | 560 | } |
michael@0 | 561 | if (!Invoke(cx, args, CONSTRUCT)) |
michael@0 | 562 | return false; |
michael@0 | 563 | |
michael@0 | 564 | JS_ASSERT(args.rval().isObject()); |
michael@0 | 565 | return true; |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | const Class *clasp = callee.getClass(); |
michael@0 | 569 | if (!clasp->construct) |
michael@0 | 570 | return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT); |
michael@0 | 571 | |
michael@0 | 572 | return CallJSNativeConstructor(cx, clasp->construct, args); |
michael@0 | 573 | } |
michael@0 | 574 | |
michael@0 | 575 | bool |
michael@0 | 576 | js::InvokeConstructor(JSContext *cx, Value fval, unsigned argc, Value *argv, Value *rval) |
michael@0 | 577 | { |
michael@0 | 578 | InvokeArgs args(cx); |
michael@0 | 579 | if (!args.init(argc)) |
michael@0 | 580 | return false; |
michael@0 | 581 | |
michael@0 | 582 | args.setCallee(fval); |
michael@0 | 583 | args.setThis(MagicValue(JS_THIS_POISON)); |
michael@0 | 584 | PodCopy(args.array(), argv, argc); |
michael@0 | 585 | |
michael@0 | 586 | if (!InvokeConstructor(cx, args)) |
michael@0 | 587 | return false; |
michael@0 | 588 | |
michael@0 | 589 | *rval = args.rval(); |
michael@0 | 590 | return true; |
michael@0 | 591 | } |
michael@0 | 592 | |
michael@0 | 593 | bool |
michael@0 | 594 | js::InvokeGetterOrSetter(JSContext *cx, JSObject *obj, Value fval, unsigned argc, |
michael@0 | 595 | Value *argv, MutableHandleValue rval) |
michael@0 | 596 | { |
michael@0 | 597 | /* |
michael@0 | 598 | * Invoke could result in another try to get or set the same id again, see |
michael@0 | 599 | * bug 355497. |
michael@0 | 600 | */ |
michael@0 | 601 | JS_CHECK_RECURSION(cx, return false); |
michael@0 | 602 | |
michael@0 | 603 | return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval); |
michael@0 | 604 | } |
michael@0 | 605 | |
michael@0 | 606 | bool |
michael@0 | 607 | js::ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChainArg, const Value &thisv, |
michael@0 | 608 | ExecuteType type, AbstractFramePtr evalInFrame, Value *result) |
michael@0 | 609 | { |
michael@0 | 610 | JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG); |
michael@0 | 611 | JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChainArg.is<ScopeObject>()); |
michael@0 | 612 | #ifdef DEBUG |
michael@0 | 613 | if (thisv.isObject()) { |
michael@0 | 614 | RootedObject thisObj(cx, &thisv.toObject()); |
michael@0 | 615 | AutoSuppressGC nogc(cx); |
michael@0 | 616 | JS_ASSERT(GetOuterObject(cx, thisObj) == thisObj); |
michael@0 | 617 | } |
michael@0 | 618 | #endif |
michael@0 | 619 | |
michael@0 | 620 | if (script->isEmpty()) { |
michael@0 | 621 | if (result) |
michael@0 | 622 | result->setUndefined(); |
michael@0 | 623 | return true; |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | TypeScript::SetThis(cx, script, thisv); |
michael@0 | 627 | |
michael@0 | 628 | probes::StartExecution(script); |
michael@0 | 629 | ExecuteState state(cx, script, thisv, scopeChainArg, type, evalInFrame, result); |
michael@0 | 630 | bool ok = RunScript(cx, state); |
michael@0 | 631 | probes::StopExecution(script); |
michael@0 | 632 | |
michael@0 | 633 | return ok; |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | bool |
michael@0 | 637 | js::Execute(JSContext *cx, HandleScript script, JSObject &scopeChainArg, Value *rval) |
michael@0 | 638 | { |
michael@0 | 639 | /* The scope chain could be anything, so innerize just in case. */ |
michael@0 | 640 | RootedObject scopeChain(cx, &scopeChainArg); |
michael@0 | 641 | scopeChain = GetInnerObject(cx, scopeChain); |
michael@0 | 642 | if (!scopeChain) |
michael@0 | 643 | return false; |
michael@0 | 644 | |
michael@0 | 645 | /* Ensure the scope chain is all same-compartment and terminates in a global. */ |
michael@0 | 646 | #ifdef DEBUG |
michael@0 | 647 | JSObject *s = scopeChain; |
michael@0 | 648 | do { |
michael@0 | 649 | assertSameCompartment(cx, s); |
michael@0 | 650 | JS_ASSERT_IF(!s->enclosingScope(), s->is<GlobalObject>()); |
michael@0 | 651 | } while ((s = s->enclosingScope())); |
michael@0 | 652 | #endif |
michael@0 | 653 | |
michael@0 | 654 | /* The VAROBJFIX option makes varObj == globalObj in global code. */ |
michael@0 | 655 | if (!cx->options().varObjFix()) { |
michael@0 | 656 | if (!scopeChain->setVarObj(cx)) |
michael@0 | 657 | return false; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | /* Use the scope chain as 'this', modulo outerization. */ |
michael@0 | 661 | JSObject *thisObj = JSObject::thisObject(cx, scopeChain); |
michael@0 | 662 | if (!thisObj) |
michael@0 | 663 | return false; |
michael@0 | 664 | Value thisv = ObjectValue(*thisObj); |
michael@0 | 665 | |
michael@0 | 666 | return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL, |
michael@0 | 667 | NullFramePtr() /* evalInFrame */, rval); |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | bool |
michael@0 | 671 | js::HasInstance(JSContext *cx, HandleObject obj, HandleValue v, bool *bp) |
michael@0 | 672 | { |
michael@0 | 673 | const Class *clasp = obj->getClass(); |
michael@0 | 674 | RootedValue local(cx, v); |
michael@0 | 675 | if (clasp->hasInstance) |
michael@0 | 676 | return clasp->hasInstance(cx, obj, &local, bp); |
michael@0 | 677 | |
michael@0 | 678 | RootedValue val(cx, ObjectValue(*obj)); |
michael@0 | 679 | js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, |
michael@0 | 680 | JSDVG_SEARCH_STACK, val, NullPtr()); |
michael@0 | 681 | return false; |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | bool |
michael@0 | 685 | js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *result) |
michael@0 | 686 | { |
michael@0 | 687 | if (SameType(lval, rval)) { |
michael@0 | 688 | if (lval.isString()) { |
michael@0 | 689 | JSString *l = lval.toString(); |
michael@0 | 690 | JSString *r = rval.toString(); |
michael@0 | 691 | return EqualStrings(cx, l, r, result); |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | if (lval.isDouble()) { |
michael@0 | 695 | double l = lval.toDouble(), r = rval.toDouble(); |
michael@0 | 696 | *result = (l == r); |
michael@0 | 697 | return true; |
michael@0 | 698 | } |
michael@0 | 699 | |
michael@0 | 700 | if (lval.isObject()) { |
michael@0 | 701 | JSObject *l = &lval.toObject(); |
michael@0 | 702 | JSObject *r = &rval.toObject(); |
michael@0 | 703 | *result = l == r; |
michael@0 | 704 | return true; |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | *result = lval.payloadAsRawUint32() == rval.payloadAsRawUint32(); |
michael@0 | 708 | return true; |
michael@0 | 709 | } |
michael@0 | 710 | |
michael@0 | 711 | if (lval.isNullOrUndefined()) { |
michael@0 | 712 | *result = rval.isNullOrUndefined() || |
michael@0 | 713 | (rval.isObject() && EmulatesUndefined(&rval.toObject())); |
michael@0 | 714 | return true; |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | if (rval.isNullOrUndefined()) { |
michael@0 | 718 | *result = (lval.isObject() && EmulatesUndefined(&lval.toObject())); |
michael@0 | 719 | return true; |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | RootedValue lvalue(cx, lval); |
michael@0 | 723 | RootedValue rvalue(cx, rval); |
michael@0 | 724 | |
michael@0 | 725 | if (!ToPrimitive(cx, &lvalue)) |
michael@0 | 726 | return false; |
michael@0 | 727 | if (!ToPrimitive(cx, &rvalue)) |
michael@0 | 728 | return false; |
michael@0 | 729 | |
michael@0 | 730 | if (lvalue.get().isString() && rvalue.get().isString()) { |
michael@0 | 731 | JSString *l = lvalue.get().toString(); |
michael@0 | 732 | JSString *r = rvalue.get().toString(); |
michael@0 | 733 | return EqualStrings(cx, l, r, result); |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | double l, r; |
michael@0 | 737 | if (!ToNumber(cx, lvalue, &l) || !ToNumber(cx, rvalue, &r)) |
michael@0 | 738 | return false; |
michael@0 | 739 | *result = (l == r); |
michael@0 | 740 | return true; |
michael@0 | 741 | } |
michael@0 | 742 | |
michael@0 | 743 | bool |
michael@0 | 744 | js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, bool *equal) |
michael@0 | 745 | { |
michael@0 | 746 | Value lval = lref, rval = rref; |
michael@0 | 747 | if (SameType(lval, rval)) { |
michael@0 | 748 | if (lval.isString()) |
michael@0 | 749 | return EqualStrings(cx, lval.toString(), rval.toString(), equal); |
michael@0 | 750 | if (lval.isDouble()) { |
michael@0 | 751 | *equal = (lval.toDouble() == rval.toDouble()); |
michael@0 | 752 | return true; |
michael@0 | 753 | } |
michael@0 | 754 | if (lval.isObject()) { |
michael@0 | 755 | *equal = lval.toObject() == rval.toObject(); |
michael@0 | 756 | return true; |
michael@0 | 757 | } |
michael@0 | 758 | if (lval.isUndefined()) { |
michael@0 | 759 | *equal = true; |
michael@0 | 760 | return true; |
michael@0 | 761 | } |
michael@0 | 762 | *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32(); |
michael@0 | 763 | return true; |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | if (lval.isDouble() && rval.isInt32()) { |
michael@0 | 767 | double ld = lval.toDouble(); |
michael@0 | 768 | double rd = rval.toInt32(); |
michael@0 | 769 | *equal = (ld == rd); |
michael@0 | 770 | return true; |
michael@0 | 771 | } |
michael@0 | 772 | if (lval.isInt32() && rval.isDouble()) { |
michael@0 | 773 | double ld = lval.toInt32(); |
michael@0 | 774 | double rd = rval.toDouble(); |
michael@0 | 775 | *equal = (ld == rd); |
michael@0 | 776 | return true; |
michael@0 | 777 | } |
michael@0 | 778 | |
michael@0 | 779 | *equal = false; |
michael@0 | 780 | return true; |
michael@0 | 781 | } |
michael@0 | 782 | |
michael@0 | 783 | static inline bool |
michael@0 | 784 | IsNegativeZero(const Value &v) |
michael@0 | 785 | { |
michael@0 | 786 | return v.isDouble() && mozilla::IsNegativeZero(v.toDouble()); |
michael@0 | 787 | } |
michael@0 | 788 | |
michael@0 | 789 | static inline bool |
michael@0 | 790 | IsNaN(const Value &v) |
michael@0 | 791 | { |
michael@0 | 792 | return v.isDouble() && mozilla::IsNaN(v.toDouble()); |
michael@0 | 793 | } |
michael@0 | 794 | |
michael@0 | 795 | bool |
michael@0 | 796 | js::SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same) |
michael@0 | 797 | { |
michael@0 | 798 | if (IsNegativeZero(v1)) { |
michael@0 | 799 | *same = IsNegativeZero(v2); |
michael@0 | 800 | return true; |
michael@0 | 801 | } |
michael@0 | 802 | if (IsNegativeZero(v2)) { |
michael@0 | 803 | *same = false; |
michael@0 | 804 | return true; |
michael@0 | 805 | } |
michael@0 | 806 | if (IsNaN(v1) && IsNaN(v2)) { |
michael@0 | 807 | *same = true; |
michael@0 | 808 | return true; |
michael@0 | 809 | } |
michael@0 | 810 | return StrictlyEqual(cx, v1, v2, same); |
michael@0 | 811 | } |
michael@0 | 812 | |
michael@0 | 813 | JSType |
michael@0 | 814 | js::TypeOfObject(JSObject *obj) |
michael@0 | 815 | { |
michael@0 | 816 | if (EmulatesUndefined(obj)) |
michael@0 | 817 | return JSTYPE_VOID; |
michael@0 | 818 | if (obj->isCallable()) |
michael@0 | 819 | return JSTYPE_FUNCTION; |
michael@0 | 820 | return JSTYPE_OBJECT; |
michael@0 | 821 | } |
michael@0 | 822 | |
michael@0 | 823 | JSType |
michael@0 | 824 | js::TypeOfValue(const Value &v) |
michael@0 | 825 | { |
michael@0 | 826 | if (v.isNumber()) |
michael@0 | 827 | return JSTYPE_NUMBER; |
michael@0 | 828 | if (v.isString()) |
michael@0 | 829 | return JSTYPE_STRING; |
michael@0 | 830 | if (v.isNull()) |
michael@0 | 831 | return JSTYPE_OBJECT; |
michael@0 | 832 | if (v.isUndefined()) |
michael@0 | 833 | return JSTYPE_VOID; |
michael@0 | 834 | if (v.isObject()) |
michael@0 | 835 | return TypeOfObject(&v.toObject()); |
michael@0 | 836 | JS_ASSERT(v.isBoolean()); |
michael@0 | 837 | return JSTYPE_BOOLEAN; |
michael@0 | 838 | } |
michael@0 | 839 | |
michael@0 | 840 | /* |
michael@0 | 841 | * Enter the new with scope using an object at sp[-1] and associate the depth |
michael@0 | 842 | * of the with block with sp + stackIndex. |
michael@0 | 843 | */ |
michael@0 | 844 | bool |
michael@0 | 845 | js::EnterWithOperation(JSContext *cx, AbstractFramePtr frame, HandleValue val, |
michael@0 | 846 | HandleObject staticWith) |
michael@0 | 847 | { |
michael@0 | 848 | JS_ASSERT(staticWith->is<StaticWithObject>()); |
michael@0 | 849 | RootedObject obj(cx); |
michael@0 | 850 | if (val.isObject()) { |
michael@0 | 851 | obj = &val.toObject(); |
michael@0 | 852 | } else { |
michael@0 | 853 | obj = ToObject(cx, val); |
michael@0 | 854 | if (!obj) |
michael@0 | 855 | return false; |
michael@0 | 856 | } |
michael@0 | 857 | |
michael@0 | 858 | RootedObject scopeChain(cx, frame.scopeChain()); |
michael@0 | 859 | DynamicWithObject *withobj = DynamicWithObject::create(cx, obj, scopeChain, staticWith); |
michael@0 | 860 | if (!withobj) |
michael@0 | 861 | return false; |
michael@0 | 862 | |
michael@0 | 863 | frame.pushOnScopeChain(*withobj); |
michael@0 | 864 | return true; |
michael@0 | 865 | } |
michael@0 | 866 | |
michael@0 | 867 | // Unwind scope chain and iterator to match the static scope corresponding to |
michael@0 | 868 | // the given bytecode position. |
michael@0 | 869 | void |
michael@0 | 870 | js::UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc) |
michael@0 | 871 | { |
michael@0 | 872 | if (si.done()) |
michael@0 | 873 | return; |
michael@0 | 874 | |
michael@0 | 875 | Rooted<NestedScopeObject *> staticScope(cx, si.frame().script()->getStaticScope(pc)); |
michael@0 | 876 | |
michael@0 | 877 | for (; si.staticScope() != staticScope; ++si) { |
michael@0 | 878 | switch (si.type()) { |
michael@0 | 879 | case ScopeIter::Block: |
michael@0 | 880 | if (cx->compartment()->debugMode()) |
michael@0 | 881 | DebugScopes::onPopBlock(cx, si); |
michael@0 | 882 | if (si.staticBlock().needsClone()) |
michael@0 | 883 | si.frame().popBlock(cx); |
michael@0 | 884 | break; |
michael@0 | 885 | case ScopeIter::With: |
michael@0 | 886 | si.frame().popWith(cx); |
michael@0 | 887 | break; |
michael@0 | 888 | case ScopeIter::Call: |
michael@0 | 889 | case ScopeIter::StrictEvalScope: |
michael@0 | 890 | break; |
michael@0 | 891 | } |
michael@0 | 892 | } |
michael@0 | 893 | } |
michael@0 | 894 | |
michael@0 | 895 | static void |
michael@0 | 896 | ForcedReturn(JSContext *cx, ScopeIter &si, InterpreterRegs ®s) |
michael@0 | 897 | { |
michael@0 | 898 | UnwindScope(cx, si, regs.fp()->script()->main()); |
michael@0 | 899 | regs.setToEndOfScript(); |
michael@0 | 900 | } |
michael@0 | 901 | |
michael@0 | 902 | static void |
michael@0 | 903 | ForcedReturn(JSContext *cx, InterpreterRegs ®s) |
michael@0 | 904 | { |
michael@0 | 905 | ScopeIter si(regs.fp(), regs.pc, cx); |
michael@0 | 906 | ForcedReturn(cx, si, regs); |
michael@0 | 907 | } |
michael@0 | 908 | |
michael@0 | 909 | void |
michael@0 | 910 | js::UnwindForUncatchableException(JSContext *cx, const InterpreterRegs ®s) |
michael@0 | 911 | { |
michael@0 | 912 | /* c.f. the regular (catchable) TryNoteIter loop in HandleError. */ |
michael@0 | 913 | for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) { |
michael@0 | 914 | JSTryNote *tn = *tni; |
michael@0 | 915 | if (tn->kind == JSTRY_ITER) { |
michael@0 | 916 | Value *sp = regs.spForStackDepth(tn->stackDepth); |
michael@0 | 917 | UnwindIteratorForUncatchableException(cx, &sp[-1].toObject()); |
michael@0 | 918 | } |
michael@0 | 919 | } |
michael@0 | 920 | } |
michael@0 | 921 | |
michael@0 | 922 | TryNoteIter::TryNoteIter(JSContext *cx, const InterpreterRegs ®s) |
michael@0 | 923 | : regs(regs), |
michael@0 | 924 | script(cx, regs.fp()->script()), |
michael@0 | 925 | pcOffset(regs.pc - script->main()) |
michael@0 | 926 | { |
michael@0 | 927 | if (script->hasTrynotes()) { |
michael@0 | 928 | tn = script->trynotes()->vector; |
michael@0 | 929 | tnEnd = tn + script->trynotes()->length; |
michael@0 | 930 | } else { |
michael@0 | 931 | tn = tnEnd = nullptr; |
michael@0 | 932 | } |
michael@0 | 933 | settle(); |
michael@0 | 934 | } |
michael@0 | 935 | |
michael@0 | 936 | void |
michael@0 | 937 | TryNoteIter::operator++() |
michael@0 | 938 | { |
michael@0 | 939 | ++tn; |
michael@0 | 940 | settle(); |
michael@0 | 941 | } |
michael@0 | 942 | |
michael@0 | 943 | bool |
michael@0 | 944 | TryNoteIter::done() const |
michael@0 | 945 | { |
michael@0 | 946 | return tn == tnEnd; |
michael@0 | 947 | } |
michael@0 | 948 | |
michael@0 | 949 | void |
michael@0 | 950 | TryNoteIter::settle() |
michael@0 | 951 | { |
michael@0 | 952 | for (; tn != tnEnd; ++tn) { |
michael@0 | 953 | /* If pc is out of range, try the next one. */ |
michael@0 | 954 | if (pcOffset - tn->start >= tn->length) |
michael@0 | 955 | continue; |
michael@0 | 956 | |
michael@0 | 957 | /* |
michael@0 | 958 | * We have a note that covers the exception pc but we must check |
michael@0 | 959 | * whether the interpreter has already executed the corresponding |
michael@0 | 960 | * handler. This is possible when the executed bytecode implements |
michael@0 | 961 | * break or return from inside a for-in loop. |
michael@0 | 962 | * |
michael@0 | 963 | * In this case the emitter generates additional [enditer] and [gosub] |
michael@0 | 964 | * opcodes to close all outstanding iterators and execute the finally |
michael@0 | 965 | * blocks. If such an [enditer] throws an exception, its pc can still |
michael@0 | 966 | * be inside several nested for-in loops and try-finally statements |
michael@0 | 967 | * even if we have already closed the corresponding iterators and |
michael@0 | 968 | * invoked the finally blocks. |
michael@0 | 969 | * |
michael@0 | 970 | * To address this, we make [enditer] always decrease the stack even |
michael@0 | 971 | * when its implementation throws an exception. Thus already executed |
michael@0 | 972 | * [enditer] and [gosub] opcodes will have try notes with the stack |
michael@0 | 973 | * depth exceeding the current one and this condition is what we use to |
michael@0 | 974 | * filter them out. |
michael@0 | 975 | */ |
michael@0 | 976 | if (tn->stackDepth <= regs.stackDepth()) |
michael@0 | 977 | break; |
michael@0 | 978 | } |
michael@0 | 979 | } |
michael@0 | 980 | |
michael@0 | 981 | enum HandleErrorContinuation |
michael@0 | 982 | { |
michael@0 | 983 | SuccessfulReturnContinuation, |
michael@0 | 984 | ErrorReturnContinuation, |
michael@0 | 985 | CatchContinuation, |
michael@0 | 986 | FinallyContinuation |
michael@0 | 987 | }; |
michael@0 | 988 | |
michael@0 | 989 | static HandleErrorContinuation |
michael@0 | 990 | HandleError(JSContext *cx, InterpreterRegs ®s) |
michael@0 | 991 | { |
michael@0 | 992 | JS_ASSERT(regs.fp()->script()->containsPC(regs.pc)); |
michael@0 | 993 | |
michael@0 | 994 | ScopeIter si(regs.fp(), regs.pc, cx); |
michael@0 | 995 | bool ok = false; |
michael@0 | 996 | |
michael@0 | 997 | again: |
michael@0 | 998 | if (cx->isExceptionPending()) { |
michael@0 | 999 | /* Call debugger throw hooks. */ |
michael@0 | 1000 | if (MOZ_UNLIKELY(cx->compartment()->debugMode())) { |
michael@0 | 1001 | JSTrapStatus status = DebugExceptionUnwind(cx, regs.fp(), regs.pc); |
michael@0 | 1002 | switch (status) { |
michael@0 | 1003 | case JSTRAP_ERROR: |
michael@0 | 1004 | goto again; |
michael@0 | 1005 | |
michael@0 | 1006 | case JSTRAP_CONTINUE: |
michael@0 | 1007 | case JSTRAP_THROW: |
michael@0 | 1008 | break; |
michael@0 | 1009 | |
michael@0 | 1010 | case JSTRAP_RETURN: |
michael@0 | 1011 | ForcedReturn(cx, si, regs); |
michael@0 | 1012 | return SuccessfulReturnContinuation; |
michael@0 | 1013 | |
michael@0 | 1014 | default: |
michael@0 | 1015 | MOZ_ASSUME_UNREACHABLE("Invalid trap status"); |
michael@0 | 1016 | } |
michael@0 | 1017 | } |
michael@0 | 1018 | |
michael@0 | 1019 | RootedValue exception(cx); |
michael@0 | 1020 | for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) { |
michael@0 | 1021 | JSTryNote *tn = *tni; |
michael@0 | 1022 | |
michael@0 | 1023 | UnwindScope(cx, si, regs.fp()->script()->main() + tn->start); |
michael@0 | 1024 | |
michael@0 | 1025 | /* |
michael@0 | 1026 | * Set pc to the first bytecode after the the try note to point |
michael@0 | 1027 | * to the beginning of catch or finally or to [enditer] closing |
michael@0 | 1028 | * the for-in loop. |
michael@0 | 1029 | */ |
michael@0 | 1030 | regs.pc = regs.fp()->script()->main() + tn->start + tn->length; |
michael@0 | 1031 | regs.sp = regs.spForStackDepth(tn->stackDepth); |
michael@0 | 1032 | |
michael@0 | 1033 | switch (tn->kind) { |
michael@0 | 1034 | case JSTRY_CATCH: |
michael@0 | 1035 | /* Catch cannot intercept the closing of a generator. */ |
michael@0 | 1036 | if (!cx->getPendingException(&exception)) |
michael@0 | 1037 | return ErrorReturnContinuation; |
michael@0 | 1038 | if (exception.isMagic(JS_GENERATOR_CLOSING)) |
michael@0 | 1039 | break; |
michael@0 | 1040 | return CatchContinuation; |
michael@0 | 1041 | |
michael@0 | 1042 | case JSTRY_FINALLY: |
michael@0 | 1043 | return FinallyContinuation; |
michael@0 | 1044 | |
michael@0 | 1045 | case JSTRY_ITER: { |
michael@0 | 1046 | /* This is similar to JSOP_ENDITER in the interpreter loop. */ |
michael@0 | 1047 | JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER); |
michael@0 | 1048 | RootedObject obj(cx, ®s.sp[-1].toObject()); |
michael@0 | 1049 | bool ok = UnwindIteratorForException(cx, obj); |
michael@0 | 1050 | regs.sp -= 1; |
michael@0 | 1051 | if (!ok) |
michael@0 | 1052 | goto again; |
michael@0 | 1053 | break; |
michael@0 | 1054 | } |
michael@0 | 1055 | |
michael@0 | 1056 | case JSTRY_LOOP: |
michael@0 | 1057 | break; |
michael@0 | 1058 | } |
michael@0 | 1059 | } |
michael@0 | 1060 | |
michael@0 | 1061 | /* |
michael@0 | 1062 | * Propagate the exception or error to the caller unless the exception |
michael@0 | 1063 | * is an asynchronous return from a generator. |
michael@0 | 1064 | */ |
michael@0 | 1065 | if (cx->isExceptionPending()) { |
michael@0 | 1066 | RootedValue exception(cx); |
michael@0 | 1067 | if (!cx->getPendingException(&exception)) |
michael@0 | 1068 | return ErrorReturnContinuation; |
michael@0 | 1069 | |
michael@0 | 1070 | if (exception.isMagic(JS_GENERATOR_CLOSING)) { |
michael@0 | 1071 | cx->clearPendingException(); |
michael@0 | 1072 | ok = true; |
michael@0 | 1073 | regs.fp()->clearReturnValue(); |
michael@0 | 1074 | } |
michael@0 | 1075 | } |
michael@0 | 1076 | } else { |
michael@0 | 1077 | UnwindForUncatchableException(cx, regs); |
michael@0 | 1078 | } |
michael@0 | 1079 | |
michael@0 | 1080 | ForcedReturn(cx, si, regs); |
michael@0 | 1081 | return ok ? SuccessfulReturnContinuation : ErrorReturnContinuation; |
michael@0 | 1082 | } |
michael@0 | 1083 | |
michael@0 | 1084 | #define REGS (activation.regs()) |
michael@0 | 1085 | #define PUSH_COPY(v) do { *REGS.sp++ = (v); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0) |
michael@0 | 1086 | #define PUSH_COPY_SKIP_CHECK(v) *REGS.sp++ = (v) |
michael@0 | 1087 | #define PUSH_NULL() REGS.sp++->setNull() |
michael@0 | 1088 | #define PUSH_UNDEFINED() REGS.sp++->setUndefined() |
michael@0 | 1089 | #define PUSH_BOOLEAN(b) REGS.sp++->setBoolean(b) |
michael@0 | 1090 | #define PUSH_DOUBLE(d) REGS.sp++->setDouble(d) |
michael@0 | 1091 | #define PUSH_INT32(i) REGS.sp++->setInt32(i) |
michael@0 | 1092 | #define PUSH_STRING(s) do { REGS.sp++->setString(s); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0) |
michael@0 | 1093 | #define PUSH_OBJECT(obj) do { REGS.sp++->setObject(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0) |
michael@0 | 1094 | #define PUSH_OBJECT_OR_NULL(obj) do { REGS.sp++->setObjectOrNull(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0) |
michael@0 | 1095 | #define PUSH_HOLE() REGS.sp++->setMagic(JS_ELEMENTS_HOLE) |
michael@0 | 1096 | #define POP_COPY_TO(v) (v) = *--REGS.sp |
michael@0 | 1097 | #define POP_RETURN_VALUE() REGS.fp()->setReturnValue(*--REGS.sp) |
michael@0 | 1098 | |
michael@0 | 1099 | #define FETCH_OBJECT(cx, n, obj) \ |
michael@0 | 1100 | JS_BEGIN_MACRO \ |
michael@0 | 1101 | HandleValue val = REGS.stackHandleAt(n); \ |
michael@0 | 1102 | obj = ToObjectFromStack((cx), (val)); \ |
michael@0 | 1103 | if (!obj) \ |
michael@0 | 1104 | goto error; \ |
michael@0 | 1105 | JS_END_MACRO |
michael@0 | 1106 | |
michael@0 | 1107 | /* |
michael@0 | 1108 | * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but |
michael@0 | 1109 | * remain distinct for the decompiler. |
michael@0 | 1110 | */ |
michael@0 | 1111 | JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); |
michael@0 | 1112 | |
michael@0 | 1113 | /* See TRY_BRANCH_AFTER_COND. */ |
michael@0 | 1114 | JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH); |
michael@0 | 1115 | JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1); |
michael@0 | 1116 | |
michael@0 | 1117 | /* |
michael@0 | 1118 | * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle |
michael@0 | 1119 | * all cases, but we inline the most frequently taken paths here. |
michael@0 | 1120 | */ |
michael@0 | 1121 | bool |
michael@0 | 1122 | js::IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, MutableHandleValue rval) |
michael@0 | 1123 | { |
michael@0 | 1124 | if (iterobj->is<PropertyIteratorObject>()) { |
michael@0 | 1125 | NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator(); |
michael@0 | 1126 | if (ni->isKeyIter()) { |
michael@0 | 1127 | *cond = (ni->props_cursor < ni->props_end); |
michael@0 | 1128 | return true; |
michael@0 | 1129 | } |
michael@0 | 1130 | } |
michael@0 | 1131 | Rooted<JSObject*> iobj(cx, iterobj); |
michael@0 | 1132 | if (!js_IteratorMore(cx, iobj, rval)) |
michael@0 | 1133 | return false; |
michael@0 | 1134 | *cond = rval.isTrue(); |
michael@0 | 1135 | return true; |
michael@0 | 1136 | } |
michael@0 | 1137 | |
michael@0 | 1138 | bool |
michael@0 | 1139 | js::IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval) |
michael@0 | 1140 | { |
michael@0 | 1141 | if (iterobj->is<PropertyIteratorObject>()) { |
michael@0 | 1142 | NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator(); |
michael@0 | 1143 | if (ni->isKeyIter()) { |
michael@0 | 1144 | JS_ASSERT(ni->props_cursor < ni->props_end); |
michael@0 | 1145 | rval.setString(*ni->current()); |
michael@0 | 1146 | ni->incCursor(); |
michael@0 | 1147 | return true; |
michael@0 | 1148 | } |
michael@0 | 1149 | } |
michael@0 | 1150 | return js_IteratorNext(cx, iterobj, rval); |
michael@0 | 1151 | } |
michael@0 | 1152 | |
michael@0 | 1153 | /* |
michael@0 | 1154 | * Compute the implicit |this| parameter for a call expression where the callee |
michael@0 | 1155 | * funval was resolved from an unqualified name reference to a property on obj |
michael@0 | 1156 | * (an object on the scope chain). |
michael@0 | 1157 | * |
michael@0 | 1158 | * We can avoid computing |this| eagerly and push the implicit callee-coerced |
michael@0 | 1159 | * |this| value, undefined, if any of these conditions hold: |
michael@0 | 1160 | * |
michael@0 | 1161 | * 1. The nominal |this|, obj, is a global object. |
michael@0 | 1162 | * |
michael@0 | 1163 | * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this |
michael@0 | 1164 | * is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be |
michael@0 | 1165 | * censored with undefined. |
michael@0 | 1166 | * |
michael@0 | 1167 | * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with| |
michael@0 | 1168 | * statements and embedding-specific scope objects fall into this category. |
michael@0 | 1169 | * |
michael@0 | 1170 | * If the callee is a strict mode function, then code implementing JSOP_THIS |
michael@0 | 1171 | * in the interpreter and JITs will leave undefined as |this|. If funval is a |
michael@0 | 1172 | * function not in strict mode, JSOP_THIS code replaces undefined with funval's |
michael@0 | 1173 | * global. |
michael@0 | 1174 | * |
michael@0 | 1175 | * We set *vp to undefined early to reduce code size and bias this code for the |
michael@0 | 1176 | * common and future-friendly cases. |
michael@0 | 1177 | */ |
michael@0 | 1178 | static inline bool |
michael@0 | 1179 | ComputeImplicitThis(JSContext *cx, HandleObject obj, MutableHandleValue vp) |
michael@0 | 1180 | { |
michael@0 | 1181 | vp.setUndefined(); |
michael@0 | 1182 | |
michael@0 | 1183 | if (obj->is<GlobalObject>()) |
michael@0 | 1184 | return true; |
michael@0 | 1185 | |
michael@0 | 1186 | if (IsCacheableNonGlobalScope(obj)) |
michael@0 | 1187 | return true; |
michael@0 | 1188 | |
michael@0 | 1189 | JSObject *nobj = JSObject::thisObject(cx, obj); |
michael@0 | 1190 | if (!nobj) |
michael@0 | 1191 | return false; |
michael@0 | 1192 | |
michael@0 | 1193 | vp.setObject(*nobj); |
michael@0 | 1194 | return true; |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | static MOZ_ALWAYS_INLINE bool |
michael@0 | 1198 | AddOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res) |
michael@0 | 1199 | { |
michael@0 | 1200 | if (lhs.isInt32() && rhs.isInt32()) { |
michael@0 | 1201 | int32_t l = lhs.toInt32(), r = rhs.toInt32(); |
michael@0 | 1202 | int32_t t; |
michael@0 | 1203 | if (MOZ_LIKELY(SafeAdd(l, r, &t))) { |
michael@0 | 1204 | res.setInt32(t); |
michael@0 | 1205 | return true; |
michael@0 | 1206 | } |
michael@0 | 1207 | } |
michael@0 | 1208 | |
michael@0 | 1209 | if (!ToPrimitive(cx, lhs)) |
michael@0 | 1210 | return false; |
michael@0 | 1211 | if (!ToPrimitive(cx, rhs)) |
michael@0 | 1212 | return false; |
michael@0 | 1213 | |
michael@0 | 1214 | bool lIsString, rIsString; |
michael@0 | 1215 | if ((lIsString = lhs.isString()) | (rIsString = rhs.isString())) { |
michael@0 | 1216 | JSString *lstr, *rstr; |
michael@0 | 1217 | if (lIsString) { |
michael@0 | 1218 | lstr = lhs.toString(); |
michael@0 | 1219 | } else { |
michael@0 | 1220 | lstr = ToString<CanGC>(cx, lhs); |
michael@0 | 1221 | if (!lstr) |
michael@0 | 1222 | return false; |
michael@0 | 1223 | } |
michael@0 | 1224 | if (rIsString) { |
michael@0 | 1225 | rstr = rhs.toString(); |
michael@0 | 1226 | } else { |
michael@0 | 1227 | // Save/restore lstr in case of GC activity under ToString. |
michael@0 | 1228 | lhs.setString(lstr); |
michael@0 | 1229 | rstr = ToString<CanGC>(cx, rhs); |
michael@0 | 1230 | if (!rstr) |
michael@0 | 1231 | return false; |
michael@0 | 1232 | lstr = lhs.toString(); |
michael@0 | 1233 | } |
michael@0 | 1234 | JSString *str = ConcatStrings<NoGC>(cx, lstr, rstr); |
michael@0 | 1235 | if (!str) { |
michael@0 | 1236 | RootedString nlstr(cx, lstr), nrstr(cx, rstr); |
michael@0 | 1237 | str = ConcatStrings<CanGC>(cx, nlstr, nrstr); |
michael@0 | 1238 | if (!str) |
michael@0 | 1239 | return false; |
michael@0 | 1240 | } |
michael@0 | 1241 | res.setString(str); |
michael@0 | 1242 | } else { |
michael@0 | 1243 | double l, r; |
michael@0 | 1244 | if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) |
michael@0 | 1245 | return false; |
michael@0 | 1246 | res.setNumber(l + r); |
michael@0 | 1247 | } |
michael@0 | 1248 | |
michael@0 | 1249 | return true; |
michael@0 | 1250 | } |
michael@0 | 1251 | |
michael@0 | 1252 | static MOZ_ALWAYS_INLINE bool |
michael@0 | 1253 | SubOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res) |
michael@0 | 1254 | { |
michael@0 | 1255 | double d1, d2; |
michael@0 | 1256 | if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) |
michael@0 | 1257 | return false; |
michael@0 | 1258 | res.setNumber(d1 - d2); |
michael@0 | 1259 | return true; |
michael@0 | 1260 | } |
michael@0 | 1261 | |
michael@0 | 1262 | static MOZ_ALWAYS_INLINE bool |
michael@0 | 1263 | MulOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res) |
michael@0 | 1264 | { |
michael@0 | 1265 | double d1, d2; |
michael@0 | 1266 | if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) |
michael@0 | 1267 | return false; |
michael@0 | 1268 | res.setNumber(d1 * d2); |
michael@0 | 1269 | return true; |
michael@0 | 1270 | } |
michael@0 | 1271 | |
michael@0 | 1272 | static MOZ_ALWAYS_INLINE bool |
michael@0 | 1273 | DivOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res) |
michael@0 | 1274 | { |
michael@0 | 1275 | double d1, d2; |
michael@0 | 1276 | if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) |
michael@0 | 1277 | return false; |
michael@0 | 1278 | res.setNumber(NumberDiv(d1, d2)); |
michael@0 | 1279 | return true; |
michael@0 | 1280 | } |
michael@0 | 1281 | |
michael@0 | 1282 | static MOZ_ALWAYS_INLINE bool |
michael@0 | 1283 | ModOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res) |
michael@0 | 1284 | { |
michael@0 | 1285 | int32_t l, r; |
michael@0 | 1286 | if (lhs.isInt32() && rhs.isInt32() && |
michael@0 | 1287 | (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) { |
michael@0 | 1288 | int32_t mod = l % r; |
michael@0 | 1289 | res.setInt32(mod); |
michael@0 | 1290 | return true; |
michael@0 | 1291 | } |
michael@0 | 1292 | |
michael@0 | 1293 | double d1, d2; |
michael@0 | 1294 | if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2)) |
michael@0 | 1295 | return false; |
michael@0 | 1296 | |
michael@0 | 1297 | res.setNumber(NumberMod(d1, d2)); |
michael@0 | 1298 | return true; |
michael@0 | 1299 | } |
michael@0 | 1300 | |
michael@0 | 1301 | static MOZ_ALWAYS_INLINE bool |
michael@0 | 1302 | SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, const Value &value, |
michael@0 | 1303 | bool strict, JSScript *script = nullptr, jsbytecode *pc = nullptr) |
michael@0 | 1304 | { |
michael@0 | 1305 | types::TypeScript::MonitorAssign(cx, obj, id); |
michael@0 | 1306 | |
michael@0 | 1307 | #ifdef JS_ION |
michael@0 | 1308 | if (obj->isNative() && JSID_IS_INT(id)) { |
michael@0 | 1309 | uint32_t length = obj->getDenseInitializedLength(); |
michael@0 | 1310 | int32_t i = JSID_TO_INT(id); |
michael@0 | 1311 | if ((uint32_t)i >= length) { |
michael@0 | 1312 | // Annotate script if provided with information (e.g. baseline) |
michael@0 | 1313 | if (script && script->hasBaselineScript() && *pc == JSOP_SETELEM) |
michael@0 | 1314 | script->baselineScript()->noteArrayWriteHole(script->pcToOffset(pc)); |
michael@0 | 1315 | } |
michael@0 | 1316 | } |
michael@0 | 1317 | #endif |
michael@0 | 1318 | |
michael@0 | 1319 | if (obj->isNative() && !JSID_IS_INT(id) && !obj->setHadElementsAccess(cx)) |
michael@0 | 1320 | return false; |
michael@0 | 1321 | |
michael@0 | 1322 | RootedValue tmp(cx, value); |
michael@0 | 1323 | return JSObject::setGeneric(cx, obj, obj, id, &tmp, strict); |
michael@0 | 1324 | } |
michael@0 | 1325 | |
michael@0 | 1326 | static MOZ_NEVER_INLINE bool |
michael@0 | 1327 | Interpret(JSContext *cx, RunState &state) |
michael@0 | 1328 | { |
michael@0 | 1329 | /* |
michael@0 | 1330 | * Define macros for an interpreter loop. Opcode dispatch may be either by a |
michael@0 | 1331 | * switch statement or by indirect goto (aka a threaded interpreter), depending |
michael@0 | 1332 | * on compiler support. |
michael@0 | 1333 | * |
michael@0 | 1334 | * Threaded interpretation appears to be well-supported by GCC 3 and higher. |
michael@0 | 1335 | * IBM's C compiler when run with the right options (e.g., -qlanglvl=extended) |
michael@0 | 1336 | * also supports threading. Ditto the SunPro C compiler. |
michael@0 | 1337 | */ |
michael@0 | 1338 | #if (__GNUC__ >= 3 || \ |
michael@0 | 1339 | (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \ |
michael@0 | 1340 | __SUNPRO_C >= 0x570) |
michael@0 | 1341 | // Non-standard but faster indirect-goto-based dispatch. |
michael@0 | 1342 | # define INTERPRETER_LOOP() |
michael@0 | 1343 | # define CASE(OP) label_##OP: |
michael@0 | 1344 | # define DEFAULT() label_default: |
michael@0 | 1345 | # define DISPATCH_TO(OP) goto *addresses[(OP)] |
michael@0 | 1346 | |
michael@0 | 1347 | # define LABEL(X) (&&label_##X) |
michael@0 | 1348 | |
michael@0 | 1349 | // Use addresses instead of offsets to optimize for runtime speed over |
michael@0 | 1350 | // load-time relocation overhead. |
michael@0 | 1351 | static const void *const addresses[EnableInterruptsPseudoOpcode + 1] = { |
michael@0 | 1352 | # define OPCODE_LABEL(op, ...) LABEL(op), |
michael@0 | 1353 | FOR_EACH_OPCODE(OPCODE_LABEL) |
michael@0 | 1354 | # undef OPCODE_LABEL |
michael@0 | 1355 | # define TRAILING_LABEL(v) \ |
michael@0 | 1356 | ((v) == EnableInterruptsPseudoOpcode \ |
michael@0 | 1357 | ? LABEL(EnableInterruptsPseudoOpcode) \ |
michael@0 | 1358 | : LABEL(default)), |
michael@0 | 1359 | FOR_EACH_TRAILING_UNUSED_OPCODE(TRAILING_LABEL) |
michael@0 | 1360 | # undef TRAILING_LABEL |
michael@0 | 1361 | }; |
michael@0 | 1362 | #else |
michael@0 | 1363 | // Portable switch-based dispatch. |
michael@0 | 1364 | # define INTERPRETER_LOOP() the_switch: switch (switchOp) |
michael@0 | 1365 | # define CASE(OP) case OP: |
michael@0 | 1366 | # define DEFAULT() default: |
michael@0 | 1367 | # define DISPATCH_TO(OP) \ |
michael@0 | 1368 | JS_BEGIN_MACRO \ |
michael@0 | 1369 | switchOp = (OP); \ |
michael@0 | 1370 | goto the_switch; \ |
michael@0 | 1371 | JS_END_MACRO |
michael@0 | 1372 | |
michael@0 | 1373 | // This variable is effectively a parameter to the_switch. |
michael@0 | 1374 | jsbytecode switchOp; |
michael@0 | 1375 | #endif |
michael@0 | 1376 | |
michael@0 | 1377 | /* |
michael@0 | 1378 | * Increment REGS.pc by N, load the opcode at that position, |
michael@0 | 1379 | * and jump to the code to execute it. |
michael@0 | 1380 | * |
michael@0 | 1381 | * When Debugger puts a script in single-step mode, all js::Interpret |
michael@0 | 1382 | * invocations that might be presently running that script must have |
michael@0 | 1383 | * interrupts enabled. It's not practical to simply check |
michael@0 | 1384 | * script->stepModeEnabled() at each point some callee could have changed |
michael@0 | 1385 | * it, because there are so many places js::Interpret could possibly cause |
michael@0 | 1386 | * JavaScript to run: each place an object might be coerced to a primitive |
michael@0 | 1387 | * or a number, for example. So instead, we expose a simple mechanism to |
michael@0 | 1388 | * let Debugger tweak the affected js::Interpret frames when an onStep |
michael@0 | 1389 | * handler is added: calling activation.enableInterruptsUnconditionally() |
michael@0 | 1390 | * will enable interrupts, and activation.opMask() is or'd with the opcode |
michael@0 | 1391 | * to implement a simple alternate dispatch. |
michael@0 | 1392 | */ |
michael@0 | 1393 | #define ADVANCE_AND_DISPATCH(N) \ |
michael@0 | 1394 | JS_BEGIN_MACRO \ |
michael@0 | 1395 | REGS.pc += (N); \ |
michael@0 | 1396 | SANITY_CHECKS(); \ |
michael@0 | 1397 | DISPATCH_TO(*REGS.pc | activation.opMask()); \ |
michael@0 | 1398 | JS_END_MACRO |
michael@0 | 1399 | |
michael@0 | 1400 | /* |
michael@0 | 1401 | * Shorthand for the common sequence at the end of a fixed-size opcode. |
michael@0 | 1402 | */ |
michael@0 | 1403 | #define END_CASE(OP) ADVANCE_AND_DISPATCH(OP##_LENGTH); |
michael@0 | 1404 | |
michael@0 | 1405 | /* |
michael@0 | 1406 | * Prepare to call a user-supplied branch handler, and abort the script |
michael@0 | 1407 | * if it returns false. |
michael@0 | 1408 | */ |
michael@0 | 1409 | #define CHECK_BRANCH() \ |
michael@0 | 1410 | JS_BEGIN_MACRO \ |
michael@0 | 1411 | if (!CheckForInterrupt(cx)) \ |
michael@0 | 1412 | goto error; \ |
michael@0 | 1413 | JS_END_MACRO |
michael@0 | 1414 | |
michael@0 | 1415 | /* |
michael@0 | 1416 | * This is a simple wrapper around ADVANCE_AND_DISPATCH which also does |
michael@0 | 1417 | * a CHECK_BRANCH() if n is not positive, which possibly indicates that it |
michael@0 | 1418 | * is the backedge of a loop. |
michael@0 | 1419 | */ |
michael@0 | 1420 | #define BRANCH(n) \ |
michael@0 | 1421 | JS_BEGIN_MACRO \ |
michael@0 | 1422 | int32_t nlen = (n); \ |
michael@0 | 1423 | if (nlen <= 0) \ |
michael@0 | 1424 | CHECK_BRANCH(); \ |
michael@0 | 1425 | ADVANCE_AND_DISPATCH(nlen); \ |
michael@0 | 1426 | JS_END_MACRO |
michael@0 | 1427 | |
michael@0 | 1428 | #define LOAD_DOUBLE(PCOFF, dbl) \ |
michael@0 | 1429 | ((dbl) = script->getConst(GET_UINT32_INDEX(REGS.pc + (PCOFF))).toDouble()) |
michael@0 | 1430 | |
michael@0 | 1431 | #define SET_SCRIPT(s) \ |
michael@0 | 1432 | JS_BEGIN_MACRO \ |
michael@0 | 1433 | script = (s); \ |
michael@0 | 1434 | if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts()) \ |
michael@0 | 1435 | activation.enableInterruptsUnconditionally(); \ |
michael@0 | 1436 | JS_END_MACRO |
michael@0 | 1437 | |
michael@0 | 1438 | #define SANITY_CHECKS() \ |
michael@0 | 1439 | JS_BEGIN_MACRO \ |
michael@0 | 1440 | js::gc::MaybeVerifyBarriers(cx); \ |
michael@0 | 1441 | JS_ASSERT_IF(script->hasScriptCounts(), \ |
michael@0 | 1442 | activation.opMask() == EnableInterruptsPseudoOpcode); \ |
michael@0 | 1443 | JS_END_MACRO |
michael@0 | 1444 | |
michael@0 | 1445 | gc::MaybeVerifyBarriers(cx, true); |
michael@0 | 1446 | JS_ASSERT(!cx->compartment()->activeAnalysis); |
michael@0 | 1447 | |
michael@0 | 1448 | InterpreterFrame *entryFrame = state.pushInterpreterFrame(cx); |
michael@0 | 1449 | if (!entryFrame) |
michael@0 | 1450 | return false; |
michael@0 | 1451 | |
michael@0 | 1452 | InterpreterActivation activation(state, cx, entryFrame); |
michael@0 | 1453 | |
michael@0 | 1454 | /* The script is used frequently, so keep a local copy. */ |
michael@0 | 1455 | RootedScript script(cx); |
michael@0 | 1456 | SET_SCRIPT(REGS.fp()->script()); |
michael@0 | 1457 | |
michael@0 | 1458 | TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); |
michael@0 | 1459 | uint32_t scriptLogId = TraceLogCreateTextId(logger, script); |
michael@0 | 1460 | TraceLogStartEvent(logger, scriptLogId); |
michael@0 | 1461 | TraceLogStartEvent(logger, TraceLogger::Interpreter); |
michael@0 | 1462 | |
michael@0 | 1463 | /* |
michael@0 | 1464 | * Pool of rooters for use in this interpreter frame. References to these |
michael@0 | 1465 | * are used for local variables within interpreter cases. This avoids |
michael@0 | 1466 | * creating new rooters each time an interpreter case is entered, and also |
michael@0 | 1467 | * correctness pitfalls due to incorrect compilation of destructor calls |
michael@0 | 1468 | * around computed gotos. |
michael@0 | 1469 | */ |
michael@0 | 1470 | RootedValue rootValue0(cx), rootValue1(cx); |
michael@0 | 1471 | RootedString rootString0(cx), rootString1(cx); |
michael@0 | 1472 | RootedObject rootObject0(cx), rootObject1(cx), rootObject2(cx); |
michael@0 | 1473 | RootedFunction rootFunction0(cx); |
michael@0 | 1474 | RootedTypeObject rootType0(cx); |
michael@0 | 1475 | RootedPropertyName rootName0(cx); |
michael@0 | 1476 | RootedId rootId0(cx); |
michael@0 | 1477 | RootedShape rootShape0(cx); |
michael@0 | 1478 | RootedScript rootScript0(cx); |
michael@0 | 1479 | DebugOnly<uint32_t> blockDepth; |
michael@0 | 1480 | |
michael@0 | 1481 | if (MOZ_UNLIKELY(REGS.fp()->isGeneratorFrame())) { |
michael@0 | 1482 | JS_ASSERT(script->containsPC(REGS.pc)); |
michael@0 | 1483 | JS_ASSERT(REGS.stackDepth() <= script->nslots()); |
michael@0 | 1484 | |
michael@0 | 1485 | /* |
michael@0 | 1486 | * To support generator_throw and to catch ignored exceptions, |
michael@0 | 1487 | * fail if cx->isExceptionPending() is true. |
michael@0 | 1488 | */ |
michael@0 | 1489 | if (cx->isExceptionPending()) { |
michael@0 | 1490 | probes::EnterScript(cx, script, script->functionNonDelazifying(), REGS.fp()); |
michael@0 | 1491 | goto error; |
michael@0 | 1492 | } |
michael@0 | 1493 | } |
michael@0 | 1494 | |
michael@0 | 1495 | /* State communicated between non-local jumps: */ |
michael@0 | 1496 | bool interpReturnOK; |
michael@0 | 1497 | |
michael@0 | 1498 | if (!activation.entryFrame()->isGeneratorFrame()) { |
michael@0 | 1499 | if (!activation.entryFrame()->prologue(cx)) |
michael@0 | 1500 | goto error; |
michael@0 | 1501 | } else { |
michael@0 | 1502 | if (!probes::EnterScript(cx, script, script->functionNonDelazifying(), |
michael@0 | 1503 | activation.entryFrame())) |
michael@0 | 1504 | { |
michael@0 | 1505 | goto error; |
michael@0 | 1506 | } |
michael@0 | 1507 | } |
michael@0 | 1508 | if (MOZ_UNLIKELY(cx->compartment()->debugMode())) { |
michael@0 | 1509 | JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc); |
michael@0 | 1510 | switch (status) { |
michael@0 | 1511 | case JSTRAP_CONTINUE: |
michael@0 | 1512 | break; |
michael@0 | 1513 | case JSTRAP_RETURN: |
michael@0 | 1514 | ForcedReturn(cx, REGS); |
michael@0 | 1515 | goto successful_return_continuation; |
michael@0 | 1516 | case JSTRAP_THROW: |
michael@0 | 1517 | case JSTRAP_ERROR: |
michael@0 | 1518 | goto error; |
michael@0 | 1519 | default: |
michael@0 | 1520 | MOZ_ASSUME_UNREACHABLE("bad ScriptDebugPrologue status"); |
michael@0 | 1521 | } |
michael@0 | 1522 | } |
michael@0 | 1523 | |
michael@0 | 1524 | if (cx->runtime()->profilingScripts || cx->runtime()->debugHooks.interruptHook) |
michael@0 | 1525 | activation.enableInterruptsUnconditionally(); |
michael@0 | 1526 | |
michael@0 | 1527 | // Enter the interpreter loop starting at the current pc. |
michael@0 | 1528 | ADVANCE_AND_DISPATCH(0); |
michael@0 | 1529 | |
michael@0 | 1530 | INTERPRETER_LOOP() { |
michael@0 | 1531 | |
michael@0 | 1532 | CASE(EnableInterruptsPseudoOpcode) |
michael@0 | 1533 | { |
michael@0 | 1534 | bool moreInterrupts = false; |
michael@0 | 1535 | jsbytecode op = *REGS.pc; |
michael@0 | 1536 | |
michael@0 | 1537 | if (cx->runtime()->profilingScripts) { |
michael@0 | 1538 | if (!script->hasScriptCounts()) |
michael@0 | 1539 | script->initScriptCounts(cx); |
michael@0 | 1540 | moreInterrupts = true; |
michael@0 | 1541 | } |
michael@0 | 1542 | |
michael@0 | 1543 | if (script->hasScriptCounts()) { |
michael@0 | 1544 | PCCounts counts = script->getPCCounts(REGS.pc); |
michael@0 | 1545 | counts.get(PCCounts::BASE_INTERP)++; |
michael@0 | 1546 | moreInterrupts = true; |
michael@0 | 1547 | } |
michael@0 | 1548 | |
michael@0 | 1549 | if (cx->compartment()->debugMode()) { |
michael@0 | 1550 | JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook; |
michael@0 | 1551 | if (hook || script->stepModeEnabled()) { |
michael@0 | 1552 | RootedValue rval(cx); |
michael@0 | 1553 | JSTrapStatus status = JSTRAP_CONTINUE; |
michael@0 | 1554 | if (hook) |
michael@0 | 1555 | status = hook(cx, script, REGS.pc, rval.address(), |
michael@0 | 1556 | cx->runtime()->debugHooks.interruptHookData); |
michael@0 | 1557 | if (status == JSTRAP_CONTINUE && script->stepModeEnabled()) |
michael@0 | 1558 | status = Debugger::onSingleStep(cx, &rval); |
michael@0 | 1559 | switch (status) { |
michael@0 | 1560 | case JSTRAP_ERROR: |
michael@0 | 1561 | goto error; |
michael@0 | 1562 | case JSTRAP_CONTINUE: |
michael@0 | 1563 | break; |
michael@0 | 1564 | case JSTRAP_RETURN: |
michael@0 | 1565 | REGS.fp()->setReturnValue(rval); |
michael@0 | 1566 | ForcedReturn(cx, REGS); |
michael@0 | 1567 | goto successful_return_continuation; |
michael@0 | 1568 | case JSTRAP_THROW: |
michael@0 | 1569 | cx->setPendingException(rval); |
michael@0 | 1570 | goto error; |
michael@0 | 1571 | default:; |
michael@0 | 1572 | } |
michael@0 | 1573 | moreInterrupts = true; |
michael@0 | 1574 | } |
michael@0 | 1575 | |
michael@0 | 1576 | if (script->hasAnyBreakpointsOrStepMode()) |
michael@0 | 1577 | moreInterrupts = true; |
michael@0 | 1578 | |
michael@0 | 1579 | if (script->hasBreakpointsAt(REGS.pc)) { |
michael@0 | 1580 | RootedValue rval(cx); |
michael@0 | 1581 | JSTrapStatus status = Debugger::onTrap(cx, &rval); |
michael@0 | 1582 | switch (status) { |
michael@0 | 1583 | case JSTRAP_ERROR: |
michael@0 | 1584 | goto error; |
michael@0 | 1585 | case JSTRAP_RETURN: |
michael@0 | 1586 | REGS.fp()->setReturnValue(rval); |
michael@0 | 1587 | ForcedReturn(cx, REGS); |
michael@0 | 1588 | goto successful_return_continuation; |
michael@0 | 1589 | case JSTRAP_THROW: |
michael@0 | 1590 | cx->setPendingException(rval); |
michael@0 | 1591 | goto error; |
michael@0 | 1592 | default: |
michael@0 | 1593 | break; |
michael@0 | 1594 | } |
michael@0 | 1595 | JS_ASSERT(status == JSTRAP_CONTINUE); |
michael@0 | 1596 | JS_ASSERT(rval.isInt32() && rval.toInt32() == op); |
michael@0 | 1597 | } |
michael@0 | 1598 | } |
michael@0 | 1599 | |
michael@0 | 1600 | JS_ASSERT(activation.opMask() == EnableInterruptsPseudoOpcode); |
michael@0 | 1601 | if (!moreInterrupts) |
michael@0 | 1602 | activation.clearInterruptsMask(); |
michael@0 | 1603 | |
michael@0 | 1604 | /* Commence executing the actual opcode. */ |
michael@0 | 1605 | SANITY_CHECKS(); |
michael@0 | 1606 | DISPATCH_TO(op); |
michael@0 | 1607 | } |
michael@0 | 1608 | |
michael@0 | 1609 | /* Various 1-byte no-ops. */ |
michael@0 | 1610 | CASE(JSOP_NOP) |
michael@0 | 1611 | CASE(JSOP_UNUSED2) |
michael@0 | 1612 | CASE(JSOP_UNUSED45) |
michael@0 | 1613 | CASE(JSOP_UNUSED46) |
michael@0 | 1614 | CASE(JSOP_UNUSED47) |
michael@0 | 1615 | CASE(JSOP_UNUSED48) |
michael@0 | 1616 | CASE(JSOP_UNUSED49) |
michael@0 | 1617 | CASE(JSOP_UNUSED50) |
michael@0 | 1618 | CASE(JSOP_UNUSED51) |
michael@0 | 1619 | CASE(JSOP_UNUSED52) |
michael@0 | 1620 | CASE(JSOP_UNUSED57) |
michael@0 | 1621 | CASE(JSOP_UNUSED101) |
michael@0 | 1622 | CASE(JSOP_UNUSED102) |
michael@0 | 1623 | CASE(JSOP_UNUSED103) |
michael@0 | 1624 | CASE(JSOP_UNUSED104) |
michael@0 | 1625 | CASE(JSOP_UNUSED105) |
michael@0 | 1626 | CASE(JSOP_UNUSED107) |
michael@0 | 1627 | CASE(JSOP_UNUSED124) |
michael@0 | 1628 | CASE(JSOP_UNUSED125) |
michael@0 | 1629 | CASE(JSOP_UNUSED126) |
michael@0 | 1630 | CASE(JSOP_UNUSED138) |
michael@0 | 1631 | CASE(JSOP_UNUSED139) |
michael@0 | 1632 | CASE(JSOP_UNUSED140) |
michael@0 | 1633 | CASE(JSOP_UNUSED141) |
michael@0 | 1634 | CASE(JSOP_UNUSED142) |
michael@0 | 1635 | CASE(JSOP_UNUSED146) |
michael@0 | 1636 | CASE(JSOP_UNUSED147) |
michael@0 | 1637 | CASE(JSOP_UNUSED148) |
michael@0 | 1638 | CASE(JSOP_BACKPATCH) |
michael@0 | 1639 | CASE(JSOP_UNUSED150) |
michael@0 | 1640 | CASE(JSOP_UNUSED156) |
michael@0 | 1641 | CASE(JSOP_UNUSED157) |
michael@0 | 1642 | CASE(JSOP_UNUSED158) |
michael@0 | 1643 | CASE(JSOP_UNUSED159) |
michael@0 | 1644 | CASE(JSOP_UNUSED161) |
michael@0 | 1645 | CASE(JSOP_UNUSED162) |
michael@0 | 1646 | CASE(JSOP_UNUSED163) |
michael@0 | 1647 | CASE(JSOP_UNUSED164) |
michael@0 | 1648 | CASE(JSOP_UNUSED165) |
michael@0 | 1649 | CASE(JSOP_UNUSED166) |
michael@0 | 1650 | CASE(JSOP_UNUSED167) |
michael@0 | 1651 | CASE(JSOP_UNUSED168) |
michael@0 | 1652 | CASE(JSOP_UNUSED169) |
michael@0 | 1653 | CASE(JSOP_UNUSED170) |
michael@0 | 1654 | CASE(JSOP_UNUSED171) |
michael@0 | 1655 | CASE(JSOP_UNUSED172) |
michael@0 | 1656 | CASE(JSOP_UNUSED173) |
michael@0 | 1657 | CASE(JSOP_UNUSED174) |
michael@0 | 1658 | CASE(JSOP_UNUSED175) |
michael@0 | 1659 | CASE(JSOP_UNUSED176) |
michael@0 | 1660 | CASE(JSOP_UNUSED177) |
michael@0 | 1661 | CASE(JSOP_UNUSED178) |
michael@0 | 1662 | CASE(JSOP_UNUSED179) |
michael@0 | 1663 | CASE(JSOP_UNUSED180) |
michael@0 | 1664 | CASE(JSOP_UNUSED181) |
michael@0 | 1665 | CASE(JSOP_UNUSED182) |
michael@0 | 1666 | CASE(JSOP_UNUSED183) |
michael@0 | 1667 | CASE(JSOP_UNUSED185) |
michael@0 | 1668 | CASE(JSOP_UNUSED186) |
michael@0 | 1669 | CASE(JSOP_UNUSED187) |
michael@0 | 1670 | CASE(JSOP_UNUSED189) |
michael@0 | 1671 | CASE(JSOP_UNUSED190) |
michael@0 | 1672 | CASE(JSOP_UNUSED191) |
michael@0 | 1673 | CASE(JSOP_UNUSED192) |
michael@0 | 1674 | CASE(JSOP_UNUSED196) |
michael@0 | 1675 | CASE(JSOP_UNUSED201) |
michael@0 | 1676 | CASE(JSOP_UNUSED205) |
michael@0 | 1677 | CASE(JSOP_UNUSED206) |
michael@0 | 1678 | CASE(JSOP_UNUSED207) |
michael@0 | 1679 | CASE(JSOP_UNUSED208) |
michael@0 | 1680 | CASE(JSOP_UNUSED209) |
michael@0 | 1681 | CASE(JSOP_UNUSED210) |
michael@0 | 1682 | CASE(JSOP_UNUSED211) |
michael@0 | 1683 | CASE(JSOP_UNUSED212) |
michael@0 | 1684 | CASE(JSOP_UNUSED213) |
michael@0 | 1685 | CASE(JSOP_UNUSED219) |
michael@0 | 1686 | CASE(JSOP_UNUSED220) |
michael@0 | 1687 | CASE(JSOP_UNUSED221) |
michael@0 | 1688 | CASE(JSOP_UNUSED222) |
michael@0 | 1689 | CASE(JSOP_UNUSED223) |
michael@0 | 1690 | CASE(JSOP_CONDSWITCH) |
michael@0 | 1691 | CASE(JSOP_TRY) |
michael@0 | 1692 | { |
michael@0 | 1693 | JS_ASSERT(js_CodeSpec[*REGS.pc].length == 1); |
michael@0 | 1694 | ADVANCE_AND_DISPATCH(1); |
michael@0 | 1695 | } |
michael@0 | 1696 | |
michael@0 | 1697 | CASE(JSOP_LOOPHEAD) |
michael@0 | 1698 | END_CASE(JSOP_LOOPHEAD) |
michael@0 | 1699 | |
michael@0 | 1700 | CASE(JSOP_LABEL) |
michael@0 | 1701 | END_CASE(JSOP_LABEL) |
michael@0 | 1702 | |
michael@0 | 1703 | CASE(JSOP_LOOPENTRY) |
michael@0 | 1704 | |
michael@0 | 1705 | #ifdef JS_ION |
michael@0 | 1706 | // Attempt on-stack replacement with Baseline code. |
michael@0 | 1707 | if (jit::IsBaselineEnabled(cx)) { |
michael@0 | 1708 | jit::MethodStatus status = jit::CanEnterBaselineAtBranch(cx, REGS.fp(), false); |
michael@0 | 1709 | if (status == jit::Method_Error) |
michael@0 | 1710 | goto error; |
michael@0 | 1711 | if (status == jit::Method_Compiled) { |
michael@0 | 1712 | bool wasSPS = REGS.fp()->hasPushedSPSFrame(); |
michael@0 | 1713 | jit::IonExecStatus maybeOsr = jit::EnterBaselineAtBranch(cx, REGS.fp(), REGS.pc); |
michael@0 | 1714 | |
michael@0 | 1715 | // We failed to call into baseline at all, so treat as an error. |
michael@0 | 1716 | if (maybeOsr == jit::IonExec_Aborted) |
michael@0 | 1717 | goto error; |
michael@0 | 1718 | |
michael@0 | 1719 | interpReturnOK = (maybeOsr == jit::IonExec_Ok); |
michael@0 | 1720 | |
michael@0 | 1721 | // Pop the SPS frame pushed by the interpreter. (The compiled version of the |
michael@0 | 1722 | // function popped a copy of the frame pushed by the OSR trampoline.) |
michael@0 | 1723 | if (wasSPS) |
michael@0 | 1724 | cx->runtime()->spsProfiler.exit(script, script->functionNonDelazifying()); |
michael@0 | 1725 | |
michael@0 | 1726 | if (activation.entryFrame() != REGS.fp()) |
michael@0 | 1727 | goto jit_return_pop_frame; |
michael@0 | 1728 | goto leave_on_safe_point; |
michael@0 | 1729 | } |
michael@0 | 1730 | } |
michael@0 | 1731 | #endif /* JS_ION */ |
michael@0 | 1732 | |
michael@0 | 1733 | END_CASE(JSOP_LOOPENTRY) |
michael@0 | 1734 | |
michael@0 | 1735 | CASE(JSOP_LINENO) |
michael@0 | 1736 | END_CASE(JSOP_LINENO) |
michael@0 | 1737 | |
michael@0 | 1738 | CASE(JSOP_UNDEFINED) |
michael@0 | 1739 | PUSH_UNDEFINED(); |
michael@0 | 1740 | END_CASE(JSOP_UNDEFINED) |
michael@0 | 1741 | |
michael@0 | 1742 | CASE(JSOP_POP) |
michael@0 | 1743 | REGS.sp--; |
michael@0 | 1744 | END_CASE(JSOP_POP) |
michael@0 | 1745 | |
michael@0 | 1746 | CASE(JSOP_POPN) |
michael@0 | 1747 | JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth()); |
michael@0 | 1748 | REGS.sp -= GET_UINT16(REGS.pc); |
michael@0 | 1749 | END_CASE(JSOP_POPN) |
michael@0 | 1750 | |
michael@0 | 1751 | CASE(JSOP_DUPAT) |
michael@0 | 1752 | { |
michael@0 | 1753 | JS_ASSERT(GET_UINT24(REGS.pc) < REGS.stackDepth()); |
michael@0 | 1754 | unsigned i = GET_UINT24(REGS.pc); |
michael@0 | 1755 | const Value &rref = REGS.sp[-int(i + 1)]; |
michael@0 | 1756 | PUSH_COPY(rref); |
michael@0 | 1757 | } |
michael@0 | 1758 | END_CASE(JSOP_DUPAT) |
michael@0 | 1759 | |
michael@0 | 1760 | CASE(JSOP_SETRVAL) |
michael@0 | 1761 | POP_RETURN_VALUE(); |
michael@0 | 1762 | END_CASE(JSOP_SETRVAL) |
michael@0 | 1763 | |
michael@0 | 1764 | CASE(JSOP_ENTERWITH) |
michael@0 | 1765 | { |
michael@0 | 1766 | RootedValue &val = rootValue0; |
michael@0 | 1767 | RootedObject &staticWith = rootObject0; |
michael@0 | 1768 | val = REGS.sp[-1]; |
michael@0 | 1769 | REGS.sp--; |
michael@0 | 1770 | staticWith = script->getObject(REGS.pc); |
michael@0 | 1771 | |
michael@0 | 1772 | if (!EnterWithOperation(cx, REGS.fp(), val, staticWith)) |
michael@0 | 1773 | goto error; |
michael@0 | 1774 | } |
michael@0 | 1775 | END_CASE(JSOP_ENTERWITH) |
michael@0 | 1776 | |
michael@0 | 1777 | CASE(JSOP_LEAVEWITH) |
michael@0 | 1778 | REGS.fp()->popWith(cx); |
michael@0 | 1779 | END_CASE(JSOP_LEAVEWITH) |
michael@0 | 1780 | |
michael@0 | 1781 | CASE(JSOP_RETURN) |
michael@0 | 1782 | POP_RETURN_VALUE(); |
michael@0 | 1783 | /* FALL THROUGH */ |
michael@0 | 1784 | |
michael@0 | 1785 | CASE(JSOP_RETRVAL) |
michael@0 | 1786 | { |
michael@0 | 1787 | /* |
michael@0 | 1788 | * When the inlined frame exits with an exception or an error, ok will be |
michael@0 | 1789 | * false after the inline_return label. |
michael@0 | 1790 | */ |
michael@0 | 1791 | CHECK_BRANCH(); |
michael@0 | 1792 | |
michael@0 | 1793 | successful_return_continuation: |
michael@0 | 1794 | interpReturnOK = true; |
michael@0 | 1795 | return_continuation: |
michael@0 | 1796 | if (activation.entryFrame() != REGS.fp()) |
michael@0 | 1797 | inline_return: |
michael@0 | 1798 | { |
michael@0 | 1799 | // Stop the engine. (No details about which engine exactly, could be |
michael@0 | 1800 | // interpreter, Baseline or IonMonkey.) |
michael@0 | 1801 | TraceLogStopEvent(logger); |
michael@0 | 1802 | // Stop the script. (Again no details about which script exactly.) |
michael@0 | 1803 | TraceLogStopEvent(logger); |
michael@0 | 1804 | |
michael@0 | 1805 | if (MOZ_UNLIKELY(cx->compartment()->debugMode())) |
michael@0 | 1806 | interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK); |
michael@0 | 1807 | |
michael@0 | 1808 | if (!REGS.fp()->isYielding()) |
michael@0 | 1809 | REGS.fp()->epilogue(cx); |
michael@0 | 1810 | else |
michael@0 | 1811 | probes::ExitScript(cx, script, script->functionNonDelazifying(), |
michael@0 | 1812 | REGS.fp()->hasPushedSPSFrame()); |
michael@0 | 1813 | |
michael@0 | 1814 | #if defined(JS_ION) |
michael@0 | 1815 | jit_return_pop_frame: |
michael@0 | 1816 | #endif |
michael@0 | 1817 | |
michael@0 | 1818 | activation.popInlineFrame(REGS.fp()); |
michael@0 | 1819 | SET_SCRIPT(REGS.fp()->script()); |
michael@0 | 1820 | |
michael@0 | 1821 | #if defined(JS_ION) |
michael@0 | 1822 | jit_return: |
michael@0 | 1823 | #endif |
michael@0 | 1824 | |
michael@0 | 1825 | JS_ASSERT(js_CodeSpec[*REGS.pc].format & JOF_INVOKE); |
michael@0 | 1826 | |
michael@0 | 1827 | /* Resume execution in the calling frame. */ |
michael@0 | 1828 | if (MOZ_LIKELY(interpReturnOK)) { |
michael@0 | 1829 | TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]); |
michael@0 | 1830 | |
michael@0 | 1831 | ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH); |
michael@0 | 1832 | } |
michael@0 | 1833 | |
michael@0 | 1834 | /* Increment pc so that |sp - fp->slots == ReconstructStackDepth(pc)|. */ |
michael@0 | 1835 | REGS.pc += JSOP_CALL_LENGTH; |
michael@0 | 1836 | goto error; |
michael@0 | 1837 | } else { |
michael@0 | 1838 | JS_ASSERT(REGS.stackDepth() == 0); |
michael@0 | 1839 | } |
michael@0 | 1840 | goto exit; |
michael@0 | 1841 | } |
michael@0 | 1842 | |
michael@0 | 1843 | CASE(JSOP_DEFAULT) |
michael@0 | 1844 | REGS.sp--; |
michael@0 | 1845 | /* FALL THROUGH */ |
michael@0 | 1846 | CASE(JSOP_GOTO) |
michael@0 | 1847 | { |
michael@0 | 1848 | BRANCH(GET_JUMP_OFFSET(REGS.pc)); |
michael@0 | 1849 | } |
michael@0 | 1850 | |
michael@0 | 1851 | CASE(JSOP_IFEQ) |
michael@0 | 1852 | { |
michael@0 | 1853 | bool cond = ToBooleanOp(REGS); |
michael@0 | 1854 | REGS.sp--; |
michael@0 | 1855 | if (!cond) |
michael@0 | 1856 | BRANCH(GET_JUMP_OFFSET(REGS.pc)); |
michael@0 | 1857 | } |
michael@0 | 1858 | END_CASE(JSOP_IFEQ) |
michael@0 | 1859 | |
michael@0 | 1860 | CASE(JSOP_IFNE) |
michael@0 | 1861 | { |
michael@0 | 1862 | bool cond = ToBooleanOp(REGS); |
michael@0 | 1863 | REGS.sp--; |
michael@0 | 1864 | if (cond) |
michael@0 | 1865 | BRANCH(GET_JUMP_OFFSET(REGS.pc)); |
michael@0 | 1866 | } |
michael@0 | 1867 | END_CASE(JSOP_IFNE) |
michael@0 | 1868 | |
michael@0 | 1869 | CASE(JSOP_OR) |
michael@0 | 1870 | { |
michael@0 | 1871 | bool cond = ToBooleanOp(REGS); |
michael@0 | 1872 | if (cond) |
michael@0 | 1873 | ADVANCE_AND_DISPATCH(GET_JUMP_OFFSET(REGS.pc)); |
michael@0 | 1874 | } |
michael@0 | 1875 | END_CASE(JSOP_OR) |
michael@0 | 1876 | |
michael@0 | 1877 | CASE(JSOP_AND) |
michael@0 | 1878 | { |
michael@0 | 1879 | bool cond = ToBooleanOp(REGS); |
michael@0 | 1880 | if (!cond) |
michael@0 | 1881 | ADVANCE_AND_DISPATCH(GET_JUMP_OFFSET(REGS.pc)); |
michael@0 | 1882 | } |
michael@0 | 1883 | END_CASE(JSOP_AND) |
michael@0 | 1884 | |
michael@0 | 1885 | #define FETCH_ELEMENT_ID(n, id) \ |
michael@0 | 1886 | JS_BEGIN_MACRO \ |
michael@0 | 1887 | if (!ValueToId<CanGC>(cx, REGS.stackHandleAt(n), &(id))) \ |
michael@0 | 1888 | goto error; \ |
michael@0 | 1889 | JS_END_MACRO |
michael@0 | 1890 | |
michael@0 | 1891 | #define TRY_BRANCH_AFTER_COND(cond,spdec) \ |
michael@0 | 1892 | JS_BEGIN_MACRO \ |
michael@0 | 1893 | JS_ASSERT(js_CodeSpec[*REGS.pc].length == 1); \ |
michael@0 | 1894 | unsigned diff_ = (unsigned) GET_UINT8(REGS.pc) - (unsigned) JSOP_IFEQ; \ |
michael@0 | 1895 | if (diff_ <= 1) { \ |
michael@0 | 1896 | REGS.sp -= (spdec); \ |
michael@0 | 1897 | if ((cond) == (diff_ != 0)) { \ |
michael@0 | 1898 | ++REGS.pc; \ |
michael@0 | 1899 | BRANCH(GET_JUMP_OFFSET(REGS.pc)); \ |
michael@0 | 1900 | } \ |
michael@0 | 1901 | ADVANCE_AND_DISPATCH(1 + JSOP_IFEQ_LENGTH); \ |
michael@0 | 1902 | } \ |
michael@0 | 1903 | JS_END_MACRO |
michael@0 | 1904 | |
michael@0 | 1905 | CASE(JSOP_IN) |
michael@0 | 1906 | { |
michael@0 | 1907 | HandleValue rref = REGS.stackHandleAt(-1); |
michael@0 | 1908 | if (!rref.isObject()) { |
michael@0 | 1909 | js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, js::NullPtr()); |
michael@0 | 1910 | goto error; |
michael@0 | 1911 | } |
michael@0 | 1912 | RootedObject &obj = rootObject0; |
michael@0 | 1913 | obj = &rref.toObject(); |
michael@0 | 1914 | RootedId &id = rootId0; |
michael@0 | 1915 | FETCH_ELEMENT_ID(-2, id); |
michael@0 | 1916 | RootedObject &obj2 = rootObject1; |
michael@0 | 1917 | RootedShape &prop = rootShape0; |
michael@0 | 1918 | if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop)) |
michael@0 | 1919 | goto error; |
michael@0 | 1920 | bool cond = prop != nullptr; |
michael@0 | 1921 | prop = nullptr; |
michael@0 | 1922 | TRY_BRANCH_AFTER_COND(cond, 2); |
michael@0 | 1923 | REGS.sp--; |
michael@0 | 1924 | REGS.sp[-1].setBoolean(cond); |
michael@0 | 1925 | } |
michael@0 | 1926 | END_CASE(JSOP_IN) |
michael@0 | 1927 | |
michael@0 | 1928 | CASE(JSOP_ITER) |
michael@0 | 1929 | { |
michael@0 | 1930 | JS_ASSERT(REGS.stackDepth() >= 1); |
michael@0 | 1931 | uint8_t flags = GET_UINT8(REGS.pc); |
michael@0 | 1932 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 1933 | if (!ValueToIterator(cx, flags, res)) |
michael@0 | 1934 | goto error; |
michael@0 | 1935 | JS_ASSERT(!res.isPrimitive()); |
michael@0 | 1936 | } |
michael@0 | 1937 | END_CASE(JSOP_ITER) |
michael@0 | 1938 | |
michael@0 | 1939 | CASE(JSOP_MOREITER) |
michael@0 | 1940 | { |
michael@0 | 1941 | JS_ASSERT(REGS.stackDepth() >= 1); |
michael@0 | 1942 | JS_ASSERT(REGS.sp[-1].isObject()); |
michael@0 | 1943 | PUSH_NULL(); |
michael@0 | 1944 | bool cond; |
michael@0 | 1945 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 1946 | if (!IteratorMore(cx, ®S.sp[-2].toObject(), &cond, res)) |
michael@0 | 1947 | goto error; |
michael@0 | 1948 | REGS.sp[-1].setBoolean(cond); |
michael@0 | 1949 | } |
michael@0 | 1950 | END_CASE(JSOP_MOREITER) |
michael@0 | 1951 | |
michael@0 | 1952 | CASE(JSOP_ITERNEXT) |
michael@0 | 1953 | { |
michael@0 | 1954 | JS_ASSERT(REGS.sp[-1].isObject()); |
michael@0 | 1955 | PUSH_NULL(); |
michael@0 | 1956 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 1957 | RootedObject &obj = rootObject0; |
michael@0 | 1958 | obj = ®S.sp[-2].toObject(); |
michael@0 | 1959 | if (!IteratorNext(cx, obj, res)) |
michael@0 | 1960 | goto error; |
michael@0 | 1961 | } |
michael@0 | 1962 | END_CASE(JSOP_ITERNEXT) |
michael@0 | 1963 | |
michael@0 | 1964 | CASE(JSOP_ENDITER) |
michael@0 | 1965 | { |
michael@0 | 1966 | JS_ASSERT(REGS.stackDepth() >= 1); |
michael@0 | 1967 | RootedObject &obj = rootObject0; |
michael@0 | 1968 | obj = ®S.sp[-1].toObject(); |
michael@0 | 1969 | bool ok = CloseIterator(cx, obj); |
michael@0 | 1970 | REGS.sp--; |
michael@0 | 1971 | if (!ok) |
michael@0 | 1972 | goto error; |
michael@0 | 1973 | } |
michael@0 | 1974 | END_CASE(JSOP_ENDITER) |
michael@0 | 1975 | |
michael@0 | 1976 | CASE(JSOP_DUP) |
michael@0 | 1977 | { |
michael@0 | 1978 | JS_ASSERT(REGS.stackDepth() >= 1); |
michael@0 | 1979 | const Value &rref = REGS.sp[-1]; |
michael@0 | 1980 | PUSH_COPY(rref); |
michael@0 | 1981 | } |
michael@0 | 1982 | END_CASE(JSOP_DUP) |
michael@0 | 1983 | |
michael@0 | 1984 | CASE(JSOP_DUP2) |
michael@0 | 1985 | { |
michael@0 | 1986 | JS_ASSERT(REGS.stackDepth() >= 2); |
michael@0 | 1987 | const Value &lref = REGS.sp[-2]; |
michael@0 | 1988 | const Value &rref = REGS.sp[-1]; |
michael@0 | 1989 | PUSH_COPY(lref); |
michael@0 | 1990 | PUSH_COPY(rref); |
michael@0 | 1991 | } |
michael@0 | 1992 | END_CASE(JSOP_DUP2) |
michael@0 | 1993 | |
michael@0 | 1994 | CASE(JSOP_SWAP) |
michael@0 | 1995 | { |
michael@0 | 1996 | JS_ASSERT(REGS.stackDepth() >= 2); |
michael@0 | 1997 | Value &lref = REGS.sp[-2]; |
michael@0 | 1998 | Value &rref = REGS.sp[-1]; |
michael@0 | 1999 | lref.swap(rref); |
michael@0 | 2000 | } |
michael@0 | 2001 | END_CASE(JSOP_SWAP) |
michael@0 | 2002 | |
michael@0 | 2003 | CASE(JSOP_PICK) |
michael@0 | 2004 | { |
michael@0 | 2005 | unsigned i = GET_UINT8(REGS.pc); |
michael@0 | 2006 | JS_ASSERT(REGS.stackDepth() >= i + 1); |
michael@0 | 2007 | Value lval = REGS.sp[-int(i + 1)]; |
michael@0 | 2008 | memmove(REGS.sp - (i + 1), REGS.sp - i, sizeof(Value) * i); |
michael@0 | 2009 | REGS.sp[-1] = lval; |
michael@0 | 2010 | } |
michael@0 | 2011 | END_CASE(JSOP_PICK) |
michael@0 | 2012 | |
michael@0 | 2013 | CASE(JSOP_SETCONST) |
michael@0 | 2014 | { |
michael@0 | 2015 | RootedPropertyName &name = rootName0; |
michael@0 | 2016 | name = script->getName(REGS.pc); |
michael@0 | 2017 | |
michael@0 | 2018 | RootedValue &rval = rootValue0; |
michael@0 | 2019 | rval = REGS.sp[-1]; |
michael@0 | 2020 | |
michael@0 | 2021 | RootedObject &obj = rootObject0; |
michael@0 | 2022 | obj = ®S.fp()->varObj(); |
michael@0 | 2023 | |
michael@0 | 2024 | if (!SetConstOperation(cx, obj, name, rval)) |
michael@0 | 2025 | goto error; |
michael@0 | 2026 | } |
michael@0 | 2027 | END_CASE(JSOP_SETCONST); |
michael@0 | 2028 | |
michael@0 | 2029 | CASE(JSOP_BINDGNAME) |
michael@0 | 2030 | PUSH_OBJECT(REGS.fp()->global()); |
michael@0 | 2031 | END_CASE(JSOP_BINDGNAME) |
michael@0 | 2032 | |
michael@0 | 2033 | CASE(JSOP_BINDINTRINSIC) |
michael@0 | 2034 | PUSH_OBJECT(*cx->global()->intrinsicsHolder()); |
michael@0 | 2035 | END_CASE(JSOP_BINDINTRINSIC) |
michael@0 | 2036 | |
michael@0 | 2037 | CASE(JSOP_BINDNAME) |
michael@0 | 2038 | { |
michael@0 | 2039 | RootedObject &scopeChain = rootObject0; |
michael@0 | 2040 | scopeChain = REGS.fp()->scopeChain(); |
michael@0 | 2041 | |
michael@0 | 2042 | RootedPropertyName &name = rootName0; |
michael@0 | 2043 | name = script->getName(REGS.pc); |
michael@0 | 2044 | |
michael@0 | 2045 | /* Assigning to an undeclared name adds a property to the global object. */ |
michael@0 | 2046 | RootedObject &scope = rootObject1; |
michael@0 | 2047 | if (!LookupNameWithGlobalDefault(cx, name, scopeChain, &scope)) |
michael@0 | 2048 | goto error; |
michael@0 | 2049 | |
michael@0 | 2050 | PUSH_OBJECT(*scope); |
michael@0 | 2051 | } |
michael@0 | 2052 | END_CASE(JSOP_BINDNAME) |
michael@0 | 2053 | |
michael@0 | 2054 | #define BITWISE_OP(OP) \ |
michael@0 | 2055 | JS_BEGIN_MACRO \ |
michael@0 | 2056 | int32_t i, j; \ |
michael@0 | 2057 | if (!ToInt32(cx, REGS.stackHandleAt(-2), &i)) \ |
michael@0 | 2058 | goto error; \ |
michael@0 | 2059 | if (!ToInt32(cx, REGS.stackHandleAt(-1), &j)) \ |
michael@0 | 2060 | goto error; \ |
michael@0 | 2061 | i = i OP j; \ |
michael@0 | 2062 | REGS.sp--; \ |
michael@0 | 2063 | REGS.sp[-1].setInt32(i); \ |
michael@0 | 2064 | JS_END_MACRO |
michael@0 | 2065 | |
michael@0 | 2066 | CASE(JSOP_BITOR) |
michael@0 | 2067 | BITWISE_OP(|); |
michael@0 | 2068 | END_CASE(JSOP_BITOR) |
michael@0 | 2069 | |
michael@0 | 2070 | CASE(JSOP_BITXOR) |
michael@0 | 2071 | BITWISE_OP(^); |
michael@0 | 2072 | END_CASE(JSOP_BITXOR) |
michael@0 | 2073 | |
michael@0 | 2074 | CASE(JSOP_BITAND) |
michael@0 | 2075 | BITWISE_OP(&); |
michael@0 | 2076 | END_CASE(JSOP_BITAND) |
michael@0 | 2077 | |
michael@0 | 2078 | #undef BITWISE_OP |
michael@0 | 2079 | |
michael@0 | 2080 | CASE(JSOP_EQ) |
michael@0 | 2081 | if (!LooseEqualityOp<true>(cx, REGS)) |
michael@0 | 2082 | goto error; |
michael@0 | 2083 | END_CASE(JSOP_EQ) |
michael@0 | 2084 | |
michael@0 | 2085 | CASE(JSOP_NE) |
michael@0 | 2086 | if (!LooseEqualityOp<false>(cx, REGS)) |
michael@0 | 2087 | goto error; |
michael@0 | 2088 | END_CASE(JSOP_NE) |
michael@0 | 2089 | |
michael@0 | 2090 | #define STRICT_EQUALITY_OP(OP, COND) \ |
michael@0 | 2091 | JS_BEGIN_MACRO \ |
michael@0 | 2092 | const Value &rref = REGS.sp[-1]; \ |
michael@0 | 2093 | const Value &lref = REGS.sp[-2]; \ |
michael@0 | 2094 | bool equal; \ |
michael@0 | 2095 | if (!StrictlyEqual(cx, lref, rref, &equal)) \ |
michael@0 | 2096 | goto error; \ |
michael@0 | 2097 | (COND) = equal OP true; \ |
michael@0 | 2098 | REGS.sp--; \ |
michael@0 | 2099 | JS_END_MACRO |
michael@0 | 2100 | |
michael@0 | 2101 | CASE(JSOP_STRICTEQ) |
michael@0 | 2102 | { |
michael@0 | 2103 | bool cond; |
michael@0 | 2104 | STRICT_EQUALITY_OP(==, cond); |
michael@0 | 2105 | REGS.sp[-1].setBoolean(cond); |
michael@0 | 2106 | } |
michael@0 | 2107 | END_CASE(JSOP_STRICTEQ) |
michael@0 | 2108 | |
michael@0 | 2109 | CASE(JSOP_STRICTNE) |
michael@0 | 2110 | { |
michael@0 | 2111 | bool cond; |
michael@0 | 2112 | STRICT_EQUALITY_OP(!=, cond); |
michael@0 | 2113 | REGS.sp[-1].setBoolean(cond); |
michael@0 | 2114 | } |
michael@0 | 2115 | END_CASE(JSOP_STRICTNE) |
michael@0 | 2116 | |
michael@0 | 2117 | CASE(JSOP_CASE) |
michael@0 | 2118 | { |
michael@0 | 2119 | bool cond; |
michael@0 | 2120 | STRICT_EQUALITY_OP(==, cond); |
michael@0 | 2121 | if (cond) { |
michael@0 | 2122 | REGS.sp--; |
michael@0 | 2123 | BRANCH(GET_JUMP_OFFSET(REGS.pc)); |
michael@0 | 2124 | } |
michael@0 | 2125 | } |
michael@0 | 2126 | END_CASE(JSOP_CASE) |
michael@0 | 2127 | |
michael@0 | 2128 | #undef STRICT_EQUALITY_OP |
michael@0 | 2129 | |
michael@0 | 2130 | CASE(JSOP_LT) |
michael@0 | 2131 | { |
michael@0 | 2132 | bool cond; |
michael@0 | 2133 | MutableHandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2134 | MutableHandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2135 | if (!LessThanOperation(cx, lval, rval, &cond)) |
michael@0 | 2136 | goto error; |
michael@0 | 2137 | TRY_BRANCH_AFTER_COND(cond, 2); |
michael@0 | 2138 | REGS.sp[-2].setBoolean(cond); |
michael@0 | 2139 | REGS.sp--; |
michael@0 | 2140 | } |
michael@0 | 2141 | END_CASE(JSOP_LT) |
michael@0 | 2142 | |
michael@0 | 2143 | CASE(JSOP_LE) |
michael@0 | 2144 | { |
michael@0 | 2145 | bool cond; |
michael@0 | 2146 | MutableHandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2147 | MutableHandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2148 | if (!LessThanOrEqualOperation(cx, lval, rval, &cond)) |
michael@0 | 2149 | goto error; |
michael@0 | 2150 | TRY_BRANCH_AFTER_COND(cond, 2); |
michael@0 | 2151 | REGS.sp[-2].setBoolean(cond); |
michael@0 | 2152 | REGS.sp--; |
michael@0 | 2153 | } |
michael@0 | 2154 | END_CASE(JSOP_LE) |
michael@0 | 2155 | |
michael@0 | 2156 | CASE(JSOP_GT) |
michael@0 | 2157 | { |
michael@0 | 2158 | bool cond; |
michael@0 | 2159 | MutableHandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2160 | MutableHandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2161 | if (!GreaterThanOperation(cx, lval, rval, &cond)) |
michael@0 | 2162 | goto error; |
michael@0 | 2163 | TRY_BRANCH_AFTER_COND(cond, 2); |
michael@0 | 2164 | REGS.sp[-2].setBoolean(cond); |
michael@0 | 2165 | REGS.sp--; |
michael@0 | 2166 | } |
michael@0 | 2167 | END_CASE(JSOP_GT) |
michael@0 | 2168 | |
michael@0 | 2169 | CASE(JSOP_GE) |
michael@0 | 2170 | { |
michael@0 | 2171 | bool cond; |
michael@0 | 2172 | MutableHandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2173 | MutableHandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2174 | if (!GreaterThanOrEqualOperation(cx, lval, rval, &cond)) |
michael@0 | 2175 | goto error; |
michael@0 | 2176 | TRY_BRANCH_AFTER_COND(cond, 2); |
michael@0 | 2177 | REGS.sp[-2].setBoolean(cond); |
michael@0 | 2178 | REGS.sp--; |
michael@0 | 2179 | } |
michael@0 | 2180 | END_CASE(JSOP_GE) |
michael@0 | 2181 | |
michael@0 | 2182 | #define SIGNED_SHIFT_OP(OP) \ |
michael@0 | 2183 | JS_BEGIN_MACRO \ |
michael@0 | 2184 | int32_t i, j; \ |
michael@0 | 2185 | if (!ToInt32(cx, REGS.stackHandleAt(-2), &i)) \ |
michael@0 | 2186 | goto error; \ |
michael@0 | 2187 | if (!ToInt32(cx, REGS.stackHandleAt(-1), &j)) \ |
michael@0 | 2188 | goto error; \ |
michael@0 | 2189 | i = i OP (j & 31); \ |
michael@0 | 2190 | REGS.sp--; \ |
michael@0 | 2191 | REGS.sp[-1].setInt32(i); \ |
michael@0 | 2192 | JS_END_MACRO |
michael@0 | 2193 | |
michael@0 | 2194 | CASE(JSOP_LSH) |
michael@0 | 2195 | SIGNED_SHIFT_OP(<<); |
michael@0 | 2196 | END_CASE(JSOP_LSH) |
michael@0 | 2197 | |
michael@0 | 2198 | CASE(JSOP_RSH) |
michael@0 | 2199 | SIGNED_SHIFT_OP(>>); |
michael@0 | 2200 | END_CASE(JSOP_RSH) |
michael@0 | 2201 | |
michael@0 | 2202 | #undef SIGNED_SHIFT_OP |
michael@0 | 2203 | |
michael@0 | 2204 | CASE(JSOP_URSH) |
michael@0 | 2205 | { |
michael@0 | 2206 | HandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2207 | HandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2208 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2209 | if (!UrshOperation(cx, lval, rval, res)) |
michael@0 | 2210 | goto error; |
michael@0 | 2211 | REGS.sp--; |
michael@0 | 2212 | } |
michael@0 | 2213 | END_CASE(JSOP_URSH) |
michael@0 | 2214 | |
michael@0 | 2215 | CASE(JSOP_ADD) |
michael@0 | 2216 | { |
michael@0 | 2217 | MutableHandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2218 | MutableHandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2219 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2220 | if (!AddOperation(cx, lval, rval, res)) |
michael@0 | 2221 | goto error; |
michael@0 | 2222 | REGS.sp--; |
michael@0 | 2223 | } |
michael@0 | 2224 | END_CASE(JSOP_ADD) |
michael@0 | 2225 | |
michael@0 | 2226 | CASE(JSOP_SUB) |
michael@0 | 2227 | { |
michael@0 | 2228 | RootedValue &lval = rootValue0, &rval = rootValue1; |
michael@0 | 2229 | lval = REGS.sp[-2]; |
michael@0 | 2230 | rval = REGS.sp[-1]; |
michael@0 | 2231 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2232 | if (!SubOperation(cx, lval, rval, res)) |
michael@0 | 2233 | goto error; |
michael@0 | 2234 | REGS.sp--; |
michael@0 | 2235 | } |
michael@0 | 2236 | END_CASE(JSOP_SUB) |
michael@0 | 2237 | |
michael@0 | 2238 | CASE(JSOP_MUL) |
michael@0 | 2239 | { |
michael@0 | 2240 | RootedValue &lval = rootValue0, &rval = rootValue1; |
michael@0 | 2241 | lval = REGS.sp[-2]; |
michael@0 | 2242 | rval = REGS.sp[-1]; |
michael@0 | 2243 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2244 | if (!MulOperation(cx, lval, rval, res)) |
michael@0 | 2245 | goto error; |
michael@0 | 2246 | REGS.sp--; |
michael@0 | 2247 | } |
michael@0 | 2248 | END_CASE(JSOP_MUL) |
michael@0 | 2249 | |
michael@0 | 2250 | CASE(JSOP_DIV) |
michael@0 | 2251 | { |
michael@0 | 2252 | RootedValue &lval = rootValue0, &rval = rootValue1; |
michael@0 | 2253 | lval = REGS.sp[-2]; |
michael@0 | 2254 | rval = REGS.sp[-1]; |
michael@0 | 2255 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2256 | if (!DivOperation(cx, lval, rval, res)) |
michael@0 | 2257 | goto error; |
michael@0 | 2258 | REGS.sp--; |
michael@0 | 2259 | } |
michael@0 | 2260 | END_CASE(JSOP_DIV) |
michael@0 | 2261 | |
michael@0 | 2262 | CASE(JSOP_MOD) |
michael@0 | 2263 | { |
michael@0 | 2264 | RootedValue &lval = rootValue0, &rval = rootValue1; |
michael@0 | 2265 | lval = REGS.sp[-2]; |
michael@0 | 2266 | rval = REGS.sp[-1]; |
michael@0 | 2267 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2268 | if (!ModOperation(cx, lval, rval, res)) |
michael@0 | 2269 | goto error; |
michael@0 | 2270 | REGS.sp--; |
michael@0 | 2271 | } |
michael@0 | 2272 | END_CASE(JSOP_MOD) |
michael@0 | 2273 | |
michael@0 | 2274 | CASE(JSOP_NOT) |
michael@0 | 2275 | { |
michael@0 | 2276 | bool cond = ToBooleanOp(REGS); |
michael@0 | 2277 | REGS.sp--; |
michael@0 | 2278 | PUSH_BOOLEAN(!cond); |
michael@0 | 2279 | } |
michael@0 | 2280 | END_CASE(JSOP_NOT) |
michael@0 | 2281 | |
michael@0 | 2282 | CASE(JSOP_BITNOT) |
michael@0 | 2283 | { |
michael@0 | 2284 | int32_t i; |
michael@0 | 2285 | HandleValue value = REGS.stackHandleAt(-1); |
michael@0 | 2286 | if (!BitNot(cx, value, &i)) |
michael@0 | 2287 | goto error; |
michael@0 | 2288 | REGS.sp[-1].setInt32(i); |
michael@0 | 2289 | } |
michael@0 | 2290 | END_CASE(JSOP_BITNOT) |
michael@0 | 2291 | |
michael@0 | 2292 | CASE(JSOP_NEG) |
michael@0 | 2293 | { |
michael@0 | 2294 | RootedValue &val = rootValue0; |
michael@0 | 2295 | val = REGS.sp[-1]; |
michael@0 | 2296 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 2297 | if (!NegOperation(cx, script, REGS.pc, val, res)) |
michael@0 | 2298 | goto error; |
michael@0 | 2299 | } |
michael@0 | 2300 | END_CASE(JSOP_NEG) |
michael@0 | 2301 | |
michael@0 | 2302 | CASE(JSOP_POS) |
michael@0 | 2303 | if (!ToNumber(cx, REGS.stackHandleAt(-1))) |
michael@0 | 2304 | goto error; |
michael@0 | 2305 | END_CASE(JSOP_POS) |
michael@0 | 2306 | |
michael@0 | 2307 | CASE(JSOP_DELNAME) |
michael@0 | 2308 | { |
michael@0 | 2309 | /* Strict mode code should never contain JSOP_DELNAME opcodes. */ |
michael@0 | 2310 | JS_ASSERT(!script->strict()); |
michael@0 | 2311 | |
michael@0 | 2312 | RootedPropertyName &name = rootName0; |
michael@0 | 2313 | name = script->getName(REGS.pc); |
michael@0 | 2314 | |
michael@0 | 2315 | RootedObject &scopeObj = rootObject0; |
michael@0 | 2316 | scopeObj = REGS.fp()->scopeChain(); |
michael@0 | 2317 | |
michael@0 | 2318 | PUSH_BOOLEAN(true); |
michael@0 | 2319 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 2320 | if (!DeleteNameOperation(cx, name, scopeObj, res)) |
michael@0 | 2321 | goto error; |
michael@0 | 2322 | } |
michael@0 | 2323 | END_CASE(JSOP_DELNAME) |
michael@0 | 2324 | |
michael@0 | 2325 | CASE(JSOP_DELPROP) |
michael@0 | 2326 | { |
michael@0 | 2327 | RootedPropertyName &name = rootName0; |
michael@0 | 2328 | name = script->getName(REGS.pc); |
michael@0 | 2329 | |
michael@0 | 2330 | RootedObject &obj = rootObject0; |
michael@0 | 2331 | FETCH_OBJECT(cx, -1, obj); |
michael@0 | 2332 | |
michael@0 | 2333 | bool succeeded; |
michael@0 | 2334 | if (!JSObject::deleteProperty(cx, obj, name, &succeeded)) |
michael@0 | 2335 | goto error; |
michael@0 | 2336 | if (!succeeded && script->strict()) { |
michael@0 | 2337 | obj->reportNotConfigurable(cx, NameToId(name)); |
michael@0 | 2338 | goto error; |
michael@0 | 2339 | } |
michael@0 | 2340 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 2341 | res.setBoolean(succeeded); |
michael@0 | 2342 | } |
michael@0 | 2343 | END_CASE(JSOP_DELPROP) |
michael@0 | 2344 | |
michael@0 | 2345 | CASE(JSOP_DELELEM) |
michael@0 | 2346 | { |
michael@0 | 2347 | /* Fetch the left part and resolve it to a non-null object. */ |
michael@0 | 2348 | RootedObject &obj = rootObject0; |
michael@0 | 2349 | FETCH_OBJECT(cx, -2, obj); |
michael@0 | 2350 | |
michael@0 | 2351 | RootedValue &propval = rootValue0; |
michael@0 | 2352 | propval = REGS.sp[-1]; |
michael@0 | 2353 | |
michael@0 | 2354 | bool succeeded; |
michael@0 | 2355 | if (!JSObject::deleteByValue(cx, obj, propval, &succeeded)) |
michael@0 | 2356 | goto error; |
michael@0 | 2357 | if (!succeeded && script->strict()) { |
michael@0 | 2358 | // XXX This observably calls ToString(propval). We should convert to |
michael@0 | 2359 | // PropertyKey and use that to delete, and to report an error if |
michael@0 | 2360 | // necessary! |
michael@0 | 2361 | RootedId id(cx); |
michael@0 | 2362 | if (!ValueToId<CanGC>(cx, propval, &id)) |
michael@0 | 2363 | goto error; |
michael@0 | 2364 | obj->reportNotConfigurable(cx, id); |
michael@0 | 2365 | goto error; |
michael@0 | 2366 | } |
michael@0 | 2367 | |
michael@0 | 2368 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2369 | res.setBoolean(succeeded); |
michael@0 | 2370 | REGS.sp--; |
michael@0 | 2371 | } |
michael@0 | 2372 | END_CASE(JSOP_DELELEM) |
michael@0 | 2373 | |
michael@0 | 2374 | CASE(JSOP_TOID) |
michael@0 | 2375 | { |
michael@0 | 2376 | /* |
michael@0 | 2377 | * Increment or decrement requires use to lookup the same property twice, |
michael@0 | 2378 | * but we need to avoid the observable stringification the second time. |
michael@0 | 2379 | * There must be an object value below the id, which will not be popped. |
michael@0 | 2380 | */ |
michael@0 | 2381 | RootedValue &objval = rootValue0, &idval = rootValue1; |
michael@0 | 2382 | objval = REGS.sp[-2]; |
michael@0 | 2383 | idval = REGS.sp[-1]; |
michael@0 | 2384 | |
michael@0 | 2385 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 2386 | if (!ToIdOperation(cx, script, REGS.pc, objval, idval, res)) |
michael@0 | 2387 | goto error; |
michael@0 | 2388 | } |
michael@0 | 2389 | END_CASE(JSOP_TOID) |
michael@0 | 2390 | |
michael@0 | 2391 | CASE(JSOP_TYPEOFEXPR) |
michael@0 | 2392 | CASE(JSOP_TYPEOF) |
michael@0 | 2393 | { |
michael@0 | 2394 | REGS.sp[-1].setString(TypeOfOperation(REGS.sp[-1], cx->runtime())); |
michael@0 | 2395 | } |
michael@0 | 2396 | END_CASE(JSOP_TYPEOF) |
michael@0 | 2397 | |
michael@0 | 2398 | CASE(JSOP_VOID) |
michael@0 | 2399 | REGS.sp[-1].setUndefined(); |
michael@0 | 2400 | END_CASE(JSOP_VOID) |
michael@0 | 2401 | |
michael@0 | 2402 | CASE(JSOP_THIS) |
michael@0 | 2403 | if (!ComputeThis(cx, REGS.fp())) |
michael@0 | 2404 | goto error; |
michael@0 | 2405 | PUSH_COPY(REGS.fp()->thisValue()); |
michael@0 | 2406 | END_CASE(JSOP_THIS) |
michael@0 | 2407 | |
michael@0 | 2408 | CASE(JSOP_GETPROP) |
michael@0 | 2409 | CASE(JSOP_GETXPROP) |
michael@0 | 2410 | CASE(JSOP_LENGTH) |
michael@0 | 2411 | CASE(JSOP_CALLPROP) |
michael@0 | 2412 | { |
michael@0 | 2413 | |
michael@0 | 2414 | MutableHandleValue lval = REGS.stackHandleAt(-1); |
michael@0 | 2415 | if (!GetPropertyOperation(cx, REGS.fp(), script, REGS.pc, lval, lval)) |
michael@0 | 2416 | goto error; |
michael@0 | 2417 | |
michael@0 | 2418 | TypeScript::Monitor(cx, script, REGS.pc, lval); |
michael@0 | 2419 | assertSameCompartmentDebugOnly(cx, lval); |
michael@0 | 2420 | } |
michael@0 | 2421 | END_CASE(JSOP_GETPROP) |
michael@0 | 2422 | |
michael@0 | 2423 | CASE(JSOP_SETINTRINSIC) |
michael@0 | 2424 | { |
michael@0 | 2425 | HandleValue value = REGS.stackHandleAt(-1); |
michael@0 | 2426 | |
michael@0 | 2427 | if (!SetIntrinsicOperation(cx, script, REGS.pc, value)) |
michael@0 | 2428 | goto error; |
michael@0 | 2429 | |
michael@0 | 2430 | REGS.sp[-2] = REGS.sp[-1]; |
michael@0 | 2431 | REGS.sp--; |
michael@0 | 2432 | } |
michael@0 | 2433 | END_CASE(JSOP_SETINTRINSIC) |
michael@0 | 2434 | |
michael@0 | 2435 | CASE(JSOP_SETGNAME) |
michael@0 | 2436 | CASE(JSOP_SETNAME) |
michael@0 | 2437 | { |
michael@0 | 2438 | RootedObject &scope = rootObject0; |
michael@0 | 2439 | scope = ®S.sp[-2].toObject(); |
michael@0 | 2440 | |
michael@0 | 2441 | HandleValue value = REGS.stackHandleAt(-1); |
michael@0 | 2442 | |
michael@0 | 2443 | if (!SetNameOperation(cx, script, REGS.pc, scope, value)) |
michael@0 | 2444 | goto error; |
michael@0 | 2445 | |
michael@0 | 2446 | REGS.sp[-2] = REGS.sp[-1]; |
michael@0 | 2447 | REGS.sp--; |
michael@0 | 2448 | } |
michael@0 | 2449 | END_CASE(JSOP_SETNAME) |
michael@0 | 2450 | |
michael@0 | 2451 | CASE(JSOP_SETPROP) |
michael@0 | 2452 | { |
michael@0 | 2453 | HandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2454 | HandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2455 | |
michael@0 | 2456 | if (!SetPropertyOperation(cx, script, REGS.pc, lval, rval)) |
michael@0 | 2457 | goto error; |
michael@0 | 2458 | |
michael@0 | 2459 | REGS.sp[-2] = REGS.sp[-1]; |
michael@0 | 2460 | REGS.sp--; |
michael@0 | 2461 | } |
michael@0 | 2462 | END_CASE(JSOP_SETPROP) |
michael@0 | 2463 | |
michael@0 | 2464 | CASE(JSOP_GETELEM) |
michael@0 | 2465 | CASE(JSOP_CALLELEM) |
michael@0 | 2466 | { |
michael@0 | 2467 | MutableHandleValue lval = REGS.stackHandleAt(-2); |
michael@0 | 2468 | HandleValue rval = REGS.stackHandleAt(-1); |
michael@0 | 2469 | MutableHandleValue res = REGS.stackHandleAt(-2); |
michael@0 | 2470 | |
michael@0 | 2471 | bool done = false; |
michael@0 | 2472 | if (!GetElemOptimizedArguments(cx, REGS.fp(), lval, rval, res, &done)) |
michael@0 | 2473 | goto error; |
michael@0 | 2474 | |
michael@0 | 2475 | if (!done) { |
michael@0 | 2476 | if (!GetElementOperation(cx, JSOp(*REGS.pc), lval, rval, res)) |
michael@0 | 2477 | goto error; |
michael@0 | 2478 | } |
michael@0 | 2479 | |
michael@0 | 2480 | TypeScript::Monitor(cx, script, REGS.pc, res); |
michael@0 | 2481 | REGS.sp--; |
michael@0 | 2482 | } |
michael@0 | 2483 | END_CASE(JSOP_GETELEM) |
michael@0 | 2484 | |
michael@0 | 2485 | CASE(JSOP_SETELEM) |
michael@0 | 2486 | { |
michael@0 | 2487 | RootedObject &obj = rootObject0; |
michael@0 | 2488 | FETCH_OBJECT(cx, -3, obj); |
michael@0 | 2489 | RootedId &id = rootId0; |
michael@0 | 2490 | FETCH_ELEMENT_ID(-2, id); |
michael@0 | 2491 | Value &value = REGS.sp[-1]; |
michael@0 | 2492 | if (!SetObjectElementOperation(cx, obj, id, value, script->strict())) |
michael@0 | 2493 | goto error; |
michael@0 | 2494 | REGS.sp[-3] = value; |
michael@0 | 2495 | REGS.sp -= 2; |
michael@0 | 2496 | } |
michael@0 | 2497 | END_CASE(JSOP_SETELEM) |
michael@0 | 2498 | |
michael@0 | 2499 | CASE(JSOP_EVAL) |
michael@0 | 2500 | { |
michael@0 | 2501 | CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp); |
michael@0 | 2502 | if (REGS.fp()->scopeChain()->global().valueIsEval(args.calleev())) { |
michael@0 | 2503 | if (!DirectEval(cx, args)) |
michael@0 | 2504 | goto error; |
michael@0 | 2505 | } else { |
michael@0 | 2506 | if (!Invoke(cx, args)) |
michael@0 | 2507 | goto error; |
michael@0 | 2508 | } |
michael@0 | 2509 | REGS.sp = args.spAfterCall(); |
michael@0 | 2510 | TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]); |
michael@0 | 2511 | } |
michael@0 | 2512 | END_CASE(JSOP_EVAL) |
michael@0 | 2513 | |
michael@0 | 2514 | CASE(JSOP_SPREADNEW) |
michael@0 | 2515 | CASE(JSOP_SPREADCALL) |
michael@0 | 2516 | if (REGS.fp()->hasPushedSPSFrame()) |
michael@0 | 2517 | cx->runtime()->spsProfiler.updatePC(script, REGS.pc); |
michael@0 | 2518 | /* FALL THROUGH */ |
michael@0 | 2519 | |
michael@0 | 2520 | CASE(JSOP_SPREADEVAL) |
michael@0 | 2521 | { |
michael@0 | 2522 | JS_ASSERT(REGS.stackDepth() >= 3); |
michael@0 | 2523 | RootedObject &aobj = rootObject0; |
michael@0 | 2524 | aobj = ®S.sp[-1].toObject(); |
michael@0 | 2525 | |
michael@0 | 2526 | uint32_t length = aobj->as<ArrayObject>().length(); |
michael@0 | 2527 | |
michael@0 | 2528 | if (length > ARGS_LENGTH_MAX) { |
michael@0 | 2529 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 2530 | *REGS.pc == JSOP_SPREADNEW ? JSMSG_TOO_MANY_CON_SPREADARGS |
michael@0 | 2531 | : JSMSG_TOO_MANY_FUN_SPREADARGS); |
michael@0 | 2532 | goto error; |
michael@0 | 2533 | } |
michael@0 | 2534 | |
michael@0 | 2535 | InvokeArgs args(cx); |
michael@0 | 2536 | |
michael@0 | 2537 | if (!args.init(length)) |
michael@0 | 2538 | return false; |
michael@0 | 2539 | |
michael@0 | 2540 | args.setCallee(REGS.sp[-3]); |
michael@0 | 2541 | args.setThis(REGS.sp[-2]); |
michael@0 | 2542 | |
michael@0 | 2543 | if (!GetElements(cx, aobj, length, args.array())) |
michael@0 | 2544 | goto error; |
michael@0 | 2545 | |
michael@0 | 2546 | switch (*REGS.pc) { |
michael@0 | 2547 | case JSOP_SPREADNEW: |
michael@0 | 2548 | if (!InvokeConstructor(cx, args)) |
michael@0 | 2549 | goto error; |
michael@0 | 2550 | break; |
michael@0 | 2551 | case JSOP_SPREADCALL: |
michael@0 | 2552 | if (!Invoke(cx, args)) |
michael@0 | 2553 | goto error; |
michael@0 | 2554 | break; |
michael@0 | 2555 | case JSOP_SPREADEVAL: |
michael@0 | 2556 | if (REGS.fp()->scopeChain()->global().valueIsEval(args.calleev())) { |
michael@0 | 2557 | if (!DirectEval(cx, args)) |
michael@0 | 2558 | goto error; |
michael@0 | 2559 | } else { |
michael@0 | 2560 | if (!Invoke(cx, args)) |
michael@0 | 2561 | goto error; |
michael@0 | 2562 | } |
michael@0 | 2563 | break; |
michael@0 | 2564 | default: |
michael@0 | 2565 | MOZ_ASSUME_UNREACHABLE("bad spread opcode"); |
michael@0 | 2566 | } |
michael@0 | 2567 | |
michael@0 | 2568 | REGS.sp -= 2; |
michael@0 | 2569 | REGS.sp[-1] = args.rval(); |
michael@0 | 2570 | TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]); |
michael@0 | 2571 | } |
michael@0 | 2572 | END_CASE(JSOP_SPREADCALL) |
michael@0 | 2573 | |
michael@0 | 2574 | CASE(JSOP_FUNAPPLY) |
michael@0 | 2575 | { |
michael@0 | 2576 | CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp); |
michael@0 | 2577 | if (!GuardFunApplyArgumentsOptimization(cx, REGS.fp(), args.calleev(), args.array(), |
michael@0 | 2578 | args.length())) |
michael@0 | 2579 | goto error; |
michael@0 | 2580 | /* FALL THROUGH */ |
michael@0 | 2581 | } |
michael@0 | 2582 | |
michael@0 | 2583 | CASE(JSOP_NEW) |
michael@0 | 2584 | CASE(JSOP_CALL) |
michael@0 | 2585 | CASE(JSOP_FUNCALL) |
michael@0 | 2586 | { |
michael@0 | 2587 | if (REGS.fp()->hasPushedSPSFrame()) |
michael@0 | 2588 | cx->runtime()->spsProfiler.updatePC(script, REGS.pc); |
michael@0 | 2589 | JS_ASSERT(REGS.stackDepth() >= 2 + GET_ARGC(REGS.pc)); |
michael@0 | 2590 | CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp); |
michael@0 | 2591 | |
michael@0 | 2592 | bool construct = (*REGS.pc == JSOP_NEW); |
michael@0 | 2593 | |
michael@0 | 2594 | RootedFunction &fun = rootFunction0; |
michael@0 | 2595 | RootedScript &funScript = rootScript0; |
michael@0 | 2596 | bool isFunction = IsFunctionObject(args.calleev(), fun.address()); |
michael@0 | 2597 | |
michael@0 | 2598 | /* |
michael@0 | 2599 | * Some builtins are marked as clone-at-callsite to increase precision of |
michael@0 | 2600 | * TI and JITs. |
michael@0 | 2601 | */ |
michael@0 | 2602 | if (isFunction && fun->isInterpreted()) { |
michael@0 | 2603 | funScript = fun->getOrCreateScript(cx); |
michael@0 | 2604 | if (!funScript) |
michael@0 | 2605 | goto error; |
michael@0 | 2606 | if (funScript->shouldCloneAtCallsite()) { |
michael@0 | 2607 | fun = CloneFunctionAtCallsite(cx, fun, script, REGS.pc); |
michael@0 | 2608 | if (!fun) |
michael@0 | 2609 | goto error; |
michael@0 | 2610 | args.setCallee(ObjectValue(*fun)); |
michael@0 | 2611 | } |
michael@0 | 2612 | } |
michael@0 | 2613 | |
michael@0 | 2614 | /* Don't bother trying to fast-path calls to scripted non-constructors. */ |
michael@0 | 2615 | if (!isFunction || !fun->isInterpretedConstructor()) { |
michael@0 | 2616 | if (construct) { |
michael@0 | 2617 | if (!InvokeConstructor(cx, args)) |
michael@0 | 2618 | goto error; |
michael@0 | 2619 | } else { |
michael@0 | 2620 | if (!Invoke(cx, args)) |
michael@0 | 2621 | goto error; |
michael@0 | 2622 | } |
michael@0 | 2623 | Value *newsp = args.spAfterCall(); |
michael@0 | 2624 | TypeScript::Monitor(cx, script, REGS.pc, newsp[-1]); |
michael@0 | 2625 | REGS.sp = newsp; |
michael@0 | 2626 | ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH); |
michael@0 | 2627 | } |
michael@0 | 2628 | |
michael@0 | 2629 | InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; |
michael@0 | 2630 | bool newType = UseNewType(cx, script, REGS.pc); |
michael@0 | 2631 | |
michael@0 | 2632 | TypeMonitorCall(cx, args, construct); |
michael@0 | 2633 | |
michael@0 | 2634 | #ifdef JS_ION |
michael@0 | 2635 | { |
michael@0 | 2636 | InvokeState state(cx, args, initial); |
michael@0 | 2637 | if (newType) |
michael@0 | 2638 | state.setUseNewType(); |
michael@0 | 2639 | |
michael@0 | 2640 | if (!newType && jit::IsIonEnabled(cx)) { |
michael@0 | 2641 | jit::MethodStatus status = jit::CanEnter(cx, state); |
michael@0 | 2642 | if (status == jit::Method_Error) |
michael@0 | 2643 | goto error; |
michael@0 | 2644 | if (status == jit::Method_Compiled) { |
michael@0 | 2645 | jit::IonExecStatus exec = jit::IonCannon(cx, state); |
michael@0 | 2646 | CHECK_BRANCH(); |
michael@0 | 2647 | REGS.sp = args.spAfterCall(); |
michael@0 | 2648 | interpReturnOK = !IsErrorStatus(exec); |
michael@0 | 2649 | goto jit_return; |
michael@0 | 2650 | } |
michael@0 | 2651 | } |
michael@0 | 2652 | |
michael@0 | 2653 | if (jit::IsBaselineEnabled(cx)) { |
michael@0 | 2654 | jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state); |
michael@0 | 2655 | if (status == jit::Method_Error) |
michael@0 | 2656 | goto error; |
michael@0 | 2657 | if (status == jit::Method_Compiled) { |
michael@0 | 2658 | jit::IonExecStatus exec = jit::EnterBaselineMethod(cx, state); |
michael@0 | 2659 | CHECK_BRANCH(); |
michael@0 | 2660 | REGS.sp = args.spAfterCall(); |
michael@0 | 2661 | interpReturnOK = !IsErrorStatus(exec); |
michael@0 | 2662 | goto jit_return; |
michael@0 | 2663 | } |
michael@0 | 2664 | } |
michael@0 | 2665 | } |
michael@0 | 2666 | #endif |
michael@0 | 2667 | |
michael@0 | 2668 | funScript = fun->nonLazyScript(); |
michael@0 | 2669 | if (!activation.pushInlineFrame(args, funScript, initial)) |
michael@0 | 2670 | goto error; |
michael@0 | 2671 | |
michael@0 | 2672 | if (newType) |
michael@0 | 2673 | REGS.fp()->setUseNewType(); |
michael@0 | 2674 | |
michael@0 | 2675 | SET_SCRIPT(REGS.fp()->script()); |
michael@0 | 2676 | |
michael@0 | 2677 | uint32_t scriptLogId = TraceLogCreateTextId(logger, script); |
michael@0 | 2678 | TraceLogStartEvent(logger, scriptLogId); |
michael@0 | 2679 | TraceLogStartEvent(logger, TraceLogger::Interpreter); |
michael@0 | 2680 | |
michael@0 | 2681 | if (!REGS.fp()->prologue(cx)) |
michael@0 | 2682 | goto error; |
michael@0 | 2683 | if (MOZ_UNLIKELY(cx->compartment()->debugMode())) { |
michael@0 | 2684 | switch (ScriptDebugPrologue(cx, REGS.fp(), REGS.pc)) { |
michael@0 | 2685 | case JSTRAP_CONTINUE: |
michael@0 | 2686 | break; |
michael@0 | 2687 | case JSTRAP_RETURN: |
michael@0 | 2688 | ForcedReturn(cx, REGS); |
michael@0 | 2689 | goto successful_return_continuation; |
michael@0 | 2690 | case JSTRAP_THROW: |
michael@0 | 2691 | case JSTRAP_ERROR: |
michael@0 | 2692 | goto error; |
michael@0 | 2693 | default: |
michael@0 | 2694 | MOZ_ASSUME_UNREACHABLE("bad ScriptDebugPrologue status"); |
michael@0 | 2695 | } |
michael@0 | 2696 | } |
michael@0 | 2697 | |
michael@0 | 2698 | /* Load first op and dispatch it (safe since JSOP_RETRVAL). */ |
michael@0 | 2699 | ADVANCE_AND_DISPATCH(0); |
michael@0 | 2700 | } |
michael@0 | 2701 | |
michael@0 | 2702 | CASE(JSOP_SETCALL) |
michael@0 | 2703 | { |
michael@0 | 2704 | JS_ALWAYS_FALSE(SetCallOperation(cx)); |
michael@0 | 2705 | goto error; |
michael@0 | 2706 | } |
michael@0 | 2707 | END_CASE(JSOP_SETCALL) |
michael@0 | 2708 | |
michael@0 | 2709 | CASE(JSOP_IMPLICITTHIS) |
michael@0 | 2710 | { |
michael@0 | 2711 | RootedPropertyName &name = rootName0; |
michael@0 | 2712 | name = script->getName(REGS.pc); |
michael@0 | 2713 | |
michael@0 | 2714 | RootedObject &scopeObj = rootObject0; |
michael@0 | 2715 | scopeObj = REGS.fp()->scopeChain(); |
michael@0 | 2716 | |
michael@0 | 2717 | RootedObject &scope = rootObject1; |
michael@0 | 2718 | if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope)) |
michael@0 | 2719 | goto error; |
michael@0 | 2720 | |
michael@0 | 2721 | RootedValue &v = rootValue0; |
michael@0 | 2722 | if (!ComputeImplicitThis(cx, scope, &v)) |
michael@0 | 2723 | goto error; |
michael@0 | 2724 | PUSH_COPY(v); |
michael@0 | 2725 | } |
michael@0 | 2726 | END_CASE(JSOP_IMPLICITTHIS) |
michael@0 | 2727 | |
michael@0 | 2728 | CASE(JSOP_GETGNAME) |
michael@0 | 2729 | CASE(JSOP_NAME) |
michael@0 | 2730 | { |
michael@0 | 2731 | RootedValue &rval = rootValue0; |
michael@0 | 2732 | |
michael@0 | 2733 | if (!NameOperation(cx, REGS.fp(), REGS.pc, &rval)) |
michael@0 | 2734 | goto error; |
michael@0 | 2735 | |
michael@0 | 2736 | PUSH_COPY(rval); |
michael@0 | 2737 | TypeScript::Monitor(cx, script, REGS.pc, rval); |
michael@0 | 2738 | } |
michael@0 | 2739 | END_CASE(JSOP_NAME) |
michael@0 | 2740 | |
michael@0 | 2741 | CASE(JSOP_GETINTRINSIC) |
michael@0 | 2742 | { |
michael@0 | 2743 | RootedValue &rval = rootValue0; |
michael@0 | 2744 | |
michael@0 | 2745 | if (!GetIntrinsicOperation(cx, REGS.pc, &rval)) |
michael@0 | 2746 | goto error; |
michael@0 | 2747 | |
michael@0 | 2748 | PUSH_COPY(rval); |
michael@0 | 2749 | TypeScript::Monitor(cx, script, REGS.pc, rval); |
michael@0 | 2750 | } |
michael@0 | 2751 | END_CASE(JSOP_GETINTRINSIC) |
michael@0 | 2752 | |
michael@0 | 2753 | CASE(JSOP_UINT16) |
michael@0 | 2754 | PUSH_INT32((int32_t) GET_UINT16(REGS.pc)); |
michael@0 | 2755 | END_CASE(JSOP_UINT16) |
michael@0 | 2756 | |
michael@0 | 2757 | CASE(JSOP_UINT24) |
michael@0 | 2758 | PUSH_INT32((int32_t) GET_UINT24(REGS.pc)); |
michael@0 | 2759 | END_CASE(JSOP_UINT24) |
michael@0 | 2760 | |
michael@0 | 2761 | CASE(JSOP_INT8) |
michael@0 | 2762 | PUSH_INT32(GET_INT8(REGS.pc)); |
michael@0 | 2763 | END_CASE(JSOP_INT8) |
michael@0 | 2764 | |
michael@0 | 2765 | CASE(JSOP_INT32) |
michael@0 | 2766 | PUSH_INT32(GET_INT32(REGS.pc)); |
michael@0 | 2767 | END_CASE(JSOP_INT32) |
michael@0 | 2768 | |
michael@0 | 2769 | CASE(JSOP_DOUBLE) |
michael@0 | 2770 | { |
michael@0 | 2771 | double dbl; |
michael@0 | 2772 | LOAD_DOUBLE(0, dbl); |
michael@0 | 2773 | PUSH_DOUBLE(dbl); |
michael@0 | 2774 | } |
michael@0 | 2775 | END_CASE(JSOP_DOUBLE) |
michael@0 | 2776 | |
michael@0 | 2777 | CASE(JSOP_STRING) |
michael@0 | 2778 | PUSH_STRING(script->getAtom(REGS.pc)); |
michael@0 | 2779 | END_CASE(JSOP_STRING) |
michael@0 | 2780 | |
michael@0 | 2781 | CASE(JSOP_OBJECT) |
michael@0 | 2782 | { |
michael@0 | 2783 | RootedObject &ref = rootObject0; |
michael@0 | 2784 | ref = script->getObject(REGS.pc); |
michael@0 | 2785 | if (JS::CompartmentOptionsRef(cx).cloneSingletons(cx)) { |
michael@0 | 2786 | JSObject *obj = js::DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject); |
michael@0 | 2787 | if (!obj) |
michael@0 | 2788 | goto error; |
michael@0 | 2789 | PUSH_OBJECT(*obj); |
michael@0 | 2790 | } else { |
michael@0 | 2791 | JS::CompartmentOptionsRef(cx).setSingletonsAsValues(); |
michael@0 | 2792 | PUSH_OBJECT(*ref); |
michael@0 | 2793 | } |
michael@0 | 2794 | } |
michael@0 | 2795 | END_CASE(JSOP_OBJECT) |
michael@0 | 2796 | |
michael@0 | 2797 | CASE(JSOP_REGEXP) |
michael@0 | 2798 | { |
michael@0 | 2799 | /* |
michael@0 | 2800 | * Push a regexp object cloned from the regexp literal object mapped by the |
michael@0 | 2801 | * bytecode at pc. |
michael@0 | 2802 | */ |
michael@0 | 2803 | JSObject *obj = CloneRegExpObject(cx, script->getRegExp(REGS.pc)); |
michael@0 | 2804 | if (!obj) |
michael@0 | 2805 | goto error; |
michael@0 | 2806 | PUSH_OBJECT(*obj); |
michael@0 | 2807 | } |
michael@0 | 2808 | END_CASE(JSOP_REGEXP) |
michael@0 | 2809 | |
michael@0 | 2810 | CASE(JSOP_ZERO) |
michael@0 | 2811 | PUSH_INT32(0); |
michael@0 | 2812 | END_CASE(JSOP_ZERO) |
michael@0 | 2813 | |
michael@0 | 2814 | CASE(JSOP_ONE) |
michael@0 | 2815 | PUSH_INT32(1); |
michael@0 | 2816 | END_CASE(JSOP_ONE) |
michael@0 | 2817 | |
michael@0 | 2818 | CASE(JSOP_NULL) |
michael@0 | 2819 | PUSH_NULL(); |
michael@0 | 2820 | END_CASE(JSOP_NULL) |
michael@0 | 2821 | |
michael@0 | 2822 | CASE(JSOP_FALSE) |
michael@0 | 2823 | PUSH_BOOLEAN(false); |
michael@0 | 2824 | END_CASE(JSOP_FALSE) |
michael@0 | 2825 | |
michael@0 | 2826 | CASE(JSOP_TRUE) |
michael@0 | 2827 | PUSH_BOOLEAN(true); |
michael@0 | 2828 | END_CASE(JSOP_TRUE) |
michael@0 | 2829 | |
michael@0 | 2830 | CASE(JSOP_TABLESWITCH) |
michael@0 | 2831 | { |
michael@0 | 2832 | jsbytecode *pc2 = REGS.pc; |
michael@0 | 2833 | int32_t len = GET_JUMP_OFFSET(pc2); |
michael@0 | 2834 | |
michael@0 | 2835 | /* |
michael@0 | 2836 | * ECMAv2+ forbids conversion of discriminant, so we will skip to the |
michael@0 | 2837 | * default case if the discriminant isn't already an int jsval. (This |
michael@0 | 2838 | * opcode is emitted only for dense int-domain switches.) |
michael@0 | 2839 | */ |
michael@0 | 2840 | const Value &rref = *--REGS.sp; |
michael@0 | 2841 | int32_t i; |
michael@0 | 2842 | if (rref.isInt32()) { |
michael@0 | 2843 | i = rref.toInt32(); |
michael@0 | 2844 | } else { |
michael@0 | 2845 | /* Use mozilla::NumberEqualsInt32 to treat -0 (double) as 0. */ |
michael@0 | 2846 | if (!rref.isDouble() || !NumberEqualsInt32(rref.toDouble(), &i)) |
michael@0 | 2847 | ADVANCE_AND_DISPATCH(len); |
michael@0 | 2848 | } |
michael@0 | 2849 | |
michael@0 | 2850 | pc2 += JUMP_OFFSET_LEN; |
michael@0 | 2851 | int32_t low = GET_JUMP_OFFSET(pc2); |
michael@0 | 2852 | pc2 += JUMP_OFFSET_LEN; |
michael@0 | 2853 | int32_t high = GET_JUMP_OFFSET(pc2); |
michael@0 | 2854 | |
michael@0 | 2855 | i -= low; |
michael@0 | 2856 | if ((uint32_t)i < (uint32_t)(high - low + 1)) { |
michael@0 | 2857 | pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; |
michael@0 | 2858 | int32_t off = (int32_t) GET_JUMP_OFFSET(pc2); |
michael@0 | 2859 | if (off) |
michael@0 | 2860 | len = off; |
michael@0 | 2861 | } |
michael@0 | 2862 | ADVANCE_AND_DISPATCH(len); |
michael@0 | 2863 | } |
michael@0 | 2864 | |
michael@0 | 2865 | CASE(JSOP_ARGUMENTS) |
michael@0 | 2866 | JS_ASSERT(!REGS.fp()->fun()->hasRest()); |
michael@0 | 2867 | if (!script->ensureHasAnalyzedArgsUsage(cx)) |
michael@0 | 2868 | goto error; |
michael@0 | 2869 | if (script->needsArgsObj()) { |
michael@0 | 2870 | ArgumentsObject *obj = ArgumentsObject::createExpected(cx, REGS.fp()); |
michael@0 | 2871 | if (!obj) |
michael@0 | 2872 | goto error; |
michael@0 | 2873 | PUSH_COPY(ObjectValue(*obj)); |
michael@0 | 2874 | } else { |
michael@0 | 2875 | PUSH_COPY(MagicValue(JS_OPTIMIZED_ARGUMENTS)); |
michael@0 | 2876 | } |
michael@0 | 2877 | END_CASE(JSOP_ARGUMENTS) |
michael@0 | 2878 | |
michael@0 | 2879 | CASE(JSOP_RUNONCE) |
michael@0 | 2880 | { |
michael@0 | 2881 | if (!RunOnceScriptPrologue(cx, script)) |
michael@0 | 2882 | goto error; |
michael@0 | 2883 | } |
michael@0 | 2884 | END_CASE(JSOP_RUNONCE) |
michael@0 | 2885 | |
michael@0 | 2886 | CASE(JSOP_REST) |
michael@0 | 2887 | { |
michael@0 | 2888 | RootedObject &rest = rootObject0; |
michael@0 | 2889 | rest = REGS.fp()->createRestParameter(cx); |
michael@0 | 2890 | if (!rest) |
michael@0 | 2891 | goto error; |
michael@0 | 2892 | PUSH_COPY(ObjectValue(*rest)); |
michael@0 | 2893 | } |
michael@0 | 2894 | END_CASE(JSOP_REST) |
michael@0 | 2895 | |
michael@0 | 2896 | CASE(JSOP_GETALIASEDVAR) |
michael@0 | 2897 | { |
michael@0 | 2898 | ScopeCoordinate sc = ScopeCoordinate(REGS.pc); |
michael@0 | 2899 | PUSH_COPY(REGS.fp()->aliasedVarScope(sc).aliasedVar(sc)); |
michael@0 | 2900 | TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]); |
michael@0 | 2901 | } |
michael@0 | 2902 | END_CASE(JSOP_GETALIASEDVAR) |
michael@0 | 2903 | |
michael@0 | 2904 | CASE(JSOP_SETALIASEDVAR) |
michael@0 | 2905 | { |
michael@0 | 2906 | ScopeCoordinate sc = ScopeCoordinate(REGS.pc); |
michael@0 | 2907 | ScopeObject &obj = REGS.fp()->aliasedVarScope(sc); |
michael@0 | 2908 | |
michael@0 | 2909 | // Avoid computing the name if no type updates are needed, as this may be |
michael@0 | 2910 | // expensive on scopes with large numbers of variables. |
michael@0 | 2911 | PropertyName *name = (obj.hasSingletonType() && !obj.hasLazyType()) |
michael@0 | 2912 | ? ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, REGS.pc) |
michael@0 | 2913 | : nullptr; |
michael@0 | 2914 | |
michael@0 | 2915 | obj.setAliasedVar(cx, sc, name, REGS.sp[-1]); |
michael@0 | 2916 | } |
michael@0 | 2917 | END_CASE(JSOP_SETALIASEDVAR) |
michael@0 | 2918 | |
michael@0 | 2919 | CASE(JSOP_GETARG) |
michael@0 | 2920 | { |
michael@0 | 2921 | unsigned i = GET_ARGNO(REGS.pc); |
michael@0 | 2922 | if (script->argsObjAliasesFormals()) |
michael@0 | 2923 | PUSH_COPY(REGS.fp()->argsObj().arg(i)); |
michael@0 | 2924 | else |
michael@0 | 2925 | PUSH_COPY(REGS.fp()->unaliasedFormal(i)); |
michael@0 | 2926 | } |
michael@0 | 2927 | END_CASE(JSOP_GETARG) |
michael@0 | 2928 | |
michael@0 | 2929 | CASE(JSOP_SETARG) |
michael@0 | 2930 | { |
michael@0 | 2931 | unsigned i = GET_ARGNO(REGS.pc); |
michael@0 | 2932 | if (script->argsObjAliasesFormals()) |
michael@0 | 2933 | REGS.fp()->argsObj().setArg(i, REGS.sp[-1]); |
michael@0 | 2934 | else |
michael@0 | 2935 | REGS.fp()->unaliasedFormal(i) = REGS.sp[-1]; |
michael@0 | 2936 | } |
michael@0 | 2937 | END_CASE(JSOP_SETARG) |
michael@0 | 2938 | |
michael@0 | 2939 | CASE(JSOP_GETLOCAL) |
michael@0 | 2940 | { |
michael@0 | 2941 | uint32_t i = GET_LOCALNO(REGS.pc); |
michael@0 | 2942 | PUSH_COPY_SKIP_CHECK(REGS.fp()->unaliasedLocal(i)); |
michael@0 | 2943 | |
michael@0 | 2944 | /* |
michael@0 | 2945 | * Skip the same-compartment assertion if the local will be immediately |
michael@0 | 2946 | * popped. We do not guarantee sync for dead locals when coming in from the |
michael@0 | 2947 | * method JIT, and a GETLOCAL followed by POP is not considered to be |
michael@0 | 2948 | * a use of the variable. |
michael@0 | 2949 | */ |
michael@0 | 2950 | if (REGS.pc[JSOP_GETLOCAL_LENGTH] != JSOP_POP) |
michael@0 | 2951 | assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); |
michael@0 | 2952 | } |
michael@0 | 2953 | END_CASE(JSOP_GETLOCAL) |
michael@0 | 2954 | |
michael@0 | 2955 | CASE(JSOP_SETLOCAL) |
michael@0 | 2956 | { |
michael@0 | 2957 | uint32_t i = GET_LOCALNO(REGS.pc); |
michael@0 | 2958 | REGS.fp()->unaliasedLocal(i) = REGS.sp[-1]; |
michael@0 | 2959 | } |
michael@0 | 2960 | END_CASE(JSOP_SETLOCAL) |
michael@0 | 2961 | |
michael@0 | 2962 | CASE(JSOP_DEFCONST) |
michael@0 | 2963 | CASE(JSOP_DEFVAR) |
michael@0 | 2964 | { |
michael@0 | 2965 | /* ES5 10.5 step 8 (with subsequent errata). */ |
michael@0 | 2966 | unsigned attrs = JSPROP_ENUMERATE; |
michael@0 | 2967 | if (!REGS.fp()->isEvalFrame()) |
michael@0 | 2968 | attrs |= JSPROP_PERMANENT; |
michael@0 | 2969 | if (*REGS.pc == JSOP_DEFCONST) |
michael@0 | 2970 | attrs |= JSPROP_READONLY; |
michael@0 | 2971 | |
michael@0 | 2972 | /* Step 8b. */ |
michael@0 | 2973 | RootedObject &obj = rootObject0; |
michael@0 | 2974 | obj = ®S.fp()->varObj(); |
michael@0 | 2975 | |
michael@0 | 2976 | RootedPropertyName &name = rootName0; |
michael@0 | 2977 | name = script->getName(REGS.pc); |
michael@0 | 2978 | |
michael@0 | 2979 | if (!DefVarOrConstOperation(cx, obj, name, attrs)) |
michael@0 | 2980 | goto error; |
michael@0 | 2981 | } |
michael@0 | 2982 | END_CASE(JSOP_DEFVAR) |
michael@0 | 2983 | |
michael@0 | 2984 | CASE(JSOP_DEFFUN) |
michael@0 | 2985 | { |
michael@0 | 2986 | /* |
michael@0 | 2987 | * A top-level function defined in Global or Eval code (see ECMA-262 |
michael@0 | 2988 | * Ed. 3), or else a SpiderMonkey extension: a named function statement in |
michael@0 | 2989 | * a compound statement (not at the top statement level of global code, or |
michael@0 | 2990 | * at the top level of a function body). |
michael@0 | 2991 | */ |
michael@0 | 2992 | RootedFunction &fun = rootFunction0; |
michael@0 | 2993 | fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); |
michael@0 | 2994 | |
michael@0 | 2995 | if (!DefFunOperation(cx, script, REGS.fp()->scopeChain(), fun)) |
michael@0 | 2996 | goto error; |
michael@0 | 2997 | } |
michael@0 | 2998 | END_CASE(JSOP_DEFFUN) |
michael@0 | 2999 | |
michael@0 | 3000 | CASE(JSOP_LAMBDA) |
michael@0 | 3001 | { |
michael@0 | 3002 | /* Load the specified function object literal. */ |
michael@0 | 3003 | RootedFunction &fun = rootFunction0; |
michael@0 | 3004 | fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); |
michael@0 | 3005 | |
michael@0 | 3006 | JSObject *obj = Lambda(cx, fun, REGS.fp()->scopeChain()); |
michael@0 | 3007 | if (!obj) |
michael@0 | 3008 | goto error; |
michael@0 | 3009 | JS_ASSERT(obj->getProto()); |
michael@0 | 3010 | PUSH_OBJECT(*obj); |
michael@0 | 3011 | } |
michael@0 | 3012 | END_CASE(JSOP_LAMBDA) |
michael@0 | 3013 | |
michael@0 | 3014 | CASE(JSOP_LAMBDA_ARROW) |
michael@0 | 3015 | { |
michael@0 | 3016 | /* Load the specified function object literal. */ |
michael@0 | 3017 | RootedFunction &fun = rootFunction0; |
michael@0 | 3018 | fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); |
michael@0 | 3019 | RootedValue &thisv = rootValue0; |
michael@0 | 3020 | thisv = REGS.sp[-1]; |
michael@0 | 3021 | JSObject *obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv); |
michael@0 | 3022 | if (!obj) |
michael@0 | 3023 | goto error; |
michael@0 | 3024 | JS_ASSERT(obj->getProto()); |
michael@0 | 3025 | REGS.sp[-1].setObject(*obj); |
michael@0 | 3026 | } |
michael@0 | 3027 | END_CASE(JSOP_LAMBDA_ARROW) |
michael@0 | 3028 | |
michael@0 | 3029 | CASE(JSOP_CALLEE) |
michael@0 | 3030 | JS_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); |
michael@0 | 3031 | PUSH_COPY(REGS.fp()->calleev()); |
michael@0 | 3032 | END_CASE(JSOP_CALLEE) |
michael@0 | 3033 | |
michael@0 | 3034 | CASE(JSOP_INITPROP_GETTER) |
michael@0 | 3035 | CASE(JSOP_INITPROP_SETTER) |
michael@0 | 3036 | { |
michael@0 | 3037 | RootedObject &obj = rootObject0; |
michael@0 | 3038 | RootedPropertyName &name = rootName0; |
michael@0 | 3039 | RootedObject &val = rootObject1; |
michael@0 | 3040 | |
michael@0 | 3041 | JS_ASSERT(REGS.stackDepth() >= 2); |
michael@0 | 3042 | obj = ®S.sp[-2].toObject(); |
michael@0 | 3043 | name = script->getName(REGS.pc); |
michael@0 | 3044 | val = ®S.sp[-1].toObject(); |
michael@0 | 3045 | |
michael@0 | 3046 | if (!InitGetterSetterOperation(cx, REGS.pc, obj, name, val)) |
michael@0 | 3047 | goto error; |
michael@0 | 3048 | |
michael@0 | 3049 | REGS.sp--; |
michael@0 | 3050 | } |
michael@0 | 3051 | END_CASE(JSOP_INITPROP_GETTER) |
michael@0 | 3052 | |
michael@0 | 3053 | CASE(JSOP_INITELEM_GETTER) |
michael@0 | 3054 | CASE(JSOP_INITELEM_SETTER) |
michael@0 | 3055 | { |
michael@0 | 3056 | RootedObject &obj = rootObject0; |
michael@0 | 3057 | RootedValue &idval = rootValue0; |
michael@0 | 3058 | RootedObject &val = rootObject1; |
michael@0 | 3059 | |
michael@0 | 3060 | JS_ASSERT(REGS.stackDepth() >= 3); |
michael@0 | 3061 | obj = ®S.sp[-3].toObject(); |
michael@0 | 3062 | idval = REGS.sp[-2]; |
michael@0 | 3063 | val = ®S.sp[-1].toObject(); |
michael@0 | 3064 | |
michael@0 | 3065 | if (!InitGetterSetterOperation(cx, REGS.pc, obj, idval, val)) |
michael@0 | 3066 | goto error; |
michael@0 | 3067 | |
michael@0 | 3068 | REGS.sp -= 2; |
michael@0 | 3069 | } |
michael@0 | 3070 | END_CASE(JSOP_INITELEM_GETTER) |
michael@0 | 3071 | |
michael@0 | 3072 | CASE(JSOP_HOLE) |
michael@0 | 3073 | PUSH_HOLE(); |
michael@0 | 3074 | END_CASE(JSOP_HOLE) |
michael@0 | 3075 | |
michael@0 | 3076 | CASE(JSOP_NEWINIT) |
michael@0 | 3077 | { |
michael@0 | 3078 | uint8_t i = GET_UINT8(REGS.pc); |
michael@0 | 3079 | JS_ASSERT(i == JSProto_Array || i == JSProto_Object); |
michael@0 | 3080 | |
michael@0 | 3081 | RootedObject &obj = rootObject0; |
michael@0 | 3082 | NewObjectKind newKind; |
michael@0 | 3083 | if (i == JSProto_Array) { |
michael@0 | 3084 | newKind = UseNewTypeForInitializer(script, REGS.pc, &ArrayObject::class_); |
michael@0 | 3085 | obj = NewDenseEmptyArray(cx, nullptr, newKind); |
michael@0 | 3086 | } else { |
michael@0 | 3087 | gc::AllocKind allocKind = GuessObjectGCKind(0); |
michael@0 | 3088 | newKind = UseNewTypeForInitializer(script, REGS.pc, &JSObject::class_); |
michael@0 | 3089 | obj = NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind); |
michael@0 | 3090 | } |
michael@0 | 3091 | if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind)) |
michael@0 | 3092 | goto error; |
michael@0 | 3093 | |
michael@0 | 3094 | PUSH_OBJECT(*obj); |
michael@0 | 3095 | } |
michael@0 | 3096 | END_CASE(JSOP_NEWINIT) |
michael@0 | 3097 | |
michael@0 | 3098 | CASE(JSOP_NEWARRAY) |
michael@0 | 3099 | { |
michael@0 | 3100 | unsigned count = GET_UINT24(REGS.pc); |
michael@0 | 3101 | RootedObject &obj = rootObject0; |
michael@0 | 3102 | NewObjectKind newKind = UseNewTypeForInitializer(script, REGS.pc, &ArrayObject::class_); |
michael@0 | 3103 | obj = NewDenseAllocatedArray(cx, count, nullptr, newKind); |
michael@0 | 3104 | if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind)) |
michael@0 | 3105 | goto error; |
michael@0 | 3106 | |
michael@0 | 3107 | PUSH_OBJECT(*obj); |
michael@0 | 3108 | } |
michael@0 | 3109 | END_CASE(JSOP_NEWARRAY) |
michael@0 | 3110 | |
michael@0 | 3111 | CASE(JSOP_NEWOBJECT) |
michael@0 | 3112 | { |
michael@0 | 3113 | RootedObject &baseobj = rootObject0; |
michael@0 | 3114 | baseobj = script->getObject(REGS.pc); |
michael@0 | 3115 | |
michael@0 | 3116 | RootedObject &obj = rootObject1; |
michael@0 | 3117 | NewObjectKind newKind = UseNewTypeForInitializer(script, REGS.pc, baseobj->getClass()); |
michael@0 | 3118 | obj = CopyInitializerObject(cx, baseobj, newKind); |
michael@0 | 3119 | if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind)) |
michael@0 | 3120 | goto error; |
michael@0 | 3121 | |
michael@0 | 3122 | PUSH_OBJECT(*obj); |
michael@0 | 3123 | } |
michael@0 | 3124 | END_CASE(JSOP_NEWOBJECT) |
michael@0 | 3125 | |
michael@0 | 3126 | CASE(JSOP_ENDINIT) |
michael@0 | 3127 | { |
michael@0 | 3128 | /* FIXME remove JSOP_ENDINIT bug 588522 */ |
michael@0 | 3129 | JS_ASSERT(REGS.stackDepth() >= 1); |
michael@0 | 3130 | JS_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined()); |
michael@0 | 3131 | } |
michael@0 | 3132 | END_CASE(JSOP_ENDINIT) |
michael@0 | 3133 | |
michael@0 | 3134 | CASE(JSOP_MUTATEPROTO) |
michael@0 | 3135 | { |
michael@0 | 3136 | MOZ_ASSERT(REGS.stackDepth() >= 2); |
michael@0 | 3137 | |
michael@0 | 3138 | if (REGS.sp[-1].isObjectOrNull()) { |
michael@0 | 3139 | RootedObject &newProto = rootObject1; |
michael@0 | 3140 | rootObject1 = REGS.sp[-1].toObjectOrNull(); |
michael@0 | 3141 | |
michael@0 | 3142 | RootedObject &obj = rootObject0; |
michael@0 | 3143 | obj = ®S.sp[-2].toObject(); |
michael@0 | 3144 | MOZ_ASSERT(obj->is<JSObject>()); |
michael@0 | 3145 | |
michael@0 | 3146 | bool succeeded; |
michael@0 | 3147 | if (!JSObject::setProto(cx, obj, newProto, &succeeded)) |
michael@0 | 3148 | goto error; |
michael@0 | 3149 | MOZ_ASSERT(succeeded); |
michael@0 | 3150 | } |
michael@0 | 3151 | |
michael@0 | 3152 | REGS.sp--; |
michael@0 | 3153 | } |
michael@0 | 3154 | END_CASE(JSOP_MUTATEPROTO); |
michael@0 | 3155 | |
michael@0 | 3156 | CASE(JSOP_INITPROP) |
michael@0 | 3157 | { |
michael@0 | 3158 | /* Load the property's initial value into rval. */ |
michael@0 | 3159 | JS_ASSERT(REGS.stackDepth() >= 2); |
michael@0 | 3160 | RootedValue &rval = rootValue0; |
michael@0 | 3161 | rval = REGS.sp[-1]; |
michael@0 | 3162 | |
michael@0 | 3163 | /* Load the object being initialized into lval/obj. */ |
michael@0 | 3164 | RootedObject &obj = rootObject0; |
michael@0 | 3165 | obj = ®S.sp[-2].toObject(); |
michael@0 | 3166 | JS_ASSERT(obj->is<JSObject>()); |
michael@0 | 3167 | |
michael@0 | 3168 | PropertyName *name = script->getName(REGS.pc); |
michael@0 | 3169 | |
michael@0 | 3170 | RootedId &id = rootId0; |
michael@0 | 3171 | id = NameToId(name); |
michael@0 | 3172 | |
michael@0 | 3173 | if (!DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE)) |
michael@0 | 3174 | goto error; |
michael@0 | 3175 | |
michael@0 | 3176 | REGS.sp--; |
michael@0 | 3177 | } |
michael@0 | 3178 | END_CASE(JSOP_INITPROP); |
michael@0 | 3179 | |
michael@0 | 3180 | CASE(JSOP_INITELEM) |
michael@0 | 3181 | { |
michael@0 | 3182 | JS_ASSERT(REGS.stackDepth() >= 3); |
michael@0 | 3183 | HandleValue val = REGS.stackHandleAt(-1); |
michael@0 | 3184 | HandleValue id = REGS.stackHandleAt(-2); |
michael@0 | 3185 | |
michael@0 | 3186 | RootedObject &obj = rootObject0; |
michael@0 | 3187 | obj = ®S.sp[-3].toObject(); |
michael@0 | 3188 | |
michael@0 | 3189 | if (!InitElemOperation(cx, obj, id, val)) |
michael@0 | 3190 | goto error; |
michael@0 | 3191 | |
michael@0 | 3192 | REGS.sp -= 2; |
michael@0 | 3193 | } |
michael@0 | 3194 | END_CASE(JSOP_INITELEM) |
michael@0 | 3195 | |
michael@0 | 3196 | CASE(JSOP_INITELEM_ARRAY) |
michael@0 | 3197 | { |
michael@0 | 3198 | JS_ASSERT(REGS.stackDepth() >= 2); |
michael@0 | 3199 | HandleValue val = REGS.stackHandleAt(-1); |
michael@0 | 3200 | |
michael@0 | 3201 | RootedObject &obj = rootObject0; |
michael@0 | 3202 | obj = ®S.sp[-2].toObject(); |
michael@0 | 3203 | |
michael@0 | 3204 | JS_ASSERT(obj->is<ArrayObject>()); |
michael@0 | 3205 | |
michael@0 | 3206 | uint32_t index = GET_UINT24(REGS.pc); |
michael@0 | 3207 | if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) |
michael@0 | 3208 | goto error; |
michael@0 | 3209 | |
michael@0 | 3210 | REGS.sp--; |
michael@0 | 3211 | } |
michael@0 | 3212 | END_CASE(JSOP_INITELEM_ARRAY) |
michael@0 | 3213 | |
michael@0 | 3214 | CASE(JSOP_INITELEM_INC) |
michael@0 | 3215 | { |
michael@0 | 3216 | JS_ASSERT(REGS.stackDepth() >= 3); |
michael@0 | 3217 | HandleValue val = REGS.stackHandleAt(-1); |
michael@0 | 3218 | |
michael@0 | 3219 | RootedObject &obj = rootObject0; |
michael@0 | 3220 | obj = ®S.sp[-3].toObject(); |
michael@0 | 3221 | |
michael@0 | 3222 | uint32_t index = REGS.sp[-2].toInt32(); |
michael@0 | 3223 | if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) |
michael@0 | 3224 | goto error; |
michael@0 | 3225 | |
michael@0 | 3226 | REGS.sp[-2].setInt32(index + 1); |
michael@0 | 3227 | REGS.sp--; |
michael@0 | 3228 | } |
michael@0 | 3229 | END_CASE(JSOP_INITELEM_INC) |
michael@0 | 3230 | |
michael@0 | 3231 | CASE(JSOP_SPREAD) |
michael@0 | 3232 | { |
michael@0 | 3233 | int32_t count = REGS.sp[-2].toInt32(); |
michael@0 | 3234 | RootedObject &arr = rootObject0; |
michael@0 | 3235 | arr = ®S.sp[-3].toObject(); |
michael@0 | 3236 | const Value iterable = REGS.sp[-1]; |
michael@0 | 3237 | ForOfIterator iter(cx); |
michael@0 | 3238 | RootedValue &iterVal = rootValue0; |
michael@0 | 3239 | iterVal.set(iterable); |
michael@0 | 3240 | if (!iter.init(iterVal)) |
michael@0 | 3241 | goto error; |
michael@0 | 3242 | while (true) { |
michael@0 | 3243 | bool done; |
michael@0 | 3244 | if (!iter.next(&iterVal, &done)) |
michael@0 | 3245 | goto error; |
michael@0 | 3246 | if (done) |
michael@0 | 3247 | break; |
michael@0 | 3248 | if (count == INT32_MAX) { |
michael@0 | 3249 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 3250 | JSMSG_SPREAD_TOO_LARGE); |
michael@0 | 3251 | goto error; |
michael@0 | 3252 | } |
michael@0 | 3253 | if (!JSObject::defineElement(cx, arr, count++, iterVal, nullptr, nullptr, |
michael@0 | 3254 | JSPROP_ENUMERATE)) |
michael@0 | 3255 | goto error; |
michael@0 | 3256 | } |
michael@0 | 3257 | REGS.sp[-2].setInt32(count); |
michael@0 | 3258 | REGS.sp--; |
michael@0 | 3259 | } |
michael@0 | 3260 | END_CASE(JSOP_SPREAD) |
michael@0 | 3261 | |
michael@0 | 3262 | CASE(JSOP_GOSUB) |
michael@0 | 3263 | { |
michael@0 | 3264 | PUSH_BOOLEAN(false); |
michael@0 | 3265 | int32_t i = script->pcToOffset(REGS.pc) + JSOP_GOSUB_LENGTH; |
michael@0 | 3266 | int32_t len = GET_JUMP_OFFSET(REGS.pc); |
michael@0 | 3267 | PUSH_INT32(i); |
michael@0 | 3268 | ADVANCE_AND_DISPATCH(len); |
michael@0 | 3269 | } |
michael@0 | 3270 | |
michael@0 | 3271 | CASE(JSOP_RETSUB) |
michael@0 | 3272 | { |
michael@0 | 3273 | /* Pop [exception or hole, retsub pc-index]. */ |
michael@0 | 3274 | Value rval, lval; |
michael@0 | 3275 | POP_COPY_TO(rval); |
michael@0 | 3276 | POP_COPY_TO(lval); |
michael@0 | 3277 | JS_ASSERT(lval.isBoolean()); |
michael@0 | 3278 | if (lval.toBoolean()) { |
michael@0 | 3279 | /* |
michael@0 | 3280 | * Exception was pending during finally, throw it *before* we adjust |
michael@0 | 3281 | * pc, because pc indexes into script->trynotes. This turns out not to |
michael@0 | 3282 | * be necessary, but it seems clearer. And it points out a FIXME: |
michael@0 | 3283 | * 350509, due to Igor Bukanov. |
michael@0 | 3284 | */ |
michael@0 | 3285 | cx->setPendingException(rval); |
michael@0 | 3286 | goto error; |
michael@0 | 3287 | } |
michael@0 | 3288 | JS_ASSERT(rval.isInt32()); |
michael@0 | 3289 | |
michael@0 | 3290 | /* Increment the PC by this much. */ |
michael@0 | 3291 | int32_t len = rval.toInt32() - int32_t(script->pcToOffset(REGS.pc)); |
michael@0 | 3292 | ADVANCE_AND_DISPATCH(len); |
michael@0 | 3293 | } |
michael@0 | 3294 | |
michael@0 | 3295 | CASE(JSOP_EXCEPTION) |
michael@0 | 3296 | { |
michael@0 | 3297 | PUSH_NULL(); |
michael@0 | 3298 | MutableHandleValue res = REGS.stackHandleAt(-1); |
michael@0 | 3299 | if (!GetAndClearException(cx, res)) |
michael@0 | 3300 | goto error; |
michael@0 | 3301 | } |
michael@0 | 3302 | END_CASE(JSOP_EXCEPTION) |
michael@0 | 3303 | |
michael@0 | 3304 | CASE(JSOP_FINALLY) |
michael@0 | 3305 | CHECK_BRANCH(); |
michael@0 | 3306 | END_CASE(JSOP_FINALLY) |
michael@0 | 3307 | |
michael@0 | 3308 | CASE(JSOP_THROWING) |
michael@0 | 3309 | { |
michael@0 | 3310 | JS_ASSERT(!cx->isExceptionPending()); |
michael@0 | 3311 | Value v; |
michael@0 | 3312 | POP_COPY_TO(v); |
michael@0 | 3313 | cx->setPendingException(v); |
michael@0 | 3314 | } |
michael@0 | 3315 | END_CASE(JSOP_THROWING) |
michael@0 | 3316 | |
michael@0 | 3317 | CASE(JSOP_THROW) |
michael@0 | 3318 | { |
michael@0 | 3319 | CHECK_BRANCH(); |
michael@0 | 3320 | RootedValue &v = rootValue0; |
michael@0 | 3321 | POP_COPY_TO(v); |
michael@0 | 3322 | JS_ALWAYS_FALSE(Throw(cx, v)); |
michael@0 | 3323 | /* let the code at error try to catch the exception. */ |
michael@0 | 3324 | goto error; |
michael@0 | 3325 | } |
michael@0 | 3326 | |
michael@0 | 3327 | CASE(JSOP_INSTANCEOF) |
michael@0 | 3328 | { |
michael@0 | 3329 | RootedValue &rref = rootValue0; |
michael@0 | 3330 | rref = REGS.sp[-1]; |
michael@0 | 3331 | if (rref.isPrimitive()) { |
michael@0 | 3332 | js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, js::NullPtr()); |
michael@0 | 3333 | goto error; |
michael@0 | 3334 | } |
michael@0 | 3335 | RootedObject &obj = rootObject0; |
michael@0 | 3336 | obj = &rref.toObject(); |
michael@0 | 3337 | bool cond = false; |
michael@0 | 3338 | if (!HasInstance(cx, obj, REGS.stackHandleAt(-2), &cond)) |
michael@0 | 3339 | goto error; |
michael@0 | 3340 | REGS.sp--; |
michael@0 | 3341 | REGS.sp[-1].setBoolean(cond); |
michael@0 | 3342 | } |
michael@0 | 3343 | END_CASE(JSOP_INSTANCEOF) |
michael@0 | 3344 | |
michael@0 | 3345 | CASE(JSOP_DEBUGGER) |
michael@0 | 3346 | { |
michael@0 | 3347 | JSTrapStatus st = JSTRAP_CONTINUE; |
michael@0 | 3348 | RootedValue rval(cx); |
michael@0 | 3349 | if (JSDebuggerHandler handler = cx->runtime()->debugHooks.debuggerHandler) |
michael@0 | 3350 | st = handler(cx, script, REGS.pc, rval.address(), cx->runtime()->debugHooks.debuggerHandlerData); |
michael@0 | 3351 | if (st == JSTRAP_CONTINUE) |
michael@0 | 3352 | st = Debugger::onDebuggerStatement(cx, &rval); |
michael@0 | 3353 | switch (st) { |
michael@0 | 3354 | case JSTRAP_ERROR: |
michael@0 | 3355 | goto error; |
michael@0 | 3356 | case JSTRAP_CONTINUE: |
michael@0 | 3357 | break; |
michael@0 | 3358 | case JSTRAP_RETURN: |
michael@0 | 3359 | REGS.fp()->setReturnValue(rval); |
michael@0 | 3360 | ForcedReturn(cx, REGS); |
michael@0 | 3361 | goto successful_return_continuation; |
michael@0 | 3362 | case JSTRAP_THROW: |
michael@0 | 3363 | cx->setPendingException(rval); |
michael@0 | 3364 | goto error; |
michael@0 | 3365 | default:; |
michael@0 | 3366 | } |
michael@0 | 3367 | } |
michael@0 | 3368 | END_CASE(JSOP_DEBUGGER) |
michael@0 | 3369 | |
michael@0 | 3370 | CASE(JSOP_PUSHBLOCKSCOPE) |
michael@0 | 3371 | { |
michael@0 | 3372 | StaticBlockObject &blockObj = script->getObject(REGS.pc)->as<StaticBlockObject>(); |
michael@0 | 3373 | |
michael@0 | 3374 | JS_ASSERT(blockObj.needsClone()); |
michael@0 | 3375 | // Clone block and push on scope chain. |
michael@0 | 3376 | if (!REGS.fp()->pushBlock(cx, blockObj)) |
michael@0 | 3377 | goto error; |
michael@0 | 3378 | } |
michael@0 | 3379 | END_CASE(JSOP_PUSHBLOCKSCOPE) |
michael@0 | 3380 | |
michael@0 | 3381 | CASE(JSOP_POPBLOCKSCOPE) |
michael@0 | 3382 | { |
michael@0 | 3383 | #ifdef DEBUG |
michael@0 | 3384 | // Pop block from scope chain. |
michael@0 | 3385 | JS_ASSERT(*(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH) == JSOP_DEBUGLEAVEBLOCK); |
michael@0 | 3386 | NestedScopeObject *scope = script->getStaticScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH); |
michael@0 | 3387 | JS_ASSERT(scope && scope->is<StaticBlockObject>()); |
michael@0 | 3388 | StaticBlockObject &blockObj = scope->as<StaticBlockObject>(); |
michael@0 | 3389 | JS_ASSERT(blockObj.needsClone()); |
michael@0 | 3390 | #endif |
michael@0 | 3391 | |
michael@0 | 3392 | // Pop block from scope chain. |
michael@0 | 3393 | REGS.fp()->popBlock(cx); |
michael@0 | 3394 | } |
michael@0 | 3395 | END_CASE(JSOP_POPBLOCKSCOPE) |
michael@0 | 3396 | |
michael@0 | 3397 | CASE(JSOP_DEBUGLEAVEBLOCK) |
michael@0 | 3398 | { |
michael@0 | 3399 | JS_ASSERT(script->getStaticScope(REGS.pc)); |
michael@0 | 3400 | JS_ASSERT(script->getStaticScope(REGS.pc)->is<StaticBlockObject>()); |
michael@0 | 3401 | |
michael@0 | 3402 | // FIXME: This opcode should not be necessary. The debugger shouldn't need |
michael@0 | 3403 | // help from bytecode to do its job. See bug 927782. |
michael@0 | 3404 | |
michael@0 | 3405 | if (MOZ_UNLIKELY(cx->compartment()->debugMode())) |
michael@0 | 3406 | DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc); |
michael@0 | 3407 | } |
michael@0 | 3408 | END_CASE(JSOP_DEBUGLEAVEBLOCK) |
michael@0 | 3409 | |
michael@0 | 3410 | CASE(JSOP_GENERATOR) |
michael@0 | 3411 | { |
michael@0 | 3412 | JS_ASSERT(!cx->isExceptionPending()); |
michael@0 | 3413 | REGS.fp()->initGeneratorFrame(); |
michael@0 | 3414 | REGS.pc += JSOP_GENERATOR_LENGTH; |
michael@0 | 3415 | JSObject *obj = js_NewGenerator(cx, REGS); |
michael@0 | 3416 | if (!obj) |
michael@0 | 3417 | goto error; |
michael@0 | 3418 | REGS.fp()->setReturnValue(ObjectValue(*obj)); |
michael@0 | 3419 | REGS.fp()->setYielding(); |
michael@0 | 3420 | interpReturnOK = true; |
michael@0 | 3421 | if (activation.entryFrame() != REGS.fp()) |
michael@0 | 3422 | goto inline_return; |
michael@0 | 3423 | goto exit; |
michael@0 | 3424 | } |
michael@0 | 3425 | |
michael@0 | 3426 | CASE(JSOP_YIELD) |
michael@0 | 3427 | JS_ASSERT(!cx->isExceptionPending()); |
michael@0 | 3428 | JS_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); |
michael@0 | 3429 | if (cx->innermostGenerator()->state == JSGEN_CLOSING) { |
michael@0 | 3430 | RootedValue &val = rootValue0; |
michael@0 | 3431 | val.setObject(REGS.fp()->callee()); |
michael@0 | 3432 | js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, js::NullPtr()); |
michael@0 | 3433 | goto error; |
michael@0 | 3434 | } |
michael@0 | 3435 | REGS.fp()->setReturnValue(REGS.sp[-1]); |
michael@0 | 3436 | REGS.fp()->setYielding(); |
michael@0 | 3437 | REGS.pc += JSOP_YIELD_LENGTH; |
michael@0 | 3438 | interpReturnOK = true; |
michael@0 | 3439 | goto exit; |
michael@0 | 3440 | |
michael@0 | 3441 | CASE(JSOP_ARRAYPUSH) |
michael@0 | 3442 | { |
michael@0 | 3443 | RootedObject &obj = rootObject0; |
michael@0 | 3444 | obj = ®S.sp[-1].toObject(); |
michael@0 | 3445 | if (!NewbornArrayPush(cx, obj, REGS.sp[-2])) |
michael@0 | 3446 | goto error; |
michael@0 | 3447 | REGS.sp -= 2; |
michael@0 | 3448 | } |
michael@0 | 3449 | END_CASE(JSOP_ARRAYPUSH) |
michael@0 | 3450 | |
michael@0 | 3451 | DEFAULT() |
michael@0 | 3452 | { |
michael@0 | 3453 | char numBuf[12]; |
michael@0 | 3454 | JS_snprintf(numBuf, sizeof numBuf, "%d", *REGS.pc); |
michael@0 | 3455 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 3456 | JSMSG_BAD_BYTECODE, numBuf); |
michael@0 | 3457 | goto error; |
michael@0 | 3458 | } |
michael@0 | 3459 | |
michael@0 | 3460 | } /* interpreter loop */ |
michael@0 | 3461 | |
michael@0 | 3462 | MOZ_ASSUME_UNREACHABLE("Interpreter loop exited via fallthrough"); |
michael@0 | 3463 | |
michael@0 | 3464 | error: |
michael@0 | 3465 | switch (HandleError(cx, REGS)) { |
michael@0 | 3466 | case SuccessfulReturnContinuation: |
michael@0 | 3467 | goto successful_return_continuation; |
michael@0 | 3468 | |
michael@0 | 3469 | case ErrorReturnContinuation: |
michael@0 | 3470 | interpReturnOK = false; |
michael@0 | 3471 | goto return_continuation; |
michael@0 | 3472 | |
michael@0 | 3473 | case CatchContinuation: |
michael@0 | 3474 | ADVANCE_AND_DISPATCH(0); |
michael@0 | 3475 | |
michael@0 | 3476 | case FinallyContinuation: |
michael@0 | 3477 | /* |
michael@0 | 3478 | * Push (true, exception) pair for finally to indicate that [retsub] |
michael@0 | 3479 | * should rethrow the exception. |
michael@0 | 3480 | */ |
michael@0 | 3481 | RootedValue &exception = rootValue0; |
michael@0 | 3482 | if (!cx->getPendingException(&exception)) { |
michael@0 | 3483 | interpReturnOK = false; |
michael@0 | 3484 | goto return_continuation; |
michael@0 | 3485 | } |
michael@0 | 3486 | PUSH_BOOLEAN(true); |
michael@0 | 3487 | PUSH_COPY(exception); |
michael@0 | 3488 | cx->clearPendingException(); |
michael@0 | 3489 | ADVANCE_AND_DISPATCH(0); |
michael@0 | 3490 | } |
michael@0 | 3491 | MOZ_ASSUME_UNREACHABLE("Invalid HandleError continuation"); |
michael@0 | 3492 | |
michael@0 | 3493 | exit: |
michael@0 | 3494 | if (MOZ_UNLIKELY(cx->compartment()->debugMode())) |
michael@0 | 3495 | interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK); |
michael@0 | 3496 | if (!REGS.fp()->isYielding()) |
michael@0 | 3497 | REGS.fp()->epilogue(cx); |
michael@0 | 3498 | else |
michael@0 | 3499 | probes::ExitScript(cx, script, script->functionNonDelazifying(), |
michael@0 | 3500 | REGS.fp()->hasPushedSPSFrame()); |
michael@0 | 3501 | |
michael@0 | 3502 | gc::MaybeVerifyBarriers(cx, true); |
michael@0 | 3503 | |
michael@0 | 3504 | TraceLogStopEvent(logger); |
michael@0 | 3505 | TraceLogStopEvent(logger, scriptLogId); |
michael@0 | 3506 | |
michael@0 | 3507 | #ifdef JS_ION |
michael@0 | 3508 | /* |
michael@0 | 3509 | * This path is used when it's guaranteed the method can be finished |
michael@0 | 3510 | * inside the JIT. |
michael@0 | 3511 | */ |
michael@0 | 3512 | leave_on_safe_point: |
michael@0 | 3513 | #endif |
michael@0 | 3514 | |
michael@0 | 3515 | if (interpReturnOK) |
michael@0 | 3516 | state.setReturnValue(activation.entryFrame()->returnValue()); |
michael@0 | 3517 | |
michael@0 | 3518 | return interpReturnOK; |
michael@0 | 3519 | } |
michael@0 | 3520 | |
michael@0 | 3521 | bool |
michael@0 | 3522 | js::Throw(JSContext *cx, HandleValue v) |
michael@0 | 3523 | { |
michael@0 | 3524 | JS_ASSERT(!cx->isExceptionPending()); |
michael@0 | 3525 | cx->setPendingException(v); |
michael@0 | 3526 | return false; |
michael@0 | 3527 | } |
michael@0 | 3528 | |
michael@0 | 3529 | bool |
michael@0 | 3530 | js::GetProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp) |
michael@0 | 3531 | { |
michael@0 | 3532 | if (name == cx->names().length) { |
michael@0 | 3533 | // Fast path for strings, arrays and arguments. |
michael@0 | 3534 | if (GetLengthProperty(v, vp)) |
michael@0 | 3535 | return true; |
michael@0 | 3536 | } |
michael@0 | 3537 | |
michael@0 | 3538 | RootedObject obj(cx, ToObjectFromStack(cx, v)); |
michael@0 | 3539 | if (!obj) |
michael@0 | 3540 | return false; |
michael@0 | 3541 | return JSObject::getProperty(cx, obj, obj, name, vp); |
michael@0 | 3542 | } |
michael@0 | 3543 | |
michael@0 | 3544 | bool |
michael@0 | 3545 | js::CallProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp) |
michael@0 | 3546 | { |
michael@0 | 3547 | if (!GetProperty(cx, v, name, vp)) |
michael@0 | 3548 | return false; |
michael@0 | 3549 | |
michael@0 | 3550 | #if JS_HAS_NO_SUCH_METHOD |
michael@0 | 3551 | if (MOZ_UNLIKELY(vp.isUndefined()) && v.isObject()) |
michael@0 | 3552 | { |
michael@0 | 3553 | RootedObject obj(cx, &v.toObject()); |
michael@0 | 3554 | if (!OnUnknownMethod(cx, obj, StringValue(name), vp)) |
michael@0 | 3555 | return false; |
michael@0 | 3556 | } |
michael@0 | 3557 | #endif |
michael@0 | 3558 | |
michael@0 | 3559 | return true; |
michael@0 | 3560 | } |
michael@0 | 3561 | |
michael@0 | 3562 | bool |
michael@0 | 3563 | js::GetScopeName(JSContext *cx, HandleObject scopeChain, HandlePropertyName name, MutableHandleValue vp) |
michael@0 | 3564 | { |
michael@0 | 3565 | RootedShape shape(cx); |
michael@0 | 3566 | RootedObject obj(cx), pobj(cx); |
michael@0 | 3567 | if (!LookupName(cx, name, scopeChain, &obj, &pobj, &shape)) |
michael@0 | 3568 | return false; |
michael@0 | 3569 | |
michael@0 | 3570 | if (!shape) { |
michael@0 | 3571 | JSAutoByteString printable; |
michael@0 | 3572 | if (AtomToPrintableString(cx, name, &printable)) |
michael@0 | 3573 | js_ReportIsNotDefined(cx, printable.ptr()); |
michael@0 | 3574 | return false; |
michael@0 | 3575 | } |
michael@0 | 3576 | |
michael@0 | 3577 | return JSObject::getProperty(cx, obj, obj, name, vp); |
michael@0 | 3578 | } |
michael@0 | 3579 | |
michael@0 | 3580 | /* |
michael@0 | 3581 | * Alternate form for NAME opcodes followed immediately by a TYPEOF, |
michael@0 | 3582 | * which do not report an exception on (typeof foo == "undefined") tests. |
michael@0 | 3583 | */ |
michael@0 | 3584 | bool |
michael@0 | 3585 | js::GetScopeNameForTypeOf(JSContext *cx, HandleObject scopeChain, HandlePropertyName name, |
michael@0 | 3586 | MutableHandleValue vp) |
michael@0 | 3587 | { |
michael@0 | 3588 | RootedShape shape(cx); |
michael@0 | 3589 | RootedObject obj(cx), pobj(cx); |
michael@0 | 3590 | if (!LookupName(cx, name, scopeChain, &obj, &pobj, &shape)) |
michael@0 | 3591 | return false; |
michael@0 | 3592 | |
michael@0 | 3593 | if (!shape) { |
michael@0 | 3594 | vp.set(UndefinedValue()); |
michael@0 | 3595 | return true; |
michael@0 | 3596 | } |
michael@0 | 3597 | |
michael@0 | 3598 | return JSObject::getProperty(cx, obj, obj, name, vp); |
michael@0 | 3599 | } |
michael@0 | 3600 | |
michael@0 | 3601 | JSObject * |
michael@0 | 3602 | js::Lambda(JSContext *cx, HandleFunction fun, HandleObject parent) |
michael@0 | 3603 | { |
michael@0 | 3604 | MOZ_ASSERT(!fun->isArrow()); |
michael@0 | 3605 | |
michael@0 | 3606 | RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent, TenuredObject)); |
michael@0 | 3607 | if (!clone) |
michael@0 | 3608 | return nullptr; |
michael@0 | 3609 | |
michael@0 | 3610 | MOZ_ASSERT(clone->global() == clone->global()); |
michael@0 | 3611 | return clone; |
michael@0 | 3612 | } |
michael@0 | 3613 | |
michael@0 | 3614 | JSObject * |
michael@0 | 3615 | js::LambdaArrow(JSContext *cx, HandleFunction fun, HandleObject parent, HandleValue thisv) |
michael@0 | 3616 | { |
michael@0 | 3617 | MOZ_ASSERT(fun->isArrow()); |
michael@0 | 3618 | |
michael@0 | 3619 | RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent, TenuredObject)); |
michael@0 | 3620 | if (!clone) |
michael@0 | 3621 | return nullptr; |
michael@0 | 3622 | |
michael@0 | 3623 | MOZ_ASSERT(clone->as<JSFunction>().isArrow()); |
michael@0 | 3624 | clone->as<JSFunction>().setExtendedSlot(0, thisv); |
michael@0 | 3625 | |
michael@0 | 3626 | MOZ_ASSERT(clone->global() == clone->global()); |
michael@0 | 3627 | return clone; |
michael@0 | 3628 | } |
michael@0 | 3629 | |
michael@0 | 3630 | bool |
michael@0 | 3631 | js::DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain, |
michael@0 | 3632 | HandleFunction funArg) |
michael@0 | 3633 | { |
michael@0 | 3634 | /* |
michael@0 | 3635 | * If static link is not current scope, clone fun's object to link to the |
michael@0 | 3636 | * current scope via parent. We do this to enable sharing of compiled |
michael@0 | 3637 | * functions among multiple equivalent scopes, amortizing the cost of |
michael@0 | 3638 | * compilation over a number of executions. Examples include XUL scripts |
michael@0 | 3639 | * and event handlers shared among Firefox or other Mozilla app chrome |
michael@0 | 3640 | * windows, and user-defined JS functions precompiled and then shared among |
michael@0 | 3641 | * requests in server-side JS. |
michael@0 | 3642 | */ |
michael@0 | 3643 | RootedFunction fun(cx, funArg); |
michael@0 | 3644 | if (fun->isNative() || fun->environment() != scopeChain) { |
michael@0 | 3645 | fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain, TenuredObject); |
michael@0 | 3646 | if (!fun) |
michael@0 | 3647 | return false; |
michael@0 | 3648 | } else { |
michael@0 | 3649 | JS_ASSERT(script->compileAndGo()); |
michael@0 | 3650 | JS_ASSERT(!script->functionNonDelazifying()); |
michael@0 | 3651 | } |
michael@0 | 3652 | |
michael@0 | 3653 | /* |
michael@0 | 3654 | * We define the function as a property of the variable object and not the |
michael@0 | 3655 | * current scope chain even for the case of function expression statements |
michael@0 | 3656 | * and functions defined by eval inside let or with blocks. |
michael@0 | 3657 | */ |
michael@0 | 3658 | RootedObject parent(cx, scopeChain); |
michael@0 | 3659 | while (!parent->isVarObj()) |
michael@0 | 3660 | parent = parent->enclosingScope(); |
michael@0 | 3661 | |
michael@0 | 3662 | /* ES5 10.5 (NB: with subsequent errata). */ |
michael@0 | 3663 | RootedPropertyName name(cx, fun->atom()->asPropertyName()); |
michael@0 | 3664 | |
michael@0 | 3665 | RootedShape shape(cx); |
michael@0 | 3666 | RootedObject pobj(cx); |
michael@0 | 3667 | if (!JSObject::lookupProperty(cx, parent, name, &pobj, &shape)) |
michael@0 | 3668 | return false; |
michael@0 | 3669 | |
michael@0 | 3670 | RootedValue rval(cx, ObjectValue(*fun)); |
michael@0 | 3671 | |
michael@0 | 3672 | /* |
michael@0 | 3673 | * ECMA requires functions defined when entering Eval code to be |
michael@0 | 3674 | * impermanent. |
michael@0 | 3675 | */ |
michael@0 | 3676 | unsigned attrs = script->isActiveEval() |
michael@0 | 3677 | ? JSPROP_ENUMERATE |
michael@0 | 3678 | : JSPROP_ENUMERATE | JSPROP_PERMANENT; |
michael@0 | 3679 | |
michael@0 | 3680 | /* Steps 5d, 5f. */ |
michael@0 | 3681 | if (!shape || pobj != parent) { |
michael@0 | 3682 | return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub, |
michael@0 | 3683 | JS_StrictPropertyStub, attrs); |
michael@0 | 3684 | } |
michael@0 | 3685 | |
michael@0 | 3686 | /* Step 5e. */ |
michael@0 | 3687 | JS_ASSERT(parent->isNative()); |
michael@0 | 3688 | if (parent->is<GlobalObject>()) { |
michael@0 | 3689 | if (shape->configurable()) { |
michael@0 | 3690 | return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub, |
michael@0 | 3691 | JS_StrictPropertyStub, attrs); |
michael@0 | 3692 | } |
michael@0 | 3693 | |
michael@0 | 3694 | if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) { |
michael@0 | 3695 | JSAutoByteString bytes; |
michael@0 | 3696 | if (AtomToPrintableString(cx, name, &bytes)) { |
michael@0 | 3697 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REDEFINE_PROP, |
michael@0 | 3698 | bytes.ptr()); |
michael@0 | 3699 | } |
michael@0 | 3700 | |
michael@0 | 3701 | return false; |
michael@0 | 3702 | } |
michael@0 | 3703 | } |
michael@0 | 3704 | |
michael@0 | 3705 | /* |
michael@0 | 3706 | * Non-global properties, and global properties which we aren't simply |
michael@0 | 3707 | * redefining, must be set. First, this preserves their attributes. |
michael@0 | 3708 | * Second, this will produce warnings and/or errors as necessary if the |
michael@0 | 3709 | * specified Call object property is not writable (const). |
michael@0 | 3710 | */ |
michael@0 | 3711 | |
michael@0 | 3712 | /* Step 5f. */ |
michael@0 | 3713 | return JSObject::setProperty(cx, parent, parent, name, &rval, script->strict()); |
michael@0 | 3714 | } |
michael@0 | 3715 | |
michael@0 | 3716 | bool |
michael@0 | 3717 | js::SetCallOperation(JSContext *cx) |
michael@0 | 3718 | { |
michael@0 | 3719 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_LEFTSIDE_OF_ASS); |
michael@0 | 3720 | return false; |
michael@0 | 3721 | } |
michael@0 | 3722 | |
michael@0 | 3723 | bool |
michael@0 | 3724 | js::GetAndClearException(JSContext *cx, MutableHandleValue res) |
michael@0 | 3725 | { |
michael@0 | 3726 | bool status = cx->getPendingException(res); |
michael@0 | 3727 | cx->clearPendingException(); |
michael@0 | 3728 | if (!status) |
michael@0 | 3729 | return false; |
michael@0 | 3730 | |
michael@0 | 3731 | // Allow interrupting deeply nested exception handling. |
michael@0 | 3732 | return CheckForInterrupt(cx); |
michael@0 | 3733 | } |
michael@0 | 3734 | |
michael@0 | 3735 | template <bool strict> |
michael@0 | 3736 | bool |
michael@0 | 3737 | js::SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value) |
michael@0 | 3738 | { |
michael@0 | 3739 | RootedValue v(cx, value); |
michael@0 | 3740 | return JSObject::setGeneric(cx, obj, obj, id, &v, strict); |
michael@0 | 3741 | } |
michael@0 | 3742 | |
michael@0 | 3743 | template bool js::SetProperty<true> (JSContext *cx, HandleObject obj, HandleId id, const Value &value); |
michael@0 | 3744 | template bool js::SetProperty<false>(JSContext *cx, HandleObject obj, HandleId id, const Value &value); |
michael@0 | 3745 | |
michael@0 | 3746 | template <bool strict> |
michael@0 | 3747 | bool |
michael@0 | 3748 | js::DeleteProperty(JSContext *cx, HandleValue v, HandlePropertyName name, bool *bp) |
michael@0 | 3749 | { |
michael@0 | 3750 | RootedObject obj(cx, ToObjectFromStack(cx, v)); |
michael@0 | 3751 | if (!obj) |
michael@0 | 3752 | return false; |
michael@0 | 3753 | |
michael@0 | 3754 | if (!JSObject::deleteProperty(cx, obj, name, bp)) |
michael@0 | 3755 | return false; |
michael@0 | 3756 | |
michael@0 | 3757 | if (strict && !*bp) { |
michael@0 | 3758 | obj->reportNotConfigurable(cx, NameToId(name)); |
michael@0 | 3759 | return false; |
michael@0 | 3760 | } |
michael@0 | 3761 | return true; |
michael@0 | 3762 | } |
michael@0 | 3763 | |
michael@0 | 3764 | template bool js::DeleteProperty<true> (JSContext *cx, HandleValue val, HandlePropertyName name, bool *bp); |
michael@0 | 3765 | template bool js::DeleteProperty<false>(JSContext *cx, HandleValue val, HandlePropertyName name, bool *bp); |
michael@0 | 3766 | |
michael@0 | 3767 | template <bool strict> |
michael@0 | 3768 | bool |
michael@0 | 3769 | js::DeleteElement(JSContext *cx, HandleValue val, HandleValue index, bool *bp) |
michael@0 | 3770 | { |
michael@0 | 3771 | RootedObject obj(cx, ToObjectFromStack(cx, val)); |
michael@0 | 3772 | if (!obj) |
michael@0 | 3773 | return false; |
michael@0 | 3774 | |
michael@0 | 3775 | if (!JSObject::deleteByValue(cx, obj, index, bp)) |
michael@0 | 3776 | return false; |
michael@0 | 3777 | |
michael@0 | 3778 | if (strict && !*bp) { |
michael@0 | 3779 | // XXX This observably calls ToString(propval). We should convert to |
michael@0 | 3780 | // PropertyKey and use that to delete, and to report an error if |
michael@0 | 3781 | // necessary! |
michael@0 | 3782 | RootedId id(cx); |
michael@0 | 3783 | if (!ValueToId<CanGC>(cx, index, &id)) |
michael@0 | 3784 | return false; |
michael@0 | 3785 | obj->reportNotConfigurable(cx, id); |
michael@0 | 3786 | return false; |
michael@0 | 3787 | } |
michael@0 | 3788 | return true; |
michael@0 | 3789 | } |
michael@0 | 3790 | |
michael@0 | 3791 | template bool js::DeleteElement<true> (JSContext *, HandleValue, HandleValue, bool *succeeded); |
michael@0 | 3792 | template bool js::DeleteElement<false>(JSContext *, HandleValue, HandleValue, bool *succeeded); |
michael@0 | 3793 | |
michael@0 | 3794 | bool |
michael@0 | 3795 | js::GetElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue vp) |
michael@0 | 3796 | { |
michael@0 | 3797 | return GetElementOperation(cx, JSOP_GETELEM, lref, rref, vp); |
michael@0 | 3798 | } |
michael@0 | 3799 | |
michael@0 | 3800 | bool |
michael@0 | 3801 | js::CallElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res) |
michael@0 | 3802 | { |
michael@0 | 3803 | return GetElementOperation(cx, JSOP_CALLELEM, lref, rref, res); |
michael@0 | 3804 | } |
michael@0 | 3805 | |
michael@0 | 3806 | bool |
michael@0 | 3807 | js::SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value, |
michael@0 | 3808 | bool strict) |
michael@0 | 3809 | { |
michael@0 | 3810 | RootedId id(cx); |
michael@0 | 3811 | if (!ValueToId<CanGC>(cx, index, &id)) |
michael@0 | 3812 | return false; |
michael@0 | 3813 | return SetObjectElementOperation(cx, obj, id, value, strict); |
michael@0 | 3814 | } |
michael@0 | 3815 | |
michael@0 | 3816 | bool |
michael@0 | 3817 | js::SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value, |
michael@0 | 3818 | bool strict, HandleScript script, jsbytecode *pc) |
michael@0 | 3819 | { |
michael@0 | 3820 | JS_ASSERT(pc); |
michael@0 | 3821 | RootedId id(cx); |
michael@0 | 3822 | if (!ValueToId<CanGC>(cx, index, &id)) |
michael@0 | 3823 | return false; |
michael@0 | 3824 | return SetObjectElementOperation(cx, obj, id, value, strict, script, pc); |
michael@0 | 3825 | } |
michael@0 | 3826 | |
michael@0 | 3827 | bool |
michael@0 | 3828 | js::InitElementArray(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue value) |
michael@0 | 3829 | { |
michael@0 | 3830 | return InitArrayElemOperation(cx, pc, obj, index, value); |
michael@0 | 3831 | } |
michael@0 | 3832 | |
michael@0 | 3833 | bool |
michael@0 | 3834 | js::AddValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res) |
michael@0 | 3835 | { |
michael@0 | 3836 | return AddOperation(cx, lhs, rhs, res); |
michael@0 | 3837 | } |
michael@0 | 3838 | |
michael@0 | 3839 | bool |
michael@0 | 3840 | js::SubValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res) |
michael@0 | 3841 | { |
michael@0 | 3842 | return SubOperation(cx, lhs, rhs, res); |
michael@0 | 3843 | } |
michael@0 | 3844 | |
michael@0 | 3845 | bool |
michael@0 | 3846 | js::MulValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res) |
michael@0 | 3847 | { |
michael@0 | 3848 | return MulOperation(cx, lhs, rhs, res); |
michael@0 | 3849 | } |
michael@0 | 3850 | |
michael@0 | 3851 | bool |
michael@0 | 3852 | js::DivValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res) |
michael@0 | 3853 | { |
michael@0 | 3854 | return DivOperation(cx, lhs, rhs, res); |
michael@0 | 3855 | } |
michael@0 | 3856 | |
michael@0 | 3857 | bool |
michael@0 | 3858 | js::ModValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res) |
michael@0 | 3859 | { |
michael@0 | 3860 | return ModOperation(cx, lhs, rhs, res); |
michael@0 | 3861 | } |
michael@0 | 3862 | |
michael@0 | 3863 | bool |
michael@0 | 3864 | js::UrshValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res) |
michael@0 | 3865 | { |
michael@0 | 3866 | return UrshOperation(cx, lhs, rhs, res); |
michael@0 | 3867 | } |
michael@0 | 3868 | |
michael@0 | 3869 | bool |
michael@0 | 3870 | js::DeleteNameOperation(JSContext *cx, HandlePropertyName name, HandleObject scopeObj, |
michael@0 | 3871 | MutableHandleValue res) |
michael@0 | 3872 | { |
michael@0 | 3873 | RootedObject scope(cx), pobj(cx); |
michael@0 | 3874 | RootedShape shape(cx); |
michael@0 | 3875 | if (!LookupName(cx, name, scopeObj, &scope, &pobj, &shape)) |
michael@0 | 3876 | return false; |
michael@0 | 3877 | |
michael@0 | 3878 | if (!scope) { |
michael@0 | 3879 | // Return true for non-existent names. |
michael@0 | 3880 | res.setBoolean(true); |
michael@0 | 3881 | return true; |
michael@0 | 3882 | } |
michael@0 | 3883 | |
michael@0 | 3884 | bool succeeded; |
michael@0 | 3885 | if (!JSObject::deleteProperty(cx, scope, name, &succeeded)) |
michael@0 | 3886 | return false; |
michael@0 | 3887 | res.setBoolean(succeeded); |
michael@0 | 3888 | return true; |
michael@0 | 3889 | } |
michael@0 | 3890 | |
michael@0 | 3891 | bool |
michael@0 | 3892 | js::ImplicitThisOperation(JSContext *cx, HandleObject scopeObj, HandlePropertyName name, |
michael@0 | 3893 | MutableHandleValue res) |
michael@0 | 3894 | { |
michael@0 | 3895 | RootedObject obj(cx); |
michael@0 | 3896 | if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &obj)) |
michael@0 | 3897 | return false; |
michael@0 | 3898 | |
michael@0 | 3899 | return ComputeImplicitThis(cx, obj, res); |
michael@0 | 3900 | } |
michael@0 | 3901 | |
michael@0 | 3902 | bool |
michael@0 | 3903 | js::RunOnceScriptPrologue(JSContext *cx, HandleScript script) |
michael@0 | 3904 | { |
michael@0 | 3905 | JS_ASSERT(script->treatAsRunOnce()); |
michael@0 | 3906 | |
michael@0 | 3907 | if (!script->hasRunOnce()) { |
michael@0 | 3908 | script->setHasRunOnce(); |
michael@0 | 3909 | return true; |
michael@0 | 3910 | } |
michael@0 | 3911 | |
michael@0 | 3912 | // Force instantiation of the script's function's type to ensure the flag |
michael@0 | 3913 | // is preserved in type information. |
michael@0 | 3914 | if (!script->functionNonDelazifying()->getType(cx)) |
michael@0 | 3915 | return false; |
michael@0 | 3916 | |
michael@0 | 3917 | types::MarkTypeObjectFlags(cx, script->functionNonDelazifying(), |
michael@0 | 3918 | types::OBJECT_FLAG_RUNONCE_INVALIDATED); |
michael@0 | 3919 | return true; |
michael@0 | 3920 | } |
michael@0 | 3921 | |
michael@0 | 3922 | bool |
michael@0 | 3923 | js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id, |
michael@0 | 3924 | HandleObject val) |
michael@0 | 3925 | { |
michael@0 | 3926 | JS_ASSERT(val->isCallable()); |
michael@0 | 3927 | PropertyOp getter; |
michael@0 | 3928 | StrictPropertyOp setter; |
michael@0 | 3929 | unsigned attrs = JSPROP_ENUMERATE | JSPROP_SHARED; |
michael@0 | 3930 | |
michael@0 | 3931 | JSOp op = JSOp(*pc); |
michael@0 | 3932 | |
michael@0 | 3933 | if (op == JSOP_INITPROP_GETTER || op == JSOP_INITELEM_GETTER) { |
michael@0 | 3934 | getter = CastAsPropertyOp(val); |
michael@0 | 3935 | setter = JS_StrictPropertyStub; |
michael@0 | 3936 | attrs |= JSPROP_GETTER; |
michael@0 | 3937 | } else { |
michael@0 | 3938 | JS_ASSERT(op == JSOP_INITPROP_SETTER || op == JSOP_INITELEM_SETTER); |
michael@0 | 3939 | getter = JS_PropertyStub; |
michael@0 | 3940 | setter = CastAsStrictPropertyOp(val); |
michael@0 | 3941 | attrs |= JSPROP_SETTER; |
michael@0 | 3942 | } |
michael@0 | 3943 | |
michael@0 | 3944 | RootedValue scratch(cx); |
michael@0 | 3945 | return JSObject::defineGeneric(cx, obj, id, scratch, getter, setter, attrs); |
michael@0 | 3946 | } |
michael@0 | 3947 | |
michael@0 | 3948 | bool |
michael@0 | 3949 | js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, |
michael@0 | 3950 | HandlePropertyName name, HandleObject val) |
michael@0 | 3951 | { |
michael@0 | 3952 | RootedId id(cx, NameToId(name)); |
michael@0 | 3953 | return InitGetterSetterOperation(cx, pc, obj, id, val); |
michael@0 | 3954 | } |
michael@0 | 3955 | |
michael@0 | 3956 | bool |
michael@0 | 3957 | js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleValue idval, |
michael@0 | 3958 | HandleObject val) |
michael@0 | 3959 | { |
michael@0 | 3960 | RootedId id(cx); |
michael@0 | 3961 | if (!ValueToId<CanGC>(cx, idval, &id)) |
michael@0 | 3962 | return false; |
michael@0 | 3963 | |
michael@0 | 3964 | return InitGetterSetterOperation(cx, pc, obj, id, val); |
michael@0 | 3965 | } |