diff --git a/browser/components/payments/res/containers/address-picker.js b/browser/components/payments/res/containers/address-picker.js index c0fdc8f9e381..75712eaef871 100644 --- a/browser/components/payments/res/containers/address-picker.js +++ b/browser/components/payments/res/containers/address-picker.js @@ -113,6 +113,8 @@ export default class AddressPicker extends RichPicker { throw new Error(`${this.selectedStateKey} option ${selectedAddressGUID} ` + `does not exist in the address picker`); } + + super.render(state); } get selectedStateKey() { diff --git a/browser/components/payments/res/containers/payment-method-picker.js b/browser/components/payments/res/containers/payment-method-picker.js index 5dadf69d62c0..2631fdaa1738 100644 --- a/browser/components/payments/res/containers/payment-method-picker.js +++ b/browser/components/payments/res/containers/payment-method-picker.js @@ -65,6 +65,8 @@ export default class PaymentMethodPicker extends RichPicker { throw new Error(`The option ${selectedPaymentCardGUID} ` + `does not exist in the payment method picker`); } + + super.render(state); } get selectedStateKey() { diff --git a/browser/components/payments/res/containers/rich-picker.js b/browser/components/payments/res/containers/rich-picker.js index fc663fddbcf8..db91d53c60af 100644 --- a/browser/components/payments/res/containers/rich-picker.js +++ b/browser/components/payments/res/containers/rich-picker.js @@ -49,6 +49,10 @@ export default class RichPicker extends PaymentStateSubscriberMixin(HTMLElement) } } + render(state) { + this.editLink.hidden = !this.dropdown.value; + } + get value() { return this.dropdown && this.dropdown.selectedOption; diff --git a/browser/components/payments/test/mochitest/test_address_picker.html b/browser/components/payments/test/mochitest/test_address_picker.html index c23e15a439eb..875fdbbc8204 100644 --- a/browser/components/payments/test/mochitest/test_address_picker.html +++ b/browser/components/payments/test/mochitest/test_address_picker.html @@ -38,6 +38,7 @@ add_task(async function test_empty() { ok(picker1, "Check picker1 exists"); let {savedAddresses} = picker1.requestStore.getState(); is(Object.keys(savedAddresses).length, 0, "Check empty initial state"); + is(picker1.editLink.hidden, true, "Check that picker edit link is hidden"); is(picker1.dropdown.popupBox.children.length, 0, "Check dropdown is empty"); }); @@ -112,6 +113,7 @@ add_task(async function test_update() { add_task(async function test_change_selected_address() { let options = picker1.dropdown.popupBox.children; is(picker1.dropdown.selectedOption, null, "Should default to no selected option"); + is(picker1.editLink.hidden, true, "Picker edit link should be hidden when no option is selected"); let {selectedShippingAddress} = picker1.requestStore.getState(); is(selectedShippingAddress, null, "store should have no option selected"); diff --git a/browser/components/payments/test/mochitest/test_shipping_option_picker.html b/browser/components/payments/test/mochitest/test_shipping_option_picker.html index cf4144444928..6311f7f9013d 100644 --- a/browser/components/payments/test/mochitest/test_shipping_option_picker.html +++ b/browser/components/payments/test/mochitest/test_shipping_option_picker.html @@ -39,6 +39,8 @@ add_task(async function test_empty() { let {shippingOptions} = state && state.request && state.request.paymentDetails; is(Object.keys(shippingOptions).length, 0, "Check empty initial state"); is(picker1.dropdown.popupBox.children.length, 0, "Check dropdown is empty"); + ok(picker1.editLink.hidden, true, "Check that picker edit link is always hidden"); + ok(picker1.addLink.hidden, true, "Check that picker add link is always hidden"); }); add_task(async function test_initialSet() { diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure index 5d74de7d20bd..3b564eb2043b 100755 --- a/build/moz.configure/toolchain.configure +++ b/build/moz.configure/toolchain.configure @@ -589,6 +589,7 @@ def get_vc_paths(topsrcdir): 'x64': [os.path.join(tools_path, 'x64')], # The x64->x86 cross toolchain requires DLLs from the native x64 toolchain. 'x86': [os.path.join(tools_path, 'x86'), os.path.join(tools_path, 'x64')], + 'arm64': [os.path.join(tools_path, 'x64')], }) @@ -614,6 +615,7 @@ def vc_compiler_path(host, target, vs_major_version, env, vs_release_name): 'x86': 'x86', 'x86_64': 'x64', 'arm': 'arm', + 'aarch64': 'arm64' }.get(target.cpu) if vc_target is None: return diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure index 8153fef80c8f..25c2e6f3df7f 100644 --- a/build/moz.configure/windows.configure +++ b/build/moz.configure/windows.configure @@ -356,12 +356,14 @@ def lib_path(target, c_compiler, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk 'x86': 'x86', 'x86_64': 'x64', 'arm': 'arm', + 'aarch64': 'arm64', }.get(target.cpu) old_target = { 'x86': '', 'x86_64': 'amd64', 'arm': 'arm', + 'aarch64': 'arm64' }.get(target.cpu) if old_target is None: return diff --git a/dom/base/NodeUbiReporting.cpp b/dom/base/NodeUbiReporting.cpp index 68731cc0f6b7..c858946a36a8 100644 --- a/dom/base/NodeUbiReporting.cpp +++ b/dom/base/NodeUbiReporting.cpp @@ -50,7 +50,7 @@ JS::ubi::Concrete::edges(JSContext* cx, bool wantNames) const return nullptr; } } - return range; + return js::UniquePtr(range.release()); } JS::ubi::Node::Size diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 24a75159e867..b82951d46ced 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1956,13 +1956,13 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) } bool -BytecodeEmitter::emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall) +BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall) { ParseNode* base = &pn->as().expression(); if (!emitSuperPropLHS(base, isCall)) return false; - if (!emitAtomOp(pn, op)) + if (!emitAtomOp(pn, JSOP_GETPROP_SUPER)) return false; if (isCall && !emit1(JSOP_SWAP)) @@ -2110,10 +2110,7 @@ BytecodeEmitter::emitElemOperands(ParseNode* pn, EmitElemOption opts) if (!emitTree(pn->pn_right)) return false; - if (opts == EmitElemOption::Set) { - if (!emit2(JSOP_PICK, 2)) - return false; - } else if (opts == EmitElemOption::IncDec || opts == EmitElemOption::CompoundAssign) { + if (opts == EmitElemOption::IncDec || opts == EmitElemOption::CompoundAssign) { if (!emit1(JSOP_TOID)) return false; } @@ -2125,37 +2122,27 @@ BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, EmitElemOption opts) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Elem) && pn->as().isSuper()); - // The ordering here is somewhat screwy. We need to evaluate the propval - // first, by spec. Do a little dance to not emit more than one JSOP_THIS. - // Since JSOP_THIS might throw in derived class constructors, we cannot - // just push it earlier as the receiver. We have to swap it down instead. + if (!emitGetThisForSuperBase(pn->pn_left)) // THIS + return false; - if (!emitTree(pn->pn_right)) + if (opts == EmitElemOption::Call) { + // We need a second |this| that will be consumed during computation of + // the property value. (The original |this| is passed to the call.) + if (!emit1(JSOP_DUP)) // THIS THIS + return false; + } + + if (!emitTree(pn->pn_right)) // THIS? THIS KEY return false; // We need to convert the key to an object id first, so that we do not do // it inside both the GETELEM and the SETELEM. if (opts == EmitElemOption::IncDec || opts == EmitElemOption::CompoundAssign) { - if (!emit1(JSOP_TOID)) + if (!emit1(JSOP_TOID)) // THIS? THIS KEY return false; } - if (!emitGetThisForSuperBase(pn->pn_left)) - return false; - - if (opts == EmitElemOption::Call) { - if (!emit1(JSOP_SWAP)) - return false; - - // We need another |this| on top, also - if (!emitDupAt(1)) - return false; - } - - if (!emit1(JSOP_SUPERBASE)) - return false; - - if (opts == EmitElemOption::Set && !emit2(JSOP_PICK, 3)) + if (!emit1(JSOP_SUPERBASE)) // THIS? THIS KEY SUPERBASE return false; return true; @@ -2174,30 +2161,27 @@ BytecodeEmitter::emitElemOpBase(JSOp op) bool BytecodeEmitter::emitElemOp(ParseNode* pn, JSOp op) { - EmitElemOption opts = EmitElemOption::Get; - if (op == JSOP_CALLELEM) - opts = EmitElemOption::Call; - else if (op == JSOP_SETELEM || op == JSOP_STRICTSETELEM) - opts = EmitElemOption::Set; + MOZ_ASSERT(op == JSOP_GETELEM || + op == JSOP_CALLELEM || + op == JSOP_DELELEM || + op == JSOP_STRICTDELELEM); + + EmitElemOption opts = op == JSOP_CALLELEM ? EmitElemOption::Call : EmitElemOption::Get; return emitElemOperands(pn, opts) && emitElemOpBase(op); } bool -BytecodeEmitter::emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall) +BytecodeEmitter::emitSuperGetElem(ParseNode* pn, bool isCall) { - EmitElemOption opts = EmitElemOption::Get; - if (isCall) - opts = EmitElemOption::Call; - else if (op == JSOP_SETELEM_SUPER || op == JSOP_STRICTSETELEM_SUPER) - opts = EmitElemOption::Set; + EmitElemOption opts = isCall ? EmitElemOption::Call : EmitElemOption::Get; - if (!emitSuperElemOperands(pn, opts)) + if (!emitSuperElemOperands(pn, opts)) // THIS? THIS KEY SUPERBASE return false; - if (!emitElemOpBase(op)) + if (!emitElemOpBase(JSOP_GETELEM_SUPER)) // THIS? VALUE return false; - if (isCall && !emit1(JSOP_SWAP)) + if (isCall && !emit1(JSOP_SWAP)) // VALUE THIS return false; return true; @@ -2228,11 +2212,11 @@ BytecodeEmitter::emitElemIncDec(ParseNode* pn) if (isSuper) { // There's no such thing as JSOP_DUP3, so we have to be creative. // Note that pushing things again is no fewer JSOps. - if (!emitDupAt(2)) // KEY THIS OBJ KEY + if (!emitDupAt(2)) // THIS KEY OBJ THIS return false; - if (!emitDupAt(2)) // KEY THIS OBJ KEY THIS + if (!emitDupAt(2)) // THIS KEY OBJ THIS KEY return false; - if (!emitDupAt(2)) // KEY THIS OBJ KEY THIS OBJ + if (!emitDupAt(2)) // THIS KEY OBJ THIS KEY OBJ return false; getOp = JSOP_GETELEM_SUPER; } else { @@ -2245,27 +2229,16 @@ BytecodeEmitter::emitElemIncDec(ParseNode* pn) return false; if (!emit1(JSOP_POS)) // OBJ KEY N return false; - if (post && !emit1(JSOP_DUP)) // OBJ KEY N? N - return false; - if (!emit1(JSOP_ONE)) // OBJ KEY N? N 1 - return false; - if (!emit1(binop)) // OBJ KEY N? N+1 - return false; - if (post) { - if (isSuper) { - // We have one more value to rotate around, because of |this| - // on the stack - if (!emit2(JSOP_PICK, 4)) - return false; - } - if (!emit2(JSOP_PICK, 3 + isSuper)) // KEY N N+1 OBJ + if (!emit1(JSOP_DUP)) // OBJ KEY N N return false; - if (!emit2(JSOP_PICK, 3 + isSuper)) // N N+1 OBJ KEY - return false; - if (!emit2(JSOP_PICK, 2 + isSuper)) // N OBJ KEY N+1 + if (!emit2(JSOP_UNPICK, 3 + isSuper)) // N OBJ KEY N return false; } + if (!emit1(JSOP_ONE)) // N? OBJ KEY N 1 + return false; + if (!emit1(binop)) // N? OBJ KEY N+1 + return false; JSOp setOp = isSuper ? (sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER) : (sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM); @@ -6481,17 +6454,26 @@ BytecodeEmitter::emitDeleteProperty(ParseNode* node) MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteProp)); MOZ_ASSERT(node->isArity(PN_UNARY)); - ParseNode* propExpr = node->pn_kid; - MOZ_ASSERT(propExpr->isKind(ParseNodeKind::Dot)); + PropertyAccess* propExpr = &node->pn_kid->as(); + + if (propExpr->isSuper()) { + // The expression |delete super.foo;| has to evaluate |super.foo|, + // which could throw if |this| hasn't yet been set by a |super(...)| + // call or the super-base is not an object, before throwing a + // ReferenceError for attempting to delete a super-reference. + if (!emitGetThisForSuperBase(&propExpr->expression())) + return false; - if (propExpr->as().isSuper()) { - // Still have to calculate the base, even though we are are going - // to throw unconditionally, as calculating the base could also - // throw. if (!emit1(JSOP_SUPERBASE)) return false; - return emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER); + // Unconditionally throw when attempting to delete a super-reference. + if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) + return false; + + // Another wrinkle: Balance the stack from the emitter's point of view. + // Execution will not reach here, as the last bytecode threw. + return emit1(JSOP_POP); } JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP; @@ -6504,23 +6486,32 @@ BytecodeEmitter::emitDeleteElement(ParseNode* node) MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteElem)); MOZ_ASSERT(node->isArity(PN_UNARY)); - ParseNode* elemExpr = node->pn_kid; - MOZ_ASSERT(elemExpr->isKind(ParseNodeKind::Elem)); + PropertyByValue* elemExpr = &node->pn_kid->as(); + + if (elemExpr->isSuper()) { + // The expression |delete super[foo];| has to evaluate |super[foo]|, + // which could throw if |this| hasn't yet been set by a |super(...)| + // call, or trigger side-effects when evaluating ToPropertyKey(foo), + // or also throw when the super-base is not an object, before throwing + // a ReferenceError for attempting to delete a super-reference. + if (!emitGetThisForSuperBase(elemExpr->pn_left)) + return false; - if (elemExpr->as().isSuper()) { - // Still have to calculate everything, even though we're gonna throw - // since it may have side effects if (!emitTree(elemExpr->pn_right)) return false; + if (!emit1(JSOP_TOID)) + return false; if (!emit1(JSOP_SUPERBASE)) return false; + + // Unconditionally throw when attempting to delete a super-reference. if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) return false; // Another wrinkle: Balance the stack from the emitter's point of view. // Execution will not reach here, as the last bytecode threw. - return emit1(JSOP_POP); + return emitPopN(2); } JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM; @@ -6754,10 +6745,10 @@ BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn) ParseNode* idNode = objNode->pn_next; ParseNode* receiverNode = idNode->pn_next; - if (!emitTree(idNode)) + if (!emitTree(receiverNode)) return false; - if (!emitTree(receiverNode)) + if (!emitTree(idNode)) return false; if (!emitTree(objNode)) @@ -6816,7 +6807,7 @@ BytecodeEmitter::emitCallee(ParseNode* callee, ParseNode* call, bool* callop) case ParseNodeKind::Dot: MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting); if (callee->as().isSuper()) { - if (!emitSuperPropOp(callee, JSOP_GETPROP_SUPER, /* isCall = */ *callop)) + if (!emitSuperGetProp(callee, /* isCall = */ *callop)) return false; } else { if (!emitPropOp(callee, *callop ? JSOP_CALLPROP : JSOP_GETPROP)) @@ -6827,7 +6818,7 @@ BytecodeEmitter::emitCallee(ParseNode* callee, ParseNode* call, bool* callop) case ParseNodeKind::Elem: MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting); if (callee->as().isSuper()) { - if (!emitSuperElemOp(callee, JSOP_GETELEM_SUPER, /* isCall = */ *callop)) + if (!emitSuperGetElem(callee, /* isCall = */ *callop)) return false; } else { if (!emitElemOp(callee, *callop ? JSOP_CALLELEM : JSOP_GETELEM)) @@ -8546,7 +8537,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: case ParseNodeKind::Dot: if (pn->as().isSuper()) { - if (!emitSuperPropOp(pn, JSOP_GETPROP_SUPER)) + if (!emitSuperGetProp(pn)) return false; } else { if (!emitPropOp(pn, JSOP_GETPROP)) @@ -8556,7 +8547,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: case ParseNodeKind::Elem: if (pn->as().isSuper()) { - if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER)) + if (!emitSuperGetElem(pn)) return false; } else { if (!emitElemOp(pn, JSOP_GETELEM)) diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index f25b34ffc1d4..e72167ccce79 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -673,7 +673,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM // opcode onto the stack in the right order. In the case of SETELEM, the // value to be assigned must already be pushed. - enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign, Ref }; + enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref }; MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts); MOZ_MUST_USE bool emitElemOpBase(JSOp op); @@ -846,10 +846,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitClass(ParseNode* pn); MOZ_MUST_USE bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false); - MOZ_MUST_USE bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false); + MOZ_MUST_USE bool emitSuperGetProp(ParseNode* pn, bool isCall = false); MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn, EmitElemOption opts = EmitElemOption::Get); - MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false); + MOZ_MUST_USE bool emitSuperGetElem(ParseNode* pn, bool isCall = false); MOZ_MUST_USE bool emitCallee(ParseNode* callee, ParseNode* call, bool* callop); diff --git a/js/src/jit-test/tests/auto-regress/bug1476417.js b/js/src/jit-test/tests/auto-regress/bug1476417.js new file mode 100644 index 000000000000..7748723f7e4a --- /dev/null +++ b/js/src/jit-test/tests/auto-regress/bug1476417.js @@ -0,0 +1,22 @@ +function f(x) { + var y, z; + + arguments; // Force creation of mapped arguments, so modifying |x| writes to the arguments object. + Math; // Access a global variable to create a resume point. + z = x + 1; // Was executed twice because only the resume point for 'Math' was present before the fix. + x = z; // Modifying |x| writes into the arguments object. We missed to create a resume point here. + y = 2 * x; // Triggers a bailout when overflowing int32 boundaries. + + return [x, y]; +} + +var x = [0, 0, 0x3FFFFFFF]; + +for (var j = 0; j < 3; ++j) { + var value = x[j]; + var expected = [value + 1, (value + 1) * 2]; + var actual = f(value); + + assertEq(actual[0], expected[0]); + assertEq(actual[1], expected[1]); +} diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index a24d991a1e14..a39000fff8ff 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2303,7 +2303,7 @@ BaselineCompiler::emit_JSOP_GETELEM_SUPER() storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2); frame.pop(); - // Keep index and receiver in R0 and R1. + // Keep receiver and index in R0 and R1. frame.popRegsAndSync(2); // Keep obj on the stack. @@ -2356,10 +2356,10 @@ BaselineCompiler::emit_JSOP_SETELEM_SUPER() { bool strict = IsCheckStrictOp(JSOp(*pc)); - // Incoming stack is |propval, receiver, obj, rval|. We need to shuffle + // Incoming stack is |receiver, propval, obj, rval|. We need to shuffle // stack to leave rval when operation is complete. - // Pop rval into R0, then load propval into R1 and replace with rval. + // Pop rval into R0, then load receiver into R1 and replace with rval. frame.popRegsAndSync(1); masm.loadValue(frame.addressOfStackValue(frame.peek(-3)), R1); masm.storeValue(R0, frame.addressOfStackValue(frame.peek(-3))); @@ -2367,10 +2367,10 @@ BaselineCompiler::emit_JSOP_SETELEM_SUPER() prepareVMCall(); pushArg(Imm32(strict)); - masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R2); - pushArg(R2); // receiver + pushArg(R1); // receiver pushArg(R0); // rval - pushArg(R1); // propval + masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); + pushArg(R0); // propval masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); pushArg(R0.scratchReg()); // obj @@ -2727,7 +2727,6 @@ BaselineCompiler::getEnvironmentCoordinateAddressFromObject(Register objReg, Reg EnvironmentCoordinate ec(pc); Shape* shape = EnvironmentCoordinateToEnvironmentShape(script, pc); - Address addr; if (shape->numFixedSlots() <= ec.slot()) { masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), reg); return Address(reg, (ec.slot() - shape->numFixedSlots()) * sizeof(Value)); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 6dc371a89524..a54bcc852c8b 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -639,7 +639,7 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_ static bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_, - HandleValue lhs, HandleValue receiver, HandleValue rhs, + HandleValue lhs, HandleValue rhs, HandleValue receiver, MutableHandleValue res) { // This fallback stub may trigger debug mode toggling. @@ -734,17 +734,17 @@ ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Super property getters use a |this| that differs from base object if (hasReceiver_) { - // State: index in R0, receiver in R1, obj on the stack + // State: receiver in R0, index in R1, obj on the stack // Ensure stack is fully synced for the expression decompiler. - // We need: index, receiver, obj + // We need: receiver, index, obj masm.pushValue(R0); masm.pushValue(R1); masm.pushValue(Address(masm.getStackPointer(), sizeof(Value) * 2)); // Push arguments. - masm.pushValue(R0); // Index - masm.pushValue(R1); // Reciver + masm.pushValue(R0); // Receiver + masm.pushValue(R1); // Index masm.pushValue(Address(masm.getStackPointer(), sizeof(Value) * 5)); // Obj masm.push(ICStubReg); pushStubPayload(masm, R0.scratchReg()); diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index abe4d7ff4656..e901aae4520e 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -1651,7 +1651,7 @@ CacheIRCompiler::emitGuardFunctionPrototype() StubFieldOffset slot(reader.stubOffset(), StubField::Type::RawWord); masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1); emitLoadStubField(slot, scratch2); - BaseValueIndex prototypeSlot(scratch1, scratch2); + BaseObjectSlotIndex prototypeSlot(scratch1, scratch2); masm.branchTestObject(Assembler::NotEqual, prototypeSlot, failure->label()); masm.unboxObject(prototypeSlot, scratch1); masm.branchPtr(Assembler::NotEqual, diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 5e5b3afc2ad0..ca69f965d0e3 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1486,22 +1486,38 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re masm.loadStringLength(input, temp2); masm.branch32(Assembler::AboveOrEqual, lastIndex, temp2, &done); + // For TrailSurrogateMin ≤ x ≤ TrailSurrogateMax and + // LeadSurrogateMin ≤ x ≤ LeadSurrogateMax, the following + // equations hold. + // + // SurrogateMin ≤ x ≤ SurrogateMax + // <> SurrogateMin ≤ x ≤ SurrogateMin + 2^10 - 1 + // <> ((x - SurrogateMin) >>> 10) = 0 where >>> is an unsigned-shift + // See Hacker's Delight, section 4-1 for details. + // + // ((x - SurrogateMin) >>> 10) = 0 + // <> floor((x - SurrogateMin) / 1024) = 0 + // <> floor((x / 1024) - (SurrogateMin / 1024)) = 0 + // <> floor(x / 1024) = SurrogateMin / 1024 + // <> floor(x / 1024) * 1024 = SurrogateMin + // <> (x >>> 10) << 10 = SurrogateMin + // <> x & ~(2^10 - 1) = SurrogateMin + + constexpr char16_t SurrogateMask = 0xFC00; + // Check if input[lastIndex] is trail surrogate. masm.loadStringChars(input, temp2, CharEncoding::TwoByte); - masm.computeEffectiveAddress(BaseIndex(temp2, lastIndex, TimesTwo), temp3); - masm.load16ZeroExtend(Address(temp3, 0), temp3); + masm.load16ZeroExtend(BaseIndex(temp2, lastIndex, TimesTwo), temp3); - masm.branch32(Assembler::Below, temp3, Imm32(unicode::TrailSurrogateMin), &done); - masm.branch32(Assembler::Above, temp3, Imm32(unicode::TrailSurrogateMax), &done); + masm.and32(Imm32(SurrogateMask), temp3); + masm.branch32(Assembler::NotEqual, temp3, Imm32(unicode::TrailSurrogateMin), &done); // Check if input[lastIndex-1] is lead surrogate. - masm.move32(lastIndex, temp3); - masm.sub32(Imm32(1), temp3); - masm.computeEffectiveAddress(BaseIndex(temp2, temp3, TimesTwo), temp3); - masm.load16ZeroExtend(Address(temp3, 0), temp3); + masm.load16ZeroExtend(BaseIndex(temp2, lastIndex, TimesTwo, -int32_t(sizeof(char16_t))), + temp3); - masm.branch32(Assembler::Below, temp3, Imm32(unicode::LeadSurrogateMin), &done); - masm.branch32(Assembler::Above, temp3, Imm32(unicode::LeadSurrogateMax), &done); + masm.and32(Imm32(SurrogateMask), temp3); + masm.branch32(Assembler::NotEqual, temp3, Imm32(unicode::LeadSurrogateMin), &done); // Move lastIndex to lead surrogate. masm.subPtr(Imm32(1), lastIndex); @@ -3691,7 +3707,7 @@ CodeGenerator::visitElements(LElements* lir) masm.loadPtr(elements, ToRegister(lir->output())); } -typedef bool (*ConvertElementsToDoublesFn)(JSContext*, uintptr_t); +typedef void (*ConvertElementsToDoublesFn)(JSContext*, uintptr_t); static const VMFunction ConvertElementsToDoublesInfo = FunctionInfo(ObjectElements::ConvertElementsToDoubles, "ObjectElements::ConvertElementsToDoubles"); @@ -7581,7 +7597,6 @@ CodeGenerator::visitSignDI(LSignDI* ins) // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 // is -Infinity instead of Infinity. - Label isNegInf; masm.loadConstantDouble(1.0, temp); masm.divDouble(input, temp); masm.branchDouble(Assembler::DoubleLessThan, temp, input, &bailout); @@ -9262,7 +9277,7 @@ CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation* index, Address dest(elements, ToInt32(index) * sizeof(js::Value) + offsetAdjustment); masm.branchTestMagic(Assembler::Equal, dest, &bail); } else { - BaseIndex dest(elements, ToRegister(index), TimesEight, offsetAdjustment); + BaseObjectElementIndex dest(elements, ToRegister(index), offsetAdjustment); masm.branchTestMagic(Assembler::Equal, dest, &bail); } bailoutFrom(&bail, snapshot); @@ -9287,7 +9302,7 @@ CodeGenerator::emitStoreElementTyped(const LAllocation* value, Address dest(elements, ToInt32(index) * sizeof(js::Value) + offsetAdjustment); masm.storeUnboxedValue(v, valueType, dest, elementType); } else { - BaseIndex dest(elements, ToRegister(index), TimesEight, offsetAdjustment); + BaseObjectElementIndex dest(elements, ToRegister(index), offsetAdjustment); masm.storeUnboxedValue(v, valueType, dest, elementType); } } @@ -9327,8 +9342,8 @@ CodeGenerator::visitStoreElementV(LStoreElementV* lir) ToInt32(lir->index()) * sizeof(js::Value) + lir->mir()->offsetAdjustment()); masm.storeValue(value, dest); } else { - BaseIndex dest(elements, ToRegister(lir->index()), TimesEight, - lir->mir()->offsetAdjustment()); + BaseObjectElementIndex dest(elements, ToRegister(lir->index()), + lir->mir()->offsetAdjustment()); masm.storeValue(value, dest); } } @@ -9360,7 +9375,7 @@ CodeGenerator::emitStoreElementHoleT(T* lir) masm.branchTest32(Assembler::NonZero, flags, Imm32(ObjectElements::FROZEN), ool->callStub()); if (lir->toFallibleStoreElementT()->mir()->needsHoleCheck()) { - masm.branchTestMagic(Assembler::Equal, BaseValueIndex(elements, index), + masm.branchTestMagic(Assembler::Equal, BaseObjectElementIndex(elements, index), ool->callStub()); } } @@ -9403,7 +9418,7 @@ CodeGenerator::emitStoreElementHoleV(T* lir) masm.branchTest32(Assembler::NonZero, flags, Imm32(ObjectElements::FROZEN), ool->callStub()); if (lir->toFallibleStoreElementV()->mir()->needsHoleCheck()) { - masm.branchTestMagic(Assembler::Equal, BaseValueIndex(elements, index), + masm.branchTestMagic(Assembler::Equal, BaseObjectElementIndex(elements, index), ool->callStub()); } } @@ -9412,7 +9427,7 @@ CodeGenerator::emitStoreElementHoleV(T* lir) emitPreBarrier(elements, lir->index(), 0); masm.bind(ool->rejoinStore()); - masm.storeValue(value, BaseIndex(elements, index, TimesEight)); + masm.storeValue(value, BaseObjectElementIndex(elements, index)); masm.bind(ool->rejoin()); } @@ -9685,7 +9700,7 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R masm.sub32(Imm32(1), lengthTemp); if (mir->mode() == MArrayPopShift::Pop) { - BaseIndex addr(elementsTemp, lengthTemp, TimesEight); + BaseObjectElementIndex addr(elementsTemp, lengthTemp); masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); } else { MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift); @@ -9768,7 +9783,7 @@ CodeGenerator::emitArrayPush(LInstruction* lir, Register obj, masm.spectreBoundsCheck32(length, capacity, spectreTemp, ool->entry()); // Do the store. - masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight)); + masm.storeConstantOrRegister(value, BaseObjectElementIndex(elementsTemp, length)); masm.add32(Imm32(1), length); @@ -11423,8 +11438,8 @@ CodeGenerator::visitLoadElementT(LLoadElementT* lir) int32_t offset = ToInt32(index) * sizeof(js::Value) + lir->mir()->offsetAdjustment(); emitLoadElementT(lir, Address(elements, offset)); } else { - emitLoadElementT(lir, BaseIndex(elements, ToRegister(index), TimesEight, - lir->mir()->offsetAdjustment())); + emitLoadElementT(lir, BaseObjectElementIndex(elements, ToRegister(index), + lir->mir()->offsetAdjustment())); } } @@ -11569,7 +11584,7 @@ CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) const MLoadUnboxedScalar* mir = lir->mir(); Scalar::Type readType = mir->readType(); - int width = Scalar::byteSize(mir->storageType()); + size_t width = Scalar::byteSize(mir->storageType()); bool canonicalizeDouble = mir->canonicalizeDoubles(); Label fail; @@ -11606,7 +11621,7 @@ CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole* lir) masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), scratch); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); Label fail; BaseIndex source(scratch, index, ScaleFromElemWidth(width)); masm.loadFromTypedArray(arrayType, source, out, lir->mir()->allowDouble(), @@ -11880,7 +11895,7 @@ CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) Scalar::Type writeType = mir->writeType(); - int width = Scalar::byteSize(mir->storageType()); + size_t width = Scalar::byteSize(mir->storageType()); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width + mir->offsetAdjustment()); @@ -11899,7 +11914,7 @@ CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole* lir) const LAllocation* value = lir->value(); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); Register index = ToRegister(lir->index()); const LAllocation* length = lir->length(); @@ -12053,7 +12068,7 @@ CodeGenerator::visitInArray(LInArray* lir) masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); if (mir->needsHoleCheck()) { - BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight); + BaseObjectElementIndex address(elements, ToRegister(lir->index())); masm.branchTestMagic(Assembler::Equal, address, &falseBranch); } masm.jump(&trueBranch); @@ -12450,14 +12465,12 @@ CodeGenerator::emitIsCallableOrConstructor(Register object, Register output, Lab if (mode == Callable) { masm.move32(Imm32(1), output); } else { - Label notConstructor; + static_assert(mozilla::IsPowerOfTwo(unsigned(JSFunction::CONSTRUCTOR)), + "JSFunction::CONSTRUCTOR has only one bit set"); + masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output); - masm.and32(Imm32(JSFunction::CONSTRUCTOR), output); - masm.branchTest32(Assembler::Zero, output, output, ¬Constructor); - masm.move32(Imm32(1), output); - masm.jump(&done); - masm.bind(¬Constructor); - masm.move32(Imm32(0), output); + masm.rshift32(Imm32(mozilla::FloorLog2(JSFunction::CONSTRUCTOR)), output); + masm.and32(Imm32(1), output); } masm.jump(&done); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 1e10872552b4..415963f249f5 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -9765,8 +9765,8 @@ AbortReasonOr IonBuilder::jsop_getelem_super() { MDefinition* obj = current->pop(); - MDefinition* receiver = current->pop(); MDefinition* id = current->pop(); + MDefinition* receiver = current->pop(); #if defined(JS_CODEGEN_X86) if (instrumentedProfiling()) @@ -12257,8 +12257,10 @@ IonBuilder::jsop_setarg(uint32_t arg) if (info().argsObjAliasesFormals()) { if (needsPostBarrier(val)) current->add(MPostWriteBarrier::New(alloc(), current->argumentsObject(), val)); - current->add(MSetArgumentsObjectArg::New(alloc(), current->argumentsObject(), - GET_ARGNO(pc), val)); + auto* ins = MSetArgumentsObjectArg::New(alloc(), current->argumentsObject(), + GET_ARGNO(pc), val); + current->add(ins); + MOZ_TRY(resumeAfter(ins)); return Ok(); } diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 0d965e0f1559..d09e88d97ea1 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -3208,8 +3208,10 @@ MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch { // 16-bit loads are slow and unaligned 32-bit loads may be too so // perform an aligned 32-bit load and adjust the bitmask accordingly. - MOZ_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0); - MOZ_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2); + static_assert(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0, + "JSFunction nargs are aligned to uint32_t"); + static_assert(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2, + "JSFunction nargs and flags are stored next to each other"); // First, ensure it's a scripted function. load32(Address(fun, JSFunction::offsetOfNargs()), scratch); diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 03eaed15fd88..3e5260f3310f 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1336,17 +1336,17 @@ class Assembler : public AssemblerShared protected: // Structure for fixing up pc-relative loads/jumps when a the machine code // gets moved (executable copy, gc, etc.). - struct RelativePatch + class RelativePatch { void* target_; - Relocation::Kind kind_; + RelocationKind kind_; public: - RelativePatch(void* target, Relocation::Kind kind) + RelativePatch(void* target, RelocationKind kind) : target_(target), kind_(kind) { } void* target() const { return target_; } - Relocation::Kind kind() const { return kind_; } + RelocationKind kind() const { return kind_; } }; // TODO: this should actually be a pool-like object. It is currently a big @@ -1776,7 +1776,7 @@ class Assembler : public AssemblerShared } void retarget(Label* label, Label* target); // I'm going to pretend this doesn't exist for now. - void retarget(Label* label, void* target, Relocation::Kind reloc); + void retarget(Label* label, void* target, RelocationKind reloc); static void Bind(uint8_t* rawCode, const CodeLabel& label); @@ -1791,7 +1791,7 @@ class Assembler : public AssemblerShared #ifdef DEBUG MOZ_ASSERT(dataRelocations_.length() == 0); for (auto& j : jumps_) - MOZ_ASSERT(j.kind() == Relocation::HARDCODED); + MOZ_ASSERT(j.kind() == RelocationKind::HARDCODED); #endif } @@ -1808,9 +1808,9 @@ class Assembler : public AssemblerShared static bool HasRoundInstruction(RoundingMode mode) { return false; } protected: - void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { + void addPendingJump(BufferOffset src, ImmPtr target, RelocationKind kind) { enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind)); - if (kind == Relocation::JITCODE) + if (kind == RelocationKind::JITCODE) writeRelocation(src); } diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index ca864f42e42e..11d30805d28f 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1806,7 +1806,7 @@ CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayE Register newval = ToRegister(lir->newval()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); @@ -1827,7 +1827,7 @@ CodeGenerator::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayEle Register value = ToRegister(lir->value()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); @@ -1850,7 +1850,7 @@ CodeGenerator::visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* Register value = ToRegister(lir->value()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address mem(elements, ToInt32(lir->index()) * width); @@ -1872,7 +1872,7 @@ CodeGenerator::visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayEleme Register flagTemp = ToRegister(lir->flagTemp()); Register value = ToRegister(lir->value()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address mem(elements, ToInt32(lir->index()) * width); diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 2a91d2be5806..4fe6a22e0d81 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3968,7 +3968,7 @@ CodeOffset MacroAssemblerARMCompat::toggledCall(JitCode* target, bool enabled) { BufferOffset bo = nextOffset(); - addPendingJump(bo, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE); ScratchRegisterScope scratch(asMasm()); ma_movPatchable(ImmPtr(target->raw()), scratch, Always); if (enabled) @@ -4541,7 +4541,7 @@ void MacroAssembler::call(ImmPtr imm) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, imm, Relocation::HARDCODED); + addPendingJump(bo, imm, RelocationKind::HARDCODED); ma_call(imm); } @@ -4563,7 +4563,7 @@ void MacroAssembler::call(JitCode* c) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); ScratchRegisterScope scratch(*this); ma_movPatchable(ImmPtr(c->raw()), scratch, Always); callJitNoProfiler(scratch); @@ -5023,7 +5023,7 @@ MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType value const Address& dest, MIRType slotType); template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, - const BaseIndex& dest, MIRType slotType); + const BaseObjectElementIndex& dest, MIRType slotType); CodeOffset MacroAssembler::wasmTrapInstruction() diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 97f40753e54f..2c612ad37d44 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -572,7 +572,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void branch(JitCode* c) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); ScratchRegisterScope scratch(asMasm()); ma_movPatchable(ImmPtr(c->raw()), scratch, Always); ma_bx(scratch); diff --git a/js/src/jit/arm64/Assembler-arm64.cpp b/js/src/jit/arm64/Assembler-arm64.cpp index cff00cc15c49..53f5364ea90a 100644 --- a/js/src/jit/arm64/Assembler-arm64.cpp +++ b/js/src/jit/arm64/Assembler-arm64.cpp @@ -315,10 +315,10 @@ Assembler::bind(RepatchLabel* label) } void -Assembler::addJumpRelocation(BufferOffset src, Relocation::Kind reloc) +Assembler::addJumpRelocation(BufferOffset src, RelocationKind reloc) { // Only JITCODE relocations are patchable at runtime. - MOZ_ASSERT(reloc == Relocation::JITCODE); + MOZ_ASSERT(reloc == RelocationKind::JITCODE); // The jump relocation table starts with a fixed-width integer pointing // to the start of the extended jump table. But, we don't know the @@ -333,11 +333,11 @@ Assembler::addJumpRelocation(BufferOffset src, Relocation::Kind reloc) } void -Assembler::addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind reloc) +Assembler::addPendingJump(BufferOffset src, ImmPtr target, RelocationKind reloc) { MOZ_ASSERT(target.value != nullptr); - if (reloc == Relocation::JITCODE) + if (reloc == RelocationKind::JITCODE) addJumpRelocation(src, reloc); // This jump is not patchable at runtime. Extended jump table entry requirements @@ -347,10 +347,10 @@ Assembler::addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind relo } size_t -Assembler::addPatchableJump(BufferOffset src, Relocation::Kind reloc) +Assembler::addPatchableJump(BufferOffset src, RelocationKind reloc) { MOZ_CRASH("TODO: This is currently unused (and untested)"); - if (reloc == Relocation::JITCODE) + if (reloc == RelocationKind::JITCODE) addJumpRelocation(src, reloc); size_t extendedTableIndex = pendingJumps_.length(); diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h index 056e499443ce..978deabfdba7 100644 --- a/js/src/jit/arm64/Assembler-arm64.h +++ b/js/src/jit/arm64/Assembler-arm64.h @@ -275,16 +275,16 @@ class Assembler : public vixl::Assembler static bool HasRoundInstruction(RoundingMode mode) { return false; } // Tracks a jump that is patchable after finalization. - void addJumpRelocation(BufferOffset src, Relocation::Kind reloc); + void addJumpRelocation(BufferOffset src, RelocationKind reloc); protected: // Add a jump whose target is unknown until finalization. // The jump may not be patched at runtime. - void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind); + void addPendingJump(BufferOffset src, ImmPtr target, RelocationKind kind); // Add a jump whose target is unknown until finalization, and may change // thereafter. The jump is patchable at runtime. - size_t addPatchableJump(BufferOffset src, Relocation::Kind kind); + size_t addPatchableJump(BufferOffset src, RelocationKind kind); public: static uint32_t PatchWrite_NearCallSize() { @@ -337,7 +337,7 @@ class Assembler : public vixl::Assembler #ifdef DEBUG MOZ_ASSERT(dataRelocations_.length() == 0); for (auto& j : pendingJumps_) - MOZ_ASSERT(j.kind == Relocation::HARDCODED); + MOZ_ASSERT(j.kind == RelocationKind::HARDCODED); #endif } @@ -376,7 +376,7 @@ class Assembler : public vixl::Assembler protected: // Because jumps may be relocated to a target inaccessible by a short jump, // each relocatable jump must have a unique entry in the extended jump table. - // Valid relocatable targets are of type Relocation::JITCODE. + // Valid relocatable targets are of type RelocationKind::JITCODE. struct JumpRelocation { BufferOffset jump; // Offset to the short jump, from the start of the code buffer. @@ -393,9 +393,9 @@ class Assembler : public vixl::Assembler { BufferOffset offset; void* target; - Relocation::Kind kind; + RelocationKind kind; - RelativePatch(BufferOffset offset, void* target, Relocation::Kind kind) + RelativePatch(BufferOffset offset, void* target, RelocationKind kind) : offset(offset), target(target), kind(kind) { } }; diff --git a/js/src/jit/arm64/CodeGenerator-arm64.cpp b/js/src/jit/arm64/CodeGenerator-arm64.cpp index 11eed2018ea5..153b26be79bf 100644 --- a/js/src/jit/arm64/CodeGenerator-arm64.cpp +++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp @@ -815,7 +815,7 @@ CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayE Register newval = ToRegister(lir->newval()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); @@ -836,7 +836,7 @@ CodeGenerator::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayEle Register value = ToRegister(lir->value()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index bbd01d60e682..ee8df5ff9cd3 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -623,7 +623,7 @@ MacroAssembler::call(JitCode* c) const ARMRegister scratch64 = temps.AcquireX(); syncStackPtr(); BufferOffset off = immPool64(scratch64, uint64_t(c->raw())); - addPendingJump(off, ImmPtr(c->raw()), Relocation::JITCODE); + addPendingJump(off, ImmPtr(c->raw()), RelocationKind::JITCODE); blr(scratch64); } @@ -1046,7 +1046,7 @@ MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType value const Address& dest, MIRType slotType); template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, - const BaseIndex& dest, MIRType slotType); + const BaseObjectElementIndex& dest, MIRType slotType); void MacroAssembler::comment(const char* msg) diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h index 7ce791776da1..4766dcea9cf7 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -676,7 +676,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler void jump(TrampolinePtr code) { syncStackPtr(); BufferOffset loc = b(-1, LabelDoc()); // The jump target will be patched by executableCopy(). - addPendingJump(loc, ImmPtr(code.value), Relocation::HARDCODED); + addPendingJump(loc, ImmPtr(code.value), RelocationKind::HARDCODED); } void jump(RepatchLabel* label) { MOZ_CRASH("jump (repatchlabel)"); @@ -1230,7 +1230,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler void branch(JitCode* target) { syncStackPtr(); BufferOffset loc = b(-1, LabelDoc()); // The jump target will be patched by executableCopy(). - addPendingJump(loc, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(loc, ImmPtr(target->raw()), RelocationKind::JITCODE); } CodeOffsetJump jumpWithPatch(RepatchLabel* label) @@ -1896,7 +1896,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler nop(); } - addPendingJump(loadOffset, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(loadOffset, ImmPtr(target->raw()), RelocationKind::JITCODE); CodeOffset ret(offset.getOffset()); return ret; } diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h index 55430c37c393..18265fe768af 100644 --- a/js/src/jit/mips-shared/Assembler-mips-shared.h +++ b/js/src/jit/mips-shared/Assembler-mips-shared.h @@ -861,9 +861,9 @@ class AssemblerMIPSShared : public AssemblerShared // we want to fix-up BufferOffset offset; void* target; - Relocation::Kind kind; + RelocationKind kind; - RelativePatch(BufferOffset offset, void* target, Relocation::Kind kind) + RelativePatch(BufferOffset offset, void* target, RelocationKind kind) : offset(offset), target(target), kind(kind) @@ -911,7 +911,7 @@ class AssemblerMIPSShared : public AssemblerShared #ifdef DEBUG MOZ_ASSERT(dataRelocations_.length() == 0); for (auto& j : jumps_) - MOZ_ASSERT(j.kind == Relocation::HARDCODED); + MOZ_ASSERT(j.kind == RelocationKind::HARDCODED); #endif } @@ -1285,9 +1285,9 @@ class AssemblerMIPSShared : public AssemblerShared protected: InstImm invertBranch(InstImm branch, BOffImm16 skipOffset); - void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { + void addPendingJump(BufferOffset src, ImmPtr target, RelocationKind kind) { enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind)); - if (kind == Relocation::JITCODE) + if (kind == RelocationKind::JITCODE) writeRelocation(src); } diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp index 64b79a900fec..39be4984a767 100644 --- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp @@ -2417,7 +2417,7 @@ CodeGenerator::visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* Register value = ToRegister(lir->value()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address mem(elements, ToInt32(lir->index()) * width); @@ -2441,7 +2441,7 @@ CodeGenerator::visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayEleme Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp()); Register value = ToRegister(lir->value()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address mem(elements, ToInt32(lir->index()) * width); @@ -2468,7 +2468,7 @@ CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayE Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); @@ -2494,7 +2494,7 @@ CodeGenerator::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayEle Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp index 5326448a7c4e..e50944cd4ad0 100644 --- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp @@ -1613,7 +1613,7 @@ void MacroAssembler::call(ImmPtr target) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, target, Relocation::HARDCODED); + addPendingJump(bo, target, RelocationKind::HARDCODED); ma_call(target); } @@ -1621,7 +1621,7 @@ void MacroAssembler::call(JitCode* c) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); ma_liPatchable(ScratchRegister, ImmPtr(c->raw())); callJitNoProfiler(ScratchRegister); } diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index 5e770f38d375..25fe6478de3f 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -2023,7 +2023,7 @@ MacroAssemblerMIPSCompat::toggledCall(JitCode* target, bool enabled) { BufferOffset bo = nextOffset(); CodeOffset offset(bo.getOffset()); - addPendingJump(bo, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE); ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); if (enabled) { as_jalr(ScratchRegister); @@ -2429,7 +2429,7 @@ MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType value const Address& dest, MIRType slotType); template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, - const BaseIndex& dest, MIRType slotType); + const BaseObjectElementIndex& dest, MIRType slotType); void diff --git a/js/src/jit/mips32/MacroAssembler-mips32.h b/js/src/jit/mips32/MacroAssembler-mips32.h index 79763960f856..11353074f639 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.h +++ b/js/src/jit/mips32/MacroAssembler-mips32.h @@ -244,7 +244,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS void branch(JitCode* c) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); ma_liPatchable(ScratchRegister, ImmPtr(c->raw())); as_jr(ScratchRegister); as_nop(); @@ -347,7 +347,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS { auto target = ImmPtr(code.value); BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, target, Relocation::HARDCODED); + addPendingJump(bo, target, RelocationKind::HARDCODED); ma_jump(target); } diff --git a/js/src/jit/mips64/MacroAssembler-mips64.cpp b/js/src/jit/mips64/MacroAssembler-mips64.cpp index c59cec333cce..155f4cd68291 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.cpp +++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp @@ -1890,7 +1890,7 @@ MacroAssemblerMIPS64Compat::toggledCall(JitCode* target, bool enabled) { BufferOffset bo = nextOffset(); CodeOffset offset(bo.getOffset()); - addPendingJump(bo, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE); ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); if (enabled) { as_jalr(ScratchRegister); @@ -2257,7 +2257,7 @@ MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType value const Address& dest, MIRType slotType); template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, - const BaseIndex& dest, MIRType slotType); + const BaseObjectElementIndex& dest, MIRType slotType); void diff --git a/js/src/jit/mips64/MacroAssembler-mips64.h b/js/src/jit/mips64/MacroAssembler-mips64.h index f8eba1fcca51..16a03d2bb260 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.h +++ b/js/src/jit/mips64/MacroAssembler-mips64.h @@ -255,7 +255,7 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 void branch(JitCode* c) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE); + addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); ma_liPatchable(ScratchRegister, ImmPtr(c->raw())); as_jr(ScratchRegister); as_nop(); @@ -362,7 +362,7 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 { auto target = ImmPtr(code.value); BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, target, Relocation::HARDCODED); + addPendingJump(bo, target, RelocationKind::HARDCODED); ma_jump(target); } diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 66814ac1f55e..9f4c028cf234 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -322,8 +322,7 @@ struct Address { } #endif - Address() : base(RegisterOrSP(Registers::Invalid)), offset(0) - { } + Address() = delete; }; #if JS_BITS_PER_WORD == 32 @@ -363,12 +362,7 @@ struct BaseIndex { } #endif - BaseIndex() - : base(RegisterOrSP(Registers::Invalid)) - , index(Registers::Invalid) - , scale(TimesOne) - , offset(0) - {} + BaseIndex() = delete; }; #if JS_BITS_PER_WORD == 32 @@ -445,17 +439,14 @@ struct BaseObjectSlotIndex : BaseValueIndex #endif }; -class Relocation { - public: - enum Kind { - // The target is immovable, so patching is only needed if the source - // buffer is relocated and the reference is relative. - HARDCODED, +enum class RelocationKind { + // The target is immovable, so patching is only needed if the source + // buffer is relocated and the reference is relative. + HARDCODED, - // The target is the start of a JitCode buffer, which must be traced - // during garbage collection. Relocations and patching may be needed. - JITCODE - }; + // The target is the start of a JitCode buffer, which must be traced + // during garbage collection. Relocations and patching may be needed. + JITCODE }; class RepatchLabel diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 41bec53a14e7..ab61e5434062 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -1497,13 +1497,14 @@ CodeGeneratorShared::omitOverRecursedCheck() const } void -CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation* index, int32_t offsetAdjustment) +CodeGeneratorShared::emitPreBarrier(Register elements, const LAllocation* index, + int32_t offsetAdjustment) { if (index->isConstant()) { - Address address(base, ToInt32(index) * sizeof(Value) + offsetAdjustment); + Address address(elements, ToInt32(index) * sizeof(Value) + offsetAdjustment); masm.guardedCallPreBarrier(address, MIRType::Value); } else { - BaseIndex address(base, ToRegister(index), TimesEight, offsetAdjustment); + BaseObjectElementIndex address(elements, ToRegister(index), offsetAdjustment); masm.guardedCallPreBarrier(address, MIRType::Value); } } diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index 6e166dbfc5b0..68fb1a4a4a59 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -317,7 +317,7 @@ class CodeGeneratorShared : public LElementVisitor void emitTruncateDouble(FloatRegister src, Register dest, MTruncateToInt32* mir); void emitTruncateFloat32(FloatRegister src, Register dest, MTruncateToInt32* mir); - void emitPreBarrier(Register base, const LAllocation* index, int32_t offsetAdjustment); + void emitPreBarrier(Register elements, const LAllocation* index, int32_t offsetAdjustment); void emitPreBarrier(Address address); // We don't emit code for trivial blocks, so if we want to branch to the diff --git a/js/src/jit/x64/Assembler-x64.cpp b/js/src/jit/x64/Assembler-x64.cpp index 964573e66d40..ce3da9bf5023 100644 --- a/js/src/jit/x64/Assembler-x64.cpp +++ b/js/src/jit/x64/Assembler-x64.cpp @@ -118,7 +118,7 @@ ABIArgGenerator::next(MIRType type) } void -Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc) +Assembler::writeRelocation(JmpSrc src, RelocationKind reloc) { if (!jumpRelocations_.length()) { // The jump relocation table starts with a fixed-width integer pointing @@ -127,26 +127,26 @@ Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc) // patch later. jumpRelocations_.writeFixedUint32_t(0); } - if (reloc == Relocation::JITCODE) { + if (reloc == RelocationKind::JITCODE) { jumpRelocations_.writeUnsigned(src.offset()); jumpRelocations_.writeUnsigned(jumps_.length()); } } void -Assembler::addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind reloc) +Assembler::addPendingJump(JmpSrc src, ImmPtr target, RelocationKind reloc) { MOZ_ASSERT(target.value != nullptr); // Emit reloc before modifying the jump table, since it computes a 0-based // index. This jump is not patchable at runtime. - if (reloc == Relocation::JITCODE) + if (reloc == RelocationKind::JITCODE) writeRelocation(src, reloc); enoughMemory_ &= jumps_.append(RelativePatch(src.offset(), target.value, reloc)); } size_t -Assembler::addPatchableJump(JmpSrc src, Relocation::Kind reloc) +Assembler::addPatchableJump(JmpSrc src, RelocationKind reloc) { // This jump is patchable at runtime so we always need to make sure the // jump table is emitted. diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h index 31036fc26610..b4e541018df9 100644 --- a/js/src/jit/x64/Assembler-x64.h +++ b/js/src/jit/x64/Assembler-x64.h @@ -288,11 +288,11 @@ class Assembler : public AssemblerX86Shared static JitCode* CodeFromJump(JitCode* code, uint8_t* jump); private: - void writeRelocation(JmpSrc src, Relocation::Kind reloc); - void addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind reloc); + void writeRelocation(JmpSrc src, RelocationKind reloc); + void addPendingJump(JmpSrc src, ImmPtr target, RelocationKind reloc); protected: - size_t addPatchableJump(JmpSrc src, Relocation::Kind reloc); + size_t addPatchableJump(JmpSrc src, RelocationKind reloc); public: using AssemblerX86Shared::j; @@ -1060,32 +1060,31 @@ class Assembler : public AssemblerX86Shared } } - void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { + void jmp(ImmPtr target, RelocationKind reloc = RelocationKind::HARDCODED) { JmpSrc src = masm.jmp(); addPendingJump(src, target, reloc); } - void j(Condition cond, ImmPtr target, - Relocation::Kind reloc = Relocation::HARDCODED) { + void j(Condition cond, ImmPtr target, RelocationKind reloc = RelocationKind::HARDCODED) { JmpSrc src = masm.jCC(static_cast(cond)); addPendingJump(src, target, reloc); } void jmp(JitCode* target) { - jmp(ImmPtr(target->raw()), Relocation::JITCODE); + jmp(ImmPtr(target->raw()), RelocationKind::JITCODE); } void j(Condition cond, JitCode* target) { - j(cond, ImmPtr(target->raw()), Relocation::JITCODE); + j(cond, ImmPtr(target->raw()), RelocationKind::JITCODE); } void call(JitCode* target) { JmpSrc src = masm.call(); - addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(src, ImmPtr(target->raw()), RelocationKind::JITCODE); } void call(ImmWord target) { call(ImmPtr((void*)target.value)); } void call(ImmPtr target) { JmpSrc src = masm.call(); - addPendingJump(src, target, Relocation::HARDCODED); + addPendingJump(src, target, RelocationKind::HARDCODED); } // Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch @@ -1093,7 +1092,7 @@ class Assembler : public AssemblerX86Shared CodeOffset toggledCall(JitCode* target, bool enabled) { CodeOffset offset(size()); JmpSrc src = enabled ? masm.call() : masm.cmp_eax(); - addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(src, ImmPtr(target->raw()), RelocationKind::JITCODE); MOZ_ASSERT_IF(!oom(), size() - offset.offset() == ToggledCallSize(nullptr)); return offset; } diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 698b18e5d1a0..1cf8216e4a10 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -584,7 +584,7 @@ MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType value const Address& dest, MIRType slotType); template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, - const BaseIndex& dest, MIRType slotType); + const BaseObjectElementIndex& dest, MIRType slotType); // ======================================================================== // wasm support diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 65f8c14fc73b..3f2a3445bcf8 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -546,7 +546,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared CodeOffsetJump jumpWithPatch(RepatchLabel* label) { JmpSrc src = jmpSrc(label); - return CodeOffsetJump(size(), addPatchableJump(src, Relocation::HARDCODED)); + return CodeOffsetJump(size(), addPatchableJump(src, RelocationKind::HARDCODED)); } void movePtr(Register src, Register dest) { diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index f83901f85108..e090add208c9 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -457,7 +457,7 @@ JitRuntime::generateArgumentsRectifier(MacroAssembler& masm) // | - sizeof(Value)| is used to put rcx such that we can read the last // argument, and not the value which is after. - BaseIndex b = BaseIndex(r9, r8, TimesEight, sizeof(RectifierFrameLayout) - sizeof(Value)); + BaseIndex b(r9, r8, TimesEight, sizeof(RectifierFrameLayout) - sizeof(Value)); masm.lea(Operand(b), rcx); // Copy & Push arguments, |nargs| + 1 times (to include |this|). diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index f2277f9d207f..e5a4657f9c81 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -255,9 +255,9 @@ class AssemblerX86Shared : public AssemblerShared struct RelativePatch { int32_t offset; void* target; - Relocation::Kind kind; + RelocationKind kind; - RelativePatch(int32_t offset, void* target, Relocation::Kind kind) + RelativePatch(int32_t offset, void* target, RelocationKind kind) : offset(offset), target(target), kind(kind) @@ -399,7 +399,7 @@ class AssemblerX86Shared : public AssemblerShared #ifdef DEBUG MOZ_ASSERT(dataRelocations_.length() == 0); for (auto& j : jumps_) - MOZ_ASSERT(j.kind == Relocation::HARDCODED); + MOZ_ASSERT(j.kind == RelocationKind::HARDCODED); #endif } diff --git a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h index f9a6f5a3ea50..8bf8d33feb12 100644 --- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h +++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h @@ -37,7 +37,7 @@ #include "jit/JitSpewer.h" // Spew formatting helpers. -#define PRETTYHEX(x) (((x)<0)?"-":""),(((x)<0)?-(x):(x)) +#define PRETTYHEX(x) (((x)<0)?"-":""),((unsigned)((x)^((x)>>31))+((unsigned)(x)>>31)) #define MEM_o "%s0x%x" #define MEM_os MEM_o "(,%s,%d)" diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 7077b75bd426..14b5c7ed5f73 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -1802,7 +1802,7 @@ public: void cmpl_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale) { - spew("cmpl $0x%x, " MEM_o32b, rhs, ADDR_o32b(offset, base)); + spew("cmpl $0x%x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale)); if (CAN_SIGN_EXTEND_8_32(rhs)) { m_formatter.oneByteOp(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_CMP); m_formatter.immediate8s(rhs); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 3261c72c7099..8442ff802781 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -499,7 +499,7 @@ class BailoutJump { { } #ifdef JS_CODEGEN_X86 void operator()(MacroAssembler& masm, uint8_t* code) const { - masm.j(cond_, ImmPtr(code), Relocation::HARDCODED); + masm.j(cond_, ImmPtr(code), RelocationKind::HARDCODED); } #endif void operator()(MacroAssembler& masm, Label* label) const { @@ -515,7 +515,7 @@ class BailoutLabel { { } #ifdef JS_CODEGEN_X86 void operator()(MacroAssembler& masm, uint8_t* code) const { - masm.retarget(label_, ImmPtr(code), Relocation::HARDCODED); + masm.retarget(label_, ImmPtr(code), RelocationKind::HARDCODED); } #endif void operator()(MacroAssembler& masm, Label* label) const { @@ -2490,7 +2490,7 @@ CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayE Register newval = ToRegister(lir->newval()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); @@ -2511,7 +2511,7 @@ CodeGenerator::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayEle Register value = ToRegister(lir->value()); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address dest(elements, ToInt32(lir->index()) * width); @@ -2549,7 +2549,7 @@ CodeGenerator::visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* const LAllocation* value = lir->value(); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address mem(elements, ToInt32(lir->index()) * width); @@ -2582,7 +2582,7 @@ CodeGenerator::visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayEleme Register elements = ToRegister(lir->elements()); const LAllocation* value = lir->value(); Scalar::Type arrayType = lir->mir()->arrayType(); - int width = Scalar::byteSize(arrayType); + size_t width = Scalar::byteSize(arrayType); if (lir->index()->isConstant()) { Address mem(elements, ToInt32(lir->index()) * width); diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 78c430810378..5c46d01de380 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -222,9 +222,9 @@ class Assembler : public AssemblerX86Shared void writeRelocation(JmpSrc src) { jumpRelocations_.writeUnsigned(src.offset()); } - void addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind kind) { + void addPendingJump(JmpSrc src, ImmPtr target, RelocationKind kind) { enoughMemory_ &= jumps_.append(RelativePatch(src.offset(), target.value, kind)); - if (kind == Relocation::JITCODE) + if (kind == RelocationKind::JITCODE) writeRelocation(src); } @@ -506,32 +506,31 @@ class Assembler : public AssemblerX86Shared } } - void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { + void jmp(ImmPtr target, RelocationKind reloc = RelocationKind::HARDCODED) { JmpSrc src = masm.jmp(); addPendingJump(src, target, reloc); } - void j(Condition cond, ImmPtr target, - Relocation::Kind reloc = Relocation::HARDCODED) { + void j(Condition cond, ImmPtr target, RelocationKind reloc = RelocationKind::HARDCODED) { JmpSrc src = masm.jCC(static_cast(cond)); addPendingJump(src, target, reloc); } void jmp(JitCode* target) { - jmp(ImmPtr(target->raw()), Relocation::JITCODE); + jmp(ImmPtr(target->raw()), RelocationKind::JITCODE); } void j(Condition cond, JitCode* target) { - j(cond, ImmPtr(target->raw()), Relocation::JITCODE); + j(cond, ImmPtr(target->raw()), RelocationKind::JITCODE); } void call(JitCode* target) { JmpSrc src = masm.call(); - addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(src, ImmPtr(target->raw()), RelocationKind::JITCODE); } void call(ImmWord target) { call(ImmPtr((void*)target.value)); } void call(ImmPtr target) { JmpSrc src = masm.call(); - addPendingJump(src, target, Relocation::HARDCODED); + addPendingJump(src, target, RelocationKind::HARDCODED); } // Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch @@ -539,7 +538,7 @@ class Assembler : public AssemblerX86Shared CodeOffset toggledCall(JitCode* target, bool enabled) { CodeOffset offset(size()); JmpSrc src = enabled ? masm.call() : masm.cmp_eax(); - addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE); + addPendingJump(src, ImmPtr(target->raw()), RelocationKind::JITCODE); MOZ_ASSERT_IF(!oom(), size() - offset.offset() == ToggledCallSize(nullptr)); return offset; } @@ -551,7 +550,7 @@ class Assembler : public AssemblerX86Shared // Re-routes pending jumps to an external target, flushing the label in the // process. - void retarget(Label* label, ImmPtr target, Relocation::Kind reloc) { + void retarget(Label* label, ImmPtr target, RelocationKind reloc) { if (label->used()) { bool more; X86Encoding::JmpSrc jmp(label->offset()); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index a11ee4e3bf8f..d98e6e5d8d5f 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -592,7 +592,7 @@ MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType value const Address& dest, MIRType slotType); template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, - const BaseIndex& dest, MIRType slotType); + const BaseObjectElementIndex& dest, MIRType slotType); // wasm specific methods, used in both the wasm baseline compiler and ion. diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index 2db730f82bd4..68baafcf128c 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -450,8 +450,7 @@ JitRuntime::generateArgumentsRectifier(MacroAssembler& masm) // Get the topmost argument. We did a push of %ebp earlier, so be sure to // account for this in the offset - BaseIndex b = BaseIndex(FramePointer, esi, TimesEight, - sizeof(RectifierFrameLayout) + sizeof(void*)); + BaseIndex b(FramePointer, esi, TimesEight, sizeof(RectifierFrameLayout) + sizeof(void*)); masm.lea(Operand(b), ecx); // Push arguments, |nargs| + 1 times (to include |this|). @@ -514,7 +513,7 @@ JitRuntime::generateArgumentsRectifier(MacroAssembler& masm) masm.pop(edi); // Discard number of actual arguments. // Discard pushed arguments, but not the pushed frame pointer. - BaseIndex unwind = BaseIndex(esp, ebx, TimesOne, -int32_t(sizeof(void*))); + BaseIndex unwind(esp, ebx, TimesOne, -int32_t(sizeof(void*))); masm.lea(Operand(unwind), esp); masm.pop(FramePointer); diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 35444e09c6fc..096e063ddb8c 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -436,9 +436,6 @@ skip script test262/annexB/language/function-code/block-decl-nested-blocks-with- # https://bugzilla.mozilla.org/show_bug.cgi?id=1406171 skip script test262/built-ins/Reflect/ownKeys/return-on-corresponding-order-large-index.js -# https://bugzilla.mozilla.org/show_bug.cgi?id=1472211 -skip script test262/language/statements/class/super/in-constructor-superproperty-evaluation.js - # https://bugzilla.mozilla.org/show_bug.cgi?id=1473228 skip script test262/intl402/RelativeTimeFormat/prototype/toStringTag/toStringTag.js diff --git a/js/src/tests/non262/class/superElemDelete.js b/js/src/tests/non262/class/superElemDelete.js new file mode 100644 index 000000000000..6132cbafc2c1 --- /dev/null +++ b/js/src/tests/non262/class/superElemDelete.js @@ -0,0 +1,68 @@ +// Make sure we get the proper side effects. +// |delete super[expr]| applies ToPropertyKey on |expr| before throwing. + +class base { + constructor() { } +} + +class derived extends base { + constructor() { super(); } + testDeleteElem() { + let sideEffect = 0; + let key = { + toString() { + sideEffect++; + return ""; + } + }; + assertThrowsInstanceOf(() => delete super[key], ReferenceError); + assertEq(sideEffect, 1); + } + testDeleteElemPropValFirst() { + // The deletion error is a reference error, but by munging the prototype + // chain, we can force a type error from JSOP_SUPERBASE. + let key = { + toString() { + Object.setPrototypeOf(derived.prototype, null); + return ""; + } + }; + delete super[key]; + } +} + +class derivedTestDeleteElem extends base { + constructor() { + let sideEffect = 0; + let key = { + toString() { + sideEffect++; + return ""; + } + }; + + assertThrowsInstanceOf(() => delete super[key], ReferenceError); + assertEq(sideEffect, 0); + + super(); + + assertThrowsInstanceOf(() => delete super[key], ReferenceError); + assertEq(sideEffect, 1); + + Object.setPrototypeOf(derivedTestDeleteElem.prototype, null); + + assertThrowsInstanceOf(() => delete super[key], TypeError); + assertEq(sideEffect, 2); + + return {}; + } +} + +var d = new derived(); +d.testDeleteElem(); +assertThrowsInstanceOf(() => d.testDeleteElemPropValFirst(), TypeError); + +new derivedTestDeleteElem(); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/non262/class/superPropDelete.js b/js/src/tests/non262/class/superPropDelete.js index 44bf0d3f6d9c..1da338558ba3 100644 --- a/js/src/tests/non262/class/superPropDelete.js +++ b/js/src/tests/non262/class/superPropDelete.js @@ -42,5 +42,23 @@ Object.setPrototypeOf(thing2, new Proxy({}, { })); assertThrowsInstanceOf(() => thing2.go(), ReferenceError); +class derivedTestDeleteProp extends base { + constructor() { + // The deletion error is a reference error, but by munging the prototype + // chain, we can force a type error from JSOP_SUPERBASE. + Object.setPrototypeOf(derivedTestDeleteProp.prototype, null); + + assertThrowsInstanceOf(() => delete super.prop, ReferenceError); + + super(); + + assertThrowsInstanceOf(() => delete super.prop, TypeError); + + return {}; + } +} + +new derivedTestDeleteProp(); + if (typeof reportCompare === 'function') reportCompare(0,0,"OK"); diff --git a/js/src/vm/BytecodeUtil.cpp b/js/src/vm/BytecodeUtil.cpp index 562217d4120e..8dfb587d418a 100644 --- a/js/src/vm/BytecodeUtil.cpp +++ b/js/src/vm/BytecodeUtil.cpp @@ -1799,7 +1799,7 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex) case JSOP_GETELEM_SUPER: return write("super[") && - decompilePCForStackOperand(pc, -3) && + decompilePCForStackOperand(pc, -2) && write("]"); case JSOP_NULL: return write(js_null_str); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 524f51845059..876f748fe414 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3083,8 +3083,8 @@ END_CASE(JSOP_GETELEM) CASE(JSOP_GETELEM_SUPER) { - ReservedRooted rval(&rootValue0, REGS.sp[-3]); - ReservedRooted receiver(&rootValue1, REGS.sp[-2]); + ReservedRooted receiver(&rootValue1, REGS.sp[-3]); + ReservedRooted rval(&rootValue0, REGS.sp[-2]); ReservedRooted obj(&rootObject1, ®S.sp[-1].toObject()); MutableHandleValue res = REGS.stackHandleAt(-3); @@ -3126,8 +3126,8 @@ CASE(JSOP_STRICTSETELEM_SUPER) static_assert(JSOP_SETELEM_SUPER_LENGTH == JSOP_STRICTSETELEM_SUPER_LENGTH, "setelem-super and strictsetelem-super must be the same size"); - ReservedRooted index(&rootValue1, REGS.sp[-4]); - ReservedRooted receiver(&rootValue0, REGS.sp[-3]); + ReservedRooted receiver(&rootValue0, REGS.sp[-4]); + ReservedRooted index(&rootValue1, REGS.sp[-3]); ReservedRooted obj(&rootObject1, ®S.sp[-2].toObject()); HandleValue value = REGS.stackHandleAt(-1); diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index db67e09043a8..0c51222305ad 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -76,11 +76,11 @@ NativeObject::canHaveNonEmptyElements() #endif // DEBUG -/* static */ bool +/* static */ void ObjectElements::ConvertElementsToDoubles(JSContext* cx, uintptr_t elementsPtr) { /* - * This function is infallible, but has a fallible interface so that it can + * This function has an otherwise unused JSContext argument so that it can * be called directly from Ion code. Only arrays can have their dense * elements converted to doubles, and arrays never have empty elements. */ @@ -100,7 +100,6 @@ ObjectElements::ConvertElementsToDoubles(JSContext* cx, uintptr_t elementsPtr) } header->setShouldConvertDoubleElements(); - return true; } /* static */ bool diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 31e94b5a469e..6cde5a6300a1 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -367,7 +367,7 @@ class ObjectElements return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements)); } - static bool ConvertElementsToDoubles(JSContext* cx, uintptr_t elements); + static void ConvertElementsToDoubles(JSContext* cx, uintptr_t elements); static bool MakeElementsCopyOnWrite(JSContext* cx, NativeObject* obj); static MOZ_MUST_USE bool PreventExtensions(JSContext* cx, NativeObject* obj); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 208f82422b40..0fa78e53a34e 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -538,7 +538,7 @@ macro(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) \ /* * Pops the top three values on the stack as 'val', 'propval' and 'obj', - * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the + * sets 'propval' property of 'obj' as 'val', pushes 'val' onto the * stack. * Category: Literals * Type: Object @@ -548,7 +548,7 @@ macro(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \ /* * Pops the top three values on the stack as 'val', 'propval' and 'obj', - * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the + * sets 'propval' property of 'obj' as 'val', pushes 'val' onto the * stack. Throws a TypeError if the set fails, per strict mode * semantics. * Category: Literals @@ -1281,7 +1281,7 @@ * Category: Literals * Type: Object * Operands: - * Stack: propval, receiver, obj => obj[propval] + * Stack: receiver, propval, obj => obj[propval] */ \ macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) \ macro(JSOP_UNUSED126, 126, "unused126", NULL, 5, 0, 1, JOF_UINT32) \ @@ -1639,7 +1639,7 @@ * Category: Literals * Type: Object * Operands: - * Stack: propval, receiver, obj, val => val + * Stack: receiver, propval, obj, val => val */ \ macro(JSOP_SETELEM_SUPER, 158, "setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \ /* @@ -1648,7 +1648,7 @@ * Category: Literals * Type: Object * Operands: - * Stack: propval, receiver, obj, val => val + * Stack: receiver, propval, obj, val => val */ \ macro(JSOP_STRICTSETELEM_SUPER, 159, "strict-setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \ \ diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 8f3677c1af5e..5b6e04d24cbd 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2879,7 +2879,7 @@ ReflectorNode::edges(JSContext* cx, bool wantNames) const } } } - return range; + return js::UniquePtr(range.release()); } } // Namespace ubi diff --git a/mfbt/HashTable.h b/mfbt/HashTable.h index a5a66d10d4ae..6b514d3d97a5 100644 --- a/mfbt/HashTable.h +++ b/mfbt/HashTable.h @@ -4,8 +4,51 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -// A note on the differences between mozilla::HashTable and PLDHashTable (and -// its subclasses, such as nsTHashtable). +//--------------------------------------------------------------------------- +// Overview +//--------------------------------------------------------------------------- +// +// This file defines HashMap and HashSet, hash tables that are +// fast and have a nice API. +// +// Both hash tables have two optional template parameters. +// +// - HashPolicy. This defines the operations for hashing and matching keys. The +// default HashPolicy is appropriate when both of the following two +// conditions are true. +// +// - The key type stored in the table (|Key| for |HashMap|, |T| +// for |HashSet|) is an integer, pointer, UniquePtr, float, double, or +// char*. +// +// - The type used for lookups (|Lookup|) is the same as the key type. This +// is usually the case, but not always. +// +// Otherwise, you must provide your own hash policy; see the "Hash Policy" +// section below. +// +// - AllocPolicy. This defines how allocations are done by the table. +// +// - |MallocAllocPolicy| is the default and is usually appropriate; note that +// operations (such as insertions) that might cause allocations are +// fallible and must be checked for OOM. These checks are enforced by the +// use of MOZ_MUST_USE. +// +// - |InfallibleAllocPolicy| is another possibility; it allows the +// abovementioned OOM checks to be done with MOZ_ALWAYS_TRUE(). +// +// See AllocPolicy.h for more details. +// +// Documentation on how to use HashMap and HashSet, including examples, is +// present within those classes. Search for "class HashMap" and "class +// HashSet". +// +// Both HashMap and HashSet are implemented on top of a third class, HashTable. +// You only need to look at HashTable if you want to understand the +// implementation. +// +// How does mozilla::HashTable (this file) compare with PLDHashTable (and its +// subclasses, such as nsTHashtable)? // // - mozilla::HashTable is a lot faster, largely because it uses templates // throughout *and* inlines everything. PLDHashTable inlines operations much @@ -18,11 +61,8 @@ // - mozilla::HashTable has a nicer API, with a proper HashSet vs. HashMap // distinction. // -// - mozilla::HashTable requires more explicit OOM checking. Use -// mozilla::InfallibleAllocPolicy to make allocations infallible; note that -// return values of possibly-allocating methods such as add() will still need -// checking in some fashion -- e.g. with MOZ_ALWAYS_TRUE() -- due to the use -// of MOZ_MUST_USE. +// - mozilla::HashTable requires more explicit OOM checking. As mentioned +// above, the use of |InfallibleAllocPolicy| can simplify things. // // - mozilla::HashTable has a default capacity on creation of 32 and a minimum // capacity of 4. PLDHashTable has a default capacity on creation of 8 and a @@ -67,8 +107,6 @@ class HashTable; } // namespace detail -/*****************************************************************************/ - // The "generation" of a hash table is an opaque value indicating the state of // modification of the hash table through its lifetime. If the generation of // a hash table compares equal at times T1 and T2, then lookups in the hash @@ -81,21 +119,22 @@ class HashTable; // tables H1 and H2. using Generation = Opaque; -// A performant, STL-like container providing a hash-based map from keys to -// values. In particular, HashMap calls constructors and destructors of all -// objects added so non-PODs may be used safely. +//--------------------------------------------------------------------------- +// HashMap +//--------------------------------------------------------------------------- + +// HashMap is a fast hash-based map from keys to values. // -// Key/Value requirements: -// - movable, destructible, assignable -// HashPolicy requirements: -// - see Hash Policy section below -// AllocPolicy: -// - see AllocPolicy.h +// Template parameter requirements: +// - Key/Value: movable, destructible, assignable. +// - HashPolicy: see the "Hash Policy" section below. +// - AllocPolicy: see AllocPolicy.h. // // Note: // - HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members // called by HashMap must not call back into the same HashMap object. // - Due to the lack of exception handling, the user must call |init()|. +// template, @@ -124,47 +163,50 @@ public: using Lookup = typename HashPolicy::Lookup; using Entry = TableEntry; - // HashMap construction is fallible (due to OOM); thus the user must call - // init after constructing a HashMap and check the return value. + // HashMap construction is fallible (due to possible OOM). The user must + // call init() after construction and check the return value. explicit HashMap(AllocPolicy aPolicy = AllocPolicy()) : mImpl(aPolicy) { } + // Initialize the map for use. Must be called after construction, before + // any other operations (other than initialized()). MOZ_MUST_USE bool init(uint32_t aLen = 16) { return mImpl.init(aLen); } + // Has the map been initialized? bool initialized() const { return mImpl.initialized(); } - // Return whether the given lookup value is present in the map. E.g.: + // Return a Ptr indicating whether a key/value matching |aLookup| is + // present in the map. E.g.: // // using HM = HashMap; // HM h; // if (HM::Ptr p = h.lookup(3)) { - // const HM::Entry& e = *p; // p acts like a pointer to Entry - // assert(p->key == 3); // Entry contains the key - // char val = p->value; // and value + // assert(p->key() == 3); + // char val = p->value(); // } // - // Also see the definition of Ptr in HashTable above (with T = Entry). using Ptr = typename Impl::Ptr; MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& aLookup) const { return mImpl.lookup(aLookup); } - // Like lookup, but does not assert if two threads call lookup at the same + // Like lookup(), but does not assert if two threads call it at the same // time. Only use this method when none of the threads will modify the map. MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& aLookup) const { return mImpl.readonlyThreadsafeLookup(aLookup); } - // Assuming |p.found()|, remove |*p|. + // Remove a previously found key/value (assuming aPtr.found()). The map + // must not have been mutated in the interim. void remove(Ptr aPtr) { mImpl.remove(aPtr); } // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using - // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.: + // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new key/value. E.g.: // // using HM = HashMap; // HM h; @@ -174,18 +216,15 @@ public: // return false; // } // } - // const HM::Entry& e = *p; // p acts like a pointer to Entry - // assert(p->key == 3); // Entry contains the key - // char val = p->value; // and value + // assert(p->key() == 3); + // char val = p->value(); // - // Also see the definition of AddPtr in HashTable above (with T = Entry). - // - // N.B. The caller must ensure that no mutating hash table operations - // occur between a pair of |lookupForAdd| and |add| calls. To avoid - // looking up the key a second time, the caller may use the more efficient - // relookupOrAdd method. This method reuses part of the hashing computation - // to more efficiently insert the key if it has not been added. For - // example, a mutation-handling version of the previous example: + // N.B. The caller must ensure that no mutating hash table operations occur + // between a pair of lookupForAdd() and add() calls. To avoid looking up the + // key a second time, the caller may use the more efficient relookupOrAdd() + // method. This method reuses part of the hashing computation to more + // efficiently insert the key if it has not been added. For example, a + // mutation-handling version of the previous example: // // HM::AddPtr p = h.lookupForAdd(3); // if (!p) { @@ -194,9 +233,8 @@ public: // return false; // } // } - // const HM::Entry& e = *p; - // assert(p->key == 3); - // char val = p->value; + // assert(p->key() == 3); + // char val = p->value(); // using AddPtr = typename Impl::AddPtr; MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& aLookup) const @@ -204,6 +242,7 @@ public: return mImpl.lookupForAdd(aLookup); } + // Add a key/value. Returns false on OOM. template MOZ_MUST_USE bool add(AddPtr& aPtr, KeyInput&& aKey, ValueInput&& aValue) { @@ -211,12 +250,14 @@ public: aPtr, std::forward(aKey), std::forward(aValue)); } + // Add a given key and a default value. Returns false on OOM. template MOZ_MUST_USE bool add(AddPtr& aPtr, KeyInput&& aKey) { return mImpl.add(aPtr, std::forward(aKey), Value()); } + // See the comment above lookupForAdd() for details. template MOZ_MUST_USE bool relookupOrAdd(AddPtr& aPtr, KeyInput&& aKey, @@ -235,7 +276,6 @@ public: // char c = iter.get().value(); // } // - // Also see the definition of Iterator in HashTable above (with T = Entry). using Iterator = typename Impl::Iterator; Iterator iter() const { return mImpl.iter(); } @@ -248,41 +288,38 @@ public: // } // } // - // Table resize may occur in ModIterator's destructor. Also see the - // definition of ModIterator in HashTable above (with T = Entry). + // Table resize may occur in ModIterator's destructor. using ModIterator = typename Impl::ModIterator; ModIterator modIter() { return mImpl.modIter(); } - // These are similar to Iterator/ModIterator/iter(), but use less common + // These are similar to Iterator/ModIterator/iter(), but use different // terminology. using Range = typename Impl::Range; using Enum = typename Impl::Enum; Range all() const { return mImpl.all(); } - // Remove all entries. This does not shrink the table. For that consider - // using the finish() method. + // Remove all keys/values without changing the capacity. void clear() { mImpl.clear(); } - // Remove all entries. Unlike clear() this method tries to shrink the table. - // Unlike finish() it does not require the map to be initialized again. + // Remove all keys/values and attempt to minimize the capacity. void clearAndShrink() { mImpl.clearAndShrink(); } - // Remove all the entries and release all internal buffers. The map must - // be initialized again before any use. + // Remove all keys/values and release entry storage. The map must be + // initialized via init() again before further use. void finish() { mImpl.finish(); } - // Does the table contain any entries? + // Is the map empty? bool empty() const { return mImpl.empty(); } - // Number of live elements in the map. + // Number of keys/values in the map. uint32_t count() const { return mImpl.count(); } - // Total number of allocation in the dynamic table. Note: resize will - // happen well before count() == capacity(). + // Number of key/value slots in the map. Note: resize will happen well before + // count() == capacity(). size_t capacity() const { return mImpl.capacity(); } - // Measure the size of the HashMap's entry storage. If the entries contain - // pointers to other heap blocks, you must iterate over the table and measure + // The size of the map's entry storage, in bytes. If the keys/values contain + // pointers to other heap blocks, you must iterate over the map and measure // them separately; hence the "shallow" prefix. size_t shallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { @@ -294,16 +331,19 @@ public: mImpl.shallowSizeOfExcludingThis(aMallocSizeOf); } + // The map's current generation. Generation generation() const { return mImpl.generation(); } /************************************************** Shorthand operations */ + // Does the map contain a key/value matching |aLookup|? bool has(const Lookup& aLookup) const { return mImpl.lookup(aLookup).found(); } - // Overwrite existing value with aValue. Return false on oom. + // Overwrite existing value with |aValue|, or add it if not present. Returns + // false on OOM. template MOZ_MUST_USE bool put(KeyInput&& aKey, ValueInput&& aValue) { @@ -316,7 +356,7 @@ public: p, std::forward(aKey), std::forward(aValue)); } - // Like put, but assert that the given key is not already present. + // Like put(), but asserts that the given key is not already present. template MOZ_MUST_USE bool putNew(KeyInput&& aKey, ValueInput&& aValue) { @@ -333,7 +373,7 @@ public: } // Add (aKey,aDefaultValue) if |aKey| is not found. Return a false-y Ptr on - // oom. + // OOM. Ptr lookupWithDefault(const Key& aKey, const Value& aDefaultValue) { AddPtr p = lookupForAdd(aKey); @@ -341,12 +381,12 @@ public: return p; } bool ok = add(p, aKey, aDefaultValue); - MOZ_ASSERT_IF(!ok, !p); // p is left false-y on oom. + MOZ_ASSERT_IF(!ok, !p); // p is left false-y on OOM. (void)ok; return p; } - // Remove if present. + // Lookup and remove the key/value matching |aLookup|, if present. void remove(const Lookup& aLookup) { if (Ptr p = lookup(aLookup)) { @@ -354,9 +394,8 @@ public: } } - // Infallibly rekey one entry, if necessary. - // Requires template parameters Key and HashPolicy::Lookup to be the same - // type. + // Infallibly rekey one entry, if necessary. Requires that template + // parameters Key and HashPolicy::Lookup are the same type. void rekeyIfMoved(const Key& aOldKey, const Key& aNewKey) { if (aOldKey != aNewKey) { @@ -376,7 +415,7 @@ public: return false; } - // HashMap is movable + // HashMap is movable. HashMap(HashMap&& aRhs) : mImpl(std::move(aRhs.mImpl)) { @@ -388,63 +427,67 @@ public: } private: - // HashMap is not copyable or assignable + // HashMap is not copyable or assignable. HashMap(const HashMap& hm) = delete; HashMap& operator=(const HashMap& hm) = delete; friend class Impl::Enum; }; -/*****************************************************************************/ +//--------------------------------------------------------------------------- +// HashSet +//--------------------------------------------------------------------------- -// A performant, STL-like container providing a hash-based set of values. In -// particular, HashSet calls constructors and destructors of all objects added -// so non-PODs may be used safely. +// HashSet is a fast hash-based set of values. // -// T requirements: -// - movable, destructible, assignable -// HashPolicy requirements: -// - see Hash Policy section below -// AllocPolicy: -// - see AllocPolicy.h +// Template parameter requirements: +// - T: movable, destructible, assignable. +// - HashPolicy: see the "Hash Policy" section below. +// - AllocPolicy: see AllocPolicy.h // // Note: // - HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by // HashSet must not call back into the same HashSet object. // - Due to the lack of exception handling, the user must call |init()|. +// template, class AllocPolicy = MallocAllocPolicy> class HashSet { - struct SetOps : HashPolicy + struct SetHashPolicy : HashPolicy { using Base = HashPolicy; using KeyType = T; static const KeyType& getKey(const T& aT) { return aT; } + static void setKey(T& aT, KeyType& aKey) { HashPolicy::rekey(aT, aKey); } }; - using Impl = detail::HashTable; + using Impl = detail::HashTable; Impl mImpl; public: using Lookup = typename HashPolicy::Lookup; using Entry = T; - // HashSet construction is fallible (due to OOM); thus the user must call - // init after constructing a HashSet and check the return value. + // HashSet construction is fallible (due to possible OOM). The user must call + // init() after construction and check the return value. explicit HashSet(AllocPolicy a = AllocPolicy()) : mImpl(a) { } + // Initialize the set for use. Must be called after construction, before + // any other operations (other than initialized()). MOZ_MUST_USE bool init(uint32_t aLen = 16) { return mImpl.init(aLen); } + // Has the set been initialized? bool initialized() const { return mImpl.initialized(); } - // Return whether the given lookup value is present in the map. E.g.: + // Return a Ptr indicating whether an element matching |aLookup| is present + // in the set. E.g.: // // using HS = HashSet; // HS h; @@ -452,21 +495,21 @@ public: // assert(*p == 3); // p acts like a pointer to int // } // - // Also see the definition of Ptr in HashTable above. using Ptr = typename Impl::Ptr; MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& aLookup) const { return mImpl.lookup(aLookup); } - // Like lookup, but does not assert if two threads call lookup at the same - // time. Only use this method when none of the threads will modify the map. + // Like lookup(), but does not assert if two threads call it at the same + // time. Only use this method when none of the threads will modify the set. MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& aLookup) const { return mImpl.readonlyThreadsafeLookup(aLookup); } - // Assuming |aPtr.found()|, remove |*aPtr|. + // Remove a previously found element (assuming aPtr.found()). The set must + // not have been mutated in the interim. void remove(Ptr aPtr) { mImpl.remove(aPtr); } // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient @@ -483,14 +526,12 @@ public: // } // assert(*p == 3); // p acts like a pointer to int // - // Also see the definition of AddPtr in HashTable above. - // - // N.B. The caller must ensure that no mutating hash table operations - // occur between a pair of |lookupForAdd| and |add| calls. To avoid - // looking up the key a second time, the caller may use the more efficient - // relookupOrAdd method. This method reuses part of the hashing computation - // to more efficiently insert the key if it has not been added. For - // example, a mutation-handling version of the previous example: + // N.B. The caller must ensure that no mutating hash table operations occur + // between a pair of lookupForAdd() and add() calls. To avoid looking up the + // key a second time, the caller may use the more efficient relookupOrAdd() + // method. This method reuses part of the hashing computation to more + // efficiently insert the key if it has not been added. For example, a + // mutation-handling version of the previous example: // // HS::AddPtr p = h.lookupForAdd(3); // if (!p) { @@ -509,12 +550,14 @@ public: return mImpl.lookupForAdd(aLookup); } + // Add an element. Returns false on OOM. template MOZ_MUST_USE bool add(AddPtr& aPtr, U&& aU) { return mImpl.add(aPtr, std::forward(aU)); } + // See the comment above lookupForAdd() for details. template MOZ_MUST_USE bool relookupOrAdd(AddPtr& aPtr, const Lookup& aLookup, U&& aU) { @@ -528,7 +571,6 @@ public: // int i = iter.get(); // } // - // Also see the definition of Iterator in HashTable above. typedef typename Impl::Iterator Iterator; Iterator iter() const { return mImpl.iter(); } @@ -541,8 +583,7 @@ public: // } // } // - // Table resize may occur in ModIterator's destructor. Also see the - // definition of ModIterator in HashTable above. + // Table resize may occur in ModIterator's destructor. typedef typename Impl::ModIterator ModIterator; ModIterator modIter() { return mImpl.modIter(); } @@ -552,30 +593,28 @@ public: using Enum = typename Impl::Enum; Range all() const { return mImpl.all(); } - // Remove all entries. This does not shrink the table. For that consider - // using the finish() method. + // Remove all elements without changing the capacity. void clear() { mImpl.clear(); } - // Remove all entries. Unlike clear() this method tries to shrink the table. - // Unlike finish() it does not require the set to be initialized again. + // Remove all elements and attempt to minimize the capacity. void clearAndShrink() { mImpl.clearAndShrink(); } - // Remove all the entries and release all internal buffers. The set must - // be initialized again before any use. + // Remove all keys/values and release entry storage. The set must be + // initialized via init() again before further use. void finish() { mImpl.finish(); } - // Does the table contain any entries? + // Is the set empty? bool empty() const { return mImpl.empty(); } - // Number of live elements in the map. + // Number of elements in the set. uint32_t count() const { return mImpl.count(); } - // Total number of allocation in the dynamic table. Note: resize will - // happen well before count() == capacity(). + // Number of element slots in the set. Note: resize will happen well before + // count() == capacity(). size_t capacity() const { return mImpl.capacity(); } - // Measure the size of the HashSet's entry storage. If the entries contain - // pointers to other heap blocks, you must iterate over the table and measure + // The size of the HashSet's entry storage, in bytes. If the elements contain + // pointers to other heap blocks, you must iterate over the set and measure // them separately; hence the "shallow" prefix. size_t shallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { @@ -587,16 +626,18 @@ public: mImpl.shallowSizeOfExcludingThis(aMallocSizeOf); } + // The set's current generation. Generation generation() const { return mImpl.generation(); } /************************************************** Shorthand operations */ + // Does the set contain an element matching |aLookup|? bool has(const Lookup& aLookup) const { return mImpl.lookup(aLookup).found(); } - // Add |aU| if it is not present already. Return false on oom. + // Add |aU| if it is not present already. Returns false on OOM. template MOZ_MUST_USE bool put(U&& aU) { @@ -604,13 +645,14 @@ public: return p ? true : add(p, std::forward(aU)); } - // Like put, but assert that the given key is not already present. + // Like put(), but asserts that the given key is not already present. template MOZ_MUST_USE bool putNew(U&& aU) { return mImpl.putNew(aU, std::forward(aU)); } + // Like the other putNew(), but for when |Lookup| is different to |T|. template MOZ_MUST_USE bool putNew(const Lookup& aLookup, U&& aU) { @@ -624,6 +666,7 @@ public: mImpl.putNewInfallible(aLookup, std::forward(aU)); } + // Lookup and remove the element matching |aLookup|, if present. void remove(const Lookup& aLookup) { if (Ptr p = lookup(aLookup)) { @@ -631,8 +674,8 @@ public: } } - // Infallibly rekey one entry, if present. - // Requires template parameters T and HashPolicy::Lookup to be the same type. + // Infallibly rekey one entry, if present. Requires that template parameters + // T and HashPolicy::Lookup are the same type. void rekeyIfMoved(const Lookup& aOldValue, const T& aNewValue) { if (aOldValue != aNewValue) { @@ -652,7 +695,7 @@ public: return false; } - // Infallibly replace the current key at |p| with an equivalent key. + // Infallibly replace the current key at |aPtr| with an equivalent key. // Specifically, both HashPolicy::hash and HashPolicy::match must return // identical results for the new and old key when applied against all // possible matching values. @@ -665,7 +708,7 @@ public: const_cast(*aPtr) = aNewValue; } - // HashSet is movable + // HashSet is movable. HashSet(HashSet&& aRhs) : mImpl(std::move(aRhs.mImpl)) { @@ -684,36 +727,37 @@ private: friend class Impl::Enum; }; -/*****************************************************************************/ - +//--------------------------------------------------------------------------- // Hash Policy +//--------------------------------------------------------------------------- + +// A hash policy |HP| for a hash table with key-type |Key| must provide: // -// A hash policy P for a hash table with key-type Key must provide: -// - a type |P::Lookup| to use to lookup table entries; -// - a static member function |P::hash| with signature +// - a type |HP::Lookup| to use to lookup table entries; // -// static mozilla::HashNumber hash(Lookup) +// - a static member function |HP::hash| that hashes lookup values: // -// to use to hash the lookup type; and -// - a static member function |P::match| with signature +// static mozilla::HashNumber hash(const Lookup&); // -// static bool match(Key, Lookup) +// - a static member function |HP::match| that tests equality of key and +// lookup values: // -// to use to test equality of key and lookup values. +// static bool match(const Key&, const Lookup&); // // Normally, Lookup = Key. In general, though, different values and types of -// values can be used to lookup and store. If a Lookup value |l| is != to the -// added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.: +// values can be used to lookup and store. If a Lookup value |l| is not equal +// to the added Key value |k|, the user must ensure that |HP::match(k,l)| is +// true. E.g.: // -// mozilla::HashSet::AddPtr p = h.lookup(l); +// mozilla::HashSet::AddPtr p = h.lookup(l); // if (!p) { -// assert(P::match(k, l)); // must hold +// assert(HP::match(k, l)); // must hold // h.add(p, k); // } -// Pointer hashing policy that uses HashGeneric() to create good hashes for -// pointers. Note that we don't shift out the lowest k bits to generate a -// good distribution for arena allocated pointers. +// A pointer hashing policy that uses HashGeneric() to create good hashes for +// pointers. Note that we don't shift out the lowest k bits because we don't +// want to assume anything about the alignment of the pointers. template struct PointerHasher { @@ -733,10 +777,7 @@ struct PointerHasher static void rekey(Key& aKey, const Key& aNewKey) { aKey = aNewKey; } }; -// Default hash policy: just use the 'lookup' value. This of course only -// works if the lookup value is integral. HashTable applies ScrambleHashCode to -// the result of the 'hash' which means that it is 'ok' if the lookup value is -// not well distributed over the HashNumber domain. +// The default hash policy, which only works with integers. template struct DefaultHasher { @@ -744,7 +785,9 @@ struct DefaultHasher static HashNumber hash(const Lookup& aLookup) { - // Hash if can implicitly cast to hash number type. + // Just convert the integer to a HashNumber and use that as is. (This + // discards the high 32-bits of 64-bit integers!) ScrambleHashCode() is + // subsequently called on the value to improve the distribution. return aLookup; } @@ -757,19 +800,18 @@ struct DefaultHasher static void rekey(Key& aKey, const Key& aNewKey) { aKey = aNewKey; } }; -// Specialize hashing policy for pointer types. It assumes that the type is -// at least word-aligned. For types with smaller size use PointerHasher. +// A DefaultHasher specialization for pointers. template struct DefaultHasher : PointerHasher { }; -// Specialize hashing policy for mozilla::UniquePtr to proxy the UniquePtr's -// raw pointer to PointerHasher. +// A DefaultHasher specialization for mozilla::UniquePtr. template struct DefaultHasher> { - using Lookup = UniquePtr; + using Key = UniquePtr; + using Lookup = Key; using PtrHasher = PointerHasher; static HashNumber hash(const Lookup& aLookup) @@ -777,7 +819,7 @@ struct DefaultHasher> return PtrHasher::hash(aLookup.get()); } - static bool match(const UniquePtr& aKey, const Lookup& aLookup) + static bool match(const Key& aKey, const Lookup& aLookup) { return PtrHasher::match(aKey.get(), aLookup.get()); } @@ -788,59 +830,69 @@ struct DefaultHasher> } }; -// For doubles, we can xor the two uint32s. +// A DefaultHasher specialization for doubles. template<> struct DefaultHasher { - using Lookup = double; + using Key = double; + using Lookup = Key; - static HashNumber hash(double aVal) + static HashNumber hash(const Lookup& aLookup) { + // Just xor the high bits with the low bits, and then treat the bits of the + // result as a uint32_t. static_assert(sizeof(HashNumber) == 4, "subsequent code assumes a four-byte hash"); - uint64_t u = BitwiseCast(aVal); + uint64_t u = BitwiseCast(aLookup); return HashNumber(u ^ (u >> 32)); } - static bool match(double aLhs, double aRhs) + static bool match(const Key& aKey, const Lookup& aLookup) { - return BitwiseCast(aLhs) == BitwiseCast(aRhs); + return BitwiseCast(aKey) == BitwiseCast(aLookup); } }; +// A DefaultHasher specialization for floats. template<> struct DefaultHasher { - using Lookup = float; + using Key = float; + using Lookup = Key; - static HashNumber hash(float aVal) + static HashNumber hash(const Lookup& aLookup) { + // Just use the value as if its bits form an integer. ScrambleHashCode() is + // subsequently called on the value to improve the distribution. static_assert(sizeof(HashNumber) == 4, "subsequent code assumes a four-byte hash"); - return HashNumber(BitwiseCast(aVal)); + return HashNumber(BitwiseCast(aLookup)); } - static bool match(float aLhs, float aRhs) + static bool match(const Key& aKey, const Lookup& aLookup) { - return BitwiseCast(aLhs) == BitwiseCast(aRhs); + return BitwiseCast(aKey) == BitwiseCast(aLookup); } }; -// A hash policy that compares C strings. +// A hash policy for C strings. struct CStringHasher { + using Key = const char*; using Lookup = const char*; - static HashNumber hash(Lookup aLookup) { return HashString(aLookup); } + static HashNumber hash(const Lookup& aLookup) { return HashString(aLookup); } - static bool match(const char* key, Lookup lookup) + static bool match(const Key& aKey, const Lookup& aLookup) { - return strcmp(key, lookup) == 0; + return strcmp(aKey, aLookup) == 0; } }; -// Fallible hashing interface. -// +//--------------------------------------------------------------------------- +// Fallible Hashing Interface +//--------------------------------------------------------------------------- + // Most of the time generating a hash code is infallible so this class provides // default methods that always succeed. Specialize this class for your own hash // policy to provide fallible hashing. @@ -883,7 +935,9 @@ EnsureHash(Lookup&& aLookup) std::forward(aLookup)); } -/*****************************************************************************/ +//--------------------------------------------------------------------------- +// Implementation Details (HashMapEntry, HashTableEntry, HashTable) +//--------------------------------------------------------------------------- // Both HashMap and HashSet are implemented by a single HashTable that is even // more heavily parameterized than the other two. This leaves HashTable gnarly @@ -927,6 +981,9 @@ public: using ValueType = Value; const Key& key() const { return key_; } + + // Use this method with caution! If the key is changed such that its hash + // value also changes, the map will be left in an invalid state. Key& mutableKey() { return key_; } const Value& value() const { return value_; } diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt index 8fbc86a3f71a..8430c80a3806 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt @@ -23,6 +23,30 @@ import org.junit.runner.RunWith @MediumTest class ProgressDelegateTest : BaseSessionTest() { + @Test fun loadProgress() { + sessionRule.session.loadTestPath(HELLO_HTML_PATH) + sessionRule.waitForPageStop() + + var counter = 0 + var lastProgress = -1 + + sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate { + @AssertCalled + override fun onProgressChange(session: GeckoSession, progress: Int) { + assertThat("Progress must be strictly increasing", progress, + greaterThan(lastProgress)) + lastProgress = progress + counter++ + } + }) + + assertThat("Callback should be called at least twice", counter, + greaterThanOrEqualTo(2)) + assertThat("Last progress value should be 100", lastProgress, + equalTo(100)) + } + + @Test fun load() { sessionRule.session.loadTestPath(HELLO_HTML_PATH) sessionRule.waitForPageStop() diff --git a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java index abaddba8cf95..5bec818b1f75 100644 --- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java +++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java @@ -34,7 +34,9 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; import android.view.WindowManager; +import android.widget.ProgressBar; import java.util.LinkedList; import java.util.Locale; @@ -63,6 +65,8 @@ public class GeckoViewActivity extends AppCompatActivity { private boolean mCanGoForward; private boolean mFullScreen; + private ProgressBar mProgressView; + private LinkedList mPendingDownloads = new LinkedList<>(); private LocationView.CommitListener mCommitListener = new LocationView.CommitListener() { @@ -95,6 +99,7 @@ public class GeckoViewActivity extends AppCompatActivity { getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); mUseMultiprocess = getIntent().getBooleanExtra(USE_MULTIPROCESS_EXTRA, true); + mProgressView = (ProgressBar) findViewById(R.id.page_progress); if (sGeckoRuntime == null) { final GeckoRuntimeSettings.Builder runtimeSettingsBuilder = @@ -436,6 +441,14 @@ public class GeckoViewActivity extends AppCompatActivity { @Override public void onProgressChange(GeckoSession session, int progress) { Log.i(LOGTAG, "onProgressChange " + progress); + + mProgressView.setProgress(progress, true); + + if (progress > 0 && progress < 100) { + mProgressView.setVisibility(View.VISIBLE); + } else { + mProgressView.setVisibility(View.GONE); + } } @Override diff --git a/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml b/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml index 3175c3621a69..f846e04d2193 100644 --- a/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml +++ b/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml @@ -17,4 +17,12 @@ android:layout_width="match_parent" android:layout_height="?android:actionBarSize" android:layout_alignParentBottom="true"/> + + diff --git a/old-configure.in b/old-configure.in index 6b482d0151dc..169b90fa20b2 100644 --- a/old-configure.in +++ b/old-configure.in @@ -274,6 +274,9 @@ case "$target" in x86_64-*) MIDL_FLAGS="${MIDL_FLAGS} -env x64" ;; + aarch64-*) + MIDL_FLAGS="${MIDL_FLAGS} -env arm64" + ;; esac unset _MSVC_VER_FILTER diff --git a/python/mozbuild/mozbuild/configure/constants.py b/python/mozbuild/mozbuild/configure/constants.py index 33ae5a4568f3..45c67b70993a 100644 --- a/python/mozbuild/mozbuild/configure/constants.py +++ b/python/mozbuild/mozbuild/configure/constants.py @@ -76,7 +76,7 @@ CPU_preprocessor_checks = OrderedDict(( ('x86', '__i386__ || _M_IX86'), ('x86_64', '__x86_64__ || _M_X64'), ('arm', '__arm__ || _M_ARM'), - ('aarch64', '__aarch64__'), + ('aarch64', '__aarch64__ || _M_ARM64'), ('ia64', '__ia64__'), ('s390x', '__s390x__'), ('s390', '__s390__'),