Interpreter-inl.h (29858B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_Interpreter_inl_h 8 #define vm_Interpreter_inl_h 9 10 #include "vm/Interpreter.h" 11 12 #include "mozilla/CheckedArithmetic.h" 13 14 #include "jslibmath.h" 15 #include "jsmath.h" 16 #include "jsnum.h" 17 18 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 19 #include "vm/BigIntType.h" 20 #include "vm/BytecodeUtil.h" // JSDVG_SEARCH_STACK 21 #include "vm/JSAtomUtils.h" // AtomizeString 22 #include "vm/Realm.h" 23 #include "vm/StaticStrings.h" 24 #include "vm/ThrowMsgKind.h" 25 26 #include "vm/GlobalObject-inl.h" 27 #include "vm/JSAtomUtils-inl.h" // PrimitiveValueToId, TypeName 28 #include "vm/JSContext-inl.h" 29 #include "vm/JSObject-inl.h" 30 #include "vm/NativeObject-inl.h" 31 #include "vm/ObjectOperations-inl.h" 32 #include "vm/StringType-inl.h" 33 34 namespace js { 35 36 /* 37 * Per ES6, lexical declarations may not be accessed in any fashion until they 38 * are initialized (i.e., until the actual declaring statement is 39 * executed). The various LEXICAL opcodes need to check if the slot is an 40 * uninitialized let declaration, represented by the magic value 41 * JS_UNINITIALIZED_LEXICAL. 42 */ 43 static inline bool IsUninitializedLexical(Value val) { 44 // Use whyMagic here because JS_OPTIMIZED_OUT could flow into here. 45 return val.isMagic() && val.whyMagic() == JS_UNINITIALIZED_LEXICAL; 46 } 47 48 static inline bool IsUninitializedLexicalSlot(HandleObject obj, 49 const PropertyResult& prop) { 50 MOZ_ASSERT(prop.isFound()); 51 if (obj->is<WithEnvironmentObject>()) { 52 return false; 53 } 54 55 // Proxy hooks may return a non-native property. 56 if (prop.isNonNativeProperty()) { 57 return false; 58 } 59 60 PropertyInfo propInfo = prop.propertyInfo(); 61 if (!propInfo.isDataProperty()) { 62 return false; 63 } 64 65 return IsUninitializedLexical( 66 obj->as<NativeObject>().getSlot(propInfo.slot())); 67 } 68 69 static inline bool CheckUninitializedLexical(JSContext* cx, 70 Handle<PropertyName*> name, 71 HandleValue val) { 72 if (IsUninitializedLexical(val)) { 73 ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name); 74 return false; 75 } 76 return true; 77 } 78 79 enum class GetNameMode { Normal, TypeOf }; 80 81 template <GetNameMode mode> 82 inline bool FetchName(JSContext* cx, HandleObject receiver, HandleObject holder, 83 Handle<PropertyName*> name, const PropertyResult& prop, 84 MutableHandleValue vp) { 85 if (prop.isNotFound()) { 86 switch (mode) { 87 case GetNameMode::Normal: 88 ReportIsNotDefined(cx, name); 89 return false; 90 case GetNameMode::TypeOf: 91 vp.setUndefined(); 92 return true; 93 } 94 } 95 96 /* Take the slow path if shape was not found in a native object. */ 97 if (!receiver->is<NativeObject>() || !holder->is<NativeObject>() || 98 (receiver->is<WithEnvironmentObject>() && 99 receiver->as<WithEnvironmentObject>().supportUnscopables())) { 100 Rooted<jsid> id(cx, NameToId(name)); 101 if (!GetProperty(cx, receiver, receiver, id, vp)) { 102 return false; 103 } 104 } else { 105 PropertyInfo propInfo = prop.propertyInfo(); 106 if (propInfo.isDataProperty()) { 107 /* Fast path for Object instance properties. */ 108 vp.set(holder->as<NativeObject>().getSlot(propInfo.slot())); 109 } else { 110 // Unwrap 'with' environments for reasons given in 111 // GetNameBoundInEnvironment. 112 RootedObject normalized(cx, MaybeUnwrapWithEnvironment(receiver)); 113 RootedId id(cx, NameToId(name)); 114 if (!NativeGetExistingProperty(cx, normalized, holder.as<NativeObject>(), 115 id, propInfo, vp)) { 116 return false; 117 } 118 } 119 } 120 121 // We do our own explicit checking for |this| 122 if (name == cx->names().dot_this_) { 123 return true; 124 } 125 126 // NAME operations are the slow paths already, so unconditionally check 127 // for uninitialized lets. 128 return CheckUninitializedLexical(cx, name, vp); 129 } 130 131 inline bool FetchNameNoGC(NativeObject* pobj, PropertyResult prop, Value* vp) { 132 if (prop.isNotFound()) { 133 return false; 134 } 135 136 PropertyInfo propInfo = prop.propertyInfo(); 137 if (!propInfo.isDataProperty()) { 138 return false; 139 } 140 141 *vp = pobj->getSlot(propInfo.slot()); 142 return !IsUninitializedLexical(*vp); 143 } 144 145 template <js::GetNameMode mode> 146 inline bool GetEnvironmentName(JSContext* cx, HandleObject envChain, 147 Handle<PropertyName*> name, 148 MutableHandleValue vp) { 149 { 150 PropertyResult prop; 151 NativeObject* pobj = nullptr; 152 if (LookupNameNoGC(cx, name, envChain, &pobj, &prop)) { 153 if (FetchNameNoGC(pobj, prop, vp.address())) { 154 return true; 155 } 156 } 157 } 158 159 PropertyResult prop; 160 RootedObject obj(cx), pobj(cx); 161 if (!LookupName(cx, name, envChain, &obj, &pobj, &prop)) { 162 return false; 163 } 164 165 return FetchName<mode>(cx, obj, pobj, name, prop, vp); 166 } 167 168 inline bool HasOwnProperty(JSContext* cx, HandleValue val, HandleValue idValue, 169 bool* result) { 170 // As an optimization, provide a fast path when rooting is not necessary and 171 // we can safely retrieve the object's shape. 172 jsid id; 173 if (val.isObject() && idValue.isPrimitive() && 174 PrimitiveValueToId<NoGC>(cx, idValue, &id)) { 175 JSObject* obj = &val.toObject(); 176 PropertyResult prop; 177 if (obj->is<NativeObject>() && 178 NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, 179 &prop)) { 180 *result = prop.isFound(); 181 return true; 182 } 183 } 184 185 // Step 1. 186 RootedId key(cx); 187 if (!ToPropertyKey(cx, idValue, &key)) { 188 return false; 189 } 190 191 // Step 2. 192 RootedObject obj(cx, ToObject(cx, val)); 193 if (!obj) { 194 return false; 195 } 196 197 // Step 3. 198 return HasOwnProperty(cx, obj, key, result); 199 } 200 201 inline bool GetIntrinsicOperation(JSContext* cx, HandleScript script, 202 jsbytecode* pc, MutableHandleValue vp) { 203 Rooted<PropertyName*> name(cx, script->getName(pc)); 204 return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp); 205 } 206 207 inline bool SetIntrinsicOperation(JSContext* cx, JSScript* script, 208 jsbytecode* pc, HandleValue val) { 209 Rooted<PropertyName*> name(cx, script->getName(pc)); 210 return GlobalObject::setIntrinsicValue(cx, cx->global(), name, val); 211 } 212 213 inline bool SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, 214 HandleObject env, HandleValue val) { 215 MOZ_ASSERT(JSOp(*pc) == JSOp::SetName || JSOp(*pc) == JSOp::StrictSetName || 216 JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName); 217 MOZ_ASSERT_IF( 218 JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName, 219 !script->hasNonSyntacticScope()); 220 MOZ_ASSERT_IF( 221 JSOp(*pc) == JSOp::SetGName || JSOp(*pc) == JSOp::StrictSetGName, 222 env == cx->global() || env == &cx->global()->lexicalEnvironment() || 223 env->is<RuntimeLexicalErrorObject>()); 224 225 bool strict = 226 JSOp(*pc) == JSOp::StrictSetName || JSOp(*pc) == JSOp::StrictSetGName; 227 Rooted<PropertyName*> name(cx, script->getName(pc)); 228 229 // In strict mode, assigning to an undeclared global variable is an 230 // error. To detect this, we call NativeSetProperty directly and pass 231 // Unqualified. It stores the error, if any, in |result|. 232 bool ok; 233 ObjectOpResult result; 234 RootedId id(cx, NameToId(name)); 235 RootedValue receiver(cx, ObjectValue(*env)); 236 if (env->isUnqualifiedVarObj()) { 237 Rooted<NativeObject*> varobj(cx); 238 if (env->is<DebugEnvironmentProxy>()) { 239 varobj = 240 &env->as<DebugEnvironmentProxy>().environment().as<NativeObject>(); 241 } else { 242 varobj = &env->as<NativeObject>(); 243 } 244 MOZ_ASSERT(!varobj->getOpsSetProperty()); 245 ok = NativeSetProperty<Unqualified>(cx, varobj, id, val, receiver, result); 246 } else { 247 ok = SetProperty(cx, env, id, val, receiver, result); 248 } 249 return ok && result.checkStrictModeError(cx, env, id, strict); 250 } 251 252 inline void InitGlobalLexicalOperation( 253 JSContext* cx, ExtensibleLexicalEnvironmentObject* lexicalEnv, 254 JSScript* script, jsbytecode* pc, HandleValue value) { 255 MOZ_ASSERT_IF(!script->hasNonSyntacticScope(), 256 lexicalEnv == &cx->global()->lexicalEnvironment()); 257 MOZ_ASSERT(JSOp(*pc) == JSOp::InitGLexical); 258 259 mozilla::Maybe<PropertyInfo> prop = 260 lexicalEnv->lookup(cx, script->getName(pc)); 261 MOZ_ASSERT(prop.isSome()); 262 MOZ_ASSERT(IsUninitializedLexical(lexicalEnv->getSlot(prop->slot()))); 263 264 // Note: we don't have to call Watchtower::watchPropertyValueChange because 265 // this is an initialization instead of a mutation. We don't optimize loads of 266 // uninitialized lexicals in the JIT. 267 lexicalEnv->setSlot(prop->slot(), value); 268 } 269 270 inline bool InitPropertyOperation(JSContext* cx, jsbytecode* pc, 271 HandleObject obj, Handle<PropertyName*> name, 272 HandleValue rhs) { 273 unsigned propAttrs = GetInitDataPropAttrs(JSOp(*pc)); 274 return DefineDataProperty(cx, obj, name, rhs, propAttrs); 275 } 276 277 static MOZ_ALWAYS_INLINE bool NegOperation(JSContext* cx, 278 MutableHandleValue val, 279 MutableHandleValue res) { 280 /* 281 * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies 282 * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the 283 * results, -0.0 or INT32_MAX + 1, are double values. 284 */ 285 int32_t i; 286 if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) { 287 res.setInt32(-i); 288 return true; 289 } 290 291 if (!ToNumeric(cx, val)) { 292 return false; 293 } 294 295 if (val.isBigInt()) { 296 return BigInt::negValue(cx, val, res); 297 } 298 299 res.setNumber(-val.toNumber()); 300 return true; 301 } 302 303 static MOZ_ALWAYS_INLINE bool IncOperation(JSContext* cx, HandleValue val, 304 MutableHandleValue res) { 305 int32_t i; 306 if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) { 307 res.setInt32(i + 1); 308 return true; 309 } 310 311 if (val.isNumber()) { 312 res.setNumber(val.toNumber() + 1); 313 return true; 314 } 315 316 MOZ_ASSERT(val.isBigInt(), "+1 only callable on result of JSOp::ToNumeric"); 317 return BigInt::incValue(cx, val, res); 318 } 319 320 static MOZ_ALWAYS_INLINE bool DecOperation(JSContext* cx, HandleValue val, 321 MutableHandleValue res) { 322 int32_t i; 323 if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) { 324 res.setInt32(i - 1); 325 return true; 326 } 327 328 if (val.isNumber()) { 329 res.setNumber(val.toNumber() - 1); 330 return true; 331 } 332 333 MOZ_ASSERT(val.isBigInt(), "-1 only callable on result of JSOp::ToNumeric"); 334 return BigInt::decValue(cx, val, res); 335 } 336 337 static MOZ_ALWAYS_INLINE bool ToPropertyKeyOperation(JSContext* cx, 338 HandleValue idval, 339 MutableHandleValue res) { 340 if (idval.isInt32()) { 341 res.set(idval); 342 return true; 343 } 344 345 RootedId id(cx); 346 if (!ToPropertyKey(cx, idval, &id)) { 347 return false; 348 } 349 350 res.set(IdToValue(id)); 351 return true; 352 } 353 354 static MOZ_ALWAYS_INLINE bool GetObjectElementOperation( 355 JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleValue receiver, 356 HandleValue key, MutableHandleValue res) { 357 MOZ_ASSERT(op == JSOp::GetElem || op == JSOp::GetElemSuper); 358 MOZ_ASSERT_IF(op == JSOp::GetElem, obj == &receiver.toObject()); 359 360 do { 361 uint32_t index; 362 if (IsDefinitelyIndex(key, &index)) { 363 if (GetElementNoGC(cx, obj, receiver, index, res.address())) { 364 break; 365 } 366 367 if (!GetElement(cx, obj, receiver, index, res)) { 368 return false; 369 } 370 break; 371 } 372 373 if (key.isString()) { 374 JSAtom* name = AtomizeString(cx, key.toString()); 375 if (!name) { 376 return false; 377 } 378 if (name->isIndex(&index)) { 379 if (GetElementNoGC(cx, obj, receiver, index, res.address())) { 380 break; 381 } 382 } else { 383 if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(), 384 res.address())) { 385 break; 386 } 387 } 388 } 389 390 RootedId id(cx); 391 if (!ToPropertyKey(cx, key, &id)) { 392 return false; 393 } 394 if (!GetProperty(cx, obj, receiver, id, res)) { 395 return false; 396 } 397 } while (false); 398 399 cx->debugOnlyCheck(res); 400 return true; 401 } 402 403 static MOZ_ALWAYS_INLINE bool GetPrimitiveElementOperation( 404 JSContext* cx, JS::HandleValue receiver, int receiverIndex, HandleValue key, 405 MutableHandleValue res) { 406 // FIXME: Bug 1234324 We shouldn't be boxing here. 407 RootedObject boxed( 408 cx, ToObjectFromStackForPropertyAccess(cx, receiver, receiverIndex, key)); 409 if (!boxed) { 410 return false; 411 } 412 413 do { 414 uint32_t index; 415 if (IsDefinitelyIndex(key, &index)) { 416 if (GetElementNoGC(cx, boxed, receiver, index, res.address())) { 417 break; 418 } 419 420 if (!GetElement(cx, boxed, receiver, index, res)) { 421 return false; 422 } 423 break; 424 } 425 426 if (key.isString()) { 427 JSAtom* name = AtomizeString(cx, key.toString()); 428 if (!name) { 429 return false; 430 } 431 if (name->isIndex(&index)) { 432 if (GetElementNoGC(cx, boxed, receiver, index, res.address())) { 433 break; 434 } 435 } else { 436 if (GetPropertyNoGC(cx, boxed, receiver, name->asPropertyName(), 437 res.address())) { 438 break; 439 } 440 } 441 } 442 443 RootedId id(cx); 444 if (!ToPropertyKey(cx, key, &id)) { 445 return false; 446 } 447 if (!GetProperty(cx, boxed, receiver, id, res)) { 448 return false; 449 } 450 } while (false); 451 452 cx->debugOnlyCheck(res); 453 return true; 454 } 455 456 static MOZ_ALWAYS_INLINE bool GetElementOperationWithStackIndex( 457 JSContext* cx, HandleValue lref, int lrefIndex, HandleValue rref, 458 MutableHandleValue res) { 459 uint32_t index; 460 if (lref.isString() && IsDefinitelyIndex(rref, &index)) { 461 JSString* str = lref.toString(); 462 if (index < str->length()) { 463 str = cx->staticStrings().getUnitStringForElement(cx, str, index); 464 if (!str) { 465 return false; 466 } 467 res.setString(str); 468 return true; 469 } 470 } 471 472 if (lref.isPrimitive()) { 473 return GetPrimitiveElementOperation(cx, lref, lrefIndex, rref, res); 474 } 475 476 RootedObject obj(cx, &lref.toObject()); 477 return GetObjectElementOperation(cx, JSOp::GetElem, obj, lref, rref, res); 478 } 479 480 // Wrapper for callVM from JIT. 481 static MOZ_ALWAYS_INLINE bool GetElementOperation(JSContext* cx, 482 HandleValue lref, 483 HandleValue rref, 484 MutableHandleValue res) { 485 return GetElementOperationWithStackIndex(cx, lref, JSDVG_SEARCH_STACK, rref, 486 res); 487 } 488 489 static MOZ_ALWAYS_INLINE JSString* TypeOfOperation(const Value& v, 490 JSRuntime* rt) { 491 JSType type = js::TypeOfValue(v); 492 return TypeName(type, *rt->commonNames); 493 } 494 495 static MOZ_ALWAYS_INLINE bool InitElemOperation(JSContext* cx, jsbytecode* pc, 496 HandleObject obj, 497 HandleValue idval, 498 HandleValue val) { 499 MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE)); 500 501 RootedId id(cx); 502 if (!ToPropertyKey(cx, idval, &id)) { 503 return false; 504 } 505 506 unsigned flags = GetInitDataPropAttrs(JSOp(*pc)); 507 return DefineDataProperty(cx, obj, id, val, flags); 508 } 509 510 static MOZ_ALWAYS_INLINE bool CheckPrivateFieldOperation(JSContext* cx, 511 jsbytecode* pc, 512 HandleValue val, 513 HandleValue idval, 514 bool* result) { 515 MOZ_ASSERT(idval.isSymbol()); 516 MOZ_ASSERT(idval.toSymbol()->isPrivateName()); 517 518 // Result had better not be a nullptr. 519 MOZ_ASSERT(result); 520 521 ThrowCondition condition; 522 ThrowMsgKind msgKind; 523 GetCheckPrivateFieldOperands(pc, &condition, &msgKind); 524 525 // When we are using OnlyCheckRhs, we are implementing PrivateInExpr 526 // This requires we throw if the rhs is not an object; 527 // 528 // The InlineCache for CheckPrivateField already checks for a 529 // non-object rhs and refuses to attach in that circumstance. 530 if (condition == ThrowCondition::OnlyCheckRhs) { 531 if (!val.isObject()) { 532 ReportInNotObjectError(cx, idval, val); 533 return false; 534 } 535 } 536 537 // Invoke the HostEnsureCanAddPrivateElement ( O ) host hook here 538 // if the code is attempting to attach a new private element (which 539 // corresponds to the ThrowHas Throw Condition). 540 if (condition == ThrowCondition::ThrowHas) { 541 if (JS::EnsureCanAddPrivateElementOp op = 542 cx->runtime()->canAddPrivateElement) { 543 if (!op(cx, val)) { 544 return false; 545 } 546 } 547 } 548 549 if (!HasOwnProperty(cx, val, idval, result)) { 550 return false; 551 } 552 553 if (!CheckPrivateFieldWillThrow(condition, *result)) { 554 return true; 555 } 556 557 // Throw! 558 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 559 ThrowMsgKindToErrNum(msgKind)); 560 return false; 561 } 562 563 static inline JS::Symbol* NewPrivateName(JSContext* cx, Handle<JSAtom*> name) { 564 return JS::Symbol::new_(cx, JS::SymbolCode::PrivateNameSymbol, name); 565 } 566 567 inline bool InitElemIncOperation(JSContext* cx, Handle<ArrayObject*> arr, 568 uint32_t index, HandleValue val) { 569 if (index == INT32_MAX) { 570 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 571 JSMSG_SPREAD_TOO_LARGE); 572 return false; 573 } 574 575 // If val is a hole, do not call DefineDataElement. 576 if (val.isMagic(JS_ELEMENTS_HOLE)) { 577 // Always call SetLengthProperty even if this is not the last element 578 // initialiser, because this may be followed by a SpreadElement loop, 579 // which will not set the array length if nothing is spread. 580 return SetLengthProperty(cx, arr, index + 1); 581 } 582 583 return DefineDataElement(cx, arr, index, val, JSPROP_ENUMERATE); 584 } 585 586 inline JSFunction* ReportIfNotFunction( 587 JSContext* cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT) { 588 if (v.isObject() && v.toObject().is<JSFunction>()) { 589 return &v.toObject().as<JSFunction>(); 590 } 591 592 ReportIsNotFunction(cx, v, -1, construct); 593 return nullptr; 594 } 595 596 static inline JSObject* SuperFunOperation(JSObject* callee) { 597 MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor()); 598 MOZ_ASSERT( 599 callee->as<JSFunction>().baseScript()->isDerivedClassConstructor()); 600 601 return callee->as<JSFunction>().staticPrototype(); 602 } 603 604 static inline JSObject* HomeObjectSuperBase(JSObject* homeObj) { 605 MOZ_ASSERT(homeObj->is<PlainObject>() || homeObj->is<JSFunction>()); 606 607 return homeObj->staticPrototype(); 608 } 609 610 static MOZ_ALWAYS_INLINE bool AddOperation(JSContext* cx, 611 MutableHandleValue lhs, 612 MutableHandleValue rhs, 613 MutableHandleValue res) { 614 if (lhs.isInt32() && rhs.isInt32()) { 615 int32_t l = lhs.toInt32(), r = rhs.toInt32(); 616 int32_t t; 617 if (MOZ_LIKELY(mozilla::SafeAdd(l, r, &t))) { 618 res.setInt32(t); 619 return true; 620 } 621 } 622 623 if (!ToPrimitive(cx, lhs)) { 624 return false; 625 } 626 if (!ToPrimitive(cx, rhs)) { 627 return false; 628 } 629 630 bool lIsString = lhs.isString(); 631 bool rIsString = rhs.isString(); 632 if (lIsString || rIsString) { 633 JSString* lstr; 634 if (lIsString) { 635 lstr = lhs.toString(); 636 } else { 637 lstr = ToString<CanGC>(cx, lhs); 638 if (!lstr) { 639 return false; 640 } 641 } 642 643 JSString* rstr; 644 if (rIsString) { 645 rstr = rhs.toString(); 646 } else { 647 // Save/restore lstr in case of GC activity under ToString. 648 lhs.setString(lstr); 649 rstr = ToString<CanGC>(cx, rhs); 650 if (!rstr) { 651 return false; 652 } 653 lstr = lhs.toString(); 654 } 655 JSString* str = ConcatStrings<NoGC>(cx, lstr, rstr); 656 if (!str) { 657 RootedString nlstr(cx, lstr), nrstr(cx, rstr); 658 str = ConcatStrings<CanGC>(cx, nlstr, nrstr); 659 if (!str) { 660 return false; 661 } 662 } 663 res.setString(str); 664 return true; 665 } 666 667 if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) { 668 return false; 669 } 670 671 if (lhs.isBigInt() || rhs.isBigInt()) { 672 return BigInt::addValue(cx, lhs, rhs, res); 673 } 674 675 res.setNumber(lhs.toNumber() + rhs.toNumber()); 676 return true; 677 } 678 679 static MOZ_ALWAYS_INLINE bool SubOperation(JSContext* cx, 680 MutableHandleValue lhs, 681 MutableHandleValue rhs, 682 MutableHandleValue res) { 683 if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) { 684 return false; 685 } 686 687 if (lhs.isBigInt() || rhs.isBigInt()) { 688 return BigInt::subValue(cx, lhs, rhs, res); 689 } 690 691 res.setNumber(lhs.toNumber() - rhs.toNumber()); 692 return true; 693 } 694 695 static MOZ_ALWAYS_INLINE bool MulOperation(JSContext* cx, 696 MutableHandleValue lhs, 697 MutableHandleValue rhs, 698 MutableHandleValue res) { 699 if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) { 700 return false; 701 } 702 703 if (lhs.isBigInt() || rhs.isBigInt()) { 704 return BigInt::mulValue(cx, lhs, rhs, res); 705 } 706 707 res.setNumber(lhs.toNumber() * rhs.toNumber()); 708 return true; 709 } 710 711 static MOZ_ALWAYS_INLINE bool DivOperation(JSContext* cx, 712 MutableHandleValue lhs, 713 MutableHandleValue rhs, 714 MutableHandleValue res) { 715 if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) { 716 return false; 717 } 718 719 if (lhs.isBigInt() || rhs.isBigInt()) { 720 return BigInt::divValue(cx, lhs, rhs, res); 721 } 722 723 res.setNumber(NumberDiv(lhs.toNumber(), rhs.toNumber())); 724 return true; 725 } 726 727 static MOZ_ALWAYS_INLINE bool ModOperation(JSContext* cx, 728 MutableHandleValue lhs, 729 MutableHandleValue rhs, 730 MutableHandleValue res) { 731 int32_t l, r; 732 if (lhs.isInt32() && rhs.isInt32() && (l = lhs.toInt32()) >= 0 && 733 (r = rhs.toInt32()) > 0) { 734 int32_t mod = l % r; 735 res.setInt32(mod); 736 return true; 737 } 738 739 if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) { 740 return false; 741 } 742 743 if (lhs.isBigInt() || rhs.isBigInt()) { 744 return BigInt::modValue(cx, lhs, rhs, res); 745 } 746 747 res.setNumber(NumberMod(lhs.toNumber(), rhs.toNumber())); 748 return true; 749 } 750 751 static MOZ_ALWAYS_INLINE bool PowOperation(JSContext* cx, 752 MutableHandleValue lhs, 753 MutableHandleValue rhs, 754 MutableHandleValue res) { 755 if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) { 756 return false; 757 } 758 759 if (lhs.isBigInt() || rhs.isBigInt()) { 760 return BigInt::powValue(cx, lhs, rhs, res); 761 } 762 763 res.setNumber(ecmaPow(lhs.toNumber(), rhs.toNumber())); 764 return true; 765 } 766 767 static MOZ_ALWAYS_INLINE bool BitNotOperation(JSContext* cx, 768 MutableHandleValue in, 769 MutableHandleValue out) { 770 if (!ToInt32OrBigInt(cx, in)) { 771 return false; 772 } 773 774 if (in.isBigInt()) { 775 return BigInt::bitNotValue(cx, in, out); 776 } 777 778 out.setInt32(~in.toInt32()); 779 return true; 780 } 781 782 static MOZ_ALWAYS_INLINE bool BitXorOperation(JSContext* cx, 783 MutableHandleValue lhs, 784 MutableHandleValue rhs, 785 MutableHandleValue out) { 786 if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) { 787 return false; 788 } 789 790 if (lhs.isBigInt() || rhs.isBigInt()) { 791 return BigInt::bitXorValue(cx, lhs, rhs, out); 792 } 793 794 out.setInt32(lhs.toInt32() ^ rhs.toInt32()); 795 return true; 796 } 797 798 static MOZ_ALWAYS_INLINE bool BitOrOperation(JSContext* cx, 799 MutableHandleValue lhs, 800 MutableHandleValue rhs, 801 MutableHandleValue out) { 802 if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) { 803 return false; 804 } 805 806 if (lhs.isBigInt() || rhs.isBigInt()) { 807 return BigInt::bitOrValue(cx, lhs, rhs, out); 808 } 809 810 out.setInt32(lhs.toInt32() | rhs.toInt32()); 811 return true; 812 } 813 814 static MOZ_ALWAYS_INLINE bool BitAndOperation(JSContext* cx, 815 MutableHandleValue lhs, 816 MutableHandleValue rhs, 817 MutableHandleValue out) { 818 if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) { 819 return false; 820 } 821 822 if (lhs.isBigInt() || rhs.isBigInt()) { 823 return BigInt::bitAndValue(cx, lhs, rhs, out); 824 } 825 826 out.setInt32(lhs.toInt32() & rhs.toInt32()); 827 return true; 828 } 829 830 static MOZ_ALWAYS_INLINE bool BitLshOperation(JSContext* cx, 831 MutableHandleValue lhs, 832 MutableHandleValue rhs, 833 MutableHandleValue out) { 834 if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) { 835 return false; 836 } 837 838 if (lhs.isBigInt() || rhs.isBigInt()) { 839 return BigInt::lshValue(cx, lhs, rhs, out); 840 } 841 842 // Signed left-shift is undefined on overflow, so |lhs << (rhs & 31)| won't 843 // work. Instead, convert to unsigned space (where overflow is treated 844 // modularly), perform the operation there, then convert back. 845 uint32_t left = static_cast<uint32_t>(lhs.toInt32()); 846 uint8_t right = rhs.toInt32() & 31; 847 out.setInt32(mozilla::WrapToSigned(left << right)); 848 return true; 849 } 850 851 static MOZ_ALWAYS_INLINE bool BitRshOperation(JSContext* cx, 852 MutableHandleValue lhs, 853 MutableHandleValue rhs, 854 MutableHandleValue out) { 855 if (!ToInt32OrBigInt(cx, lhs) || !ToInt32OrBigInt(cx, rhs)) { 856 return false; 857 } 858 859 if (lhs.isBigInt() || rhs.isBigInt()) { 860 return BigInt::rshValue(cx, lhs, rhs, out); 861 } 862 863 out.setInt32(lhs.toInt32() >> (rhs.toInt32() & 31)); 864 return true; 865 } 866 867 static MOZ_ALWAYS_INLINE bool UrshOperation(JSContext* cx, 868 MutableHandleValue lhs, 869 MutableHandleValue rhs, 870 MutableHandleValue out) { 871 if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) { 872 return false; 873 } 874 875 if (lhs.isBigInt() || rhs.isBigInt()) { 876 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 877 JSMSG_BIGINT_TO_NUMBER); 878 return false; 879 } 880 881 uint32_t left; 882 int32_t right; 883 if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) { 884 return false; 885 } 886 left >>= right & 31; 887 out.setNumber(uint32_t(left)); 888 return true; 889 } 890 891 static MOZ_ALWAYS_INLINE void InitElemArrayOperation(JSContext* cx, 892 jsbytecode* pc, 893 Handle<ArrayObject*> arr, 894 HandleValue val) { 895 MOZ_ASSERT(JSOp(*pc) == JSOp::InitElemArray); 896 897 // The dense elements must have been initialized up to this index. The JIT 898 // implementation also depends on this. 899 uint32_t index = GET_UINT32(pc); 900 MOZ_ASSERT(index < arr->getDenseCapacity()); 901 MOZ_ASSERT(index == arr->getDenseInitializedLength()); 902 903 // Bump the initialized length even for hole values to ensure the 904 // index == initLength invariant holds for later InitElemArray ops. 905 arr->setDenseInitializedLength(index + 1); 906 907 if (val.isMagic(JS_ELEMENTS_HOLE)) { 908 arr->initDenseElementHole(index); 909 } else { 910 arr->initDenseElement(index, val); 911 } 912 } 913 914 /* 915 * As an optimization, the interpreter creates a handful of reserved rooted 916 * variables at the beginning, thus inserting them into the Rooted list once 917 * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it 918 * within a local scope, resetting the value to nullptr (or the appropriate 919 * equivalent for T) at scope end. This avoids inserting/removing the Rooted 920 * from the rooter list, while preventing stale values from being kept alive 921 * unnecessarily. 922 */ 923 template <typename T> 924 class ReservedRooted : public RootedOperations<T, ReservedRooted<T>> { 925 MutableHandle<T> savedRoot; 926 927 public: 928 ReservedRooted(MutableHandle<T> root, const T& ptr) : savedRoot(root) { 929 root.set(ptr); 930 } 931 932 explicit ReservedRooted(MutableHandle<T> root) : savedRoot(root) { clear(); } 933 934 ~ReservedRooted() { clear(); } 935 936 void clear() { savedRoot.set(JS::SafelyInitialized<T>::create()); } 937 void set(const T& p) { savedRoot.set(p); } 938 operator Handle<T>() { return savedRoot; } 939 MutableHandle<T> operator&() { return savedRoot; } 940 941 DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot.get()) 942 DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot.get()) 943 DECLARE_POINTER_CONSTREF_OPS(T) 944 DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T) 945 }; 946 947 } /* namespace js */ 948 949 #endif /* vm_Interpreter_inl_h */