diff options
Diffstat (limited to 'js/src')
793 files changed, 12345 insertions, 5614 deletions
diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4 index 6f641abda9..c9ad56b61e 100644 --- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -6,7 +6,6 @@ dnl builtin(include, ../../build/autoconf/hooks.m4)dnl builtin(include, ../../build/autoconf/config.status.m4)dnl builtin(include, ../../build/autoconf/toolchain.m4)dnl -builtin(include, ../../build/autoconf/codeset.m4)dnl builtin(include, ../../build/autoconf/altoptions.m4)dnl builtin(include, ../../build/autoconf/mozprog.m4)dnl builtin(include, ../../build/autoconf/mozheader.m4)dnl diff --git a/js/src/builtin/.eslintrc.js b/js/src/builtin/.eslintrc.js index 89beca7d4c..76aad2f6fd 100644 --- a/js/src/builtin/.eslintrc.js +++ b/js/src/builtin/.eslintrc.js @@ -121,6 +121,14 @@ module.exports = { message: "'const' declarations are disallowed to avoid TDZ checks, use 'var' instead", }, ], + // Method signatures are important in builtins so disable unused argument errors. + "no-unused-vars": [ + "error", + { + args: "none", + vars: "local", + }, + ], }, globals: { diff --git a/js/src/builtin/DataViewObject.cpp b/js/src/builtin/DataViewObject.cpp index 1f7be86a70..425fdce51d 100644 --- a/js/src/builtin/DataViewObject.cpp +++ b/js/src/builtin/DataViewObject.cpp @@ -65,150 +65,27 @@ DataViewObject* DataViewObject::create( } ResizableDataViewObject* ResizableDataViewObject::create( - JSContext* cx, size_t byteOffset, size_t byteLength, bool autoLength, + JSContext* cx, size_t byteOffset, size_t byteLength, AutoLength autoLength, Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, HandleObject proto) { MOZ_ASSERT(arrayBuffer->isResizable()); MOZ_ASSERT(!arrayBuffer->isDetached()); - MOZ_ASSERT(!autoLength || byteLength == 0, + MOZ_ASSERT(autoLength == AutoLength::No || byteLength == 0, "byte length is zero for 'auto' length views"); auto* obj = NewObjectWithClassProto<ResizableDataViewObject>(cx, proto); - if (!obj || !obj->init(cx, arrayBuffer, byteOffset, byteLength, - /* bytesPerElement = */ 1)) { + if (!obj || !obj->initResizable(cx, arrayBuffer, byteOffset, byteLength, + /* bytesPerElement = */ 1, autoLength)) { return nullptr; } - obj->setFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(autoLength)); - return obj; } -/** - * GetViewByteLength ( viewRecord ) - * - * GetViewByteLength can be rewritten into the following spec steps when - * inlining the calls to MakeDataViewWithBufferWitnessRecord and - * IsViewOutOfBounds. - * - * 1. Let buffer be view.[[ViewedArrayBuffer]]. - * 2. If IsDetachedBuffer(buffer) is true, then - * a. Return out-of-bounds. - * 3. If IsFixedLengthArrayBuffer(buffer) is true, - * a. Return view.[[ByteLength]]. - * 4. Let bufferByteLength be ArrayBufferByteLength(buffer, order). - * 5. Let byteOffsetStart be view.[[ByteOffset]]. - * 6. If byteOffsetStart > bufferByteLength, then - * a. Return out-of-bounds. - * 7. If view.[[ByteLength]] is auto, then - * a. Return bufferByteLength - byteOffsetStart. - * 8. Let viewByteLength be view.[[ByteLength]]. - * 9. Let byteOffsetEnd be byteOffsetStart + viewByteLength. - * 10. If byteOffsetEnd > bufferByteLength, then - * a. Return out-of-bounds. - * 11. Return viewByteLength. - * - * The additional call to IsFixedLengthArrayBuffer is an optimization to skip - * unnecessary validation which doesn't apply for fixed length data-views. - * - * https://tc39.es/ecma262/#sec-getviewbytelength - * https://tc39.es/ecma262/#sec-makedataviewwithbufferwitnessrecord - * https://tc39.es/ecma262/#sec-isviewoutofbounds - */ -mozilla::Maybe<size_t> DataViewObject::byteLength() { - if (MOZ_UNLIKELY(hasDetachedBuffer())) { - return mozilla::Nothing{}; - } - - if (MOZ_LIKELY(is<FixedLengthDataViewObject>())) { - size_t viewByteLength = rawByteLength(); - return mozilla::Some(viewByteLength); - } - - auto* buffer = bufferEither(); - MOZ_ASSERT(buffer->isResizable()); - - size_t bufferByteLength = buffer->byteLength(); - size_t byteOffsetStart = ArrayBufferViewObject::byteOffset(); - if (byteOffsetStart > bufferByteLength) { - return mozilla::Nothing{}; - } - - if (as<ResizableDataViewObject>().isAutoLength()) { - return mozilla::Some(bufferByteLength - byteOffsetStart); - } - - size_t viewByteLength = rawByteLength(); - size_t byteOffsetEnd = byteOffsetStart + viewByteLength; - if (byteOffsetEnd > bufferByteLength) { - return mozilla::Nothing{}; - } - return mozilla::Some(viewByteLength); -} - -/** - * IsViewOutOfBounds ( viewRecord ) - * - * IsViewOutOfBounds can be rewritten into the following spec steps when - * inlining the call to MakeDataViewWithBufferWitnessRecord. - * - * 1. Let buffer be obj.[[ViewedArrayBuffer]]. - * 2. If IsDetachedBuffer(buffer) is true, then - * a. Return true. - * 3. If IsFixedLengthArrayBuffer(buffer) is true, then - * a. Return false. - * 4. Let byteLength be ArrayBufferByteLength(buffer, order). - * 5. Let byteOffsetStart be view.[[ByteOffset]]. - * 6. If byteOffsetStart > bufferByteLength, then - * a. Return true. - * 7. If view.[[ByteLength]] is auto, then - * a. Return false. - * 8. Let byteOffsetEnd be byteOffsetStart + view.[[ByteLength]]. - * 9. If byteOffsetEnd > bufferByteLength, then - * a. Return true. - * 10. Return false. - * - * The additional call to IsFixedLengthArrayBuffer is an optimization to skip - * unnecessary validation which doesn't apply for fixed length data-views. - * - * https://tc39.es/ecma262/#sec-makedataviewwithbufferwitnessrecord - * https://tc39.es/ecma262/#sec-isviewoutofbounds - */ -mozilla::Maybe<size_t> DataViewObject::byteOffset() { - if (MOZ_UNLIKELY(hasDetachedBuffer())) { - return mozilla::Nothing{}; - } - - size_t byteOffsetStart = ArrayBufferViewObject::byteOffset(); - - if (MOZ_LIKELY(is<FixedLengthDataViewObject>())) { - return mozilla::Some(byteOffsetStart); - } - - auto* buffer = bufferEither(); - MOZ_ASSERT(buffer->isResizable()); - - size_t bufferByteLength = buffer->byteLength(); - if (byteOffsetStart > bufferByteLength) { - return mozilla::Nothing{}; - } - - if (as<ResizableDataViewObject>().isAutoLength()) { - return mozilla::Some(byteOffsetStart); - } - - size_t viewByteLength = rawByteLength(); - size_t byteOffsetEnd = byteOffsetStart + viewByteLength; - if (byteOffsetEnd > bufferByteLength) { - return mozilla::Nothing{}; - } - return mozilla::Some(byteOffsetStart); -} - // ES2017 draft rev 931261ecef9b047b14daacf82884134da48dfe0f // 24.3.2.1 DataView (extracted part of the main algorithm) bool DataViewObject::getAndCheckConstructorArgs( JSContext* cx, HandleObject bufobj, const CallArgs& args, - size_t* byteOffsetPtr, size_t* byteLengthPtr, bool* autoLengthPtr) { + size_t* byteOffsetPtr, size_t* byteLengthPtr, AutoLength* autoLengthPtr) { // Step 3. if (!bufobj->is<ArrayBufferObjectMaybeShared>()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, @@ -243,10 +120,10 @@ bool DataViewObject::getAndCheckConstructorArgs( MOZ_ASSERT(offset <= ArrayBufferObject::ByteLengthLimit); uint64_t viewByteLength = 0; - bool autoLength = false; + auto autoLength = AutoLength::No; if (!args.hasDefined(2)) { if (buffer->isResizable()) { - autoLength = true; + autoLength = AutoLength::Yes; } else { // Step 8.a viewByteLength = bufferByteLength - offset; @@ -305,7 +182,7 @@ bool DataViewObject::constructSameCompartment(JSContext* cx, size_t byteOffset = 0; size_t byteLength = 0; - bool autoLength = false; + auto autoLength = AutoLength::No; if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength, &autoLength)) { return false; @@ -365,7 +242,7 @@ bool DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, // NB: This entails the IsArrayBuffer check size_t byteOffset = 0; size_t byteLength = 0; - bool autoLength = false; + auto autoLength = AutoLength::No; if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength, &autoLength)) { return false; diff --git a/js/src/builtin/DataViewObject.h b/js/src/builtin/DataViewObject.h index a17b3e1174..db134a5696 100644 --- a/js/src/builtin/DataViewObject.h +++ b/js/src/builtin/DataViewObject.h @@ -48,7 +48,7 @@ class DataViewObject : public ArrayBufferViewObject { const CallArgs& args, size_t* byteOffsetPtr, size_t* byteLengthPtr, - bool* autoLengthPtr); + AutoLength* autoLengthPtr); static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args); static bool constructWrapped(JSContext* cx, HandleObject bufobj, @@ -58,16 +58,16 @@ class DataViewObject : public ArrayBufferViewObject { JSContext* cx, size_t byteOffset, size_t byteLength, Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, HandleObject proto); - protected: - size_t rawByteLength() const { - return size_t(getFixedSlot(LENGTH_SLOT).toPrivate()); - } - public: static const JSClass protoClass_; - mozilla::Maybe<size_t> byteLength(); - mozilla::Maybe<size_t> byteOffset(); + /** + * Return the current byteLength, or |Nothing| if the DataView is detached or + * out-of-bounds. + */ + mozilla::Maybe<size_t> byteLength() { + return ArrayBufferViewObject::length(); + } template <typename NativeType> static bool offsetIsInBounds(uint64_t offset, size_t byteLength) { @@ -173,19 +173,11 @@ class FixedLengthDataViewObject : public DataViewObject { public: static const JSClass class_; - size_t byteOffset() const { return ArrayBufferViewObject::byteOffset(); } - - size_t byteLength() const { return rawByteLength(); } - - bool offsetIsInBounds(uint32_t byteSize, uint64_t offset) const { - return DataViewObject::offsetIsInBounds(byteSize, offset, byteLength()); + size_t byteOffset() const { + return ArrayBufferViewObject::byteOffsetSlotValue(); } - template <typename NativeType> - NativeType read(uint64_t offset, bool isLittleEndian) { - return DataViewObject::read<NativeType>(offset, byteLength(), - isLittleEndian); - } + size_t byteLength() const { return ArrayBufferViewObject::lengthSlotValue(); } }; /** @@ -195,19 +187,14 @@ class ResizableDataViewObject : public DataViewObject { friend class DataViewObject; static ResizableDataViewObject* create( - JSContext* cx, size_t byteOffset, size_t byteLength, bool autoLength, - Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, HandleObject proto); + JSContext* cx, size_t byteOffset, size_t byteLength, + AutoLength autoLength, Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, + HandleObject proto); public: - static const uint8_t AUTO_LENGTH_SLOT = DataViewObject::RESERVED_SLOTS; - - static const uint8_t RESERVED_SLOTS = DataViewObject::RESERVED_SLOTS + 1; + static const uint8_t RESERVED_SLOTS = RESIZABLE_RESERVED_SLOTS; static const JSClass class_; - - bool isAutoLength() const { - return getFixedSlot(AUTO_LENGTH_SLOT).toBoolean(); - } }; // For structured cloning. diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 0be9aec17b..b9db9bf02d 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -20,7 +20,7 @@ #include "gc/Tracer.h" #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin, JS::LimitedColumnNumberOneOrigin #include "js/friend/ErrorMessages.h" // JSMSG_* -#include "js/Modules.h" // JS::GetModulePrivate, JS::ModuleDynamicImportHook +#include "js/Modules.h" // JS::GetModulePrivate, JS::ModuleDynamicImportHook, JS::ModuleType #include "vm/EqualityOperations.h" // js::SameValue #include "vm/Interpreter.h" // Execute, Lambda, ReportRuntimeLexicalError #include "vm/ModuleBuilder.h" // js::ModuleBuilder @@ -31,6 +31,7 @@ #include "builtin/HandlerFunction-inl.h" // js::ExtraValueFromHandler, js::NewHandler{,WithExtraValue}, js::TargetFromHandler #include "gc/GCContext-inl.h" +#include "vm/EnvironmentObject-inl.h" // EnvironmentObject::setAliasedBinding #include "vm/JSObject-inl.h" #include "vm/JSScript-inl.h" #include "vm/List-inl.h" @@ -184,8 +185,8 @@ ResolvedBindingObject* ResolvedBindingObject::create( DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ModuleRequestObject, specifier, SpecifierSlot) -ArrayObject* ModuleRequestObject::assertions() const { - JSObject* obj = getReservedSlot(AssertionSlot).toObjectOrNull(); +ArrayObject* ModuleRequestObject::attributes() const { + JSObject* obj = getReservedSlot(AttributesSlot).toObjectOrNull(); if (!obj) { return nullptr; } @@ -193,6 +194,52 @@ ArrayObject* ModuleRequestObject::assertions() const { return &obj->as<ArrayObject>(); } +bool ModuleRequestObject::hasAttributes() const { + return !getReservedSlot(ModuleRequestObject::AttributesSlot) + .isNullOrUndefined(); +} + +/* static */ +bool ModuleRequestObject::getModuleType( + JSContext* cx, const Handle<ModuleRequestObject*> moduleRequest, + JS::ModuleType& moduleType) { + if (!moduleRequest->hasAttributes()) { + moduleType = JS::ModuleType::JavaScript; + return true; + } + + Rooted<ArrayObject*> attributesArray(cx, moduleRequest->attributes()); + RootedObject attributeObject(cx); + RootedId typeId(cx, NameToId(cx->names().type)); + RootedValue value(cx); + + uint32_t numberOfAttributes = attributesArray->length(); + for (uint32_t i = 0; i < numberOfAttributes; i++) { + attributeObject = &attributesArray->getDenseElement(i).toObject(); + + if (!GetProperty(cx, attributeObject, attributeObject, typeId, &value)) { + continue; + } + + int32_t isJsonString; + if (!js::CompareStrings(cx, cx->names().json, value.toString(), + &isJsonString)) { + return false; + } + + if (isJsonString == 0) { + moduleType = JS::ModuleType::JSON; + return true; + } + + moduleType = JS::ModuleType::Unknown; + return true; + } + + moduleType = JS::ModuleType::JavaScript; + return true; +} + /* static */ bool ModuleRequestObject::isInstance(HandleValue value) { return value.isObject() && value.toObject().is<ModuleRequestObject>(); @@ -201,7 +248,7 @@ bool ModuleRequestObject::isInstance(HandleValue value) { /* static */ ModuleRequestObject* ModuleRequestObject::create( JSContext* cx, Handle<JSAtom*> specifier, - Handle<ArrayObject*> maybeAssertions) { + Handle<ArrayObject*> maybeAttributes) { ModuleRequestObject* self = NewObjectWithGivenProto<ModuleRequestObject>(cx, nullptr); if (!self) { @@ -209,7 +256,7 @@ ModuleRequestObject* ModuleRequestObject::create( } self->initReservedSlot(SpecifierSlot, StringOrNullValue(specifier)); - self->initReservedSlot(AssertionSlot, ObjectOrNullValue(maybeAssertions)); + self->initReservedSlot(AttributesSlot, ObjectOrNullValue(maybeAttributes)); return self; } @@ -618,6 +665,21 @@ void ModuleNamespaceObject::ProxyHandler::finalize(JS::GCContext* gcx, } /////////////////////////////////////////////////////////////////////////// +// SyntheticModuleFields + +// The fields of a synthetic module record, as described in: +// https://tc39.es/proposal-json-modules/#sec-synthetic-module-records +class js::SyntheticModuleFields { + public: + ExportNameVector exportNames; + + public: + void trace(JSTracer* trc); +}; + +void SyntheticModuleFields::trace(JSTracer* trc) { exportNames.trace(trc); } + +/////////////////////////////////////////////////////////////////////////// // CyclicModuleFields // The fields of a cyclic module record, as described in: @@ -857,6 +919,10 @@ Span<const ExportEntry> ModuleObject::starExportEntries() const { return cyclicModuleFields()->starExportEntries(); } +const ExportNameVector& ModuleObject::syntheticExportNames() const { + return syntheticModuleFields()->exportNames; +} + void ModuleObject::initFunctionDeclarations( UniquePtr<FunctionDeclarationVector> decls) { cyclicModuleFields()->functionDeclarations = std::move(decls); @@ -883,12 +949,39 @@ ModuleObject* ModuleObject::create(JSContext* cx) { } /* static */ +ModuleObject* ModuleObject::createSynthetic( + JSContext* cx, MutableHandle<ExportNameVector> exportNames) { + Rooted<UniquePtr<SyntheticModuleFields>> syntheticFields(cx); + syntheticFields = cx->make_unique<SyntheticModuleFields>(); + if (!syntheticFields) { + return nullptr; + } + + Rooted<ModuleObject*> self( + cx, NewObjectWithGivenProto<ModuleObject>(cx, nullptr)); + if (!self) { + return nullptr; + } + + InitReservedSlot(self, SyntheticModuleFieldsSlot, syntheticFields.release(), + MemoryUse::ModuleSyntheticFields); + + self->syntheticModuleFields()->exportNames = std::move(exportNames.get()); + + return self; +} + +/* static */ void ModuleObject::finalize(JS::GCContext* gcx, JSObject* obj) { ModuleObject* self = &obj->as<ModuleObject>(); if (self->hasCyclicModuleFields()) { gcx->delete_(obj, self->cyclicModuleFields(), MemoryUse::ModuleCyclicFields); } + if (self->hasSyntheticModuleFields()) { + gcx->delete_(obj, self->syntheticModuleFields(), + MemoryUse::ModuleSyntheticFields); + } } ModuleEnvironmentObject& ModuleObject::initialEnvironment() const { @@ -960,6 +1053,7 @@ void ModuleObject::setAsyncEvaluating() { void ModuleObject::initScriptSlots(HandleScript script) { MOZ_ASSERT(script); MOZ_ASSERT(script->sourceObject()); + MOZ_ASSERT(script->filename()); initReservedSlot(ScriptSlot, PrivateGCThingValue(script)); cyclicModuleFields()->scriptSourceObject = script->sourceObject(); } @@ -1021,10 +1115,12 @@ static inline void AssertValidModuleStatus(ModuleStatus status) { } ModuleStatus ModuleObject::status() const { - // TODO: When implementing synthetic module records it may be convenient to - // make this method always return a ModuleStatus::Evaluated for such a module - // so we can assert a module's status without checking which kind it is, even - // though synthetic modules don't have this field according to the spec. + // Always return `ModuleStatus::Evaluated` so we can assert a module's status + // without checking which kind it is, even though synthetic modules don't have + // this field according to the spec. + if (hasSyntheticModuleFields()) { + return ModuleStatus::Evaluated; + } ModuleStatus status = cyclicModuleFields()->status; AssertValidModuleStatus(status); @@ -1161,6 +1257,22 @@ ModuleObject* ModuleObject::getCycleRoot() const { return cyclicModuleFields()->cycleRoot; } +bool ModuleObject::hasSyntheticModuleFields() const { + bool result = !getReservedSlot(SyntheticModuleFieldsSlot).isUndefined(); + MOZ_ASSERT_IF(result, !hasCyclicModuleFields()); + return result; +} + +SyntheticModuleFields* ModuleObject::syntheticModuleFields() { + MOZ_ASSERT(!hasCyclicModuleFields()); + void* ptr = getReservedSlot(SyntheticModuleFieldsSlot).toPrivate(); + MOZ_ASSERT(ptr); + return static_cast<SyntheticModuleFields*>(ptr); +} +const SyntheticModuleFields* ModuleObject::syntheticModuleFields() const { + return const_cast<ModuleObject*>(this)->syntheticModuleFields(); +} + bool ModuleObject::hasTopLevelCapability() const { return cyclicModuleFields()->topLevelCapability; } @@ -1206,6 +1318,9 @@ void ModuleObject::trace(JSTracer* trc, JSObject* obj) { if (module.hasCyclicModuleFields()) { module.cyclicModuleFields()->trace(trc); } + if (module.hasSyntheticModuleFields()) { + module.syntheticModuleFields()->trace(trc); + } } /* static */ @@ -1328,6 +1443,27 @@ bool ModuleObject::createEnvironment(JSContext* cx, return true; } +/*static*/ +bool ModuleObject::createSyntheticEnvironment(JSContext* cx, + Handle<ModuleObject*> self, + Handle<GCVector<Value>> values) { + Rooted<ModuleEnvironmentObject*> env( + cx, ModuleEnvironmentObject::createSynthetic(cx, self)); + if (!env) { + return false; + } + + MOZ_ASSERT(env->shape()->propMapLength() == values.length()); + + for (uint32_t i = 0; i < values.length(); i++) { + env->setAliasedBinding(env->firstSyntheticValueSlot() + i, values[i]); + } + + self->setInitialEnvironment(env); + + return true; +} + /////////////////////////////////////////////////////////////////////////// // ModuleBuilder @@ -2542,10 +2678,11 @@ static bool OnResolvedDynamicModule(JSContext* cx, unsigned argc, Value* vp) { return RejectPromiseWithPendingError(cx, promise); } - MOZ_ASSERT(module->getCycleRoot() - ->topLevelCapability() - ->as<PromiseObject>() - .state() == JS::PromiseState::Fulfilled); + MOZ_ASSERT_IF(module->hasCyclicModuleFields(), + module->getCycleRoot() + ->topLevelCapability() + ->as<PromiseObject>() + .state() == JS::PromiseState::Fulfilled); RootedObject ns(cx, GetOrCreateModuleNamespace(cx, module)); if (!ns) { diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index 015cb42a5f..d39d65b18c 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -43,6 +43,7 @@ namespace js { class ArrayObject; class CyclicModuleFields; +class SyntheticModuleFields; class ListObject; class ModuleEnvironmentObject; class ModuleObject; @@ -51,16 +52,20 @@ class ScriptSourceObject; class ModuleRequestObject : public NativeObject { public: - enum { SpecifierSlot = 0, AssertionSlot, SlotCount }; + enum { SpecifierSlot = 0, AttributesSlot, SlotCount }; static const JSClass class_; static bool isInstance(HandleValue value); [[nodiscard]] static ModuleRequestObject* create( JSContext* cx, Handle<JSAtom*> specifier, - Handle<ArrayObject*> maybeAssertions); + Handle<ArrayObject*> maybeAttributes); JSAtom* specifier() const; - ArrayObject* assertions() const; + ArrayObject* attributes() const; + bool hasAttributes() const; + static bool getModuleType(JSContext* cx, + const Handle<ModuleRequestObject*> moduleRequest, + JS::ModuleType& moduleType); }; using ModuleRequestVector = @@ -309,6 +314,10 @@ constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_INIT = 1; // Value that the field is set to after being cleared. constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_CLEARED = 0; +// Currently, the ModuleObject class is used to represent both the Source Text +// Module Record and the Synthetic Module Record. Ideally, this is something +// that should be refactored to follow the same hierarchy as in the spec. +// TODO: See Bug 1880519. class ModuleObject : public NativeObject { public: // Module fields including those for AbstractModuleRecords described by: @@ -318,6 +327,8 @@ class ModuleObject : public NativeObject { EnvironmentSlot, NamespaceSlot, CyclicModuleFieldsSlot, + // `SyntheticModuleFields` if a synthetic module. Otherwise `undefined`. + SyntheticModuleFieldsSlot, SlotCount }; @@ -327,6 +338,9 @@ class ModuleObject : public NativeObject { static ModuleObject* create(JSContext* cx); + static ModuleObject* createSynthetic( + JSContext* cx, MutableHandle<ExportNameVector> exportNames); + // Initialize the slots on this object that are dependent on the script. void initScriptSlots(HandleScript script); @@ -364,6 +378,8 @@ class ModuleObject : public NativeObject { mozilla::Span<const ExportEntry> localExportEntries() const; mozilla::Span<const ExportEntry> indirectExportEntries() const; mozilla::Span<const ExportEntry> starExportEntries() const; + const ExportNameVector& syntheticExportNames() const; + IndirectBindingMap& importBindings(); void setStatus(ModuleStatus newStatus); @@ -390,6 +406,8 @@ class ModuleObject : public NativeObject { void clearAsyncEvaluatingPostOrder(); void setCycleRoot(ModuleObject* cycleRoot); ModuleObject* getCycleRoot() const; + bool hasCyclicModuleFields() const; + bool hasSyntheticModuleFields() const; static void onTopLevelEvaluationFinished(ModuleObject* module); @@ -413,6 +431,9 @@ class ModuleObject : public NativeObject { MutableHandle<UniquePtr<ExportNameVector>> exports); static bool createEnvironment(JSContext* cx, Handle<ModuleObject*> self); + static bool createSyntheticEnvironment(JSContext* cx, + Handle<ModuleObject*> self, + Handle<GCVector<Value>> values); void initAsyncSlots(JSContext* cx, bool hasTopLevelAwait, Handle<ListObject*> asyncParentModules); @@ -423,9 +444,11 @@ class ModuleObject : public NativeObject { static void trace(JSTracer* trc, JSObject* obj); static void finalize(JS::GCContext* gcx, JSObject* obj); - bool hasCyclicModuleFields() const; CyclicModuleFields* cyclicModuleFields(); const CyclicModuleFields* cyclicModuleFields() const; + + SyntheticModuleFields* syntheticModuleFields(); + const SyntheticModuleFields* syntheticModuleFields() const; }; JSObject* GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module); diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 62eac477ad..dde953143e 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2885,7 +2885,8 @@ bool ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) { } case ParseNodeKind::DotExpr: - case ParseNodeKind::OptionalDotExpr: { + case ParseNodeKind::OptionalDotExpr: + case ParseNodeKind::ArgumentsLength: { PropertyAccessBase* prop = &pn->as<PropertyAccessBase>(); MOZ_ASSERT(prop->pn_pos.encloses(prop->expression().pn_pos)); diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp index 16e92b554c..1da2270cfb 100644 --- a/js/src/builtin/String.cpp +++ b/js/src/builtin/String.cpp @@ -566,15 +566,10 @@ static inline void CopyChars(CharT* to, const JSLinearString* from, MOZ_ASSERT(begin + length <= from->length()); JS::AutoCheckCannotGC nogc; - if constexpr (std::is_same_v<CharT, Latin1Char>) { - MOZ_ASSERT(from->hasLatin1Chars()); + if (from->hasLatin1Chars()) { CopyChars(to, from->latin1Chars(nogc) + begin, length); } else { - if (from->hasLatin1Chars()) { - CopyChars(to, from->latin1Chars(nogc) + begin, length); - } else { - CopyChars(to, from->twoByteChars(nogc) + begin, length); - } + CopyChars(to, from->twoByteChars(nogc) + begin, length); } } diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index f762f28f3e..498fa1746d 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1595,17 +1595,20 @@ static bool WasmLosslessInvoke(JSContext* cx, unsigned argc, Value* vp) { JS_ReportErrorASCII(cx, "not enough arguments"); return false; } - if (!args.get(0).isObject()) { + if (!args.get(0).isObject() || !args.get(0).toObject().is<JSFunction>()) { JS_ReportErrorASCII(cx, "argument is not an object"); return false; } - RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>()); + RootedFunction func(cx, &args[0].toObject().as<JSFunction>()); if (!func || !wasm::IsWasmExportedFunction(func)) { JS_ReportErrorASCII(cx, "argument is not an exported wasm function"); return false; } + // Switch to the function's realm + AutoRealm ar(cx, func); + // Get the instance and funcIndex for calling the function wasm::Instance& instance = wasm::ExportedFunctionToInstance(func); uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func); @@ -2042,6 +2045,95 @@ static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) { return false; } +static bool ToIonDumpContents(JSContext* cx, HandleValue value, + wasm::IonDumpContents* contents) { + RootedString option(cx, JS::ToString(cx, value)); + + if (!option) { + return false; + } + + bool isEqual = false; + if (!JS_StringEqualsLiteral(cx, option, "mir", &isEqual) || isEqual) { + *contents = wasm::IonDumpContents::UnoptimizedMIR; + return isEqual; + } else if (!JS_StringEqualsLiteral(cx, option, "unopt-mir", &isEqual) || + isEqual) { + *contents = wasm::IonDumpContents::UnoptimizedMIR; + return isEqual; + } else if (!JS_StringEqualsLiteral(cx, option, "opt-mir", &isEqual) || + isEqual) { + *contents = wasm::IonDumpContents::OptimizedMIR; + return isEqual; + } else if (!JS_StringEqualsLiteral(cx, option, "lir", &isEqual) || isEqual) { + *contents = wasm::IonDumpContents::LIR; + return isEqual; + } else { + return false; + } +} + +static bool WasmDumpIon(JSContext* cx, unsigned argc, Value* vp) { + if (!wasm::HasSupport(cx)) { + JS_ReportErrorASCII(cx, "wasm support unavailable"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + + args.rval().set(UndefinedValue()); + + SharedMem<uint8_t*> dataPointer; + size_t byteLength; + if (!args.get(0).isObject() || !IsBufferSource(args.get(0).toObjectOrNull(), + &dataPointer, &byteLength)) { + JS_ReportErrorASCII(cx, "argument is not a buffer source"); + return false; + } + + uint32_t targetFuncIndex; + if (!ToUint32(cx, args.get(1), &targetFuncIndex)) { + JS_ReportErrorASCII(cx, "argument is not a func index"); + return false; + } + + wasm::IonDumpContents contents = wasm::IonDumpContents::Default; + if (args.length() > 2 && !ToIonDumpContents(cx, args.get(2), &contents)) { + JS_ReportErrorASCII(cx, "argument is not a valid dump contents"); + return false; + } + + wasm::MutableBytes bytecode = cx->new_<wasm::ShareableBytes>(); + if (!bytecode) { + return false; + } + if (!bytecode->append(dataPointer.unwrap(), byteLength)) { + ReportOutOfMemory(cx); + return false; + } + + UniqueChars error; + JSSprinter out(cx); + if (!out.init()) { + ReportOutOfMemory(cx); + return false; + } + + if (!wasm::DumpIonFunctionInModule(*bytecode, targetFuncIndex, contents, out, + &error)) { + if (error) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, + JSMSG_WASM_COMPILE_ERROR, error.get()); + return false; + } + ReportOutOfMemory(cx); + return false; + } + + args.rval().set(StringValue(out.release(cx))); + return true; +} + enum class Flag { Tier2Complete, Deserialized }; static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) { @@ -2073,7 +2165,6 @@ static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) { return true; } -#if defined(DEBUG) static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -2082,10 +2173,6 @@ static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) { return false; } - if (!cx->options().wasmTestMetadata()) { - return false; - } - if (args[0].toObject().is<WasmModuleObject>()) { HashMap<const char*, uint32_t, mozilla::CStringHasher, SystemAllocPolicy> hashmap = args[0] @@ -2096,6 +2183,7 @@ static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) { .metadataAnalysis(cx); if (hashmap.empty()) { JS_ReportErrorASCII(cx, "Metadata analysis has failed"); + return false; } // metadataAnalysis returned a map of {key, value} with various statistics @@ -2107,16 +2195,21 @@ static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) { auto value = iter.get().value(); JSString* string = JS_NewStringCopyZ(cx, key); + if (!string) { + return false; + } + if (!props.append( IdValuePair(NameToId(string->asLinear().toPropertyName(cx)), NumberValue(value)))) { - ReportOutOfMemory(cx); return false; } } - JSObject* results = - NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + JSObject* results = NewPlainObjectWithUniqueNames(cx, props); + if (!results) { + return false; + } args.rval().setObject(*results); return true; @@ -2127,7 +2220,6 @@ static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) { return false; } -#endif static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc, Value* vp) { @@ -4523,7 +4615,9 @@ static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) { case JS::ProfilingFrameIterator::Frame_Ion: frameKindStr = "ion"; break; - case JS::ProfilingFrameIterator::Frame_Wasm: + case JS::ProfilingFrameIterator::Frame_WasmBaseline: + case JS::ProfilingFrameIterator::Frame_WasmIon: + case JS::ProfilingFrameIterator::Frame_WasmOther: frameKindStr = "wasm"; break; default: @@ -5264,15 +5358,17 @@ class CustomSerializableObject : public NativeObject { static ActivityLog* getThreadLog() { if (!self.initialized() || !self.get()) { self.infallibleInit(); + AutoEnterOOMUnsafeRegion oomUnsafe; self.set(js_new<ActivityLog>()); - MOZ_RELEASE_ASSERT(self.get()); + if (!self.get()) { + oomUnsafe.crash("allocating activity log"); + } if (!TlsContext.get()->runtime()->atExit( [](void* vpData) { auto* log = static_cast<ActivityLog*>(vpData); js_delete(log); }, self.get())) { - AutoEnterOOMUnsafeRegion oomUnsafe; oomUnsafe.crash("atExit"); } } @@ -7199,6 +7295,8 @@ static bool CompileToStencil(JSContext* cx, uint32_t argc, Value* vp) { } CompileOptions options(cx); + options.setFile("<compileToStencil>"); + RootedString displayURL(cx); RootedString sourceMapURL(cx); UniqueChars fileNameBytes; @@ -7230,13 +7328,10 @@ static bool CompileToStencil(JSContext* cx, uint32_t argc, Value* vp) { AutoReportFrontendContext fc(cx); RefPtr<JS::Stencil> stencil; - JS::CompilationStorage compileStorage; if (isModule) { - stencil = - JS::CompileModuleScriptToStencil(&fc, options, srcBuf, compileStorage); + stencil = JS::CompileModuleScriptToStencil(&fc, options, srcBuf); } else { - stencil = - JS::CompileGlobalScriptToStencil(&fc, options, srcBuf, compileStorage); + stencil = JS::CompileGlobalScriptToStencil(&fc, options, srcBuf); } if (!stencil) { return false; @@ -7368,6 +7463,8 @@ static bool CompileToStencilXDR(JSContext* cx, uint32_t argc, Value* vp) { } CompileOptions options(cx); + options.setFile("<compileToStencilXDR>"); + RootedString displayURL(cx); RootedString sourceMapURL(cx); UniqueChars fileNameBytes; @@ -9750,6 +9847,15 @@ JS_FOR_WASM_FEATURES(WASM_FEATURE) " ImportJitExit - wasm-to-jitted-JS stubs\n" " all - all kinds, including obscure ones\n"), + JS_FN_HELP("wasmDumpIon", WasmDumpIon, 2, 0, +"wasmDumpIon(bytecode, funcIndex, [, contents])\n", +"wasmDumpIon(bytecode, funcIndex, [, contents])" +" Returns a dump of compiling a function in the specified module with Ion." +" The `contents` flag controls what is dumped. one of:" +" `mir` | `unopt-mir`: Unoptimized MIR (the default)" +" `opt-mir`: Optimized MIR" +" `lir`: LIR"), + JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0, "wasmHasTier2CompilationCompleted(module)", " Returns a boolean indicating whether a given module has finished compiled code for tier2. \n" @@ -9948,11 +10054,9 @@ JS_FOR_WASM_FEATURES(WASM_FEATURE) " element's edge is the node of the i+1'th array element; the destination of\n" " the last array element is implicitly |target|.\n"), -#if defined(DEBUG) JS_FN_HELP("wasmMetadataAnalysis", wasmMetadataAnalysis, 1, 0, "wasmMetadataAnalysis(wasmObject)", " Prints an analysis of the size of metadata on this wasm object.\n"), -#endif #if defined(DEBUG) || defined(JS_JITSPEW) JS_FN_HELP("dumpObject", DumpObject, 1, 0, diff --git a/js/src/builtin/intl/NumberFormat.js b/js/src/builtin/intl/NumberFormat.js index be3b74a8ac..6dc77fb639 100644 --- a/js/src/builtin/intl/NumberFormat.js +++ b/js/src/builtin/intl/NumberFormat.js @@ -190,7 +190,7 @@ function UnwrapNumberFormat(nf) { * * Applies digit options used for number formatting onto the intl object. * - * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 + * ES2024 Intl draft rev a1db4567870dbe505121a4255f1210338757190a */ function SetNumberFormatDigitOptions( lazyData, @@ -216,15 +216,6 @@ function SetNumberFormatDigitOptions( lazyData.minimumIntegerDigits = mnid; // Step 7. - var roundingPriority = GetOption( - options, - "roundingPriority", - "string", - ["auto", "morePrecision", "lessPrecision"], - "auto" - ); - - // Step 8. var roundingIncrement = GetNumberOption( options, "roundingIncrement", @@ -233,7 +224,7 @@ function SetNumberFormatDigitOptions( 1 ); - // Step 9. + // Step 8. switch (roundingIncrement) { case 1: case 2: @@ -259,7 +250,7 @@ function SetNumberFormatDigitOptions( ); } - // Step 10. + // Step 9. var roundingMode = GetOption( options, "roundingMode", @@ -278,6 +269,15 @@ function SetNumberFormatDigitOptions( "halfExpand" ); + // Step 10. + var roundingPriority = GetOption( + options, + "roundingPriority", + "string", + ["auto", "morePrecision", "lessPrecision"], + "auto" + ); + // Step 11. var trailingZeroDisplay = GetOption( options, @@ -303,52 +303,52 @@ function SetNumberFormatDigitOptions( // Step 16. lazyData.trailingZeroDisplay = trailingZeroDisplay; - // Steps 17-18. + // Step 17. var hasSignificantDigits = mnsd !== undefined || mxsd !== undefined; - // Step 19-20. + // Step 28. var hasFractionDigits = mnfd !== undefined || mxfd !== undefined; - // Steps 21 and 23.a. + // Steps 19 and 21.a. var needSignificantDigits = roundingPriority !== "auto" || hasSignificantDigits; - // Steps 22 and 23.b.i. + // Steps 20 and 21.b.i. var needFractionalDigits = roundingPriority !== "auto" || !(hasSignificantDigits || (!hasFractionDigits && notation === "compact")); - // Step 24. + // Step 22. if (needSignificantDigits) { - // Step 24.a. + // Step 22.a. if (hasSignificantDigits) { - // Step 24.a.i. + // Step 22.a.i. mnsd = DefaultNumberOption(mnsd, 1, 21, 1); lazyData.minimumSignificantDigits = mnsd; - // Step 24.a.ii. + // Step 22.a.ii. mxsd = DefaultNumberOption(mxsd, mnsd, 21, 21); lazyData.maximumSignificantDigits = mxsd; } else { - // Step 24.b.i. + // Step 22.b.i. lazyData.minimumSignificantDigits = 1; - // Step 24.b.ii. + // Step 22.b.ii. lazyData.maximumSignificantDigits = 21; } } - // Step 25. + // Step 23. if (needFractionalDigits) { - // Step 25.a. + // Step 23.a. if (hasFractionDigits) { - // Step 25.a.i. + // Step 23.a.i. mnfd = DefaultNumberOption(mnfd, 0, 100, undefined); - // Step 25.a.ii. + // Step 23.a.ii. mxfd = DefaultNumberOption(mxfd, 0, 100, undefined); - // Step 25.a.iii. + // Step 23.a.iii. if (mnfd === undefined) { assert( mxfd !== undefined, @@ -357,31 +357,31 @@ function SetNumberFormatDigitOptions( mnfd = std_Math_min(mnfdDefault, mxfd); } - // Step 25.a.iv. + // Step 23.a.iv. else if (mxfd === undefined) { mxfd = std_Math_max(mxfdDefault, mnfd); } - // Step 25.a.v. + // Step 23.a.v. else if (mnfd > mxfd) { ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, mxfd); } - // Step 25.a.vi. + // Step 23.a.vi. lazyData.minimumFractionDigits = mnfd; - // Step 25.a.vii. + // Step 23.a.vii. lazyData.maximumFractionDigits = mxfd; } else { - // Step 25.b.i. + // Step 23.b.i. lazyData.minimumFractionDigits = mnfdDefault; - // Step 25.b.ii. + // Step 23.b.ii. lazyData.maximumFractionDigits = mxfdDefault; } } - // Steps 26-30. + // Steps 24-28. if (!needSignificantDigits && !needFractionalDigits) { assert(!hasSignificantDigits, "bad significant digits in fallback case"); assert( @@ -393,23 +393,23 @@ function SetNumberFormatDigitOptions( `bad notation in fallback case: ${notation}` ); - // Steps 26.a-e. + // Steps 24.a-f. lazyData.minimumFractionDigits = 0; lazyData.maximumFractionDigits = 0; lazyData.minimumSignificantDigits = 1; lazyData.maximumSignificantDigits = 2; lazyData.roundingPriority = "morePrecision"; } else { - // Steps 27-30. + // Steps 25-28. // // Our implementation stores |roundingPriority| instead of using // [[RoundingType]]. lazyData.roundingPriority = roundingPriority; } - // Step 31. + // Step 29. if (roundingIncrement !== 1) { - // Step 31.a. + // Step 29.a. // // [[RoundingType]] is `fractionDigits` if |roundingPriority| is equal to // "auto" and |hasSignificantDigits| is false. @@ -428,7 +428,7 @@ function SetNumberFormatDigitOptions( ); } - // Step 31.b. + // Step 29.b. // // Minimum and maximum fraction digits must be equal. if ( @@ -1128,7 +1128,7 @@ function Intl_NumberFormat_formatRangeToParts(start, end) { * * Returns the resolved options for a NumberFormat object. * - * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 + * ES2024 Intl draft rev a1db4567870dbe505121a4255f1210338757190a */ function Intl_NumberFormat_resolvedOptions() { // Steps 1-3. @@ -1244,20 +1244,15 @@ function Intl_NumberFormat_resolvedOptions() { } DefineDataProperty(result, "signDisplay", internals.signDisplay); - DefineDataProperty(result, "roundingMode", internals.roundingMode); DefineDataProperty(result, "roundingIncrement", internals.roundingIncrement); + DefineDataProperty(result, "roundingMode", internals.roundingMode); + DefineDataProperty(result, "roundingPriority", internals.roundingPriority); DefineDataProperty( result, "trailingZeroDisplay", internals.trailingZeroDisplay ); - // Steps 6-8. - // - // Our implementation doesn't use [[RoundingType]], but instead directly - // stores the computed `roundingPriority` value. - DefineDataProperty(result, "roundingPriority", internals.roundingPriority); - - // Step 9. + // Step 6. return result; } diff --git a/js/src/builtin/intl/PluralRules.js b/js/src/builtin/intl/PluralRules.js index 1dbf6656df..260fdbd568 100644 --- a/js/src/builtin/intl/PluralRules.js +++ b/js/src/builtin/intl/PluralRules.js @@ -339,7 +339,7 @@ function Intl_PluralRules_selectRange(start, end) { * * Returns the resolved options for a PluralRules object. * - * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 + * ES2024 Intl draft rev a1db4567870dbe505121a4255f1210338757190a */ function Intl_PluralRules_resolvedOptions() { // Step 1. @@ -359,7 +359,20 @@ function Intl_PluralRules_resolvedOptions() { var internals = getPluralRulesInternals(pluralRules); - // Steps 3-4. + // Step 4. + var internalsPluralCategories = internals.pluralCategories; + if (internalsPluralCategories === null) { + internalsPluralCategories = intl_GetPluralCategories(pluralRules); + internals.pluralCategories = internalsPluralCategories; + } + + // Step 5.b. + var pluralCategories = []; + for (var i = 0; i < internalsPluralCategories.length; i++) { + DefineDataProperty(pluralCategories, i, internalsPluralCategories[i]); + } + + // Steps 3 and 5. var result = { locale: internals.locale, type: internals.type, @@ -406,35 +419,16 @@ function Intl_PluralRules_resolvedOptions() { ); } - DefineDataProperty(result, "roundingMode", internals.roundingMode); + DefineDataProperty(result, "pluralCategories", pluralCategories); DefineDataProperty(result, "roundingIncrement", internals.roundingIncrement); + DefineDataProperty(result, "roundingMode", internals.roundingMode); + DefineDataProperty(result, "roundingPriority", internals.roundingPriority); DefineDataProperty( result, "trailingZeroDisplay", internals.trailingZeroDisplay ); - // Step 5. - var internalsPluralCategories = internals.pluralCategories; - if (internalsPluralCategories === null) { - internalsPluralCategories = intl_GetPluralCategories(pluralRules); - internals.pluralCategories = internalsPluralCategories; - } - - var pluralCategories = []; - for (var i = 0; i < internalsPluralCategories.length; i++) { - DefineDataProperty(pluralCategories, i, internalsPluralCategories[i]); - } - // Step 6. - DefineDataProperty(result, "pluralCategories", pluralCategories); - - // Steps 7-9. - // - // Our implementation doesn't use [[RoundingType]], but instead directly - // stores the computed `roundingPriority` value. - DefineDataProperty(result, "roundingPriority", internals.roundingPriority); - - // Step 10. return result; } diff --git a/js/src/builtin/intl/make_intl_data.py b/js/src/builtin/intl/make_intl_data.py index 7042c0a005..a8357445c4 100755 --- a/js/src/builtin/intl/make_intl_data.py +++ b/js/src/builtin/intl/make_intl_data.py @@ -2213,7 +2213,7 @@ def listIANAFiles(tzdataDir): def readIANAFiles(tzdataDir, files): """Read all IANA time zone files from the given iterable.""" - nameSyntax = "[\w/+\-]+" + nameSyntax = r"[\w/+\-]+" pZone = re.compile(r"Zone\s+(?P<name>%s)\s+.*" % nameSyntax) pLink = re.compile( r"Link\s+(?P<target>%s)\s+(?P<name>%s)(?:\s+#.*)?" % (nameSyntax, nameSyntax) @@ -2310,7 +2310,7 @@ def readICUResourceFile(filename): maybeMultiComments = r"(?:/\*[^*]*\*/)*" maybeSingleComment = r"(?://.*)?" lineStart = "^%s" % maybeMultiComments - lineEnd = "%s\s*%s$" % (maybeMultiComments, maybeSingleComment) + lineEnd = r"%s\s*%s$" % (maybeMultiComments, maybeSingleComment) return re.compile(r"\s*".join(chain([lineStart], args, [lineEnd]))) tableName = r'(?P<quote>"?)(?P<name>.+?)(?P=quote)' @@ -2554,7 +2554,7 @@ def icuTzDataVersion(icuTzDir): zoneinfo = os.path.join(icuTzDir, "zoneinfo64.txt") if not os.path.isfile(zoneinfo): raise RuntimeError("file not found: %s" % zoneinfo) - version = searchInFile("^//\s+tz version:\s+([0-9]{4}[a-z])$", zoneinfo) + version = searchInFile(r"^//\s+tz version:\s+([0-9]{4}[a-z])$", zoneinfo) if version is None: raise RuntimeError( "%s does not contain a valid tzdata version string" % zoneinfo @@ -3711,7 +3711,7 @@ const allUnits = {}; """.format( all_units_array ) - + """ + + r""" // Test only sanctioned unit identifiers are allowed. for (const typeAndUnit of allUnits) { diff --git a/js/src/builtin/temporal/PlainDate.cpp b/js/src/builtin/temporal/PlainDate.cpp index 759456c9cc..a4ad0e418f 100644 --- a/js/src/builtin/temporal/PlainDate.cpp +++ b/js/src/builtin/temporal/PlainDate.cpp @@ -2295,8 +2295,7 @@ static bool PlainDate_getISOFields(JSContext* cx, const CallArgs& args) { } // Step 8. - auto* obj = - NewPlainObjectWithUniqueNames(cx, fields.begin(), fields.length()); + auto* obj = NewPlainObjectWithUniqueNames(cx, fields); if (!obj) { return false; } diff --git a/js/src/builtin/temporal/PlainDateTime.cpp b/js/src/builtin/temporal/PlainDateTime.cpp index 8861b484bd..8f137cfe43 100644 --- a/js/src/builtin/temporal/PlainDateTime.cpp +++ b/js/src/builtin/temporal/PlainDateTime.cpp @@ -2507,8 +2507,7 @@ static bool PlainDateTime_getISOFields(JSContext* cx, const CallArgs& args) { } // Step 14. - auto* obj = - NewPlainObjectWithUniqueNames(cx, fields.begin(), fields.length()); + auto* obj = NewPlainObjectWithUniqueNames(cx, fields); if (!obj) { return false; } diff --git a/js/src/builtin/temporal/PlainMonthDay.cpp b/js/src/builtin/temporal/PlainMonthDay.cpp index f97b7ad68c..0896100a3f 100644 --- a/js/src/builtin/temporal/PlainMonthDay.cpp +++ b/js/src/builtin/temporal/PlainMonthDay.cpp @@ -903,8 +903,7 @@ static bool PlainMonthDay_getISOFields(JSContext* cx, const CallArgs& args) { } // Step 8. - auto* obj = - NewPlainObjectWithUniqueNames(cx, fields.begin(), fields.length()); + auto* obj = NewPlainObjectWithUniqueNames(cx, fields); if (!obj) { return false; } diff --git a/js/src/builtin/temporal/PlainTime.cpp b/js/src/builtin/temporal/PlainTime.cpp index 9501b5853b..bf35b9d93e 100644 --- a/js/src/builtin/temporal/PlainTime.cpp +++ b/js/src/builtin/temporal/PlainTime.cpp @@ -2432,8 +2432,7 @@ static bool PlainTime_getISOFields(JSContext* cx, const CallArgs& args) { } // Step 10. - auto* obj = - NewPlainObjectWithUniqueNames(cx, fields.begin(), fields.length()); + auto* obj = NewPlainObjectWithUniqueNames(cx, fields); if (!obj) { return false; } diff --git a/js/src/builtin/temporal/PlainYearMonth.cpp b/js/src/builtin/temporal/PlainYearMonth.cpp index a4e2f8f9e4..b95efd3179 100644 --- a/js/src/builtin/temporal/PlainYearMonth.cpp +++ b/js/src/builtin/temporal/PlainYearMonth.cpp @@ -1534,8 +1534,7 @@ static bool PlainYearMonth_getISOFields(JSContext* cx, const CallArgs& args) { } // Step 8. - auto* obj = - NewPlainObjectWithUniqueNames(cx, fields.begin(), fields.length()); + auto* obj = NewPlainObjectWithUniqueNames(cx, fields); if (!obj) { return false; } diff --git a/js/src/builtin/temporal/ZonedDateTime.cpp b/js/src/builtin/temporal/ZonedDateTime.cpp index 690ff223b1..92842a9626 100644 --- a/js/src/builtin/temporal/ZonedDateTime.cpp +++ b/js/src/builtin/temporal/ZonedDateTime.cpp @@ -3958,8 +3958,7 @@ static bool ZonedDateTime_getISOFields(JSContext* cx, const CallArgs& args) { } // Step 22. - auto* obj = - NewPlainObjectWithUniqueNames(cx, fields.begin(), fields.length()); + auto* obj = NewPlainObjectWithUniqueNames(cx, fields); if (!obj) { return false; } diff --git a/js/src/debugger/Object.cpp b/js/src/debugger/Object.cpp index c5a4f1f6dc..17528b0fd9 100644 --- a/js/src/debugger/Object.cpp +++ b/js/src/debugger/Object.cpp @@ -209,6 +209,7 @@ struct MOZ_STACK_CLASS DebuggerObject::CallData { bool createSource(); bool makeDebuggeeValueMethod(); bool isSameNativeMethod(); + bool isSameNativeWithJitInfoMethod(); bool isNativeGetterWithJitInfo(); bool unsafeDereferenceMethod(); bool unwrapMethod(); @@ -1338,7 +1339,18 @@ bool DebuggerObject::CallData::isSameNativeMethod() { return false; } - return DebuggerObject::isSameNative(cx, object, args[0], args.rval()); + return DebuggerObject::isSameNative(cx, object, args[0], CheckJitInfo::No, + args.rval()); +} + +bool DebuggerObject::CallData::isSameNativeWithJitInfoMethod() { + if (!args.requireAtLeast( + cx, "Debugger.Object.prototype.isSameNativeWithJitInfo", 1)) { + return false; + } + + return DebuggerObject::isSameNative(cx, object, args[0], CheckJitInfo::Yes, + args.rval()); } bool DebuggerObject::CallData::isNativeGetterWithJitInfo() { @@ -1424,6 +1436,11 @@ struct DebuggerObject::PromiseReactionRecordBuilder // so we ignore it. return true; } + if (!unwrappedGenerator->realm()->isDebuggee()) { + // Caller can keep the reference to the debugger object even after + // removing the realm from debuggee. Do nothing for this case. + return true; + } return dbg->getFrame(cx, unwrappedGenerator, &frame) && push(cx, frame); } @@ -1535,6 +1552,7 @@ const JSFunctionSpec DebuggerObject::methods_[] = { JS_DEBUG_FN("createSource", createSource, 1), JS_DEBUG_FN("makeDebuggeeValue", makeDebuggeeValueMethod, 1), JS_DEBUG_FN("isSameNative", isSameNativeMethod, 1), + JS_DEBUG_FN("isSameNativeWithJitInfo", isSameNativeWithJitInfoMethod, 1), JS_DEBUG_FN("isNativeGetterWithJitInfo", isNativeGetterWithJitInfo, 1), JS_DEBUG_FN("unsafeDereference", unsafeDereferenceMethod, 0), JS_DEBUG_FN("unwrap", unwrapMethod, 0), @@ -2576,9 +2594,36 @@ static JSAtom* MaybeGetSelfHostedFunctionName(const Value& v) { return GetClonedSelfHostedFunctionName(fun); } +static bool IsSameNative(JSFunction* a, JSFunction* b, + DebuggerObject::CheckJitInfo checkJitInfo) { + if (a->native() != b->native()) { + return false; + } + + if (checkJitInfo == DebuggerObject::CheckJitInfo::No) { + return true; + } + + // Both function should agree with the existence of JitInfo. + + if (a->hasJitInfo() != b->hasJitInfo()) { + return false; + } + + if (!a->hasJitInfo()) { + return true; + } + + if (a->jitInfo() == b->jitInfo()) { + return true; + } + + return false; +} + /* static */ bool DebuggerObject::isSameNative(JSContext* cx, Handle<DebuggerObject*> object, - HandleValue value, + HandleValue value, CheckJitInfo checkJitInfo, MutableHandleValue result) { RootedValue referentValue(cx, ObjectValue(*object->referent())); @@ -2602,7 +2647,8 @@ bool DebuggerObject::isSameNative(JSContext* cx, Handle<DebuggerObject*> object, RootedFunction referentFun(cx, EnsureNativeFunction(referentValue)); - result.setBoolean(referentFun && referentFun->native() == fun->native()); + result.setBoolean(referentFun && + IsSameNative(referentFun, fun, checkJitInfo)); return true; } diff --git a/js/src/debugger/Object.h b/js/src/debugger/Object.h index 5141bd3133..15d2800e76 100644 --- a/js/src/debugger/Object.h +++ b/js/src/debugger/Object.h @@ -145,9 +145,11 @@ class DebuggerObject : public NativeObject { Handle<DebuggerObject*> object, HandleValue value, MutableHandleValue result); + enum class CheckJitInfo { No, Yes }; [[nodiscard]] static bool isSameNative(JSContext* cx, Handle<DebuggerObject*> object, HandleValue value, + CheckJitInfo checkJitInfo, MutableHandleValue result); [[nodiscard]] static bool isNativeGetterWithJitInfo( JSContext* cx, Handle<DebuggerObject*> object, MutableHandleValue result); diff --git a/js/src/devtools/automation/autospider.py b/js/src/devtools/automation/autospider.py index a49d2fd505..5e427fd3a5 100755 --- a/js/src/devtools/automation/autospider.py +++ b/js/src/devtools/automation/autospider.py @@ -217,9 +217,40 @@ def ensure_dir_exists( with open(os.path.join(DIR.scripts, "variants", args.variant)) as fh: variant = json.load(fh) +# Some of the variants request a particular word size (eg ARM simulators). +word_bits = variant.get("bits") + +# On Linux and Windows, we build 32- and 64-bit versions on a 64 bit +# host, so the caller has to specify what is desired. +if word_bits is None and args.platform: + platform_arch = args.platform.split("-")[0] + if platform_arch in ("win32", "linux"): + word_bits = 32 + elif platform_arch in ("win64", "linux64"): + word_bits = 64 + +# Fall back to the word size of the host. +if word_bits is None: + word_bits = 64 if platform.architecture()[0] == "64bit" else 32 + +# Need a platform name to use as a key in variant files. +if args.platform: + variant_platform = args.platform.split("-")[0] +elif platform.system() == "Windows": + variant_platform = "win64" if word_bits == 64 else "win32" +elif platform.system() == "Linux": + variant_platform = "linux64" if word_bits == 64 else "linux" +elif platform.system() == "Darwin": + variant_platform = "macosx64" +else: + variant_platform = "other" + CONFIGURE_ARGS = variant["configure-args"] -compiler = variant.get("compiler") +if variant_platform in ("win32", "win64"): + compiler = "clang-cl" +else: + compiler = variant.get("compiler") if compiler != "gcc" and "clang-plugin" not in CONFIGURE_ARGS: CONFIGURE_ARGS += " --enable-clang-plugin" @@ -254,34 +285,6 @@ opt = variant.get("nspr") if opt is None or opt: CONFIGURE_ARGS += " --enable-nspr-build" -# Some of the variants request a particular word size (eg ARM simulators). -word_bits = variant.get("bits") - -# On Linux and Windows, we build 32- and 64-bit versions on a 64 bit -# host, so the caller has to specify what is desired. -if word_bits is None and args.platform: - platform_arch = args.platform.split("-")[0] - if platform_arch in ("win32", "linux"): - word_bits = 32 - elif platform_arch in ("win64", "linux64"): - word_bits = 64 - -# Fall back to the word size of the host. -if word_bits is None: - word_bits = 64 if platform.architecture()[0] == "64bit" else 32 - -# Need a platform name to use as a key in variant files. -if args.platform: - variant_platform = args.platform.split("-")[0] -elif platform.system() == "Windows": - variant_platform = "win64" if word_bits == 64 else "win32" -elif platform.system() == "Linux": - variant_platform = "linux64" if word_bits == 64 else "linux" -elif platform.system() == "Darwin": - variant_platform = "macosx64" -else: - variant_platform = "other" - env["LD_LIBRARY_PATH"] = ":".join( d for d in [ @@ -437,7 +440,7 @@ CONFIGURE_ARGS += " --prefix={OBJDIR}/dist".format(OBJDIR=quote(OBJDIR)) # Generate a mozconfig. with open(mozconfig, "wt") as fh: if AUTOMATION and platform.system() == "Windows": - fh.write('. "$topsrcdir/build/%s/mozconfig.vs-latest"\n' % variant_platform) + fh.write('. "$topsrcdir/build/mozconfig.clang-cl"\n') fh.write("ac_add_options --enable-project=js\n") fh.write("ac_add_options " + CONFIGURE_ARGS + "\n") fh.write("mk_add_options MOZ_OBJDIR=" + quote(OBJDIR) + "\n") @@ -665,7 +668,7 @@ if use_minidump: [ mach, "python", - "virtualenv=build", + "--virtualenv=build", os.path.join(DIR.source, "testing/mozbase/mozcrash/mozcrash/mozcrash.py"), os.getenv("TMPDIR", "/tmp"), os.path.join(OBJDIR, "dist/crashreporter-symbols"), diff --git a/js/src/devtools/automation/variants/pbl-debug b/js/src/devtools/automation/variants/pbl-debug new file mode 100644 index 0000000000..984589a4f0 --- /dev/null +++ b/js/src/devtools/automation/variants/pbl-debug @@ -0,0 +1,7 @@ +{ + "configure-args": "--enable-portable-baseline-interp --enable-portable-baseline-interp-force", + "debug": true, + "env": { + "JSTESTS_EXTRA_ARGS": "--jitflags=debug" + } +} diff --git a/js/src/devtools/gc-ubench/harness.js b/js/src/devtools/gc-ubench/harness.js index db7fa06d63..124baa17a5 100644 --- a/js/src/devtools/gc-ubench/harness.js +++ b/js/src/devtools/gc-ubench/harness.js @@ -254,14 +254,19 @@ var AllocationLoadManager = class { var gLoadMgr = undefined; function format_with_units(n, label, shortlabel, kbase) { + function format(n, prefix, unit) { + let s = Number.isInteger(n) ? n.toString() : n.toFixed(2); + return `${s}${prefix}${unit}`; + } + if (n < kbase * 4) { return `${n} ${label}`; } else if (n < kbase ** 2 * 4) { - return `${(n / kbase).toFixed(2)}K${shortlabel}`; + return format(n / kbase, 'K', shortlabel); } else if (n < kbase ** 3 * 4) { - return `${(n / kbase ** 2).toFixed(2)}M${shortlabel}`; + return format(n / kbase ** 2, 'M', shortlabel); } - return `${(n / kbase ** 3).toFixed(2)}G${shortlabel}`; + return format(n / kbase ** 3, 'G', shortlabel); } function format_bytes(bytes) { diff --git a/js/src/devtools/gc-ubench/index.html b/js/src/devtools/gc-ubench/index.html index 4abce385f2..4efbb75def 100644 --- a/js/src/devtools/gc-ubench/index.html +++ b/js/src/devtools/gc-ubench/index.html @@ -15,61 +15,59 @@ <!-- List of garbage-creating test loads --> <script src="test_list.js"></script> - - <!-- Collect all test loads into a `tests` Map --> - <script> - var tests = new Map(); - foreach_test_file(path => import("./" + path)); - </script> - </head> <body onload="onload()" onunload="onunload()"> -<canvas id="graph" width="1080" height="400" style="padding-left:10px"></canvas> -<canvas id="memgraph" width="1080" height="400" style="padding-left:10px"></canvas> +<canvas id="graph" width="980" height="320" style="padding-left:10px"></canvas> +<canvas id="memgraph" width="980" height="320" style="padding-left:10px"></canvas> <div id="memgraph-disabled" style="display: none"><i>No performance.mozMemory object available. If running Firefox, set dom.enable_memory_stats to True to see heap size info.</i></div> <hr> -<div id='track-sizes-div'> - Show heap size graph: <input id='track-sizes' type='checkbox' onclick="trackHeapSizes(this.checked)"> -</div> +<form> + <div id='track-sizes-div'> + <input id='track-sizes' type='checkbox' onclick="trackHeapSizes(this.checked)"> + <label for='track-sizes'>Show heap size graph</label> + </div> -<div> - Update display: - <input type="checkbox" id="do-graph" onchange="onUpdateDisplayChanged()" checked></input> -</div> + <div> + <input type="checkbox" id="do-graph" onchange="onUpdateDisplayChanged()" checked></input> + <label for='do-graph'>Update display</label> + </div> -<div> - Run allocation load - <input type="checkbox" id="do-load" onchange="onDoLoadChange()" checked></input> -</div> + <div> + <input type="checkbox" id="do-load" onchange="onDoLoadChange()" checked></input> + <label for='do-load'>Run allocation load</label> + </div> -<div> - Allocation load: + <div> + <label for='test-selection'>Allocation load:</label> <select id="test-selection" required onchange="onLoadChange()"></select> <span id="load-running">(init)</span> -</div> + </div> -<div> - Garbage items per frame: - <input type="text" id="garbage-per-frame" size="5" value="8K" + <div> + <label for='garbage-per-frame'> Garbage items per frame:</label> + <input type="text" id="garbage-per-frame" size="8" value="8K" onchange="garbage_per_frame_changed()"></input> -</div> -<div> - Garbage piles: - <input type="text" id="garbage-piles" size="5" value="8" + </div> + + <div> + <label for='garbage-piles'> Garbage piles:</label> + <input type="text" id="garbage-piles" size="8" value="8" onchange="garbage_piles_changed()"></input> -</div> + </div> +</form> <hr> -<div> - Duration: <input type="text" id="test-duration" size="3" value="8" onchange="duration_changed()"></input>s - <input type="button" id="test-one" value="Run Test" onclick="run_one_test()"></input> - <input type="button" id="test-all" value="Run All Tests" onclick="run_all_tests()"></input> -</div> +<form> + <label for='test-duration'>Duration:</label> + <input type="text" id="test-duration" size="3" value="8" onchange="duration_changed()"></input>s + <input type="button" id="test-one" value="Run Test" onclick="run_one_test()"></input> + <input type="button" id="test-all" value="Run All Tests" onclick="run_all_tests()"></input> +</form> <div> Time remaining: <span id="test-progress">(not running)</span> diff --git a/js/src/devtools/gc-ubench/ui.js b/js/src/devtools/gc-ubench/ui.js index 4905f97904..4ff84481c9 100644 --- a/js/src/devtools/gc-ubench/ui.js +++ b/js/src/devtools/gc-ubench/ui.js @@ -10,6 +10,8 @@ var stroke = { var numSamples = 500; +var tests = new Map(); + var gHistogram = new Map(); // {ms: count} var gHistory = new FrameHistory(numSamples); var gPerf = new PerfTracker(); @@ -51,9 +53,15 @@ var Firefox = class extends Host { get gcBytes() { return gMemory.zone.gcBytes; } + get mallocBytes() { + return gMemory.zone.mallocBytes; + } get gcAllocTrigger() { return gMemory.zone.gcAllocTrigger; } + get mallocTrigger() { + return gMemory.zone.mallocTriggerBytes; + } features = { haveMemorySizes: 'gcBytes' in gMemory, @@ -85,22 +93,32 @@ function parse_units(v) { } var Graph = class { - constructor(ctx) { - this.ctx = ctx; + constructor(canvas) { + this.ctx = canvas.getContext('2d'); + + // Adjust scale for high-DPI displays. + this.scale = window.devicePixelRatio || 1; + let rect = canvas.getBoundingClientRect(); + canvas.width = Math.floor(rect.width * this.scale); + canvas.height = Math.floor(rect.height * this.scale); + canvas.style.width = rect.width; + canvas.style.height = rect.height; + + // Record canvas size to draw into. + this.width = canvas.width; + this.height = canvas.height; - var { height } = ctx.canvas; this.layout = { - xAxisLabel_Y: height - 20, + xAxisLabel_Y: this.height - 20 * this.scale, }; } xpos(index) { - return index * 2; + return (index / numSamples) * (this.width - 100 * this.scale); } clear() { - const { width, height } = this.ctx.canvas; - this.ctx.clearRect(0, 0, width, height); + this.ctx.clearRect(0, 0, this.width, this.height); } drawScale(delay) { @@ -117,20 +135,22 @@ var Graph = class { drawAxisLabels(x_label, y_label) { const ctx = this.ctx; - const { width, height } = ctx.canvas; - ctx.fillText(x_label, width / 2, this.layout.xAxisLabel_Y); + ctx.font = `${10 * this.scale}px sans-serif`; + + ctx.fillText(x_label, this.width / 2, this.layout.xAxisLabel_Y); ctx.save(); ctx.rotate(Math.PI / 2); - var start = height / 2 - ctx.measureText(y_label).width / 2; - ctx.fillText(y_label, start, -width + 20); + var start = this.height / 2 - ctx.measureText(y_label).width / 2; + ctx.fillText(y_label, start, -this.width + 20 * this.scale); ctx.restore(); } drawFrame() { const ctx = this.ctx; - const { width, height } = ctx.canvas; + const width = this.width; + const height = this.height; // Draw frame to show size ctx.strokeStyle = "rgb(0,0,0)"; @@ -148,22 +168,17 @@ var Graph = class { var LatencyGraph = class extends Graph { constructor(ctx) { super(ctx); - console.log(this.ctx); } ypos(delay) { - const { height } = this.ctx.canvas; - - const r = height + 100 - Math.log(delay) * 64; - if (r < 5) { - return 5; - } - return r; + return this.height + this.scale * (100 - Math.log(delay) * 64); } drawHBar(delay, label, color = "rgb(0,0,0)", label_offset = 0) { const ctx = this.ctx; + let y = this.ypos(delay); + ctx.fillStyle = color; ctx.strokeStyle = color; ctx.fillText( @@ -277,20 +292,19 @@ var LatencyGraph = class extends Graph { var MemoryGraph = class extends Graph { constructor(ctx) { super(ctx); - this.worstEver = this.bestEver = gHost.gcBytes; - this.limit = Math.max(this.worstEver, gHost.gcAllocTrigger); + this.range = 1; } ypos(size) { - const { height } = this.ctx.canvas; - - const range = this.limit - this.bestEver; - const percent = (size - this.bestEver) / range; + const percent = size / this.range; + return (1 - percent) * this.height * 0.9 + this.scale * 20; + } - return (1 - percent) * height * 0.9 + 20; + drawHBarForBytes(size, name, color) { + this.drawHBar(size, `${format_bytes(size)} ${name}`, color) } - drawHBar(size, label, color = "rgb(150,150,150)") { + drawHBar(size, label, color) { const ctx = this.ctx; const y = this.ypos(size); @@ -313,50 +327,48 @@ var MemoryGraph = class extends Graph { this.clear(); this.drawFrame(); - var worst = 0, - worstpos = 0; + let gcMaxPos = 0; + let mallocMaxPos = 0; + let gcMax = 0; + let mallocMax = 0; for (let i = 0; i < numSamples; i++) { - if (gHistory.gcBytes[i] >= worst) { - worst = gHistory.gcBytes[i]; - worstpos = i; + if (gHistory.gcBytes[i] >= gcMax) { + gcMax = gHistory.gcBytes[i]; + gcMaxPos = i; } - if (gHistory.gcBytes[i] < this.bestEver) { - this.bestEver = gHistory.gcBytes[i]; + if (gHistory.mallocBytes[i] >= mallocMax) { + mallocMax = gHistory.mallocBytes[i]; + mallocMaxPos = i; } } - if (this.worstEver < worst) { - this.worstEver = worst; - this.limit = Math.max(this.worstEver, gHost.gcAllocTrigger); - } + this.range = Math.max(gcMax, mallocMax, gHost.gcAllocTrigger, gHost.mallocTrigger); - this.drawHBar( - this.bestEver, - `${format_bytes(this.bestEver)} min`, - "#00cf61" - ); - this.drawHBar( - this.worstEver, - `${format_bytes(this.worstEver)} max`, - "#cc1111" - ); - this.drawHBar( - gHost.gcAllocTrigger, - `${format_bytes(gHost.gcAllocTrigger)} trigger`, - "#cc11cc" - ); + this.drawHBarForBytes(gcMax, "GC max", "#00cf61"); + this.drawHBarForBytes(mallocMax, "Malloc max", "#cc1111"); + this.drawHBarForBytes(gHost.gcAllocTrigger, "GC trigger", "#cc11cc"); + this.drawHBarForBytes(gHost.mallocTrigger, "Malloc trigger", "#cc11cc"); ctx.fillStyle = "rgb(255,0,0)"; - if (worst) { + + if (gcMax !== 0) { ctx.fillText( - format_bytes(worst), - this.xpos(worstpos) - 10, - this.ypos(worst) - 14 + format_bytes(gcMax), + this.xpos(gcMaxPos) - 10, + this.ypos(gcMax) - 14 + ); + } + if (mallocMax !== 0) { + ctx.fillText( + format_bytes(mallocMax), + this.xpos(mallocMaxPos) - 10, + this.ypos(mallocMax) - 14 ); } + const where = sampleIndex % numSamples; + ctx.beginPath(); - var where = sampleIndex % numSamples; ctx.arc( this.xpos(where), this.ypos(gHistory.gcBytes[where]), @@ -366,13 +378,40 @@ var MemoryGraph = class extends Graph { true ); ctx.fill(); + ctx.beginPath(); + ctx.arc( + this.xpos(where), + this.ypos(gHistory.mallocBytes[where]), + 5, + 0, + Math.PI * 2, + true + ); + ctx.fill(); + + ctx.beginPath(); + for (let i = 0; i < numSamples; i++) { + let x = this.xpos(i); + let y = this.ypos(gHistory.gcBytes[i]); + if (i == (sampleIndex + 1) % numSamples) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + if (i == where) { + ctx.stroke(); + } + } + ctx.stroke(); ctx.beginPath(); for (let i = 0; i < numSamples; i++) { + let x = this.xpos(i); + let y = this.ypos(gHistory.mallocBytes[i]); if (i == (sampleIndex + 1) % numSamples) { - ctx.moveTo(this.xpos(i), this.ypos(gHistory.gcBytes[i])); + ctx.moveTo(x, y); } else { - ctx.lineTo(this.xpos(i), this.ypos(gHistory.gcBytes[i])); + ctx.lineTo(x, y); } if (i == where) { ctx.stroke(); @@ -380,6 +419,8 @@ var MemoryGraph = class extends Graph { } ctx.stroke(); + ctx.fillStyle = "rgb(0,0,0)"; + this.drawAxisLabels("Time", "Heap Memory Usage"); } }; @@ -466,10 +507,17 @@ function reset_draw_state() { } function onunload() { - gLoadMgr.deactivateLoad(); + if (gLoadMgr) { + gLoadMgr.deactivateLoad(); + } } -function onload() { +async function onload() { + // Collect all test loads into the `tests` Map. + let imports = []; + foreach_test_file(path => imports.push(import("./" + path))); + await Promise.all(imports); + // The order of `tests` is currently based on their asynchronous load // order, rather than the listed order. Rearrange by extracting the test // names from their filenames, which is kind of gross. @@ -517,7 +565,7 @@ function onload() { // Acquire our canvas. var canvas = document.getElementById("graph"); - latencyGraph = new LatencyGraph(canvas.getContext("2d")); + latencyGraph = new LatencyGraph(canvas); if (!gHost.features.haveMemorySizes) { document.getElementById("memgraph-disabled").style.display = "block"; @@ -676,7 +724,7 @@ function garbage_per_frame_changed() { return; } if (gLoadMgr.load_running()) { - gLoadMgr.change_garbagePerFrame = value; + gLoadMgr.change_garbagePerFrame(value); console.log( `Updated garbage-per-frame to ${ gLoadMgr.activeLoad().garbagePerFrame @@ -692,7 +740,7 @@ function trackHeapSizes(track) { if (enabled.trackingSizes) { canvas.style.display = "block"; - memoryGraph = new MemoryGraph(canvas.getContext("2d")); + memoryGraph = new MemoryGraph(canvas); } else { canvas.style.display = "none"; memoryGraph = null; diff --git a/js/src/doc/Debugger/Debugger.Memory.md b/js/src/doc/Debugger/Debugger.Memory.md index c20354ef3c..5e8c594433 100644 --- a/js/src/doc/Debugger/Debugger.Memory.md +++ b/js/src/doc/Debugger/Debugger.Memory.md @@ -305,7 +305,7 @@ which produces a result like this: In general, a `breakdown` value has one of the following forms: -* <code>{ by: "count", count:<i>count<i>, bytes:<i>bytes</i> }</code> +* <code>{ by: "count", count:<i>count</i>, bytes:<i>bytes</i> }</code> The trivial categorization: none whatsoever. Simply tally up the items visited. If <i>count</i> is true, count the number of items visited; if @@ -409,6 +409,16 @@ In general, a `breakdown` value has one of the following forms: breakdown value produces. All breakdown values are optional, and default to `{ type: "count" }`. +* `{ by: "filename", then:breakdown, noFilename:noFilenameBreakdown }` + + For scripts only, group by the filename of the script. + + Further categorize all of the scripts from each distinct filename + using breakdown. + + Scripts that lack a filename are counted using noFilenameBreakdown. + These appear in the result `Map` under the key string `"noFilename"`. + * `{ by: "internalType", then: breakdown }` Group items by the names given their types internally by SpiderMonkey. @@ -441,6 +451,9 @@ In general, a `breakdown` value has one of the following forms: To simplify breakdown values, all `then` and `other` properties are optional. If omitted, they are treated as if they were `{ type: "count" }`. +Breakdown groupings cannot be nested within themselves. This would not be +useful, and forbidding this prevents infinite recursion. + If the `options` argument has no `breakdown` property, `takeCensus` defaults to the following: diff --git a/js/src/doc/Debugger/Debugger.Object.md b/js/src/doc/Debugger/Debugger.Object.md index c6f8b90a49..d81bffdaec 100644 --- a/js/src/doc/Debugger/Debugger.Object.md +++ b/js/src/doc/Debugger/Debugger.Object.md @@ -469,6 +469,15 @@ by code in <i>d</i>'s compartment. If <i>value</i> is a native function in the debugger's compartment, return whether the referent is a native function for the same C++ native. +### `isSameNativeWithJitInfo(value)` +If <i>value</i> is a native function in the debugger's compartment, return +whether the referent is a native function for the same C++ native with the +same JSJitInfo pointer value. + +This can be used to distinguish functions with shared native function +implementation with different JSJitInfo pointer to define the underlying +functionality. + ### `isNativeGetterWithJitInfo()` Return whether the referent is a native getter function with JSJitInfo. diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index df44743768..2759fa5924 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -925,6 +925,7 @@ restart: // Watch out for getters! case ParseNodeKind::OptionalDotExpr: case ParseNodeKind::DotExpr: + case ParseNodeKind::ArgumentsLength: MOZ_ASSERT(pn->is<BinaryNode>()); *answer = true; return true; @@ -2601,6 +2602,7 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, *emitted = 0; break; + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &target->as<PropertyAccess>(); bool isSuper = prop->isSuper(); @@ -2760,6 +2762,7 @@ bool BytecodeEmitter::emitSetOrInitializeDestructuring( break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { // The reference is already pushed by emitDestructuringLHSRef. // [stack] # if Super @@ -4367,6 +4370,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, : NameOpEmitter::Kind::SimpleAssignment); break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); bool isSuper = prop->isSuper(); @@ -4466,6 +4470,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, if (isCompound) { MOZ_ASSERT(rhs); switch (lhs->getKind()) { + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); if (!poe->emitGet(prop->key().atom())) { @@ -4512,6 +4517,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, } offset += noe->emittedBindOp(); break; + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: if (!poe->prepareForRhs()) { // [stack] # if Simple Assignment with Super @@ -4579,6 +4585,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, } break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); if (!poe->emitAssignment(prop->key().atom())) { @@ -4666,7 +4673,7 @@ bool BytecodeEmitter::emitShortCircuitAssignment(AssignmentNode* node) { numPushed = noe->emittedBindOp(); break; } - + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); bool isSuper = prop->isSuper(); @@ -4802,7 +4809,7 @@ bool BytecodeEmitter::emitShortCircuitAssignment(AssignmentNode* node) { } break; } - + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); @@ -7326,6 +7333,7 @@ bool BytecodeEmitter::emitDeleteOptionalChain(UnaryNode* deleteNode) { break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: case ParseNodeKind::OptionalDotExpr: { auto* propExpr = &kid->as<PropertyAccessBase>(); @@ -7978,6 +7986,7 @@ bool BytecodeEmitter::emitOptionalCalleeAndThis(ParseNode* callee, } break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting); PropertyAccess* prop = &callee->as<PropertyAccess>(); @@ -8076,6 +8085,7 @@ bool BytecodeEmitter::emitCalleeAndThis(ParseNode* callee, CallNode* maybeCall, } break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting); PropertyAccess* prop = &callee->as<PropertyAccess>(); @@ -8197,6 +8207,7 @@ ParseNode* BytecodeEmitter::getCoordNode(ParseNode* callNode, coordNode = argsList; switch (calleeNode->getKind()) { + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: // Use the position of a property access identifier. // @@ -8658,6 +8669,7 @@ bool BytecodeEmitter::emitOptionalTree( } break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &pn->as<PropertyAccess>(); bool isSuper = prop->isSuper(); @@ -9015,6 +9027,7 @@ bool BytecodeEmitter::emitSequenceExpr(ListNode* node, ValueUsage valueUsage) { MOZ_NEVER_INLINE bool BytecodeEmitter::emitIncOrDec(UnaryNode* incDec, ValueUsage valueUsage) { switch (incDec->kid()->getKind()) { + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: return emitPropIncDec(incDec, valueUsage); case ParseNodeKind::ElemExpr: @@ -9859,6 +9872,12 @@ static bool NeedsPrivateBrand(ParseNode* member) { !member->as<ClassMethod>().isStatic(); } +#ifdef ENABLE_DECORATORS +static bool HasDecorators(ParseNode* member) { + return member->is<ClassMethod>() && member->as<ClassMethod>().decorators(); +} +#endif + mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( ListNode* classMembers, FieldPlacement placement) { bool isStatic = placement == FieldPlacement::Static; @@ -9866,6 +9885,9 @@ mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( size_t numFields = 0; size_t numPrivateInitializers = 0; bool hasPrivateBrand = false; +#ifdef ENABLE_DECORATORS + bool hasDecorators = false; +#endif for (ParseNode* member : classMembers->contents()) { if (NeedsFieldInitializer(member, isStatic)) { numFields++; @@ -9875,6 +9897,11 @@ mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( } else if (NeedsPrivateBrand(member)) { hasPrivateBrand = true; } +#ifdef ENABLE_DECORATORS + if (!hasDecorators && HasDecorators(member)) { + hasDecorators = true; + } +#endif } // If there are more initializers than can be represented, return invalid. @@ -9882,8 +9909,11 @@ mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( MemberInitializers::MaxInitializers) { return Nothing(); } - return Some( - MemberInitializers(hasPrivateBrand, numFields + numPrivateInitializers)); + return Some(MemberInitializers(hasPrivateBrand, +#ifdef ENABLE_DECORATORS + hasDecorators, +#endif + numFields + numPrivateInitializers)); } // Purpose of .fieldKeys: @@ -10691,119 +10721,122 @@ bool BytecodeEmitter::emitInitializeInstanceMembers( } } #ifdef ENABLE_DECORATORS - // Decorators Proposal - // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-initializeinstanceelements - // 4. For each element e of elements, do - // 4.a. If elementRecord.[[Kind]] is field or accessor, then - // 4.a.i. Perform ? InitializeFieldOrAccessor(O, elementRecord). - // + if (memberInitializers.hasDecorators) { + // Decorators Proposal + // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-initializeinstanceelements + // 4. For each element e of elements, do + // 4.a. If elementRecord.[[Kind]] is field or accessor, then + // 4.a.i. Perform ? InitializeFieldOrAccessor(O, elementRecord). + // - // TODO: (See Bug 1817993) At the moment, we're applying the initialization - // logic in two steps. The pre-decorator initialization code runs, stores - // the initial value, and then we retrieve it here and apply the - // initializers added by decorators. We should unify these two steps. - if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) { - // [stack] ARRAY - return false; - } + // TODO: (See Bug 1817993) At the moment, we're applying the + // initialization logic in two steps. The pre-decorator initialization + // code runs, stores the initial value, and then we retrieve it here and + // apply the initializers added by decorators. We should unify these two + // steps. + if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) { + // [stack] ARRAY + return false; + } - if (!emit1(JSOp::Dup)) { - // [stack] ARRAY ARRAY - return false; - } + if (!emit1(JSOp::Dup)) { + // [stack] ARRAY ARRAY + return false; + } - if (!emitAtomOp(JSOp::GetProp, - TaggedParserAtomIndex::WellKnown::length())) { - // [stack] ARRAY LENGTH - return false; - } + if (!emitAtomOp(JSOp::GetProp, + TaggedParserAtomIndex::WellKnown::length())) { + // [stack] ARRAY LENGTH + return false; + } - if (!emitNumberOp(static_cast<double>(numInitializers))) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!emitNumberOp(static_cast<double>(numInitializers))) { + // [stack] ARRAY LENGTH INDEX + return false; + } - InternalWhileEmitter wh(this); - // At this point, we have no context to determine offsets in the - // code for this while statement. Ideally, it would correspond to - // the field we're initializing. - if (!wh.emitCond()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + InternalWhileEmitter wh(this); + // At this point, we have no context to determine offsets in the + // code for this while statement. Ideally, it would correspond to + // the field we're initializing. + if (!wh.emitCond()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emit1(JSOp::Dup)) { - // [stack] ARRAY LENGTH INDEX INDEX - return false; - } + if (!emit1(JSOp::Dup)) { + // [stack] ARRAY LENGTH INDEX INDEX + return false; + } - if (!emitDupAt(2)) { - // [stack] ARRAY LENGTH INDEX INDEX LENGTH - return false; - } + if (!emitDupAt(2)) { + // [stack] ARRAY LENGTH INDEX INDEX LENGTH + return false; + } - if (!emit1(JSOp::Lt)) { - // [stack] ARRAY LENGTH INDEX BOOL - return false; - } + if (!emit1(JSOp::Lt)) { + // [stack] ARRAY LENGTH INDEX BOOL + return false; + } - if (!wh.emitBody()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!wh.emitBody()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emitDupAt(2)) { - // [stack] ARRAY LENGTH INDEX ARRAY - return false; - } + if (!emitDupAt(2)) { + // [stack] ARRAY LENGTH INDEX ARRAY + return false; + } - if (!emitDupAt(1)) { - // [stack] ARRAY LENGTH INDEX ARRAY INDEX - return false; - } + if (!emitDupAt(1)) { + // [stack] ARRAY LENGTH INDEX ARRAY INDEX + return false; + } - // Retrieve initializers for this field - if (!emit1(JSOp::GetElem)) { - // [stack] ARRAY LENGTH INDEX INITIALIZERS - return false; - } + // Retrieve initializers for this field + if (!emit1(JSOp::GetElem)) { + // [stack] ARRAY LENGTH INDEX INITIALIZERS + return false; + } - // This is guaranteed to run after super(), so we don't need TDZ checks. - if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) { - // [stack] ARRAY LENGTH INDEX INITIALIZERS THIS - return false; - } + // This is guaranteed to run after super(), so we don't need TDZ checks. + if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) { + // [stack] ARRAY LENGTH INDEX INITIALIZERS THIS + return false; + } - if (!emit1(JSOp::Swap)) { - // [stack] ARRAY LENGTH INDEX THIS INITIALIZERS - return false; - } + if (!emit1(JSOp::Swap)) { + // [stack] ARRAY LENGTH INDEX THIS INITIALIZERS + return false; + } - DecoratorEmitter de(this); - if (!de.emitInitializeFieldOrAccessor()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + DecoratorEmitter de(this); + if (!de.emitInitializeFieldOrAccessor()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emit1(JSOp::Inc)) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!emit1(JSOp::Inc)) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!wh.emitEnd()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!wh.emitEnd()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emitPopN(3)) { - // [stack] - return false; - } - // 5. Return unused. + if (!emitPopN(3)) { + // [stack] + return false; + } + // 5. Return unused. - if (!de.emitCallExtraInitializers(TaggedParserAtomIndex::WellKnown:: - dot_instanceExtraInitializers_())) { - return false; + if (!de.emitCallExtraInitializers(TaggedParserAtomIndex::WellKnown:: + dot_instanceExtraInitializers_())) { + return false; + } } #endif } @@ -12502,6 +12535,32 @@ bool BytecodeEmitter::emitTree( break; } + case ParseNodeKind::ArgumentsLength: { + if (sc->isFunctionBox() && + sc->asFunctionBox()->isEligibleForArgumentsLength() && + !sc->asFunctionBox()->needsArgsObj()) { + if (!emit1(JSOp::ArgumentsLength)) { + return false; + } + } else { + PropOpEmitter poe(this, PropOpEmitter::Kind::Get, + PropOpEmitter::ObjKind::Other); + if (!poe.prepareForObj()) { + return false; + } + + NameOpEmitter noe(this, TaggedParserAtomIndex::WellKnown::arguments(), + NameOpEmitter::Kind::Get); + if (!noe.emitGet()) { + return false; + } + if (!poe.emitGet(TaggedParserAtomIndex::WellKnown::length())) { + return false; + } + } + break; + } + case ParseNodeKind::ElemExpr: { PropertyByValue* elem = &pn->as<PropertyByValue>(); bool isSuper = elem->isSuper(); diff --git a/js/src/frontend/CompileScript.cpp b/js/src/frontend/CompileScript.cpp index 925b8201a2..b561d7d124 100644 --- a/js/src/frontend/CompileScript.cpp +++ b/js/src/frontend/CompileScript.cpp @@ -87,73 +87,46 @@ JS_PUBLIC_API const JSErrorReport* JS::GetFrontendWarningAt( return &fc->warnings()[index]; } -JS::CompilationStorage::~CompilationStorage() { - if (input_ && !isBorrowed_) { - js_delete(input_); - input_ = nullptr; - } -} - -size_t JS::CompilationStorage::sizeOfIncludingThis( - mozilla::MallocSizeOf mallocSizeOf) const { - size_t sizeOfCompilationInput = - input_ ? input_->sizeOfExcludingThis(mallocSizeOf) : 0; - return mallocSizeOf(this) + sizeOfCompilationInput; -} - -bool JS::CompilationStorage::allocateInput( - FrontendContext* fc, const JS::ReadOnlyCompileOptions& options) { - MOZ_ASSERT(!input_); - input_ = fc->getAllocator()->new_<frontend::CompilationInput>(options); - return !!input_; -} - -void JS::CompilationStorage::trace(JSTracer* trc) { - if (input_) { - input_->trace(trc); - } -} - template <typename CharT> static already_AddRefed<JS::Stencil> CompileGlobalScriptToStencilImpl( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, - JS::SourceText<CharT>& srcBuf, JS::CompilationStorage& compilationStorage) { + JS::SourceText<CharT>& srcBuf) { ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global; JS::SourceText<CharT> data(std::move(srcBuf)); - compilationStorage.allocateInput(fc, options); - if (!compilationStorage.hasInput()) { - return nullptr; - } + frontend::CompilationInput compilationInput(options); frontend::NoScopeBindingCache scopeCache; LifoAlloc tempLifoAlloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); - RefPtr<frontend::CompilationStencil> stencil_ = - frontend::CompileGlobalScriptToStencil(nullptr, fc, tempLifoAlloc, - compilationStorage.getInput(), - &scopeCache, data, scopeKind); + RefPtr<JS::Stencil> stencil_ = frontend::CompileGlobalScriptToStencil( + nullptr, fc, tempLifoAlloc, compilationInput, &scopeCache, data, + scopeKind); + // CompilationInput initialized with CompileGlobalScriptToStencil only + // references information from the JS::Stencil context and the + // ref-counted ScriptSource, which are both GC-free. + JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); return stencil_.forget(); } template <typename CharT> static already_AddRefed<JS::Stencil> CompileModuleScriptToStencilImpl( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& optionsInput, - JS::SourceText<CharT>& srcBuf, JS::CompilationStorage& compilationStorage) { + JS::SourceText<CharT>& srcBuf) { JS::CompileOptions options(nullptr, optionsInput); options.setModule(); - compilationStorage.allocateInput(fc, options); - if (!compilationStorage.hasInput()) { - return nullptr; - } + frontend::CompilationInput compilationInput(options); NoScopeBindingCache scopeCache; js::LifoAlloc tempLifoAlloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); - RefPtr<JS::Stencil> stencil = - ParseModuleToStencil(nullptr, fc, tempLifoAlloc, - compilationStorage.getInput(), &scopeCache, srcBuf); + RefPtr<JS::Stencil> stencil = ParseModuleToStencil( + nullptr, fc, tempLifoAlloc, compilationInput, &scopeCache, srcBuf); + // CompilationInput initialized with ParseModuleToStencil only + // references information from the JS::Stencil context and the + // ref-counted ScriptSource, which are both GC-free. + JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); if (!stencil) { return nullptr; } @@ -164,42 +137,38 @@ static already_AddRefed<JS::Stencil> CompileModuleScriptToStencilImpl( already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, - JS::SourceText<mozilla::Utf8Unit>& srcBuf, - JS::CompilationStorage& compileStorage) { + JS::SourceText<mozilla::Utf8Unit>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileGlobalScriptToStencilImpl(fc, options, srcBuf, compileStorage); + return CompileGlobalScriptToStencilImpl(fc, options, srcBuf); } already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, - JS::SourceText<char16_t>& srcBuf, JS::CompilationStorage& compileStorage) { + JS::SourceText<char16_t>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileGlobalScriptToStencilImpl(fc, options, srcBuf, compileStorage); + return CompileGlobalScriptToStencilImpl(fc, options, srcBuf); } already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& optionsInput, - JS::SourceText<mozilla::Utf8Unit>& srcBuf, - JS::CompilationStorage& compileStorage) { + JS::SourceText<mozilla::Utf8Unit>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf, - compileStorage); + return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf); } already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& optionsInput, - JS::SourceText<char16_t>& srcBuf, JS::CompilationStorage& compileStorage) { + JS::SourceText<char16_t>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf, - compileStorage); + return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf); } bool JS::PrepareForInstantiate(JS::FrontendContext* fc, JS::Stencil& stencil, diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 8a434418b1..b72c6d4726 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -407,6 +407,7 @@ restart: case ParseNodeKind::ObjectExpr: case ParseNodeKind::PropertyNameExpr: case ParseNodeKind::DotExpr: + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::ElemExpr: case ParseNodeKind::Arguments: case ParseNodeKind::CallExpr: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 384d16b7d8..9209ba4d67 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -103,7 +103,8 @@ class FullParseHandler { bool isPropertyOrPrivateMemberAccess(Node node) { return node->isKind(ParseNodeKind::DotExpr) || node->isKind(ParseNodeKind::ElemExpr) || - node->isKind(ParseNodeKind::PrivateMemberExpr); + node->isKind(ParseNodeKind::PrivateMemberExpr) || + node->isKind(ParseNodeKind::ArgumentsLength); } bool isOptionalPropertyOrPrivateMemberAccess(Node node) { @@ -887,6 +888,11 @@ class FullParseHandler { key->pn_pos.end); } + ArgumentsLengthResult newArgumentsLength(Node expr, NameNodeType key) { + return newResult<ArgumentsLength>(expr, key, expr->pn_pos.begin, + key->pn_pos.end); + } + PropertyByValueResult newPropertyByValue(Node lhs, Node index, uint32_t end) { return newResult<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end); } @@ -1137,6 +1143,12 @@ class FullParseHandler { TaggedParserAtomIndex::WellKnown::arguments(); } + bool isLengthName(Node node) { + return node->isKind(ParseNodeKind::PropertyNameExpr) && + node->as<NameNode>().atom() == + TaggedParserAtomIndex::WellKnown::length(); + } + bool isEvalName(Node node) { return node->isKind(ParseNodeKind::Name) && node->as<NameNode>().atom() == @@ -1150,6 +1162,10 @@ class FullParseHandler { TaggedParserAtomIndex::WellKnown::async(); } + bool isArgumentsLength(Node node) { + return node->isKind(ParseNodeKind::ArgumentsLength); + } + bool isPrivateName(Node node) { return node->isKind(ParseNodeKind::PrivateName); } diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 0ad8e55758..46b5bb074c 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -92,6 +92,7 @@ class NameResolver : public ParseNodeVisitor<NameResolver> { */ bool nameExpression(ParseNode* n, bool* foundName) { switch (n->getKind()) { + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &n->as<PropertyAccess>(); if (!nameExpression(&prop->expression(), foundName)) { diff --git a/js/src/frontend/ParseContext.cpp b/js/src/frontend/ParseContext.cpp index ececac705b..622c467822 100644 --- a/js/src/frontend/ParseContext.cpp +++ b/js/src/frontend/ParseContext.cpp @@ -593,6 +593,14 @@ bool ParseContext::hasUsedName(const UsedNameTracker& usedNames, return false; } +bool ParseContext::hasClosedOverName(const UsedNameTracker& usedNames, + TaggedParserAtomIndex name) { + if (auto p = usedNames.lookup(name)) { + return p->value().isClosedOver(scriptId()); + } + return false; +} + bool ParseContext::hasUsedFunctionSpecialName(const UsedNameTracker& usedNames, TaggedParserAtomIndex name) { MOZ_ASSERT(name == TaggedParserAtomIndex::WellKnown::arguments() || @@ -602,6 +610,13 @@ bool ParseContext::hasUsedFunctionSpecialName(const UsedNameTracker& usedNames, functionBox()->bindingsAccessedDynamically(); } +bool ParseContext::hasClosedOverFunctionSpecialName( + const UsedNameTracker& usedNames, TaggedParserAtomIndex name) { + MOZ_ASSERT(name == TaggedParserAtomIndex::WellKnown::arguments()); + return hasClosedOverName(usedNames, name) || + functionBox()->bindingsAccessedDynamically(); +} + bool ParseContext::declareFunctionThis(const UsedNameTracker& usedNames, bool canSkipLazyClosedOverBindings) { // The asm.js validator does all its own symbol-table management so, as an @@ -644,17 +659,41 @@ bool ParseContext::declareFunctionArgumentsObject( ParseContext::Scope& funScope = functionScope(); ParseContext::Scope& _varScope = varScope(); - bool usesArguments = false; bool hasExtraBodyVarScope = &funScope != &_varScope; // Time to implement the odd semantics of 'arguments'. auto argumentsName = TaggedParserAtomIndex::WellKnown::arguments(); - bool tryDeclareArguments; + bool tryDeclareArguments = false; + bool needsArgsObject = false; + + // When delazifying simply defer to the function box. if (canSkipLazyClosedOverBindings) { tryDeclareArguments = funbox->shouldDeclareArguments(); + needsArgsObject = funbox->needsArgsObj(); } else { - tryDeclareArguments = hasUsedFunctionSpecialName(usedNames, argumentsName); + // We cannot compute these values when delazifying, hence why we need to + // rely on the function box flags instead. + bool bindingClosedOver = + hasClosedOverFunctionSpecialName(usedNames, argumentsName); + bool bindingUsedOnlyHere = + hasUsedFunctionSpecialName(usedNames, argumentsName) && + !bindingClosedOver; + + // Declare arguments if there's a closed-over consumer of the binding, or if + // there is a non-length use and we will reference the binding during + // bytecode emission. + tryDeclareArguments = + !funbox->isEligibleForArgumentsLength() || bindingClosedOver; + // If we have a use and the binding isn't closed over, then we will do + // bytecode emission with the arguments intrinsic. + if (bindingUsedOnlyHere && funbox->isEligibleForArgumentsLength()) { + // If we're using the intrinsic we should not be declaring the binding. + MOZ_ASSERT(!tryDeclareArguments); + funbox->setUsesArgumentsIntrinsics(); + } else if (tryDeclareArguments) { + needsArgsObject = true; + } } // ES 9.2.12 steps 19 and 20 say formal parameters, lexical bindings, @@ -670,9 +709,19 @@ bool ParseContext::declareFunctionArgumentsObject( DeclaredNamePtr p = _varScope.lookupDeclaredName(argumentsName); if (p && p->value()->kind() == DeclarationKind::Var) { if (hasExtraBodyVarScope) { + // While there is a binding in the var scope, we should declare + // the binding in the function scope. tryDeclareArguments = true; } else { - usesArguments = true; + // A binding in the function scope (since varScope and functionScope are + // the same) exists, so arguments is used. + if (needsArgsObject) { + funbox->setNeedsArgsObj(); + } + + // There is no point in continuing on below: We know we already have + // a declaration of arguments in the function scope. + return true; } } @@ -685,17 +734,11 @@ bool ParseContext::declareFunctionArgumentsObject( return false; } funbox->setShouldDeclareArguments(); - usesArguments = true; - } else if (hasExtraBodyVarScope) { - // Formal parameters shadow the arguments object. - return true; + if (needsArgsObject) { + funbox->setNeedsArgsObj(); + } } } - - if (usesArguments) { - funbox->setNeedsArgsObj(); - } - return true; } diff --git a/js/src/frontend/ParseContext.h b/js/src/frontend/ParseContext.h index 8124073bf9..796b776d85 100644 --- a/js/src/frontend/ParseContext.h +++ b/js/src/frontend/ParseContext.h @@ -661,8 +661,12 @@ class ParseContext : public Nestable<ParseContext> { bool hasUsedName(const UsedNameTracker& usedNames, TaggedParserAtomIndex name); + bool hasClosedOverName(const UsedNameTracker& usedNames, + TaggedParserAtomIndex name); bool hasUsedFunctionSpecialName(const UsedNameTracker& usedNames, TaggedParserAtomIndex name); + bool hasClosedOverFunctionSpecialName(const UsedNameTracker& usedNames, + TaggedParserAtomIndex name); bool declareFunctionThis(const UsedNameTracker& usedNames, bool canSkipLazyClosedOverBindings); @@ -673,6 +677,13 @@ class ParseContext : public Nestable<ParseContext> { bool declareDotGeneratorName(); bool declareTopLevelDotGeneratorName(); + // Used to determine if we have non-length uses of the arguments binding. + // This works by incrementing this counter each time we encounter the + // arguments name, and decrementing each time it is combined into + // arguments.length; as a result, if this is non-zero at the end of parsing, + // we have identified a non-length use of the arguments binding. + size_t numberOfArgumentsNames = 0; + private: [[nodiscard]] bool isVarRedeclaredInInnermostScope( TaggedParserAtomIndex name, ParserBase* parser, DeclarationKind kind, diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 61c009c6e4..a6747897d6 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -75,6 +75,7 @@ class FunctionBox; F(PostDecrementExpr, UnaryNode) \ F(PropertyNameExpr, NameNode) \ F(DotExpr, PropertyAccess) \ + F(ArgumentsLength, ArgumentsLength) \ F(ElemExpr, PropertyByValue) \ F(PrivateMemberExpr, PrivateMemberAccess) \ F(OptionalDotExpr, OptionalPropertyAccess) \ @@ -616,6 +617,7 @@ inline bool IsTypeofKind(ParseNodeKind kind) { MACRO(ClassNames) \ MACRO(ForNode) \ MACRO(PropertyAccess) \ + MACRO(ArgumentsLength) \ MACRO(OptionalPropertyAccess) \ MACRO(PropertyByValue) \ MACRO(OptionalPropertyByValue) \ @@ -2014,7 +2016,8 @@ class PropertyAccessBase : public BinaryNode { static bool test(const ParseNode& node) { bool match = node.isKind(ParseNodeKind::DotExpr) || - node.isKind(ParseNodeKind::OptionalDotExpr); + node.isKind(ParseNodeKind::OptionalDotExpr) || + node.isKind(ParseNodeKind::ArgumentsLength); MOZ_ASSERT_IF(match, node.is<BinaryNode>()); MOZ_ASSERT_IF(match, node.as<BinaryNode>().right()->isKind( ParseNodeKind::PropertyNameExpr)); @@ -2042,7 +2045,8 @@ class PropertyAccess : public PropertyAccessBase { } static bool test(const ParseNode& node) { - bool match = node.isKind(ParseNodeKind::DotExpr); + bool match = node.isKind(ParseNodeKind::DotExpr) || + node.isKind(ParseNodeKind::ArgumentsLength); MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>()); return match; } @@ -2051,6 +2055,26 @@ class PropertyAccess : public PropertyAccessBase { // ParseNodeKind::SuperBase cannot result from any expression syntax. return expression().isKind(ParseNodeKind::SuperBase); } + + protected: + using PropertyAccessBase::PropertyAccessBase; +}; + +class ArgumentsLength : public PropertyAccess { + public: + ArgumentsLength(ParseNode* lhs, NameNode* name, uint32_t begin, uint32_t end) + : PropertyAccess(ParseNodeKind::ArgumentsLength, lhs, name, begin, end) { + MOZ_ASSERT(lhs); + MOZ_ASSERT(name); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ArgumentsLength); + MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>()); + return match; + } + + bool isSuper() const { return false; } }; class OptionalPropertyAccess : public PropertyAccessBase { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index bcd6c30c02..5cb47f2425 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2472,6 +2472,11 @@ GeneralParser<ParseHandler, Unit>::functionBody(InHandling inHandling, } } + if (pc_->numberOfArgumentsNames > 0 || kind == FunctionSyntaxKind::Arrow) { + MOZ_ASSERT(pc_->isFunctionBox()); + pc_->sc()->setIneligibleForArgumentsLength(); + } + // Declare the 'arguments', 'this', and 'new.target' bindings if necessary // before finishing up the scope so these special bindings get marked as // closed over if necessary. Arrow functions don't have these bindings. @@ -6570,6 +6575,8 @@ bool GeneralParser<ParseHandler, Unit>::forHeadStart( return false; } } + } else if (handler_.isArgumentsLength(*forInitialPart)) { + pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(*forInitialPart)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(*forInitialPart)) { @@ -7917,7 +7924,12 @@ bool GeneralParser<ParseHandler, Unit>::finishClassConstructor( bool hasPrivateBrand = classInitializedMembers.hasPrivateBrand(); if (hasPrivateBrand || numMemberInitializers > 0) { // Now that we have full set of initializers, update the constructor. - MemberInitializers initializers(hasPrivateBrand, numMemberInitializers); + MemberInitializers initializers( + hasPrivateBrand, +#ifdef ENABLE_DECORATORS + classInitializedMembers.hasInstanceDecorators, +#endif + numMemberInitializers); ctorbox->setMemberInitializers(initializers); // Field initialization need access to `this`. @@ -10220,6 +10232,8 @@ typename ParseHandler::NodeResult GeneralParser<ParseHandler, Unit>::assignExpr( return errorResult(); } } + } else if (handler_.isArgumentsLength(lhs)) { + pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(lhs)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(lhs)) { @@ -10280,6 +10294,8 @@ bool GeneralParser<ParseHandler, Unit>::checkIncDecOperand( return false; } } + } else if (handler_.isArgumentsLength(operand)) { + pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(operand)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(operand)) { @@ -10898,6 +10914,9 @@ template <class ParseHandler> inline typename ParseHandler::NameNodeResult PerHandlerParser<ParseHandler>::newName(TaggedParserAtomIndex name, TokenPos pos) { + if (name == TaggedParserAtomIndex::WellKnown::arguments()) { + this->pc_->numberOfArgumentsNames++; + } return handler_.newName(name, pos); } @@ -10926,6 +10945,13 @@ GeneralParser<ParseHandler, Unit>::memberPropertyAccess( MOZ_ASSERT(!handler_.isSuperBase(lhs)); return handler_.newOptionalPropertyAccess(lhs, name); } + + if (handler_.isArgumentsName(lhs) && handler_.isLengthName(name)) { + MOZ_ASSERT(pc_->numberOfArgumentsNames > 0); + pc_->numberOfArgumentsNames--; + return handler_.newArgumentsLength(lhs, name); + } + return handler_.newPropertyAccess(lhs, name); } @@ -11484,6 +11510,10 @@ void GeneralParser<ParseHandler, Unit>::checkDestructuringAssignmentName( return; } + if (handler_.isArgumentsLength(name)) { + pc_->sc()->setIneligibleForArgumentsLength(); + } + if (pc_->sc()->strict()) { if (handler_.isArgumentsName(name)) { if (pc_->sc()->strict()) { @@ -12143,6 +12173,10 @@ GeneralParser<ParseHandler, Unit>::objectLiteral(YieldHandling yieldHandling, } } + if (handler_.isArgumentsLength(lhs)) { + pc_->sc()->setIneligibleForArgumentsLength(); + } + Node rhs; MOZ_TRY_VAR(rhs, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); diff --git a/js/src/frontend/SharedContext.cpp b/js/src/frontend/SharedContext.cpp index 7fa3b724fb..488e3bd384 100644 --- a/js/src/frontend/SharedContext.cpp +++ b/js/src/frontend/SharedContext.cpp @@ -45,7 +45,8 @@ SharedContext::SharedContext(FrontendContext* fc, Kind kind, inClass_(false), localStrict(false), hasExplicitUseStrict_(false), - isScriptExtraFieldCopiedToStencil(false) { + isScriptExtraFieldCopiedToStencil(false), + eligibleForArgumentsLength(true) { // Compute the script kind "input" flags. if (kind == Kind::FunctionBox) { setFlag(ImmutableFlags::IsFunction); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 12f9a6ed12..ab0606527e 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -180,6 +180,10 @@ class SharedContext { // FunctionBox::copyUpdated* methods. bool isScriptExtraFieldCopiedToStencil : 1; + // Indicates this shared context is eligible to use JSOp::ArgumentsLength + // when emitting the ArgumentsLength parse node. + bool eligibleForArgumentsLength : 1; + // End of fields. enum class Kind : uint8_t { FunctionBox, Global, Eval, Module }; @@ -273,6 +277,11 @@ class SharedContext { return retVal; } + bool isEligibleForArgumentsLength() { + return eligibleForArgumentsLength && !bindingsAccessedDynamically(); + } + void setIneligibleForArgumentsLength() { eligibleForArgumentsLength = false; } + void copyScriptExtraFields(ScriptStencilExtra& scriptExtra); }; diff --git a/js/src/frontend/Stencil.cpp b/js/src/frontend/Stencil.cpp index 7c6eba4c5a..30d1588415 100644 --- a/js/src/frontend/Stencil.cpp +++ b/js/src/frontend/Stencil.cpp @@ -575,7 +575,7 @@ void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) { } bool hasEnv = si.hasSyntacticEnvironment(); - auto setCacthAll = [&](NameLocation loc) { + auto setCatchAll = [&](NameLocation loc) { return si.scope().match([&](auto& scope_ref) { using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref)); BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref); @@ -604,7 +604,7 @@ void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) { case ScopeKind::Function: if (hasEnv) { if (si.scope().funHasExtensibleScope()) { - setCacthAll(NameLocation::Dynamic()); + setCatchAll(NameLocation::Dynamic()); return; } @@ -733,21 +733,21 @@ void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) { if (!hasEnv) { ScopeKind kind = si.scope().enclosing().kind(); if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) { - setCacthAll(NameLocation::Global(BindingKind::Var)); + setCatchAll(NameLocation::Global(BindingKind::Var)); return; } } - setCacthAll(NameLocation::Dynamic()); + setCatchAll(NameLocation::Dynamic()); return; case ScopeKind::Global: - setCacthAll(NameLocation::Global(BindingKind::Var)); + setCatchAll(NameLocation::Global(BindingKind::Var)); return; case ScopeKind::With: case ScopeKind::NonSyntactic: - setCacthAll(NameLocation::Dynamic()); + setCatchAll(NameLocation::Dynamic()); return; case ScopeKind::WasmInstance: diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index aa06eaa246..fa63b1e9d3 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -57,8 +57,9 @@ enum SyntaxParseHandlerNode { // casing. NodeName, - // Nodes representing the names "arguments" and "eval". + // Nodes representing the names "arguments", "length" and "eval". NodeArgumentsName, + NodeLengthName, NodeEvalName, // Node representing the "async" name, which may actually be a @@ -77,6 +78,10 @@ enum SyntaxParseHandlerNode { NodePrivateMemberAccess, NodeOptionalPrivateMemberAccess, + // Node representing the compound Arguments.length expression; + // Used only for property access, not assignment. + NodeArgumentsLength, + // Destructuring target patterns can't be parenthesized: |([a]) = [3];| // must be a syntax error. (We can't use NodeGeneric instead of these // because that would trigger invalid-left-hand-side ReferenceError @@ -164,7 +169,7 @@ class SyntaxParseHandler { bool isPropertyOrPrivateMemberAccess(Node node) { return node == NodeDottedProperty || node == NodeElement || - node == NodePrivateMemberAccess; + node == NodePrivateMemberAccess || node == NodeArgumentsLength; } bool isOptionalPropertyOrPrivateMemberAccess(Node node) { @@ -572,6 +577,9 @@ class SyntaxParseHandler { NameNodeResult newPropertyName(TaggedParserAtomIndex name, const TokenPos& pos) { lastAtom = name; + if (name == TaggedParserAtomIndex::WellKnown::length()) { + return NodeLengthName; + } return NodeGeneric; } @@ -579,6 +587,10 @@ class SyntaxParseHandler { return NodeDottedProperty; } + PropertyAccessResult newArgumentsLength(Node expr, NameNodeType key) { + return NodeArgumentsLength; + } + PropertyAccessResult newOptionalPropertyAccess(Node expr, NameNodeType key) { return NodeOptionalDottedProperty; } @@ -777,13 +789,17 @@ class SyntaxParseHandler { bool isName(Node node) { return node == NodeName || node == NodeArgumentsName || - node == NodeEvalName || node == NodePotentialAsyncKeyword; + node == NodeLengthName || node == NodeEvalName || + node == NodePotentialAsyncKeyword; } bool isArgumentsName(Node node) { return node == NodeArgumentsName; } + bool isLengthName(Node node) { return node == NodeLengthName; } bool isEvalName(Node node) { return node == NodeEvalName; } bool isAsyncKeyword(Node node) { return node == NodePotentialAsyncKeyword; } + bool isArgumentsLength(Node node) { return node == NodeArgumentsLength; } + bool isPrivateName(Node node) { return node == NodePrivateName; } bool isPrivateMemberAccess(Node node) { return node == NodePrivateMemberAccess; @@ -795,7 +811,8 @@ class SyntaxParseHandler { // |this|. It's not really eligible for the funapply/funcall // optimizations as they're currently implemented (assuming a single // value is used for both retrieval and |this|). - if (node != NodeDottedProperty && node != NodeOptionalDottedProperty) { + if (node != NodeDottedProperty && node != NodeOptionalDottedProperty && + node != NodeArgumentsLength) { return TaggedParserAtomIndex::null(); } return lastAtom; diff --git a/js/src/frontend/UsedNameTracker.h b/js/src/frontend/UsedNameTracker.h index 2a52208128..f118d6101b 100644 --- a/js/src/frontend/UsedNameTracker.h +++ b/js/src/frontend/UsedNameTracker.h @@ -160,6 +160,10 @@ class UsedNameTracker { return !uses_.empty() && uses_.back().scriptId >= scriptId; } + bool isClosedOver(uint32_t scriptId) const { + return !uses_.empty() && uses_.back().scriptId > scriptId; + } + // To allow disambiguating public and private symbols bool isPublic() { return visibility_ == NameVisibility::Public; } diff --git a/js/src/frontend/align_stack_comment.py b/js/src/frontend/align_stack_comment.py index 28d5d8cf7f..6e279a90c6 100755 --- a/js/src/frontend/align_stack_comment.py +++ b/js/src/frontend/align_stack_comment.py @@ -22,7 +22,7 @@ ALIGNMENT_COLUMN = 20 # The maximum column for comment MAX_CHARS_PER_LINE = 80 -stack_comment_pat = re.compile("^( *//) *(\[stack\].*)$") +stack_comment_pat = re.compile(r"^( *//) *(\[stack\].*)$") def align_stack_comment(path): diff --git a/js/src/fuzz-tests/testWasm.cpp b/js/src/fuzz-tests/testWasm.cpp index 719c38174a..d7ba0511b4 100644 --- a/js/src/fuzz-tests/testWasm.cpp +++ b/js/src/fuzz-tests/testWasm.cpp @@ -9,6 +9,7 @@ #include "fuzz-tests/tests.h" #include "js/CallAndConstruct.h" +#include "js/Prefs.h" #include "js/PropertyAndElement.h" // JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetProperty #include "vm/GlobalObject.h" #include "vm/Interpreter.h" @@ -40,13 +41,11 @@ static int testWasmInit(int* argc, char*** argv) { MOZ_CRASH("Wasm is not supported"); } - JS::ContextOptionsRef(gCx) -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, PREF) \ - .setWasm##NAME(FLAG_FUZZ_ON) - JS_FOR_WASM_FEATURES(WASM_FEATURE) +#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \ + FLAG_FORCE_ON, FLAG_FUZZ_ON, PREF) \ + JS::Prefs::setAtStartup_wasm_##PREF(FLAG_FUZZ_ON); + JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE - ; if (!GlobalObject::getOrCreateConstructor(gCx, JSProto_WebAssembly)) { MOZ_CRASH("Failed to initialize wasm engine"); diff --git a/js/src/gc/AllocKind.h b/js/src/gc/AllocKind.h index cb3d063f89..f73352e557 100644 --- a/js/src/gc/AllocKind.h +++ b/js/src/gc/AllocKind.h @@ -197,13 +197,14 @@ constexpr auto SomeAllocKinds(AllocKind first = AllocKind::FIRST, // with each index corresponding to a particular alloc kind. template <typename ValueType> using AllAllocKindArray = - mozilla::EnumeratedArray<AllocKind, AllocKind::LIMIT, ValueType>; + mozilla::EnumeratedArray<AllocKind, ValueType, size_t(AllocKind::LIMIT)>; // ObjectAllocKindArray<ValueType> gives an enumerated array of ValueTypes, // with each index corresponding to a particular object alloc kind. template <typename ValueType> using ObjectAllocKindArray = - mozilla::EnumeratedArray<AllocKind, AllocKind::OBJECT_LIMIT, ValueType>; + mozilla::EnumeratedArray<AllocKind, ValueType, + size_t(AllocKind::OBJECT_LIMIT)>; /* * Map from C++ type to alloc kind for non-object types. JSObject does not have diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 7ec63a571d..c01dfe3660 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -930,6 +930,8 @@ void GCRuntime::finish() { } #endif + releaseMarkingThreads(); + #ifdef JS_GC_ZEAL // Free memory associated with GC verification. finishVerifier(); @@ -1064,9 +1066,8 @@ bool GCRuntime::setParameter(JSGCParamKey key, uint32_t value, compactingEnabled = value != 0; break; case JSGC_PARALLEL_MARKING_ENABLED: - // Not supported on workers. - parallelMarkingEnabled = rt->isMainRuntime() && value != 0; - return initOrDisableParallelMarking(); + setParallelMarkingEnabled(value != 0); + break; case JSGC_INCREMENTAL_WEAKMAP_ENABLED: for (auto& marker : markers) { marker->incrementalWeakMapMarkingEnabled = value != 0; @@ -1151,8 +1152,7 @@ void GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) { compactingEnabled = TuningDefaults::CompactingEnabled; break; case JSGC_PARALLEL_MARKING_ENABLED: - parallelMarkingEnabled = TuningDefaults::ParallelMarkingEnabled; - initOrDisableParallelMarking(); + setParallelMarkingEnabled(TuningDefaults::ParallelMarkingEnabled); break; case JSGC_INCREMENTAL_WEAKMAP_ENABLED: for (auto& marker : markers) { @@ -1350,16 +1350,56 @@ void GCRuntime::assertNoMarkingWork() const { } #endif +bool GCRuntime::setParallelMarkingEnabled(bool enabled) { + if (enabled == parallelMarkingEnabled) { + return true; + } + + parallelMarkingEnabled = enabled; + return initOrDisableParallelMarking(); +} + bool GCRuntime::initOrDisableParallelMarking() { - // Attempt to initialize parallel marking state or disable it on failure. + // Attempt to initialize parallel marking state or disable it on failure. This + // is called when parallel marking is enabled or disabled. MOZ_ASSERT(markers.length() != 0); - if (!updateMarkersVector()) { - parallelMarkingEnabled = false; + if (updateMarkersVector()) { + return true; + } + + // Failed to initialize parallel marking so disable it instead. + MOZ_ASSERT(parallelMarkingEnabled); + parallelMarkingEnabled = false; + MOZ_ALWAYS_TRUE(updateMarkersVector()); + return false; +} + +void GCRuntime::releaseMarkingThreads() { + MOZ_ALWAYS_TRUE(reserveMarkingThreads(0)); +} + +bool GCRuntime::reserveMarkingThreads(size_t newCount) { + if (reservedMarkingThreads == newCount) { + return true; + } + + // Update the helper thread system's global count by subtracting this + // runtime's current contribution |reservedMarkingThreads| and adding the new + // contribution |newCount|. + + AutoLockHelperThreadState lock; + auto& globalCount = HelperThreadState().gcParallelMarkingThreads; + MOZ_ASSERT(globalCount >= reservedMarkingThreads); + size_t newGlobalCount = globalCount - reservedMarkingThreads + newCount; + if (newGlobalCount > HelperThreadState().threadCount) { + // Not enough total threads. return false; } + globalCount = newGlobalCount; + reservedMarkingThreads = newCount; return true; } @@ -1378,6 +1418,16 @@ bool GCRuntime::updateMarkersVector() { // concurrently, otherwise one thread can deadlock waiting on another. size_t targetCount = std::min(markingWorkerCount(), getMaxParallelThreads()); + if (rt->isMainRuntime()) { + // For the main runtime, reserve helper threads as long as parallel marking + // is enabled. Worker runtimes may not mark in parallel if there are + // insufficient threads available at the time. + size_t threadsToReserve = targetCount > 1 ? targetCount : 0; + if (!reserveMarkingThreads(threadsToReserve)) { + return false; + } + } + if (markers.length() > targetCount) { return markers.resize(targetCount); } @@ -2870,7 +2920,7 @@ void GCRuntime::beginMarkPhase(AutoGCSession& session) { stats().measureInitialHeapSize(); useParallelMarking = SingleThreadedMarking; - if (canMarkInParallel() && initParallelMarkers()) { + if (canMarkInParallel() && initParallelMarking()) { useParallelMarking = AllowParallelMarking; } @@ -2989,9 +3039,19 @@ inline bool GCRuntime::canMarkInParallel() const { tunables.parallelMarkingThresholdBytes(); } -bool GCRuntime::initParallelMarkers() { +bool GCRuntime::initParallelMarking() { + // This is called at the start of collection. + MOZ_ASSERT(canMarkInParallel()); + // Reserve/release helper threads for worker runtimes. These are released at + // the end of sweeping. If there are not enough helper threads because + // other runtimes are marking in parallel then parallel marking will not be + // used. + if (!rt->isMainRuntime() && !reserveMarkingThreads(markers.length())) { + return false; + } + // Allocate stack for parallel markers. The first marker always has stack // allocated. Other markers have their stack freed in // GCRuntime::finishCollection. diff --git a/js/src/gc/GC.h b/js/src/gc/GC.h index 3b7dec3201..4e4634d804 100644 --- a/js/src/gc/GC.h +++ b/js/src/gc/GC.h @@ -69,12 +69,12 @@ class TenuredChunk; _("parallelMarkingEnabled", JSGC_PARALLEL_MARKING_ENABLED, true) \ _("parallelMarkingThresholdMB", JSGC_PARALLEL_MARKING_THRESHOLD_MB, true) \ _("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true) \ - _("nurseryFreeThresholdForIdleCollection", \ - JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, true) \ - _("nurseryFreeThresholdForIdleCollectionPercent", \ - JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, true) \ - _("nurseryTimeoutForIdleCollectionMS", \ - JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS, true) \ + _("nurseryEagerCollectionThresholdKB", \ + JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_KB, true) \ + _("nurseryEagerCollectionThresholdPercent", \ + JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_PERCENT, true) \ + _("nurseryEagerCollectionTimeoutMS", \ + JSGC_NURSERY_EAGER_COLLECTION_TIMEOUT_MS, true) \ _("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true) \ _("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true) \ _("urgentThreshold", JSGC_URGENT_THRESHOLD_MB, true) \ diff --git a/js/src/gc/GCAPI.cpp b/js/src/gc/GCAPI.cpp index ab6c3c297a..293bfce80d 100644 --- a/js/src/gc/GCAPI.cpp +++ b/js/src/gc/GCAPI.cpp @@ -817,11 +817,17 @@ JS_PUBLIC_API void js::gc::SetPerformanceHint(JSContext* cx, AutoSelectGCHeap::AutoSelectGCHeap(JSContext* cx, size_t allowedNurseryCollections) : cx_(cx), allowedNurseryCollections_(allowedNurseryCollections) { - JS::AddGCNurseryCollectionCallback(cx, &NurseryCollectionCallback, this); + if (!JS::AddGCNurseryCollectionCallback(cx, &NurseryCollectionCallback, + this)) { + cx_ = nullptr; + } } AutoSelectGCHeap::~AutoSelectGCHeap() { - JS::RemoveGCNurseryCollectionCallback(cx_, &NurseryCollectionCallback, this); + if (cx_) { + JS::RemoveGCNurseryCollectionCallback(cx_, &NurseryCollectionCallback, + this); + } } /* static */ diff --git a/js/src/gc/GCEnum.h b/js/src/gc/GCEnum.h index 6b1a00f4db..d60cfaea76 100644 --- a/js/src/gc/GCEnum.h +++ b/js/src/gc/GCEnum.h @@ -120,6 +120,7 @@ enum class GCAbortReason { _(PropMapTable) \ _(ModuleBindingMap) \ _(ModuleCyclicFields) \ + _(ModuleSyntheticFields) \ _(ModuleExports) \ _(BaselineScript) \ _(IonScript) \ diff --git a/js/src/gc/GCMarker.h b/js/src/gc/GCMarker.h index 2d47349794..9d34d0a0dc 100644 --- a/js/src/gc/GCMarker.h +++ b/js/src/gc/GCMarker.h @@ -156,11 +156,10 @@ class MarkStack { MarkStack(); ~MarkStack(); - explicit MarkStack(const MarkStack& other); - MarkStack& operator=(const MarkStack& other); + MarkStack(const MarkStack& other) = delete; + MarkStack& operator=(const MarkStack& other) = delete; - MarkStack(MarkStack&& other) noexcept; - MarkStack& operator=(MarkStack&& other) noexcept; + void swap(MarkStack& other); // The unit for MarkStack::capacity() is mark stack words. size_t capacity() { return stack().length(); } diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index a7198f5bbc..c9f660b4d7 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -640,6 +640,7 @@ class GCRuntime { const AutoLockHelperThreadState& lock); // Parallel marking. + bool setParallelMarkingEnabled(bool enabled); bool initOrDisableParallelMarking(); [[nodiscard]] bool updateMarkersVector(); size_t markingWorkerCount() const; @@ -799,9 +800,12 @@ class GCRuntime { ParallelMarking allowParallelMarking = SingleThreadedMarking, ShouldReportMarkTime reportTime = ReportMarkTime); bool canMarkInParallel() const; - bool initParallelMarkers(); + bool initParallelMarking(); void finishParallelMarkers(); + bool reserveMarkingThreads(size_t count); + void releaseMarkingThreads(); + bool hasMarkingWork(MarkColor color) const; void drainMarkStack(); @@ -1120,6 +1124,13 @@ class GCRuntime { /* Incremented on every GC slice. */ MainThreadData<uint64_t> sliceNumber; + /* + * This runtime's current contribution to the global number of helper threads + * 'reserved' for parallel marking. Does not affect other uses of helper + * threads. + */ + MainThreadData<size_t> reservedMarkingThreads; + /* Whether the currently running GC can finish in multiple slices. */ MainThreadOrGCTaskData<bool> isIncremental; diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 78fcc3dedc..6b8742c980 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1006,7 +1006,6 @@ void js::gc::PerformIncrementalPreWriteBarrier(TenuredCell* cell) { // runtime for cells in atoms zone. Zone* zone = cell->zoneFromAnyThread(); - MOZ_ASSERT(zone->needsIncrementalBarrier()); MOZ_ASSERT(cell); if (cell->isMarkedBlack()) { @@ -1023,6 +1022,7 @@ void js::gc::PerformIncrementalPreWriteBarrier(TenuredCell* cell) { return; } + MOZ_ASSERT(zone->needsIncrementalBarrier()); MOZ_ASSERT(CurrentThreadIsMainThread()); MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting()); @@ -1809,29 +1809,15 @@ MarkStack::MarkStack() { MOZ_ASSERT(isEmpty()); } MarkStack::~MarkStack() { MOZ_ASSERT(isEmpty()); } -MarkStack::MarkStack(const MarkStack& other) { - MOZ_CRASH("Compiler requires this but doesn't call it"); -} - -MarkStack& MarkStack::operator=(const MarkStack& other) { - new (this) MarkStack(other); - return *this; -} - -MarkStack::MarkStack(MarkStack&& other) noexcept - : stack_(std::move(other.stack_.ref())), - topIndex_(other.topIndex_.ref()) +void MarkStack::swap(MarkStack& other) { + std::swap(stack_, other.stack_); + std::swap(topIndex_, other.topIndex_); #ifdef JS_GC_ZEAL - , - maxCapacity_(other.maxCapacity_) + std::swap(maxCapacity_, other.maxCapacity_); +#endif +#ifdef DEBUG + std::swap(elementsRangesAreValid, other.elementsRangesAreValid); #endif -{ - other.topIndex_ = 0; -} - -MarkStack& MarkStack::operator=(MarkStack&& other) noexcept { - new (this) MarkStack(std::move(other)); - return *this; } bool MarkStack::init() { return resetStackCapacity(); } @@ -2186,7 +2172,7 @@ void GCMarker::setMarkColor(gc::MarkColor newColor) { // Switch stacks. We only need to do this if there are any stack entries (as // empty stacks are interchangeable) or to swtich back to the original stack. if (!isDrained() || haveSwappedStacks) { - std::swap(stack, otherStack); + stack.swap(otherStack); haveSwappedStacks = !haveSwappedStacks; } } diff --git a/js/src/gc/MaybeRooted.h b/js/src/gc/MaybeRooted.h index fbeb0c553c..6b38172472 100644 --- a/js/src/gc/MaybeRooted.h +++ b/js/src/gc/MaybeRooted.h @@ -35,7 +35,7 @@ class MOZ_RAII FakeRooted : public RootedOperations<T, FakeRooted<T>> { explicit FakeRooted(JSContext* cx) : ptr(JS::SafelyInitialized<T>::create()) {} - FakeRooted(JSContext* cx, T initial) : ptr(initial) {} + FakeRooted(JSContext* cx, const T& initial) : ptr(initial) {} FakeRooted(const FakeRooted&) = delete; @@ -44,6 +44,8 @@ class MOZ_RAII FakeRooted : public RootedOperations<T, FakeRooted<T>> { DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr); + operator JS::Handle<T>() { return JS::Handle<T>::fromMarkedLocation(&ptr); } + private: T ptr; diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index a78db5cc9a..660daa8d4c 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -1058,7 +1058,7 @@ TimeStamp js::Nursery::lastCollectionEndTime() const { return previousGC.endTime; } -bool js::Nursery::shouldCollect() const { +bool js::Nursery::wantEagerCollection() const { if (!isEnabled()) { return false; } @@ -1071,8 +1071,7 @@ bool js::Nursery::shouldCollect() const { return true; } - // Eagerly collect the nursery in idle time if it's nearly full. - if (isNearlyFull()) { + if (freeSpaceIsBelowEagerThreshold()) { return true; } @@ -1081,32 +1080,27 @@ bool js::Nursery::shouldCollect() const { return isUnderused(); } -inline bool js::Nursery::isNearlyFull() const { - bool belowBytesThreshold = - freeSpace() < tunables().nurseryFreeThresholdForIdleCollection(); - bool belowFractionThreshold = - double(freeSpace()) / double(capacity()) < - tunables().nurseryFreeThresholdForIdleCollectionFraction(); - - // We want to use belowBytesThreshold when the nursery is sufficiently large, - // and belowFractionThreshold when it's small. - // - // When the nursery is small then belowBytesThreshold is a lower threshold - // (triggered earlier) than belowFractionThreshold. So if the fraction - // threshold is true, the bytes one will be true also. The opposite is true - // when the nursery is large. - // - // Therefore, by the time we cross the threshold we care about, we've already - // crossed the other one, and we can boolean AND to use either condition - // without encoding any "is the nursery big/small" test/threshold. The point - // at which they cross is when the nursery is: BytesThreshold / - // FractionThreshold large. - // - // With defaults that's: +inline bool js::Nursery::freeSpaceIsBelowEagerThreshold() const { + // The threshold is specified in terms of free space so that it doesn't depend + // on the size of the nursery. // - // 1MB = 256KB / 0.25 + // There two thresholds, an absolute free bytes threshold and a free space + // fraction threshold. Two thresholds are used so that we don't collect too + // eagerly for small nurseries (or even all the time if nursery size is less + // than the free bytes threshold) or too eagerly for large nurseries (where a + // fractional threshold may leave a significant amount of nursery unused). // - return belowBytesThreshold && belowFractionThreshold; + // Since the aim is making this less eager we require both thresholds to be + // met. + + size_t freeBytes = freeSpace(); + double freeFraction = double(freeBytes) / double(capacity()); + + size_t bytesThreshold = tunables().nurseryEagerCollectionThresholdBytes(); + double fractionThreshold = + tunables().nurseryEagerCollectionThresholdPercent(); + + return freeBytes < bytesThreshold && freeFraction < fractionThreshold; } inline bool js::Nursery::isUnderused() const { @@ -1124,7 +1118,7 @@ inline bool js::Nursery::isUnderused() const { // simplest. TimeDuration timeSinceLastCollection = TimeStamp::NowLoRes() - previousGC.endTime; - return timeSinceLastCollection > tunables().nurseryTimeoutForIdleCollection(); + return timeSinceLastCollection > tunables().nurseryEagerCollectionTimeout(); } void js::Nursery::collect(JS::GCOptions options, JS::GCReason reason) { @@ -1874,7 +1868,7 @@ size_t js::Nursery::targetSize(JS::GCOptions options, JS::GCReason reason) { // If the nursery is completely unused then minimise it. if (hasRecentGrowthData && previousGC.nurseryUsedBytes == 0 && now - lastCollectionEndTime() > - tunables().nurseryTimeoutForIdleCollection() && + tunables().nurseryEagerCollectionTimeout() && !js::SupportDifferentialTesting()) { clearRecentGrowthData(); return 0; diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 2bab1623b0..0d7b607ff8 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -328,9 +328,7 @@ class Nursery { } JS::GCReason minorGCTriggerReason() const { return minorGCTriggerReason_; } - bool shouldCollect() const; - bool isNearlyFull() const; - bool isUnderused() const; + bool wantEagerCollection() const; bool enableProfiling() const { return enableProfiling_; } @@ -383,12 +381,11 @@ class Nursery { KeyCount }; - using ProfileTimes = - mozilla::EnumeratedArray<ProfileKey, ProfileKey::KeyCount, - mozilla::TimeStamp>; + using ProfileTimes = mozilla::EnumeratedArray<ProfileKey, mozilla::TimeStamp, + size_t(ProfileKey::KeyCount)>; using ProfileDurations = - mozilla::EnumeratedArray<ProfileKey, ProfileKey::KeyCount, - mozilla::TimeDuration>; + mozilla::EnumeratedArray<ProfileKey, mozilla::TimeDuration, + size_t(ProfileKey::KeyCount)>; // Calculate the promotion rate of the most recent minor GC. // The valid_for_tenuring parameter is used to return whether this @@ -445,6 +442,9 @@ class Nursery { [[nodiscard]] bool moveToNextChunk(); + bool freeSpaceIsBelowEagerThreshold() const; + bool isUnderused() const; + struct CollectionResult { size_t tenuredBytes; size_t tenuredCells; diff --git a/js/src/gc/Scheduling.h b/js/src/gc/Scheduling.h index 09a9f834eb..cbaeb1f353 100644 --- a/js/src/gc/Scheduling.h +++ b/js/src/gc/Scheduling.h @@ -447,23 +447,26 @@ NoCheck, 16 * 1024 * 1024) \ \ /* \ - * JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION \ - * JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_FRACTION \ - * JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS \ + * JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_KB \ + * JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_PERCENT \ + * JSGC_NURSERY_EAGER_COLLECTION_TIMEOUT_MS \ * \ - * Attempt to run a minor GC in the idle time if the free space falls below \ - * this threshold or if it hasn't been collected for too long. The absolute \ - * threshold is used when the nursery is large and the percentage when it is \ - * small. See Nursery::shouldCollect(). \ + * JS::MaybeRunNurseryCollection will run a minor GC if the free space falls \ + * below a threshold or if it hasn't been collected for too long. \ + * \ + * To avoid making this too eager, two thresholds must be met. The free \ + * space must fall below a size threshold and the fraction of free space \ + * remaining must also fall below a threshold. \ + * \ + * See Nursery::wantEagerCollection() for more details. \ */ \ - _(JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, size_t, \ - nurseryFreeThresholdForIdleCollection, ConvertSize, NoCheck, \ - ChunkSize / 4) \ - _(JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, double, \ - nurseryFreeThresholdForIdleCollectionFraction, ConvertTimes100, \ + _(JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_KB, size_t, \ + nurseryEagerCollectionThresholdBytes, ConvertKB, NoCheck, ChunkSize / 4) \ + _(JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_PERCENT, double, \ + nurseryEagerCollectionThresholdPercent, ConvertTimes100, \ CheckNonZeroUnitRange, 0.25) \ - _(JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS, mozilla::TimeDuration, \ - nurseryTimeoutForIdleCollection, ConvertMillis, NoCheck, \ + _(JSGC_NURSERY_EAGER_COLLECTION_TIMEOUT_MS, mozilla::TimeDuration, \ + nurseryEagerCollectionTimeout, ConvertMillis, NoCheck, \ mozilla::TimeDuration::FromSeconds(5)) \ \ /* \ diff --git a/js/src/gc/StableCellHasher-inl.h b/js/src/gc/StableCellHasher-inl.h index b4054342c0..af0caaad89 100644 --- a/js/src/gc/StableCellHasher-inl.h +++ b/js/src/gc/StableCellHasher-inl.h @@ -137,7 +137,7 @@ inline void TransferUniqueId(Cell* tgt, Cell* src) { MOZ_ASSERT(src->zone() == tgt->zone()); Zone* zone = tgt->zone(); - MOZ_ASSERT(!zone->uniqueIds().has(tgt)); + MOZ_ASSERT_IF(zone->uniqueIds().has(src), !zone->uniqueIds().has(tgt)); zone->uniqueIds().rekeyIfMoved(src, tgt); } diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index e50e7500cf..c12d44db97 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -154,11 +154,11 @@ struct PhaseInfo { }; // A table of PhaseInfo indexed by Phase. -using PhaseTable = EnumeratedArray<Phase, Phase::LIMIT, PhaseInfo>; +using PhaseTable = EnumeratedArray<Phase, PhaseInfo, size_t(Phase::LIMIT)>; // A table of PhaseKindInfo indexed by PhaseKind. using PhaseKindTable = - EnumeratedArray<PhaseKind, PhaseKind::LIMIT, PhaseKindInfo>; + EnumeratedArray<PhaseKind, PhaseKindInfo, size_t(PhaseKind::LIMIT)>; #include "gc/StatsPhasesGenerated.inc" @@ -595,7 +595,7 @@ UniqueChars Statistics::formatDetailedTotals() const { void Statistics::formatJsonSlice(size_t sliceNum, JSONPrinter& json) const { /* * We number each of the slice properties to keep the code in - * GCTelemetry.jsm in sync. See MAX_SLICE_KEYS. + * GCTelemetry.sys.mjs in sync. See MAX_SLICE_KEYS. */ json.beginObject(); formatJsonSliceDescription(sliceNum, slices_[sliceNum], json); // # 1-11 diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index f03bc2ea38..bc6d7bf5dd 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -136,19 +136,19 @@ struct Statistics { template <typename T, size_t Length> using Array = mozilla::Array<T, Length>; - template <typename IndexType, IndexType SizeAsEnumValue, typename ValueType> + template <typename IndexType, typename ValueType, IndexType SizeAsEnumValue> using EnumeratedArray = - mozilla::EnumeratedArray<IndexType, SizeAsEnumValue, ValueType>; + mozilla::EnumeratedArray<IndexType, ValueType, size_t(SizeAsEnumValue)>; using TimeDuration = mozilla::TimeDuration; using TimeStamp = mozilla::TimeStamp; // Create types for tables of times, by phase and phase kind. - using PhaseTimes = EnumeratedArray<Phase, Phase::LIMIT, TimeDuration>; + using PhaseTimes = EnumeratedArray<Phase, TimeDuration, Phase::LIMIT>; using PhaseKindTimes = - EnumeratedArray<PhaseKind, PhaseKind::LIMIT, TimeDuration>; + EnumeratedArray<PhaseKind, TimeDuration, PhaseKind::LIMIT>; - using PhaseTimeStamps = EnumeratedArray<Phase, Phase::LIMIT, TimeStamp>; + using PhaseTimeStamps = EnumeratedArray<Phase, TimeStamp, Phase::LIMIT>; [[nodiscard]] static bool initialize(); @@ -370,12 +370,12 @@ struct Statistics { TimeDuration totalGCTime_; /* Number of events of this type for this GC. */ - EnumeratedArray<Count, COUNT_LIMIT, - mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire>> + EnumeratedArray<Count, mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire>, + COUNT_LIMIT> counts; /* Other GC statistics. */ - EnumeratedArray<Stat, STAT_LIMIT, uint32_t> stats; + EnumeratedArray<Stat, uint32_t, STAT_LIMIT> stats; /* * These events cannot be kept in the above array, we need to take their @@ -440,7 +440,7 @@ struct Statistics { }; using ProfileDurations = - EnumeratedArray<ProfileKey, ProfileKey::KeyCount, TimeDuration>; + EnumeratedArray<ProfileKey, TimeDuration, ProfileKey::KeyCount>; bool enableProfiling_ = false; bool profileWorkers_ = false; diff --git a/js/src/gc/Sweeping.cpp b/js/src/gc/Sweeping.cpp index 3686695978..123b2c9650 100644 --- a/js/src/gc/Sweeping.cpp +++ b/js/src/gc/Sweeping.cpp @@ -2394,6 +2394,14 @@ void GCRuntime::endSweepPhase(bool destroyingRuntime) { MOZ_ASSERT_IF(destroyingRuntime, !useBackgroundThreads); + // Release parallel marking threads for worker runtimes now we've finished + // marking. The main thread keeps the reservation as long as parallel marking + // is enabled. + if (!rt->isMainRuntime()) { + MOZ_ASSERT_IF(useParallelMarking, reservedMarkingThreads != 0); + releaseMarkingThreads(); + } + { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::DESTROY); diff --git a/js/src/gc/Tenuring.cpp b/js/src/gc/Tenuring.cpp index 84526e2109..a9506cfa14 100644 --- a/js/src/gc/Tenuring.cpp +++ b/js/src/gc/Tenuring.cpp @@ -74,6 +74,14 @@ void TenuringTracer::onObjectEdge(JSObject** objp, const char* name) { return; } + onNonForwardedNurseryObjectEdge(objp); +} + +void TenuringTracer::onNonForwardedNurseryObjectEdge(JSObject** objp) { + JSObject* obj = *objp; + MOZ_ASSERT(IsInsideNursery(obj)); + MOZ_ASSERT(!obj->isForwarded()); + UpdateAllocSiteOnTenure(obj); // Take a fast path for tenuring a plain object which is by far the most @@ -98,6 +106,14 @@ void TenuringTracer::onStringEdge(JSString** strp, const char* name) { return; } + onNonForwardedNurseryStringEdge(strp); +} + +void TenuringTracer::onNonForwardedNurseryStringEdge(JSString** strp) { + JSString* str = *strp; + MOZ_ASSERT(IsInsideNursery(str)); + MOZ_ASSERT(!str->isForwarded()); + UpdateAllocSiteOnTenure(str); *strp = moveToTenured(str); @@ -115,6 +131,14 @@ void TenuringTracer::onBigIntEdge(JS::BigInt** bip, const char* name) { return; } + onNonForwardedNurseryBigIntEdge(bip); +} + +void TenuringTracer::onNonForwardedNurseryBigIntEdge(JS::BigInt** bip) { + JS::BigInt* bi = *bip; + MOZ_ASSERT(IsInsideNursery(bi)); + MOZ_ASSERT(!bi->isForwarded()); + UpdateAllocSiteOnTenure(bi); *bip = moveToTenured(bi); @@ -137,37 +161,52 @@ void TenuringTracer::traverse(JS::Value* thingp) { Value value = *thingp; CheckTracedThing(this, value); + if (!value.isGCThing()) { + return; + } + + Cell* cell = value.toGCThing(); + if (!IsInsideNursery(cell)) { + return; + } + + if (cell->isForwarded()) { + const gc::RelocationOverlay* overlay = + gc::RelocationOverlay::fromCell(cell); + thingp->changeGCThingPayload(overlay->forwardingAddress()); + return; + } + // We only care about a few kinds of GC thing here and this generates much // tighter code than using MapGCThingTyped. - Value post; if (value.isObject()) { JSObject* obj = &value.toObject(); - onObjectEdge(&obj, "value"); - post = JS::ObjectValue(*obj); + onNonForwardedNurseryObjectEdge(&obj); + MOZ_ASSERT(obj != &value.toObject()); + *thingp = JS::ObjectValue(*obj); + return; } #ifdef ENABLE_RECORD_TUPLE - else if (value.isExtendedPrimitive()) { + if (value.isExtendedPrimitive()) { JSObject* obj = &value.toExtendedPrimitive(); - onObjectEdge(&obj, "value"); - post = JS::ExtendedPrimitiveValue(*obj); + onNonForwardedNurseryObjectEdge(&obj); + MOZ_ASSERT(obj != &value.toExtendedPrimitive()); + *thingp = JS::ExtendedPrimitiveValue(*obj); + return; } #endif - else if (value.isString()) { + if (value.isString()) { JSString* str = value.toString(); - onStringEdge(&str, "value"); - post = JS::StringValue(str); - } else if (value.isBigInt()) { - JS::BigInt* bi = value.toBigInt(); - onBigIntEdge(&bi, "value"); - post = JS::BigIntValue(bi); - } else { - MOZ_ASSERT_IF(value.isGCThing(), !IsInsideNursery(value.toGCThing())); + onNonForwardedNurseryStringEdge(&str); + MOZ_ASSERT(str != value.toString()); + *thingp = JS::StringValue(str); return; } - - if (post != value) { - *thingp = post; - } + MOZ_ASSERT(value.isBigInt()); + JS::BigInt* bi = value.toBigInt(); + onNonForwardedNurseryBigIntEdge(&bi); + MOZ_ASSERT(bi != value.toBigInt()); + *thingp = JS::BigIntValue(bi); } void TenuringTracer::traverse(wasm::AnyRef* thingp) { diff --git a/js/src/gc/Tenuring.h b/js/src/gc/Tenuring.h index 560d98d178..3eca5f4bc3 100644 --- a/js/src/gc/Tenuring.h +++ b/js/src/gc/Tenuring.h @@ -92,6 +92,10 @@ class TenuringTracer final : public JSTracer { void traceBigInt(JS::BigInt* bi); private: + MOZ_ALWAYS_INLINE void onNonForwardedNurseryObjectEdge(JSObject** objp); + MOZ_ALWAYS_INLINE void onNonForwardedNurseryStringEdge(JSString** strp); + MOZ_ALWAYS_INLINE void onNonForwardedNurseryBigIntEdge(JS::BigInt** bip); + // The dependent string chars needs to be relocated if the base which it's // using chars from has been deduplicated. template <typename CharT> diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index e2c67aee7b..d0586d5d56 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -632,11 +632,13 @@ void Zone::purgeAtomCache() { } void Zone::addSizeOfIncludingThis( - mozilla::MallocSizeOf mallocSizeOf, JS::CodeSizes* code, size_t* regexpZone, - size_t* jitZone, size_t* cacheIRStubs, size_t* uniqueIdMap, - size_t* initialPropMapTable, size_t* shapeTables, size_t* atomsMarkBitmaps, - size_t* compartmentObjects, size_t* crossCompartmentWrappersTables, - size_t* compartmentsPrivateData, size_t* scriptCountsMapArg) { + mozilla::MallocSizeOf mallocSizeOf, size_t* zoneObject, JS::CodeSizes* code, + size_t* regexpZone, size_t* jitZone, size_t* cacheIRStubs, + size_t* uniqueIdMap, size_t* initialPropMapTable, size_t* shapeTables, + size_t* atomsMarkBitmaps, size_t* compartmentObjects, + size_t* crossCompartmentWrappersTables, size_t* compartmentsPrivateData, + size_t* scriptCountsMapArg) { + *zoneObject += mallocSizeOf(this); *regexpZone += regExps().sizeOfIncludingThis(mallocSizeOf); if (jitZone_) { jitZone_->addSizeOfIncludingThis(mallocSizeOf, code, jitZone, cacheIRStubs); diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 457e586cea..fd91de8626 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -375,15 +375,13 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> { bool registerObjectWithWeakPointers(JSObject* obj); void sweepObjectsWithWeakPointers(JSTracer* trc); - void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - JS::CodeSizes* code, size_t* regexpZone, - size_t* jitZone, size_t* cacheIRStubs, - size_t* uniqueIdMap, size_t* initialPropMapTable, - size_t* shapeTables, size_t* atomsMarkBitmaps, - size_t* compartmentObjects, - size_t* crossCompartmentWrappersTables, - size_t* compartmentsPrivateData, - size_t* scriptCountsMapArg); + void addSizeOfIncludingThis( + mozilla::MallocSizeOf mallocSizeOf, size_t* zoneObject, + JS::CodeSizes* code, size_t* regexpZone, size_t* jitZone, + size_t* cacheIRStubs, size_t* uniqueIdMap, size_t* initialPropMapTable, + size_t* shapeTables, size_t* atomsMarkBitmaps, size_t* compartmentObjects, + size_t* crossCompartmentWrappersTables, size_t* compartmentsPrivateData, + size_t* scriptCountsMapArg); // Iterate over all cells in the zone. See the definition of ZoneCellIter // in gc/GC-inl.h for the possible arguments and documentation. diff --git a/js/src/gdb/mozilla/prettyprinters.py b/js/src/gdb/mozilla/prettyprinters.py index b533d1f1f5..12847cae7c 100644 --- a/js/src/gdb/mozilla/prettyprinters.py +++ b/js/src/gdb/mozilla/prettyprinters.py @@ -271,7 +271,7 @@ def implemented_types(t): yield t2 -template_regexp = re.compile("([\w_:]+)<") +template_regexp = re.compile(r"([\w_:]+)<") def is_struct_or_union(t): diff --git a/js/src/gdb/run-tests.py b/js/src/gdb/run-tests.py index cb9501e904..063a550b17 100644 --- a/js/src/gdb/run-tests.py +++ b/js/src/gdb/run-tests.py @@ -40,9 +40,9 @@ def _relpath(path, start=None): os.path.relpath = _relpath # Characters that need to be escaped when used in shell words. -shell_need_escapes = re.compile("[^\w\d%+,-./:=@'\"]", re.DOTALL) +shell_need_escapes = re.compile("[^\\w\\d%+,-./:=@'\"]", re.DOTALL) # Characters that need to be escaped within double-quoted strings. -shell_dquote_escapes = re.compile('[^\w\d%+,-./:=@"]', re.DOTALL) +shell_dquote_escapes = re.compile('[^\\w\\d%+,-./:=@"]', re.DOTALL) def make_shell_cmd(l): diff --git a/js/src/gdb/tests/test-ExecutableAllocator.py b/js/src/gdb/tests/test-ExecutableAllocator.py index bec2dda623..6a3ae4aae1 100644 --- a/js/src/gdb/tests/test-ExecutableAllocator.py +++ b/js/src/gdb/tests/test-ExecutableAllocator.py @@ -13,10 +13,10 @@ run_fragment("ExecutableAllocator.onepool") reExecPool = "ExecutablePool [a-f0-9]{8,}-[a-f0-9]{8,}" assert_regexp_pretty("pool", reExecPool) -assert_regexp_pretty("execAlloc", "ExecutableAllocator\(\[" + reExecPool + "\]\)") +assert_regexp_pretty("execAlloc", r"ExecutableAllocator\(\[" + reExecPool + r"\]\)") run_fragment("ExecutableAllocator.twopools") assert_regexp_pretty( - "execAlloc", "ExecutableAllocator\(\[" + reExecPool + ", " + reExecPool + "\]\)" + "execAlloc", r"ExecutableAllocator\(\[" + reExecPool + ", " + reExecPool + r"\]\)" ) diff --git a/js/src/intgemm/IntegerGemmIntrinsic.cpp b/js/src/intgemm/IntegerGemmIntrinsic.cpp index aebb0f5f02..0e79ccad36 100644 --- a/js/src/intgemm/IntegerGemmIntrinsic.cpp +++ b/js/src/intgemm/IntegerGemmIntrinsic.cpp @@ -22,10 +22,9 @@ #if defined(USE_AVX512BW) # if defined(USE_AVX512VNNI) -# define SUPPORTED_ARCHS \ - xsimd::arch_list<xsimd::avx512vnni<xsimd::avx512bw>, \ - xsimd::avx512bw, xsimd::avx2, \ - xsimd::ssse3, xsimd::sse2> +# define SUPPORTED_ARCHS \ + xsimd::arch_list<xsimd::avx512vnni<xsimd::avx512bw>, xsimd::avx512bw, \ + xsimd::avx2, xsimd::ssse3, xsimd::sse2> # elif defined(USE_AVXVNNI) # define SUPPORTED_ARCHS \ xsimd::arch_list<xsimd::avx512bw, xsimd::avxvnni, xsimd::avx2, \ @@ -45,7 +44,12 @@ #elif defined(USE_SSE2) # define SUPPORTED_ARCHS xsimd::arch_list<xsimd::sse2> #elif defined(USE_NEON) and defined(XSIMD_WITH_NEON64) -# define SUPPORTED_ARCHS xsimd::arch_list<xsimd::neon64> +# if defined(USE_NEON_I8MM) +# define SUPPORTED_ARCHS \ + xsimd::arch_list<xsimd::i8mm<xsimd::neon64>, xsimd::neon64> +# else +# define SUPPORTED_ARCHS xsimd::arch_list<xsimd::neon64> +# endif #else # error no supported architecture #endif diff --git a/js/src/intgemm/moz.build b/js/src/intgemm/moz.build index 9249938cbb..af4450b4e6 100644 --- a/js/src/intgemm/moz.build +++ b/js/src/intgemm/moz.build @@ -64,6 +64,12 @@ if CONFIG["TARGET_CPU"] == "aarch64": "NEON_FLAGS" ] + DEFINES["USE_NEON_I8MM"] = True + SOURCES += ["/third_party/gemmology/kernels/GemmologyEngineNeon64I8mm.cpp"] + SOURCES[ + "/third_party/gemmology/kernels/GemmologyEngineNeon64I8mm.cpp" + ].flags += CONFIG["NEON_I8MM_FLAGS"] + SOURCES += [ "IntegerGemmIntrinsic.cpp", ] diff --git a/js/src/irregexp/RegExpAPI.cpp b/js/src/irregexp/RegExpAPI.cpp index f1ba1fbc4b..39a6f8ccc9 100644 --- a/js/src/irregexp/RegExpAPI.cpp +++ b/js/src/irregexp/RegExpAPI.cpp @@ -632,7 +632,7 @@ enum class AssembleResult { // RegExpShared. ByteArray bytecode = v8::internal::ByteArray::cast(*result.code).takeOwnership(cx->isolate); - uint32_t length = bytecode->length; + uint32_t length = bytecode->length(); re->setByteCode(bytecode.release(), isLatin1); js::AddCellMemory(re, length, MemoryUse::RegExpSharedBytecode); } @@ -773,7 +773,7 @@ bool CompilePattern(JSContext* cx, MutableHandleRegExpShared re, bool isLatin1 = input->hasLatin1Chars(); SampleCharacters(input, compiler); - data.node = compiler.PreprocessRegExp(&data, flags, isLatin1); + data.node = compiler.PreprocessRegExp(&data, isLatin1); data.error = AnalyzeRegExp(cx->isolate, isLatin1, flags, data.node); if (data.error != RegExpError::kNone) { MOZ_ASSERT(data.error == RegExpError::kAnalysisStackOverflow); diff --git a/js/src/irregexp/RegExpNativeMacroAssembler.cpp b/js/src/irregexp/RegExpNativeMacroAssembler.cpp index 2a8b1749c2..99cfc31bfc 100644 --- a/js/src/irregexp/RegExpNativeMacroAssembler.cpp +++ b/js/src/irregexp/RegExpNativeMacroAssembler.cpp @@ -247,8 +247,8 @@ void SMRegExpMacroAssembler::CheckCharacterNotInRange(base::uc16 from, bool SMRegExpMacroAssembler::IsCharacterInRangeArray(uint32_t c, ByteArrayData* ranges) { js::AutoUnsafeCallWithABI unsafe; - MOZ_ASSERT(ranges->length % sizeof(uint16_t) == 0); - uint32_t length = ranges->length / sizeof(uint16_t); + MOZ_ASSERT(ranges->length() % sizeof(uint16_t) == 0); + uint32_t length = ranges->length() / sizeof(uint16_t); MOZ_ASSERT(length > 0); // Fast paths. diff --git a/js/src/irregexp/RegExpShim.cpp b/js/src/irregexp/RegExpShim.cpp index 2b2c3cd4a0..da388e0057 100644 --- a/js/src/irregexp/RegExpShim.cpp +++ b/js/src/irregexp/RegExpShim.cpp @@ -227,13 +227,13 @@ Handle<ByteArray> Isolate::NewByteArray(int length, AllocationType alloc) { js::AutoEnterOOMUnsafeRegion oomUnsafe; - size_t alloc_size = sizeof(uint32_t) + length; + size_t alloc_size = sizeof(ByteArrayData) + length; ByteArrayData* data = static_cast<ByteArrayData*>(allocatePseudoHandle(alloc_size)); if (!data) { oomUnsafe.crash("Irregexp NewByteArray"); } - data->length = length; + new (data) ByteArrayData(length); return Handle<ByteArray>(JS::PrivateValue(data), this); } @@ -261,7 +261,7 @@ Handle<FixedIntegerArray<T>> Isolate::NewFixedIntegerArray(uint32_t length) { if (!data) { oomUnsafe.crash("Irregexp NewFixedIntegerArray"); } - data->length = rawLength; + new (data) ByteArrayData(rawLength); return Handle<FixedIntegerArray<T>>(JS::PrivateValue(data), this); } diff --git a/js/src/irregexp/RegExpShim.h b/js/src/irregexp/RegExpShim.h index 3f85413421..4d32c84920 100644 --- a/js/src/irregexp/RegExpShim.h +++ b/js/src/irregexp/RegExpShim.h @@ -586,15 +586,6 @@ class Object { // IsCharacterInRangeArray in regexp-macro-assembler.cc. Object(uintptr_t raw) : asBits_(raw) { MOZ_CRASH("unused"); } - // Used in regexp-interpreter.cc to check the return value of - // isolate->stack_guard()->HandleInterrupts(). We want to handle - // interrupts in the caller, so we always return false from - // HandleInterrupts and true here. - inline bool IsException(Isolate*) const { - MOZ_ASSERT(!value().toBoolean()); - return true; - } - JS::Value value() const { return JS::Value::fromRawBits(asBits_); } inline static Object cast(Object object) { return object; } @@ -604,6 +595,14 @@ class Object { uint64_t asBits_; } JS_HAZ_GC_POINTER; +// Used in regexp-interpreter.cc to check the return value of +// isolate->stack_guard()->HandleInterrupts(). We want to handle +// interrupts in the caller, so we return a magic value from +// HandleInterrupts and check for it here. +inline bool IsException(Object obj, Isolate*) { + return obj.value().isMagic(JS_INTERRUPT_REGEXP); +} + class Smi : public Object { public: static Smi FromInt(int32_t value) { @@ -626,6 +625,27 @@ class HeapObject : public Object { } }; +// V8's values use low-bit tagging. If the LSB is 0, it's a small +// integer. If the LSB is 1, it's a pointer to some GC thing. In V8, +// this wrapper class is used to represent a pointer that has the low +// bit set, or a small integer that has been shifted left by one +// bit. We don't use the same tagging system, so all we need is a +// transparent wrapper that automatically converts to/from the wrapped +// type. +template <typename T> +class Tagged { + public: + Tagged() {} + MOZ_IMPLICIT Tagged(const T& value) : value_(value) {} + MOZ_IMPLICIT Tagged(T&& value) : value_(std::move(value)) {} + + T* operator->() { return &value_; } + constexpr operator T() const { return value_; } + + private: + T value_; +}; + // A fixed-size array with Objects (aka Values) as element types. // Implemented using the dense elements of an ArrayObject. // Used for named captures. @@ -668,13 +688,13 @@ T* ByteArrayData::typedData() { template <typename T> T ByteArrayData::getTyped(uint32_t index) { - MOZ_ASSERT(index < length / sizeof(T)); + MOZ_ASSERT(index < length() / sizeof(T)); return typedData<T>()[index]; } template <typename T> void ByteArrayData::setTyped(uint32_t index, T value) { - MOZ_ASSERT(index < length / sizeof(T)); + MOZ_ASSERT(index < length() / sizeof(T)); typedData<T>()[index] = value; } @@ -684,6 +704,7 @@ class ByteArray : public HeapObject { ByteArrayData* inner() const { return static_cast<ByteArrayData*>(value().toPrivate()); } + friend bool IsByteArray(Object obj); public: PseudoHandle<ByteArrayData> takeOwnership(Isolate* isolate); @@ -692,8 +713,8 @@ class ByteArray : public HeapObject { uint8_t get(uint32_t index) { return inner()->get(index); } void set(uint32_t index, uint8_t val) { inner()->set(index, val); } - uint32_t length() const { return inner()->length; } - uint8_t* GetDataStartAddress() { return inner()->data(); } + uint32_t length() const { return inner()->length(); } + uint8_t* begin() { return inner()->data(); } static ByteArray cast(Object object) { ByteArray b; @@ -701,11 +722,17 @@ class ByteArray : public HeapObject { return b; } - bool IsByteArray() const { return true; } - friend class SMRegExpMacroAssembler; }; +// This is only used in assertions. In debug builds, we put a magic value +// in the header of each ByteArrayData, and assert here that it matches. +inline bool IsByteArray(Object obj) { + MOZ_ASSERT(ByteArray::cast(obj).inner()->magic() == + ByteArrayData::ExpectedMagic); + return true; +} + // This is a convenience class used in V8 for treating a ByteArray as an array // of fixed-size integers. This version supports integral types up to 32 bits. template <typename T> @@ -1030,6 +1057,7 @@ class JSRegExp : public HeapObject { }; using RegExpFlags = JS::RegExpFlags; +using RegExpFlag = JS::RegExpFlags::Flag; inline bool IsUnicode(RegExpFlags flags) { return flags.unicode(); } inline bool IsGlobal(RegExpFlags flags) { return flags.global(); } @@ -1042,6 +1070,22 @@ inline bool IsEitherUnicode(RegExpFlags flags) { return flags.unicode() || flags.unicodeSets(); } +inline base::Optional<RegExpFlag> TryRegExpFlagFromChar(char c) { + RegExpFlag flag; + + // The parser only calls this after verifying that it's a supported flag. + MOZ_ALWAYS_TRUE(JS::MaybeParseRegExpFlag(c, &flag)); + + return base::Optional(flag); +} + +inline bool operator==(const RegExpFlags& lhs, const int& rhs) { + return lhs.value() == rhs; +} +inline bool operator!=(const RegExpFlags& lhs, const int& rhs) { + return !(lhs == rhs); +} + class Histogram { public: inline void AddSample(int sample) {} @@ -1126,9 +1170,11 @@ class Isolate { // This is called from inside no-GC code. V8 runs the interrupt // inside the no-GC code and then "manually relocates unhandlified - // references" afterwards. We just return false and let the caller - // handle interrupts. - Object HandleInterrupts() { return Object(JS::BooleanValue(false)); } + // references" afterwards. We just return a magic value and let the + // caller handle interrupts. + Object HandleInterrupts() { + return Object(JS::MagicValue(JS_INTERRUPT_REGEXP)); + } JSContext* cx() const { return cx_; } diff --git a/js/src/irregexp/RegExpTypes.h b/js/src/irregexp/RegExpTypes.h index e2a619689c..620fac4ed5 100644 --- a/js/src/irregexp/RegExpTypes.h +++ b/js/src/irregexp/RegExpTypes.h @@ -21,15 +21,17 @@ namespace internal { class ByteArrayData { public: - uint32_t length; + ByteArrayData(uint32_t length) : length_(length) {} + + uint32_t length() { return length_; }; uint8_t* data(); uint8_t get(uint32_t index) { - MOZ_ASSERT(index < length); + MOZ_ASSERT(index < length()); return data()[index]; } void set(uint32_t index, uint8_t val) { - MOZ_ASSERT(index < length); + MOZ_ASSERT(index < length()); data()[index] = val; } @@ -39,9 +41,19 @@ class ByteArrayData { template <typename T> void setTyped(uint32_t index, T value); +#ifdef DEBUG + const static uint32_t ExpectedMagic = 0x12344321; + uint32_t magic() const { return magic_; } + + private: + uint32_t magic_ = ExpectedMagic; +#endif + private: template <typename T> T* typedData(); + + uint32_t length_; }; class Isolate; diff --git a/js/src/irregexp/imported/gen-regexp-special-case.cc b/js/src/irregexp/imported/gen-regexp-special-case.cc index 8f6557ed30..0875568250 100644 --- a/js/src/irregexp/imported/gen-regexp-special-case.cc +++ b/js/src/irregexp/imported/gen-regexp-special-case.cc @@ -8,7 +8,6 @@ #include <sstream> #include "irregexp/imported/special-case.h" -#include "unicode/usetiter.h" namespace v8 { namespace internal { @@ -126,52 +125,6 @@ void PrintSpecial(std::ofstream& out) { PrintSet(out, "SpecialAddSet", special_add); } -void PrintUnicodeSpecial(std::ofstream& out) { - icu::UnicodeSet non_simple_folding; - icu::UnicodeSet current; - UErrorCode status = U_ZERO_ERROR; - // Look at all characters except white spaces. - icu::UnicodeSet interestingCP(u"[^[:White_Space:]]", status); - CHECK_EQ(status, U_ZERO_ERROR); - icu::UnicodeSetIterator iter(interestingCP); - while (iter.next()) { - UChar32 c = iter.getCodepoint(); - current.set(c, c); - current.closeOver(USET_CASE_INSENSITIVE).removeAllStrings(); - CHECK(!current.isBogus()); - // Remove characters from the closeover that have a simple case folding. - icu::UnicodeSet toRemove; - icu::UnicodeSetIterator closeOverIter(current); - while (closeOverIter.next()) { - UChar32 closeOverChar = closeOverIter.getCodepoint(); - UChar32 closeOverSCF = u_foldCase(closeOverChar, U_FOLD_CASE_DEFAULT); - if (closeOverChar != closeOverSCF) { - toRemove.add(closeOverChar); - } - } - CHECK(!toRemove.isBogus()); - current.removeAll(toRemove); - - // The current character and its simple case folding are also always OK. - UChar32 scf = u_foldCase(c, U_FOLD_CASE_DEFAULT); - current.remove(c); - current.remove(scf); - - // If there are any characters remaining, they were added due to full case - // foldings and shouldn't match the current charcter according to the spec. - if (!current.isEmpty()) { - // Ensure that the character doesn't have a simple case folding. - // Otherwise the current approach of simply removing the character from - // the set before calling closeOver won't work. - CHECK_EQ(c, scf); - non_simple_folding.add(c); - } - } - CHECK(!non_simple_folding.isBogus()); - - PrintSet(out, "UnicodeNonSimpleCloseOverSet", non_simple_folding); -} - void WriteHeader(const char* header_filename) { std::ofstream out(header_filename); out << std::hex << std::setfill('0') << std::setw(4); @@ -192,7 +145,6 @@ void WriteHeader(const char* header_filename) { << "namespace internal {\n\n"; PrintSpecial(out); - PrintUnicodeSpecial(out); out << "\n" << "} // namespace internal\n" diff --git a/js/src/irregexp/imported/regexp-ast.cc b/js/src/irregexp/imported/regexp-ast.cc index 63eeb5c05d..34946bd80c 100644 --- a/js/src/irregexp/imported/regexp-ast.cc +++ b/js/src/irregexp/imported/regexp-ast.cc @@ -307,7 +307,7 @@ void* RegExpUnparser::VisitCapture(RegExpCapture* that, void* data) { } void* RegExpUnparser::VisitGroup(RegExpGroup* that, void* data) { - os_ << "(?: "; + os_ << "(?" << that->flags() << ": "; that->body()->Accept(this, data); os_ << ")"; return nullptr; @@ -325,7 +325,11 @@ void* RegExpUnparser::VisitLookaround(RegExpLookaround* that, void* data) { void* RegExpUnparser::VisitBackReference(RegExpBackReference* that, void* data) { - os_ << "(<- " << that->index() << ")"; + os_ << "(<- " << that->captures()->first()->index(); + for (int i = 1; i < that->captures()->length(); ++i) { + os_ << "," << that->captures()->at(i)->index(); + } + os_ << ")"; return nullptr; } @@ -406,10 +410,17 @@ RegExpClassSetExpression::RegExpClassSetExpression( may_contain_strings_(may_contain_strings), operands_(operands) { DCHECK_NOT_NULL(operands); - DCHECK_IMPLIES(is_negated_, !may_contain_strings_); - max_match_ = 0; - for (auto op : *operands) { - max_match_ = std::max(max_match_, op->max_match()); + if (is_negated) { + DCHECK(!may_contain_strings_); + // We don't know anything about max matches for negated classes. + // As there are no strings involved, assume that we can match a unicode + // character (2 code points). + max_match_ = 2; + } else { + max_match_ = 0; + for (auto op : *operands) { + max_match_ = std::max(max_match_, op->max_match()); + } } } diff --git a/js/src/irregexp/imported/regexp-ast.h b/js/src/irregexp/imported/regexp-ast.h index af90b1dda3..b2b88515d3 100644 --- a/js/src/irregexp/imported/regexp-ast.h +++ b/js/src/irregexp/imported/regexp-ast.h @@ -130,12 +130,6 @@ class CharacterRange { static void AddUnicodeCaseEquivalents(ZoneList<CharacterRange>* ranges, Zone* zone); -#ifdef V8_INTL_SUPPORT - // Creates the closeOver of the given UnicodeSet, removing all - // characters/strings that can't be derived via simple case folding. - static void UnicodeSimpleCloseOver(icu::UnicodeSet& set); -#endif // V8_INTL_SUPPORT - bool Contains(base::uc32 i) const { return from_ <= i && i <= to_; } base::uc32 from() const { return from_; } base::uc32 to() const { return to_; } @@ -311,9 +305,12 @@ class RegExpClassRanges final : public RegExpTree { // the specified ranges. // CONTAINS_SPLIT_SURROGATE: The character class contains part of a split // surrogate and should not be unicode-desugared (crbug.com/641091). + // IS_CASE_FOLDED: If case folding is required (/i), it was already + // performed on individual ranges and should not be applied again. enum Flag { NEGATED = 1 << 0, CONTAINS_SPLIT_SURROGATE = 1 << 1, + IS_CASE_FOLDED = 1 << 2, }; using ClassRangesFlags = base::Flags<Flag>; @@ -356,6 +353,9 @@ class RegExpClassRanges final : public RegExpTree { bool contains_split_surrogate() const { return (class_ranges_flags_ & CONTAINS_SPLIT_SURROGATE) != 0; } + bool is_case_folded() const { + return (class_ranges_flags_ & IS_CASE_FOLDED) != 0; + } private: CharacterSet set_; @@ -626,8 +626,9 @@ class RegExpCapture final : public RegExpTree { class RegExpGroup final : public RegExpTree { public: - explicit RegExpGroup(RegExpTree* body) + explicit RegExpGroup(RegExpTree* body, RegExpFlags flags) : body_(body), + flags_(flags), min_match_(body->min_match()), max_match_(body->max_match()) {} @@ -639,9 +640,11 @@ class RegExpGroup final : public RegExpTree { int max_match() override { return max_match_; } Interval CaptureRegisters() override { return body_->CaptureRegisters(); } RegExpTree* body() const { return body_; } + RegExpFlags flags() const { return flags_; } private: RegExpTree* body_; + const RegExpFlags flags_; int min_match_; int max_match_; }; @@ -651,12 +654,13 @@ class RegExpLookaround final : public RegExpTree { enum Type { LOOKAHEAD, LOOKBEHIND }; RegExpLookaround(RegExpTree* body, bool is_positive, int capture_count, - int capture_from, Type type) + int capture_from, Type type, int index) : body_(body), is_positive_(is_positive), capture_count_(capture_count), capture_from_(capture_from), - type_(type) {} + type_(type), + index_(index) {} DECL_BOILERPLATE(Lookaround); @@ -669,6 +673,7 @@ class RegExpLookaround final : public RegExpTree { int capture_count() const { return capture_count_; } int capture_from() const { return capture_from_; } Type type() const { return type_; } + int index() const { return index_; } class Builder { public: @@ -692,14 +697,17 @@ class RegExpLookaround final : public RegExpTree { int capture_count_; int capture_from_; Type type_; + int index_; }; class RegExpBackReference final : public RegExpTree { public: - explicit RegExpBackReference(RegExpFlags flags) : flags_(flags) {} - RegExpBackReference(RegExpCapture* capture, RegExpFlags flags) - : capture_(capture), flags_(flags) {} + explicit RegExpBackReference(Zone* zone) : captures_(1, zone) {} + explicit RegExpBackReference(RegExpCapture* capture, Zone* zone) + : captures_(1, zone) { + captures_.Add(capture, zone); + } DECL_BOILERPLATE(BackReference); @@ -707,16 +715,16 @@ class RegExpBackReference final : public RegExpTree { // The back reference may be recursive, e.g. /(\2)(\1)/. To avoid infinite // recursion, we give up. Ignorance is bliss. int max_match() override { return kInfinity; } - int index() const { return capture_->index(); } - RegExpCapture* capture() const { return capture_; } - void set_capture(RegExpCapture* capture) { capture_ = capture; } + const ZoneList<RegExpCapture*>* captures() const { return &captures_; } + void add_capture(RegExpCapture* capture, Zone* zone) { + captures_.Add(capture, zone); + } const ZoneVector<base::uc16>* name() const { return name_; } void set_name(const ZoneVector<base::uc16>* name) { name_ = name; } private: - RegExpCapture* capture_ = nullptr; + ZoneList<RegExpCapture*> captures_; const ZoneVector<base::uc16>* name_ = nullptr; - const RegExpFlags flags_; }; diff --git a/js/src/irregexp/imported/regexp-bytecode-generator.cc b/js/src/irregexp/imported/regexp-bytecode-generator.cc index c83e10a598..251ed1cda5 100644 --- a/js/src/irregexp/imported/regexp-bytecode-generator.cc +++ b/js/src/irregexp/imported/regexp-bytecode-generator.cc @@ -383,7 +383,7 @@ Handle<HeapObject> RegExpBytecodeGenerator::GetCode(Handle<String> source) { isolate_, zone(), source, buffer_.data(), length(), jump_edges_); } else { array = isolate_->factory()->NewByteArray(length()); - Copy(array->GetDataStartAddress()); + Copy(array->begin()); } return array; diff --git a/js/src/irregexp/imported/regexp-bytecode-peephole.cc b/js/src/irregexp/imported/regexp-bytecode-peephole.cc index ec8dcf1108..0ef0bab702 100644 --- a/js/src/irregexp/imported/regexp-bytecode-peephole.cc +++ b/js/src/irregexp/imported/regexp-bytecode-peephole.cc @@ -1012,13 +1012,13 @@ Handle<ByteArray> RegExpBytecodePeepholeOptimization::OptimizeBytecode( RegExpBytecodePeephole peephole(zone, length, jump_edges); bool did_optimize = peephole.OptimizeBytecode(bytecode, length); Handle<ByteArray> array = isolate->factory()->NewByteArray(peephole.Length()); - peephole.CopyOptimizedBytecode(array->GetDataStartAddress()); + peephole.CopyOptimizedBytecode(array->begin()); if (did_optimize && v8_flags.trace_regexp_peephole_optimization) { PrintF("Original Bytecode:\n"); RegExpBytecodeDisassemble(bytecode, length, source->ToCString().get()); PrintF("Optimized Bytecode:\n"); - RegExpBytecodeDisassemble(array->GetDataStartAddress(), peephole.Length(), + RegExpBytecodeDisassemble(array->begin(), peephole.Length(), source->ToCString().get()); } diff --git a/js/src/irregexp/imported/regexp-compiler-tonode.cc b/js/src/irregexp/imported/regexp-compiler-tonode.cc index f5087bdb08..b1340123d8 100644 --- a/js/src/irregexp/imported/regexp-compiler-tonode.cc +++ b/js/src/irregexp/imported/regexp-compiler-tonode.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "irregexp/imported/regexp-compiler.h" - #include "irregexp/imported/regexp.h" #ifdef V8_INTL_SUPPORT @@ -418,27 +417,6 @@ RegExpNode* UnanchoredAdvance(RegExpCompiler* compiler, } // namespace -#ifdef V8_INTL_SUPPORT -// static -void CharacterRange::UnicodeSimpleCloseOver(icu::UnicodeSet& set) { - // Remove characters for which closeOver() adds full-case-folding equivalents - // because we should work only with simple case folding mappings. - icu::UnicodeSet non_simple = icu::UnicodeSet(set); - non_simple.retainAll(RegExpCaseFolding::UnicodeNonSimpleCloseOverSet()); - set.removeAll(non_simple); - - set.closeOver(USET_CASE_INSENSITIVE); - // Full case folding maps single characters to multiple characters. - // Those are represented as strings in the set. Remove them so that - // we end up with only simple and common case mappings. - set.removeAllStrings(); - - // Add characters that have non-simple case foldings again (they match - // themselves). - set.addAll(non_simple); -} -#endif // V8_INTL_SUPPORT - // static void CharacterRange::AddUnicodeCaseEquivalents(ZoneList<CharacterRange>* ranges, Zone* zone) { @@ -460,8 +438,7 @@ void CharacterRange::AddUnicodeCaseEquivalents(ZoneList<CharacterRange>* ranges, } // Clear the ranges list without freeing the backing store. ranges->Rewind(0); - - UnicodeSimpleCloseOver(set); + set.closeOver(USET_SIMPLE_CASE_INSENSITIVE); for (int i = 0; i < set.getRangeCount(); i++) { ranges->Add(Range(set.getRangeStart(i), set.getRangeEnd(i)), zone); } @@ -476,7 +453,9 @@ RegExpNode* RegExpClassRanges::ToNode(RegExpCompiler* compiler, Zone* const zone = compiler->zone(); ZoneList<CharacterRange>* ranges = this->ranges(zone); - if (NeedsUnicodeCaseEquivalents(compiler->flags())) { + const bool needs_case_folding = + NeedsUnicodeCaseEquivalents(compiler->flags()) && !is_case_folded(); + if (needs_case_folding) { CharacterRange::AddUnicodeCaseEquivalents(ranges, zone); } @@ -487,8 +466,7 @@ RegExpNode* RegExpClassRanges::ToNode(RegExpCompiler* compiler, if (is_negated()) { // With /v, character classes are never negated. - // TODO(v8:11935): Change permalink once proposal is in stage 4. - // https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#sec-compileatom + // https://tc39.es/ecma262/#sec-compileatom // Atom :: CharacterClass // 4. Assert: cc.[[Invert]] is false. // Instead the complement is created when evaluating the class set. @@ -561,7 +539,12 @@ RegExpNode* RegExpClassSetOperand::ToNode(RegExpCompiler* compiler, } } if (!ranges()->is_empty()) { - alternatives->Add(zone->template New<RegExpClassRanges>(zone, ranges()), + // In unicode sets mode case folding has to be done at precise locations + // (e.g. before building complements). + // It is therefore the parsers responsibility to case fold (sub-) ranges + // before creating ClassSetOperands. + alternatives->Add(zone->template New<RegExpClassRanges>( + zone, ranges(), RegExpClassRanges::IS_CASE_FOLDED), zone); } if (empty_string != nullptr) { @@ -1034,9 +1017,8 @@ namespace { // \B to (?<=\w)(?=\w)|(?<=\W)(?=\W) RegExpNode* BoundaryAssertionAsLookaround(RegExpCompiler* compiler, RegExpNode* on_success, - RegExpAssertion::Type type, - RegExpFlags flags) { - CHECK(NeedsUnicodeCaseEquivalents(flags)); + RegExpAssertion::Type type) { + CHECK(NeedsUnicodeCaseEquivalents(compiler->flags())); Zone* zone = compiler->zone(); ZoneList<CharacterRange>* word_range = zone->New<ZoneList<CharacterRange>>(2, zone); @@ -1080,14 +1062,13 @@ RegExpNode* RegExpAssertion::ToNode(RegExpCompiler* compiler, return AssertionNode::AtStart(on_success); case Type::BOUNDARY: return NeedsUnicodeCaseEquivalents(compiler->flags()) - ? BoundaryAssertionAsLookaround( - compiler, on_success, Type::BOUNDARY, compiler->flags()) + ? BoundaryAssertionAsLookaround(compiler, on_success, + Type::BOUNDARY) : AssertionNode::AtBoundary(on_success); case Type::NON_BOUNDARY: return NeedsUnicodeCaseEquivalents(compiler->flags()) ? BoundaryAssertionAsLookaround(compiler, on_success, - Type::NON_BOUNDARY, - compiler->flags()) + Type::NON_BOUNDARY) : AssertionNode::AtNonBoundary(on_success); case Type::END_OF_INPUT: return AssertionNode::AtEnd(on_success); @@ -1130,10 +1111,17 @@ RegExpNode* RegExpAssertion::ToNode(RegExpCompiler* compiler, RegExpNode* RegExpBackReference::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) { - return compiler->zone()->New<BackReferenceNode>( - RegExpCapture::StartRegister(index()), - RegExpCapture::EndRegister(index()), flags_, compiler->read_backward(), - on_success); + RegExpNode* backref_node = on_success; + // Only one of the captures in the list can actually match. Since + // back-references to unmatched captures are treated as empty, we can simply + // create back-references to all possible captures. + for (auto capture : *captures()) { + backref_node = compiler->zone()->New<BackReferenceNode>( + RegExpCapture::StartRegister(capture->index()), + RegExpCapture::EndRegister(capture->index()), compiler->read_backward(), + backref_node); + } + return backref_node; } RegExpNode* RegExpEmpty::ToNode(RegExpCompiler* compiler, @@ -1141,9 +1129,40 @@ RegExpNode* RegExpEmpty::ToNode(RegExpCompiler* compiler, return on_success; } +namespace { + +class V8_NODISCARD ModifiersScope { + public: + ModifiersScope(RegExpCompiler* compiler, RegExpFlags flags) + : compiler_(compiler), previous_flags_(compiler->flags()) { + compiler->set_flags(flags); + } + ~ModifiersScope() { compiler_->set_flags(previous_flags_); } + + private: + RegExpCompiler* compiler_; + const RegExpFlags previous_flags_; +}; + +} // namespace + RegExpNode* RegExpGroup::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) { - return body_->ToNode(compiler, on_success); + // If no flags are modified, simply convert and return the body. + if (flags() == compiler->flags()) { + return body_->ToNode(compiler, on_success); + } + // Reset flags for successor node. + const RegExpFlags old_flags = compiler->flags(); + on_success = ActionNode::ModifyFlags(old_flags, on_success); + + // Convert body using modifier. + ModifiersScope modifiers_scope(compiler, flags()); + RegExpNode* body = body_->ToNode(compiler, on_success); + + // Wrap body into modifier node. + RegExpNode* modified_body = ActionNode::ModifyFlags(flags(), body); + return modified_body; } RegExpLookaround::Builder::Builder(bool is_positive, RegExpNode* on_success, diff --git a/js/src/irregexp/imported/regexp-compiler.cc b/js/src/irregexp/imported/regexp-compiler.cc index 514975d8ed..73dfe1d2ad 100644 --- a/js/src/irregexp/imported/regexp-compiler.cc +++ b/js/src/irregexp/imported/regexp-compiler.cc @@ -707,6 +707,13 @@ ActionNode* ActionNode::EmptyMatchCheck(int start_register, return result; } +ActionNode* ActionNode::ModifyFlags(RegExpFlags flags, RegExpNode* on_success) { + ActionNode* result = + on_success->zone()->New<ActionNode>(MODIFY_FLAGS, on_success); + result->data_.u_modify_flags.flags = flags; + return result; +} + #define DEFINE_ACCEPT(Type) \ void Type##Node::Accept(NodeVisitor* visitor) { visitor->Visit##Type(this); } FOR_EACH_NODE_TYPE(DEFINE_ACCEPT) @@ -1377,6 +1384,9 @@ void ActionNode::GetQuickCheckDetails(QuickCheckDetails* details, on_success()->GetQuickCheckDetailsFromLoopEntry(details, compiler, filled_in, not_at_start); } else { + if (action_type() == MODIFY_FLAGS) { + compiler->set_flags(flags()); + } on_success()->GetQuickCheckDetails(details, compiler, filled_in, not_at_start); } @@ -2867,7 +2877,7 @@ int BoyerMooreLookahead::GetSkipTable(int min_lookahead, int max_lookahead, const int kSkipArrayEntry = 0; const int kDontSkipArrayEntry = 1; - std::memset(boolean_skip_table->GetDataStartAddress(), kSkipArrayEntry, + std::memset(boolean_skip_table->begin(), kSkipArrayEntry, boolean_skip_table->length()); for (int i = max_lookahead; i >= min_lookahead; i--) { @@ -3454,6 +3464,11 @@ void ActionNode::Emit(RegExpCompiler* compiler, Trace* trace) { assembler->Backtrack(); return; } + case MODIFY_FLAGS: { + compiler->set_flags(flags()); + on_success()->Emit(compiler, trace); + break; + } default: UNREACHABLE(); } @@ -3473,8 +3488,8 @@ void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) { RecursionCheck rc(compiler); DCHECK_EQ(start_reg_ + 1, end_reg_); - if (IsIgnoreCase(flags_)) { - bool unicode = IsEitherUnicode(flags_); + if (IsIgnoreCase(compiler->flags())) { + bool unicode = IsEitherUnicode(compiler->flags()); assembler->CheckNotBackReferenceIgnoreCase(start_reg_, read_backward(), unicode, trace->backtrack()); } else { @@ -3485,7 +3500,7 @@ void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) { if (read_backward()) trace->set_at_start(Trace::UNKNOWN); // Check that the back reference does not end inside a surrogate pair. - if (IsEitherUnicode(flags_) && !compiler->one_byte()) { + if (IsEitherUnicode(compiler->flags()) && !compiler->one_byte()) { assembler->CheckNotInSurrogatePair(trace->cp_offset(), trace->backtrack()); } on_success()->Emit(compiler, trace); @@ -3707,7 +3722,7 @@ class Analysis : public NodeVisitor { } while (false) void VisitText(TextNode* that) override { - that->MakeCaseIndependent(isolate(), is_one_byte_, flags_); + that->MakeCaseIndependent(isolate(), is_one_byte_, flags()); EnsureAnalyzed(that->on_success()); if (has_failed()) return; that->CalculateOffsets(); @@ -3715,6 +3730,9 @@ class Analysis : public NodeVisitor { } void VisitAction(ActionNode* that) override { + if (that->action_type() == ActionNode::MODIFY_FLAGS) { + set_flags(that->flags()); + } EnsureAnalyzed(that->on_success()); if (has_failed()) return; STATIC_FOR_EACH(Propagators::VisitAction(that)); @@ -3773,9 +3791,12 @@ class Analysis : public NodeVisitor { #undef STATIC_FOR_EACH private: + RegExpFlags flags() const { return flags_; } + void set_flags(RegExpFlags flags) { flags_ = flags; } + Isolate* isolate_; const bool is_one_byte_; - const RegExpFlags flags_; + RegExpFlags flags_; RegExpError error_; DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis); @@ -3903,13 +3924,12 @@ RegExpNode* RegExpCompiler::OptionallyStepBackToLeadSurrogate( } RegExpNode* RegExpCompiler::PreprocessRegExp(RegExpCompileData* data, - RegExpFlags flags, bool is_one_byte) { // Wrap the body of the regexp in capture #0. RegExpNode* captured_body = RegExpCapture::ToNode(data->tree, 0, this, accept()); RegExpNode* node = captured_body; - if (!data->tree->IsAnchoredAtStart() && !IsSticky(flags)) { + if (!data->tree->IsAnchoredAtStart() && !IsSticky(flags())) { // Add a .*? at the beginning, outside the body capture, unless // this expression is anchored at the beginning or sticky. RegExpNode* loop_node = RegExpQuantifier::ToNode( @@ -3931,13 +3951,14 @@ RegExpNode* RegExpCompiler::PreprocessRegExp(RegExpCompileData* data, } } if (is_one_byte) { - node = node->FilterOneByte(RegExpCompiler::kMaxRecursion, flags); + node = node->FilterOneByte(RegExpCompiler::kMaxRecursion, flags()); // Do it again to propagate the new nodes to places where they were not // put because they had not been calculated yet. if (node != nullptr) { - node = node->FilterOneByte(RegExpCompiler::kMaxRecursion, flags); + node = node->FilterOneByte(RegExpCompiler::kMaxRecursion, flags()); } - } else if (IsEitherUnicode(flags) && (IsGlobal(flags) || IsSticky(flags))) { + } else if (IsEitherUnicode(flags()) && + (IsGlobal(flags()) || IsSticky(flags()))) { node = OptionallyStepBackToLeadSurrogate(node); } diff --git a/js/src/irregexp/imported/regexp-compiler.h b/js/src/irregexp/imported/regexp-compiler.h index 91dd43ab8a..7a369430bb 100644 --- a/js/src/irregexp/imported/regexp-compiler.h +++ b/js/src/irregexp/imported/regexp-compiler.h @@ -501,8 +501,7 @@ class RegExpCompiler { // - Inserting the implicit .* before/after the regexp if necessary. // - If the input is a one-byte string, filtering out nodes that can't match. // - Fixing up regexp matches that start within a surrogate pair. - RegExpNode* PreprocessRegExp(RegExpCompileData* data, RegExpFlags flags, - bool is_one_byte); + RegExpNode* PreprocessRegExp(RegExpCompileData* data, bool is_one_byte); // If the regexp matching starts within a surrogate pair, step back to the // lead surrogate and start matching from there. @@ -527,7 +526,8 @@ class RegExpCompiler { inline void IncrementRecursionDepth() { recursion_depth_++; } inline void DecrementRecursionDepth() { recursion_depth_--; } - RegExpFlags flags() const { return flags_; } + inline RegExpFlags flags() const { return flags_; } + inline void set_flags(RegExpFlags flags) { flags_ = flags; } void SetRegExpTooBig() { reg_exp_too_big_ = true; } @@ -571,7 +571,7 @@ class RegExpCompiler { int unicode_lookaround_position_register_; ZoneVector<RegExpNode*>* work_list_; int recursion_depth_; - const RegExpFlags flags_; + RegExpFlags flags_; RegExpMacroAssembler* macro_assembler_; bool one_byte_; bool reg_exp_too_big_; diff --git a/js/src/irregexp/imported/regexp-dotprinter.cc b/js/src/irregexp/imported/regexp-dotprinter.cc index 6746992a0a..cd0ca5dea8 100644 --- a/js/src/irregexp/imported/regexp-dotprinter.cc +++ b/js/src/irregexp/imported/regexp-dotprinter.cc @@ -231,6 +231,10 @@ void DotPrinterImpl::VisitAction(ActionNode* that) { << "\", shape=septagon"; break; } + case ActionNode::MODIFY_FLAGS: { + os_ << "label=\"flags $" << that->flags() << "\", shape=septagon"; + break; + } } os_ << "];\n"; PrintAttributes(that); diff --git a/js/src/irregexp/imported/regexp-interpreter.cc b/js/src/irregexp/imported/regexp-interpreter.cc index 43c8a4a5a4..2de1b12968 100644 --- a/js/src/irregexp/imported/regexp-interpreter.cc +++ b/js/src/irregexp/imported/regexp-interpreter.cc @@ -88,8 +88,7 @@ int32_t Load32Aligned(const uint8_t* pc) { return *reinterpret_cast<const int32_t*>(pc); } -// TODO(jgruber): Rename to Load16AlignedUnsigned. -uint32_t Load16Aligned(const uint8_t* pc) { +uint32_t Load16AlignedUnsigned(const uint8_t* pc) { DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1); return *reinterpret_cast<const uint16_t*>(pc); } @@ -221,17 +220,17 @@ IrregexpInterpreter::Result MaybeThrowStackOverflow( template <typename Char> void UpdateCodeAndSubjectReferences( Isolate* isolate, Handle<ByteArray> code_array, - Handle<String> subject_string, ByteArray* code_array_out, + Handle<String> subject_string, Tagged<ByteArray>* code_array_out, const uint8_t** code_base_out, const uint8_t** pc_out, - String* subject_string_out, + Tagged<String>* subject_string_out, base::Vector<const Char>* subject_string_vector_out) { DisallowGarbageCollection no_gc; - if (*code_base_out != code_array->GetDataStartAddress()) { + if (*code_base_out != code_array->begin()) { *code_array_out = *code_array; const intptr_t pc_offset = *pc_out - *code_base_out; DCHECK_GT(pc_offset, 0); - *code_base_out = code_array->GetDataStartAddress(); + *code_base_out = code_array->begin(); *pc_out = *code_base_out + pc_offset; } @@ -244,8 +243,9 @@ void UpdateCodeAndSubjectReferences( // necessary. template <typename Char> IrregexpInterpreter::Result HandleInterrupts( - Isolate* isolate, RegExp::CallOrigin call_origin, ByteArray* code_array_out, - String* subject_string_out, const uint8_t** code_base_out, + Isolate* isolate, RegExp::CallOrigin call_origin, + Tagged<ByteArray>* code_array_out, Tagged<String>* subject_string_out, + const uint8_t** code_base_out, base::Vector<const Char>* subject_string_vector_out, const uint8_t** pc_out) { DisallowGarbageCollection no_gc; @@ -276,12 +276,12 @@ IrregexpInterpreter::Result HandleInterrupts( } else if (check.InterruptRequested()) { const bool was_one_byte = String::IsOneByteRepresentationUnderneath(*subject_string_out); - Object result; + Tagged<Object> result; { AllowGarbageCollection yes_gc; result = isolate->stack_guard()->HandleInterrupts(); } - if (result.IsException(isolate)) { + if (IsException(result, isolate)) { return IrregexpInterpreter::EXCEPTION; } @@ -375,10 +375,10 @@ bool IndexIsInBounds(int index, int length) { template <typename Char> IrregexpInterpreter::Result RawMatch( - Isolate* isolate, ByteArray code_array, String subject_string, - base::Vector<const Char> subject, int* output_registers, - int output_register_count, int total_register_count, int current, - uint32_t current_char, RegExp::CallOrigin call_origin, + Isolate* isolate, Tagged<ByteArray> code_array, + Tagged<String> subject_string, base::Vector<const Char> subject, + int* output_registers, int output_register_count, int total_register_count, + int current, uint32_t current_char, RegExp::CallOrigin call_origin, const uint32_t backtrack_limit) { DisallowGarbageCollection no_gc; @@ -430,7 +430,7 @@ IrregexpInterpreter::Result RawMatch( #endif // V8_USE_COMPUTED_GOTO - const uint8_t* pc = code_array.GetDataStartAddress(); + const uint8_t* pc = code_array->begin(); const uint8_t* code_base = pc; InterpreterRegisters registers(total_register_count, output_registers, @@ -702,8 +702,8 @@ IrregexpInterpreter::Result RawMatch( } BYTECODE(MINUS_AND_CHECK_NOT_CHAR) { uint32_t c = LoadPacked24Unsigned(insn); - uint32_t minus = Load16Aligned(pc + 4); - uint32_t mask = Load16Aligned(pc + 6); + uint32_t minus = Load16AlignedUnsigned(pc + 4); + uint32_t mask = Load16AlignedUnsigned(pc + 6); if (c != ((current_char - minus) & mask)) { SET_PC_FROM_OFFSET(Load32Aligned(pc + 8)); } else { @@ -712,8 +712,8 @@ IrregexpInterpreter::Result RawMatch( DISPATCH(); } BYTECODE(CHECK_CHAR_IN_RANGE) { - uint32_t from = Load16Aligned(pc + 4); - uint32_t to = Load16Aligned(pc + 6); + uint32_t from = Load16AlignedUnsigned(pc + 4); + uint32_t to = Load16AlignedUnsigned(pc + 6); if (from <= current_char && current_char <= to) { SET_PC_FROM_OFFSET(Load32Aligned(pc + 8)); } else { @@ -722,8 +722,8 @@ IrregexpInterpreter::Result RawMatch( DISPATCH(); } BYTECODE(CHECK_CHAR_NOT_IN_RANGE) { - uint32_t from = Load16Aligned(pc + 4); - uint32_t to = Load16Aligned(pc + 6); + uint32_t from = Load16AlignedUnsigned(pc + 4); + uint32_t to = Load16AlignedUnsigned(pc + 6); if (from > current_char || current_char > to) { SET_PC_FROM_OFFSET(Load32Aligned(pc + 8)); } else { @@ -914,7 +914,7 @@ IrregexpInterpreter::Result RawMatch( BYTECODE(SKIP_UNTIL_CHAR) { int32_t load_offset = LoadPacked24Signed(insn); int32_t advance = Load16AlignedSigned(pc + 4); - uint32_t c = Load16Aligned(pc + 6); + uint32_t c = Load16AlignedUnsigned(pc + 6); while (IndexIsInBounds(current + load_offset, subject.length())) { current_char = subject[current + load_offset]; if (c == current_char) { @@ -929,7 +929,7 @@ IrregexpInterpreter::Result RawMatch( BYTECODE(SKIP_UNTIL_CHAR_AND) { int32_t load_offset = LoadPacked24Signed(insn); int32_t advance = Load16AlignedSigned(pc + 4); - uint16_t c = Load16Aligned(pc + 6); + uint16_t c = Load16AlignedUnsigned(pc + 6); uint32_t mask = Load32Aligned(pc + 8); int32_t maximum_offset = Load32Aligned(pc + 12); while (static_cast<uintptr_t>(current + maximum_offset) <= @@ -947,7 +947,7 @@ IrregexpInterpreter::Result RawMatch( BYTECODE(SKIP_UNTIL_CHAR_POS_CHECKED) { int32_t load_offset = LoadPacked24Signed(insn); int32_t advance = Load16AlignedSigned(pc + 4); - uint16_t c = Load16Aligned(pc + 6); + uint16_t c = Load16AlignedUnsigned(pc + 6); int32_t maximum_offset = Load32Aligned(pc + 8); while (static_cast<uintptr_t>(current + maximum_offset) <= static_cast<uintptr_t>(subject.length())) { @@ -979,7 +979,7 @@ IrregexpInterpreter::Result RawMatch( BYTECODE(SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE) { int32_t load_offset = LoadPacked24Signed(insn); int32_t advance = Load16AlignedSigned(pc + 4); - uint16_t limit = Load16Aligned(pc + 6); + uint16_t limit = Load16AlignedUnsigned(pc + 6); const uint8_t* table = pc + 8; while (IndexIsInBounds(current + load_offset, subject.length())) { current_char = subject[current + load_offset]; @@ -999,8 +999,8 @@ IrregexpInterpreter::Result RawMatch( BYTECODE(SKIP_UNTIL_CHAR_OR_CHAR) { int32_t load_offset = LoadPacked24Signed(insn); int32_t advance = Load32Aligned(pc + 4); - uint16_t c = Load16Aligned(pc + 8); - uint16_t c2 = Load16Aligned(pc + 10); + uint16_t c = Load16AlignedUnsigned(pc + 8); + uint16_t c2 = Load16AlignedUnsigned(pc + 10); while (IndexIsInBounds(current + load_offset, subject.length())) { current_char = subject[current + load_offset]; // The two if-statements below are split up intentionally, as combining @@ -1047,29 +1047,29 @@ IrregexpInterpreter::Result RawMatch( // static IrregexpInterpreter::Result IrregexpInterpreter::Match( - Isolate* isolate, JSRegExp regexp, String subject_string, + Isolate* isolate, Tagged<JSRegExp> regexp, Tagged<String> subject_string, int* output_registers, int output_register_count, int start_position, RegExp::CallOrigin call_origin) { - if (v8_flags.regexp_tier_up) regexp.TierUpTick(); + if (v8_flags.regexp_tier_up) regexp->TierUpTick(); bool is_one_byte = String::IsOneByteRepresentationUnderneath(subject_string); - ByteArray code_array = ByteArray::cast(regexp.bytecode(is_one_byte)); - int total_register_count = regexp.max_register_count(); + Tagged<ByteArray> code_array = ByteArray::cast(regexp->bytecode(is_one_byte)); + int total_register_count = regexp->max_register_count(); return MatchInternal(isolate, code_array, subject_string, output_registers, output_register_count, total_register_count, - start_position, call_origin, regexp.backtrack_limit()); + start_position, call_origin, regexp->backtrack_limit()); } IrregexpInterpreter::Result IrregexpInterpreter::MatchInternal( - Isolate* isolate, ByteArray code_array, String subject_string, - int* output_registers, int output_register_count, int total_register_count, - int start_position, RegExp::CallOrigin call_origin, - uint32_t backtrack_limit) { - DCHECK(subject_string.IsFlat()); + Isolate* isolate, Tagged<ByteArray> code_array, + Tagged<String> subject_string, int* output_registers, + int output_register_count, int total_register_count, int start_position, + RegExp::CallOrigin call_origin, uint32_t backtrack_limit) { + DCHECK(subject_string->IsFlat()); // TODO(chromium:1262676): Remove this CHECK once fixed. - CHECK(code_array.IsByteArray()); + CHECK(IsByteArray(code_array)); // Note: Heap allocation *is* allowed in two situations if calling from // Runtime: @@ -1080,7 +1080,7 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchInternal( DisallowGarbageCollection no_gc; base::uc16 previous_char = '\n'; - String::FlatContent subject_content = subject_string.GetFlatContent(no_gc); + String::FlatContent subject_content = subject_string->GetFlatContent(no_gc); // Because interrupts can result in GC and string content relocation, the // checksum verification in FlatContent may fail even though this code is // safe. See (2) above. @@ -1122,10 +1122,10 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs( DisallowHandleAllocation no_handles; DisallowHandleDereference no_deref; - String subject_string = String::cast(Object(subject)); - JSRegExp regexp_obj = JSRegExp::cast(Object(regexp)); + Tagged<String> subject_string = String::cast(Tagged<Object>(subject)); + Tagged<JSRegExp> regexp_obj = JSRegExp::cast(Tagged<Object>(regexp)); - if (regexp_obj.MarkedForTierUp()) { + if (regexp_obj->MarkedForTierUp()) { // Returning RETRY will re-enter through runtime, where actual recompilation // for tier-up takes place. return IrregexpInterpreter::RETRY; diff --git a/js/src/irregexp/imported/regexp-interpreter.h b/js/src/irregexp/imported/regexp-interpreter.h index bc55be2b8c..825916291f 100644 --- a/js/src/irregexp/imported/regexp-interpreter.h +++ b/js/src/irregexp/imported/regexp-interpreter.h @@ -49,17 +49,18 @@ class V8_EXPORT_PRIVATE IrregexpInterpreter : public AllStatic { RegExp::CallOrigin call_origin, Isolate* isolate, Address regexp); - static Result MatchInternal(Isolate* isolate, ByteArray code_array, - String subject_string, int* output_registers, - int output_register_count, + static Result MatchInternal(Isolate* isolate, Tagged<ByteArray> code_array, + Tagged<String> subject_string, + int* output_registers, int output_register_count, int total_register_count, int start_position, RegExp::CallOrigin call_origin, uint32_t backtrack_limit); private: - static Result Match(Isolate* isolate, JSRegExp regexp, String subject_string, - int* output_registers, int output_register_count, - int start_position, RegExp::CallOrigin call_origin); + static Result Match(Isolate* isolate, Tagged<JSRegExp> regexp, + Tagged<String> subject_string, int* output_registers, + int output_register_count, int start_position, + RegExp::CallOrigin call_origin); }; } // namespace internal diff --git a/js/src/irregexp/imported/regexp-macro-assembler.cc b/js/src/irregexp/imported/regexp-macro-assembler.cc index b4d99bf775..b99c08424e 100644 --- a/js/src/irregexp/imported/regexp-macro-assembler.cc +++ b/js/src/irregexp/imported/regexp-macro-assembler.cc @@ -182,24 +182,25 @@ uint32_t RegExpMacroAssembler::IsCharacterInRangeArray(uint32_t current_char, static constexpr uint32_t kTrue = 1; static constexpr uint32_t kFalse = 0; - FixedUInt16Array ranges = FixedUInt16Array::cast(Object(raw_byte_array)); - DCHECK_GE(ranges.length(), 1); + Tagged<FixedUInt16Array> ranges = + FixedUInt16Array::cast(Tagged<Object>(raw_byte_array)); + DCHECK_GE(ranges->length(), 1); // Shortcut for fully out of range chars. - if (current_char < ranges.get(0)) return kFalse; - if (current_char >= ranges.get(ranges.length() - 1)) { + if (current_char < ranges->get(0)) return kFalse; + if (current_char >= ranges->get(ranges->length() - 1)) { // The last range may be open-ended. - return (ranges.length() % 2) == 0 ? kFalse : kTrue; + return (ranges->length() % 2) == 0 ? kFalse : kTrue; } // Binary search for the matching range. `ranges` is encoded as // [from0, to0, from1, to1, ..., fromN, toN], or // [from0, to0, from1, to1, ..., fromN] (open-ended last interval). - int mid, lower = 0, upper = ranges.length(); + int mid, lower = 0, upper = ranges->length(); do { mid = lower + (upper - lower) / 2; - const base::uc16 elem = ranges.get(mid); + const base::uc16 elem = ranges->get(mid); if (current_char < elem) { upper = mid; } else if (current_char > elem) { @@ -210,7 +211,7 @@ uint32_t RegExpMacroAssembler::IsCharacterInRangeArray(uint32_t current_char, } } while (lower < upper); - const bool current_char_ge_last_elem = current_char >= ranges.get(mid); + const bool current_char_ge_last_elem = current_char >= ranges->get(mid); const int current_range_start_index = current_char_ge_last_elem ? mid : mid - 1; @@ -277,15 +278,16 @@ bool NativeRegExpMacroAssembler::CanReadUnaligned() const { // static int NativeRegExpMacroAssembler::CheckStackGuardState( Isolate* isolate, int start_index, RegExp::CallOrigin call_origin, - Address* return_address, InstructionStream re_code, Address* subject, - const uint8_t** input_start, const uint8_t** input_end) { + Address* return_address, Tagged<InstructionStream> re_code, + Address* subject, const uint8_t** input_start, const uint8_t** input_end, + uintptr_t gap) { DisallowGarbageCollection no_gc; Address old_pc = PointerAuthentication::AuthenticatePC(return_address, 0); - DCHECK_LE(re_code.instruction_start(), old_pc); - DCHECK_LE(old_pc, re_code.code(kAcquireLoad).instruction_end()); + DCHECK_LE(re_code->instruction_start(), old_pc); + DCHECK_LE(old_pc, re_code->code(kAcquireLoad)->instruction_end()); StackLimitCheck check(isolate); - bool js_has_overflowed = check.JsHasOverflowed(); + bool js_has_overflowed = check.JsHasOverflowed(gap); if (call_origin == RegExp::CallOrigin::kFromJs) { // Direct calls from JavaScript can be interrupted in two ways: @@ -310,7 +312,8 @@ int NativeRegExpMacroAssembler::CheckStackGuardState( // Prepare for possible GC. HandleScope handles(isolate); Handle<InstructionStream> code_handle(re_code, isolate); - Handle<String> subject_handle(String::cast(Object(*subject)), isolate); + Handle<String> subject_handle(String::cast(Tagged<Object>(*subject)), + isolate); bool is_one_byte = String::IsOneByteRepresentationUnderneath(*subject_handle); int return_value = 0; @@ -322,8 +325,8 @@ int NativeRegExpMacroAssembler::CheckStackGuardState( return_value = EXCEPTION; } else if (check.InterruptRequested()) { AllowGarbageCollection yes_gc; - Object result = isolate->stack_guard()->HandleInterrupts(); - if (result.IsException(isolate)) return_value = EXCEPTION; + Tagged<Object> result = isolate->stack_guard()->HandleInterrupts(); + if (IsException(result, isolate)) return_value = EXCEPTION; } // We are not using operator == here because it does a slow DCHECK @@ -371,34 +374,34 @@ int NativeRegExpMacroAssembler::Match(Handle<JSRegExp> regexp, // DisallowGarbageCollection, since regexps might be preempted, and another // thread might do allocation anyway. - String subject_ptr = *subject; + Tagged<String> subject_ptr = *subject; // Character offsets into string. int start_offset = previous_index; - int char_length = subject_ptr.length() - start_offset; + int char_length = subject_ptr->length() - start_offset; int slice_offset = 0; // The string has been flattened, so if it is a cons string it contains the // full string in the first part. if (StringShape(subject_ptr).IsCons()) { - DCHECK_EQ(0, ConsString::cast(subject_ptr).second().length()); - subject_ptr = ConsString::cast(subject_ptr).first(); + DCHECK_EQ(0, ConsString::cast(subject_ptr)->second()->length()); + subject_ptr = ConsString::cast(subject_ptr)->first(); } else if (StringShape(subject_ptr).IsSliced()) { - SlicedString slice = SlicedString::cast(subject_ptr); - subject_ptr = slice.parent(); - slice_offset = slice.offset(); + Tagged<SlicedString> slice = SlicedString::cast(subject_ptr); + subject_ptr = slice->parent(); + slice_offset = slice->offset(); } if (StringShape(subject_ptr).IsThin()) { - subject_ptr = ThinString::cast(subject_ptr).actual(); + subject_ptr = ThinString::cast(subject_ptr)->actual(); } // Ensure that an underlying string has the same representation. - bool is_one_byte = subject_ptr.IsOneByteRepresentation(); - DCHECK(subject_ptr.IsExternalString() || subject_ptr.IsSeqString()); + bool is_one_byte = subject_ptr->IsOneByteRepresentation(); + DCHECK(IsExternalString(subject_ptr) || IsSeqString(subject_ptr)); // String is now either Sequential or External int char_size_shift = is_one_byte ? 0 : 1; DisallowGarbageCollection no_gc; const uint8_t* input_start = - subject_ptr.AddressOfCharacterAt(start_offset + slice_offset, no_gc); + subject_ptr->AddressOfCharacterAt(start_offset + slice_offset, no_gc); int byte_length = char_length << char_size_shift; const uint8_t* input_end = input_start + byte_length; return Execute(*subject, start_offset, input_start, input_end, offsets_vector, @@ -407,9 +410,9 @@ int NativeRegExpMacroAssembler::Match(Handle<JSRegExp> regexp, // static int NativeRegExpMacroAssembler::ExecuteForTesting( - String input, int start_offset, const uint8_t* input_start, + Tagged<String> input, int start_offset, const uint8_t* input_start, const uint8_t* input_end, int* output, int output_size, Isolate* isolate, - JSRegExp regexp) { + Tagged<JSRegExp> regexp) { return Execute(input, start_offset, input_start, input_end, output, output_size, isolate, regexp); } @@ -419,13 +422,14 @@ int NativeRegExpMacroAssembler::ExecuteForTesting( // the signature of the interpreter. We should get rid of JS objects passed to // internal methods. int NativeRegExpMacroAssembler::Execute( - String input, // This needs to be the unpacked (sliced, cons) string. + Tagged<String> + input, // This needs to be the unpacked (sliced, cons) string. int start_offset, const uint8_t* input_start, const uint8_t* input_end, - int* output, int output_size, Isolate* isolate, JSRegExp regexp) { + int* output, int output_size, Isolate* isolate, Tagged<JSRegExp> regexp) { RegExpStackScope stack_scope(isolate); bool is_one_byte = String::IsOneByteRepresentationUnderneath(input); - Code code = Code::cast(regexp.code(is_one_byte)); + Tagged<Code> code = Code::cast(regexp->code(isolate, is_one_byte)); RegExp::CallOrigin call_origin = RegExp::CallOrigin::kFromRuntime; using RegexpMatcherSig = @@ -439,7 +443,7 @@ int NativeRegExpMacroAssembler::Execute( output, output_size, call_origin, isolate, regexp.ptr()); DCHECK_GE(result, SMALLEST_REGEXP_RESULT); - if (result == EXCEPTION && !isolate->has_pending_exception()) { + if (result == EXCEPTION && !isolate->has_exception()) { // We detected a stack overflow (on the backtrack stack) in RegExp code, // but haven't created the exception yet. Additionally, we allow heap // allocation because even though it invalidates {input_start} and diff --git a/js/src/irregexp/imported/regexp-macro-assembler.h b/js/src/irregexp/imported/regexp-macro-assembler.h index af7e4f5297..6863adbaff 100644 --- a/js/src/irregexp/imported/regexp-macro-assembler.h +++ b/js/src/irregexp/imported/regexp-macro-assembler.h @@ -301,12 +301,10 @@ class NativeRegExpMacroAssembler: public RegExpMacroAssembler { int* offsets_vector, int offsets_vector_length, int previous_index, Isolate* isolate); - V8_EXPORT_PRIVATE static int ExecuteForTesting(String input, int start_offset, - const uint8_t* input_start, - const uint8_t* input_end, - int* output, int output_size, - Isolate* isolate, - JSRegExp regexp); + V8_EXPORT_PRIVATE static int ExecuteForTesting( + Tagged<String> input, int start_offset, const uint8_t* input_start, + const uint8_t* input_end, int* output, int output_size, Isolate* isolate, + Tagged<JSRegExp> regexp); bool CanReadUnaligned() const override; @@ -330,9 +328,9 @@ class NativeRegExpMacroAssembler: public RegExpMacroAssembler { static int CheckStackGuardState(Isolate* isolate, int start_index, RegExp::CallOrigin call_origin, Address* return_address, - InstructionStream re_code, Address* subject, - const uint8_t** input_start, - const uint8_t** input_end); + Tagged<InstructionStream> re_code, + Address* subject, const uint8_t** input_start, + const uint8_t** input_end, uintptr_t gap); static Address word_character_map_address() { return reinterpret_cast<Address>(&word_character_map[0]); @@ -348,9 +346,10 @@ class NativeRegExpMacroAssembler: public RegExpMacroAssembler { private: // Returns a {Result} sentinel, or the number of successful matches. - static int Execute(String input, int start_offset, const uint8_t* input_start, - const uint8_t* input_end, int* output, int output_size, - Isolate* isolate, JSRegExp regexp); + static int Execute(Tagged<String> input, int start_offset, + const uint8_t* input_start, const uint8_t* input_end, + int* output, int output_size, Isolate* isolate, + Tagged<JSRegExp> regexp); ZoneUnorderedMap<uint32_t, Handle<FixedUInt16Array>> range_array_cache_; }; diff --git a/js/src/irregexp/imported/regexp-nodes.h b/js/src/irregexp/imported/regexp-nodes.h index 9407f1c5ec..f3d7e6c58f 100644 --- a/js/src/irregexp/imported/regexp-nodes.h +++ b/js/src/irregexp/imported/regexp-nodes.h @@ -318,7 +318,8 @@ class ActionNode : public SeqRegExpNode { BEGIN_NEGATIVE_SUBMATCH, POSITIVE_SUBMATCH_SUCCESS, EMPTY_MATCH_CHECK, - CLEAR_CAPTURES + CLEAR_CAPTURES, + MODIFY_FLAGS }; static ActionNode* SetRegisterForLoop(int reg, int val, RegExpNode* on_success); @@ -341,6 +342,7 @@ class ActionNode : public SeqRegExpNode { int repetition_register, int repetition_limit, RegExpNode* on_success); + static ActionNode* ModifyFlags(RegExpFlags flags, RegExpNode* on_success); void Accept(NodeVisitor* visitor) override; void Emit(RegExpCompiler* compiler, Trace* trace) override; void GetQuickCheckDetails(QuickCheckDetails* details, @@ -353,6 +355,10 @@ class ActionNode : public SeqRegExpNode { int GreedyLoopTextLength() override { return kNodeIsTooComplexForGreedyLoops; } + RegExpFlags flags() { + DCHECK_EQ(action_type(), MODIFY_FLAGS); + return RegExpFlags{data_.u_modify_flags.flags}; + } private: union { @@ -382,9 +388,13 @@ class ActionNode : public SeqRegExpNode { int range_from; int range_to; } u_clear_captures; + struct { + int flags; + } u_modify_flags; } data_; ActionNode(ActionType action_type, RegExpNode* on_success) : SeqRegExpNode(on_success), action_type_(action_type) {} + ActionType action_type_; friend class DotPrinterImpl; friend Zone; @@ -499,12 +509,11 @@ class AssertionNode : public SeqRegExpNode { class BackReferenceNode : public SeqRegExpNode { public: - BackReferenceNode(int start_reg, int end_reg, RegExpFlags flags, - bool read_backward, RegExpNode* on_success) + BackReferenceNode(int start_reg, int end_reg, bool read_backward, + RegExpNode* on_success) : SeqRegExpNode(on_success), start_reg_(start_reg), end_reg_(end_reg), - flags_(flags), read_backward_(read_backward) {} void Accept(NodeVisitor* visitor) override; int start_register() { return start_reg_; } @@ -522,7 +531,6 @@ class BackReferenceNode : public SeqRegExpNode { private: int start_reg_; int end_reg_; - RegExpFlags flags_; bool read_backward_; }; diff --git a/js/src/irregexp/imported/regexp-parser.cc b/js/src/irregexp/imported/regexp-parser.cc index ea2a6c6d7a..965fc567b7 100644 --- a/js/src/irregexp/imported/regexp-parser.cc +++ b/js/src/irregexp/imported/regexp-parser.cc @@ -13,7 +13,7 @@ #include "unicode/unistr.h" #include "unicode/usetiter.h" #include "unicode/utf16.h" // For U16_NEXT -#endif // V8_INTL_SUPPORT +#endif // V8_INTL_SUPPORT namespace v8 { namespace internal { @@ -67,8 +67,7 @@ class RegExpTextBuilder { bool ignore_case() const { return IsIgnoreCase(flags_); } bool IsUnicodeMode() const { // Either /v or /u enable UnicodeMode - // TODO(v8:11935): Change permalink once proposal is in stage 4. - // https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#sec-parsepattern + // https://tc39.es/ecma262/#sec-parsepattern return IsUnicode(flags_) || IsUnicodeSets(flags_); } Zone* zone() const { return zone_; } @@ -264,7 +263,7 @@ RegExpTree* RegExpTextBuilder::PopLastAtom() { characters_ = nullptr; atom = zone()->New<RegExpAtom>(char_vector); return atom; - } else if (text_.size() > 0) { + } else if (!text_.empty()) { atom = text_.back(); text_.pop_back(); return atom; @@ -315,8 +314,7 @@ class RegExpBuilder { void FlushTerms(); bool IsUnicodeMode() const { // Either /v or /u enable UnicodeMode - // TODO(v8:11935): Change permalink once proposal is in stage 4. - // https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#sec-parsepattern + // https://tc39.es/ecma262/#sec-parsepattern return IsUnicode(flags_) || IsUnicodeSets(flags_); } Zone* zone() const { return zone_; } @@ -354,7 +352,12 @@ class RegExpParserState : public ZoneObject { group_type_(group_type), lookaround_type_(lookaround_type), disjunction_capture_index_(disjunction_capture_index), - capture_name_(capture_name) {} + capture_name_(capture_name) { + if (previous_state != nullptr) { + non_participating_capture_group_interval_ = + previous_state->non_participating_capture_group_interval(); + } + } // Parser state of containing expression, if any. RegExpParserState* previous_state() const { return previous_state_; } bool IsSubexpression() { return previous_state_ != nullptr; } @@ -371,6 +374,9 @@ class RegExpParserState : public ZoneObject { // The name of the current sub-expression, if group_type is CAPTURE. Only // used for named captures. const ZoneVector<base::uc16>* capture_name() const { return capture_name_; } + std::pair<int, int> non_participating_capture_group_interval() const { + return non_participating_capture_group_interval_; + } bool IsNamedCapture() const { return capture_name_ != nullptr; } @@ -398,6 +404,18 @@ class RegExpParserState : public ZoneObject { return false; } + void NewAlternative(int captures_started) { + if (non_participating_capture_group_interval().second != 0) { + // Extend the non-participating interval. + non_participating_capture_group_interval_.second = captures_started; + } else { + // Create new non-participating interval from the start of the current + // enclosing group to all captures created within that group so far. + non_participating_capture_group_interval_ = + std::make_pair(capture_index(), captures_started); + } + } + private: // Linked list implementation of stack of states. RegExpParserState* const previous_state_; @@ -411,6 +429,11 @@ class RegExpParserState : public ZoneObject { const int disjunction_capture_index_; // Stored capture name (if any). const ZoneVector<base::uc16>* const capture_name_; + // Interval of (named) capture indices ]from, to] that are not participating + // in the current state (i.e. they cannot match). + // Capture indices are not participating if they were created in a different + // alternative. + std::pair<int, int> non_participating_capture_group_interval_; }; template <class CharT> @@ -463,17 +486,22 @@ class RegExpParserImpl final { RegExpTree* ParseClassSetOperand(const RegExpBuilder* builder, ClassSetOperandType* type_out, ZoneList<CharacterRange>* ranges, - CharacterClassStrings* strings); + CharacterClassStrings* strings, + base::uc32* character); base::uc32 ParseClassSetCharacter(); // Parses and returns a single escaped character. base::uc32 ParseCharacterEscape(InClassEscapeState in_class_escape_state, bool* is_escaped_unicode_character); + void AddMaybeSimpleCaseFoldedRange(ZoneList<CharacterRange>* ranges, + CharacterRange new_range); + RegExpTree* ParseClassUnion(const RegExpBuilder* builder, bool is_negated, RegExpTree* first_operand, ClassSetOperandType first_operand_type, ZoneList<CharacterRange>* ranges, - CharacterClassStrings* strings); + CharacterClassStrings* strings, + base::uc32 first_character); RegExpTree* ParseClassIntersection(const RegExpBuilder* builder, bool is_negated, RegExpTree* first_operand, ClassSetOperandType first_operand_type); @@ -504,11 +532,10 @@ class RegExpParserImpl final { int captures_started() const { return captures_started_; } int position() const { return next_pos_ - 1; } bool failed() const { return failed_; } - RegExpFlags flags() const { return top_level_flags_; } + RegExpFlags flags() const { return flags_; } bool IsUnicodeMode() const { // Either /v or /u enable UnicodeMode - // TODO(v8:11935): Change permalink once proposal is in stage 4. - // https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#sec-parsepattern + // https://tc39.es/ecma262/#sec-parsepattern return IsUnicode(flags()) || IsUnicodeSets(flags()) || force_unicode_; } bool unicode_sets() const { return IsUnicodeSets(flags()); } @@ -528,7 +555,7 @@ class RegExpParserImpl final { // Creates a new named capture at the specified index. Must be called exactly // once for each named capture. Fails if a capture with the same name is // encountered. - bool CreateNamedCaptureAtIndex(const ZoneVector<base::uc16>* name, int index); + bool CreateNamedCaptureAtIndex(const RegExpParserState* state, int index); // Parses the name of a capture group (?<name>pattern). The name must adhere // to IdentifierName in the ECMAScript standard. @@ -543,7 +570,7 @@ class RegExpParserImpl final { // to avoid complicating cases in which references comes before the capture. void PatchNamedBackReferences(); - ZoneVector<RegExpCapture*>* GetNamedCaptures() const; + ZoneVector<RegExpCapture*>* GetNamedCaptures(); // Returns true iff the pattern contains named captures. May call // ScanForCaptures to look ahead at the remaining pattern. @@ -593,16 +620,20 @@ class RegExpParserImpl final { RegExpError error_ = RegExpError::kNone; int error_pos_ = 0; ZoneList<RegExpCapture*>* captures_; - ZoneSet<RegExpCapture*, RegExpCaptureNameLess>* named_captures_; + // Maps capture names to a list of capture indices with this name. + ZoneMap<RegExpCapture*, ZoneList<int>*, RegExpCaptureNameLess>* + named_captures_; ZoneList<RegExpBackReference*>* named_back_references_; + ZoneList<CharacterRange>* temp_ranges_; const CharT* const input_; const int input_length_; base::uc32 current_; - const RegExpFlags top_level_flags_; + RegExpFlags flags_; bool force_unicode_ = false; // Force parser to act as if unicode were set. int next_pos_; int captures_started_; int capture_count_; // Only valid after we have scanned for captures. + int lookaround_count_; // Only valid after we have scanned for lookbehinds. bool has_more_; bool simple_; bool contains_anchor_; @@ -625,10 +656,11 @@ RegExpParserImpl<CharT>::RegExpParserImpl( input_(input), input_length_(input_length), current_(kEndMarker), - top_level_flags_(flags), + flags_(flags), next_pos_(0), captures_started_(0), capture_count_(0), + lookaround_count_(0), has_more_(true), simple_(false), contains_anchor_(false), @@ -909,21 +941,21 @@ RegExpTree* RegExpParserImpl<CharT>::ParseDisjunction() { // Build result of subexpression. if (group_type == CAPTURE) { if (state->IsNamedCapture()) { - CreateNamedCaptureAtIndex(state->capture_name(), - capture_index CHECK_FAILED); + CreateNamedCaptureAtIndex(state, capture_index CHECK_FAILED); } RegExpCapture* capture = GetCapture(capture_index); capture->set_body(body); body = capture; } else if (group_type == GROUPING) { - body = zone()->template New<RegExpGroup>(body); + body = zone()->template New<RegExpGroup>(body, builder->flags()); } else { DCHECK(group_type == POSITIVE_LOOKAROUND || group_type == NEGATIVE_LOOKAROUND); bool is_positive = (group_type == POSITIVE_LOOKAROUND); body = zone()->template New<RegExpLookaround>( body, is_positive, end_capture_index - capture_index, - capture_index, state->lookaround_type()); + capture_index, state->lookaround_type(), lookaround_count_); + lookaround_count_++; } // Restore previous state. @@ -937,6 +969,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseDisjunction() { } case '|': { Advance(); + state->NewAlternative(captures_started()); builder->NewAlternative(); continue; } @@ -984,6 +1017,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseDisjunction() { case '(': { state = ParseOpenParenthesis(state CHECK_FAILED); builder = state->builder(); + flags_ = builder->flags(); continue; } case '[': { @@ -1037,8 +1071,8 @@ RegExpTree* RegExpParserImpl<CharT>::ParseDisjunction() { builder->AddEmpty(); } else { RegExpCapture* capture = GetCapture(index); - RegExpTree* atom = zone()->template New<RegExpBackReference>( - capture, builder->flags()); + RegExpTree* atom = + zone()->template New<RegExpBackReference>(capture, zone()); builder->AddAtom(atom); } break; @@ -1246,43 +1280,91 @@ RegExpParserState* RegExpParserImpl<CharT>::ParseOpenParenthesis( bool is_named_capture = false; const ZoneVector<base::uc16>* capture_name = nullptr; SubexpressionType subexpr_type = CAPTURE; + RegExpFlags flags = state->builder()->flags(); + bool parsing_modifiers = false; + bool modifiers_polarity = true; + RegExpFlags modifiers; Advance(); if (current() == '?') { - switch (Next()) { - case ':': - Advance(2); - subexpr_type = GROUPING; - break; - case '=': - Advance(2); - lookaround_type = RegExpLookaround::LOOKAHEAD; - subexpr_type = POSITIVE_LOOKAROUND; - break; - case '!': - Advance(2); - lookaround_type = RegExpLookaround::LOOKAHEAD; - subexpr_type = NEGATIVE_LOOKAROUND; - break; - case '<': - Advance(); - if (Next() == '=') { + do { + switch (Next()) { + case '-': + if (!v8_flags.js_regexp_modifiers) { + ReportError(RegExpError::kInvalidGroup); + return nullptr; + } + Advance(); + parsing_modifiers = true; + if (modifiers_polarity == false) { + ReportError(RegExpError::kMultipleFlagDashes); + return nullptr; + } + modifiers_polarity = false; + break; + case 'm': + case 'i': + case 's': { + if (!v8_flags.js_regexp_modifiers) { + ReportError(RegExpError::kInvalidGroup); + return nullptr; + } + Advance(); + parsing_modifiers = true; + RegExpFlag flag = TryRegExpFlagFromChar(current()).value(); + if ((modifiers & flag) != 0) { + ReportError(RegExpError::kRepeatedFlag); + return nullptr; + } + modifiers |= flag; + flags.set(flag, modifiers_polarity); + break; + } + case ':': + Advance(2); + parsing_modifiers = false; + subexpr_type = GROUPING; + break; + case '=': Advance(2); - lookaround_type = RegExpLookaround::LOOKBEHIND; + parsing_modifiers = false; + lookaround_type = RegExpLookaround::LOOKAHEAD; subexpr_type = POSITIVE_LOOKAROUND; break; - } else if (Next() == '!') { + case '!': Advance(2); - lookaround_type = RegExpLookaround::LOOKBEHIND; + parsing_modifiers = false; + lookaround_type = RegExpLookaround::LOOKAHEAD; subexpr_type = NEGATIVE_LOOKAROUND; break; - } - is_named_capture = true; - has_named_captures_ = true; - Advance(); - break; - default: - ReportError(RegExpError::kInvalidGroup); - return nullptr; + case '<': + Advance(); + parsing_modifiers = false; + if (Next() == '=') { + Advance(2); + lookaround_type = RegExpLookaround::LOOKBEHIND; + subexpr_type = POSITIVE_LOOKAROUND; + break; + } else if (Next() == '!') { + Advance(2); + lookaround_type = RegExpLookaround::LOOKBEHIND; + subexpr_type = NEGATIVE_LOOKAROUND; + break; + } + is_named_capture = true; + has_named_captures_ = true; + Advance(); + break; + default: + ReportError(RegExpError::kInvalidGroup); + return nullptr; + } + } while (parsing_modifiers); + } + if (modifiers_polarity == false) { + // We encountered a dash. + if (modifiers == 0) { + ReportError(RegExpError::kInvalidFlagGroup); + return nullptr; } } if (subexpr_type == CAPTURE) { @@ -1299,7 +1381,7 @@ RegExpParserState* RegExpParserImpl<CharT>::ParseOpenParenthesis( // Store current state and begin new disjunction parsing. return zone()->template New<RegExpParserState>( state, subexpr_type, lookaround_type, captures_started_, capture_name, - state->builder()->flags(), zone()); + flags, zone()); } // In order to know whether an escape is a backreference or not we have to scan @@ -1511,7 +1593,10 @@ const ZoneVector<base::uc16>* RegExpParserImpl<CharT>::ParseCaptureGroupName() { template <class CharT> bool RegExpParserImpl<CharT>::CreateNamedCaptureAtIndex( - const ZoneVector<base::uc16>* name, int index) { + const RegExpParserState* state, int index) { + const ZoneVector<base::uc16>* name = state->capture_name(); + const std::pair<int, int> non_participating_capture_group_interval = + state->non_participating_capture_group_interval(); DCHECK(0 < index && index <= captures_started_); DCHECK_NOT_NULL(name); @@ -1521,21 +1606,33 @@ bool RegExpParserImpl<CharT>::CreateNamedCaptureAtIndex( capture->set_name(name); if (named_captures_ == nullptr) { - named_captures_ = - zone_->template New<ZoneSet<RegExpCapture*, RegExpCaptureNameLess>>( - zone()); + named_captures_ = zone_->template New< + ZoneMap<RegExpCapture*, ZoneList<int>*, RegExpCaptureNameLess>>(zone()); } else { // Check for duplicates and bail if we find any. - const auto& named_capture_it = named_captures_->find(capture); if (named_capture_it != named_captures_->end()) { - ReportError(RegExpError::kDuplicateCaptureGroupName); - return false; + if (v8_flags.js_regexp_duplicate_named_groups) { + ZoneList<int>* named_capture_indices = named_capture_it->second; + DCHECK_NOT_NULL(named_capture_indices); + DCHECK(!named_capture_indices->is_empty()); + for (int named_index : *named_capture_indices) { + if (named_index < non_participating_capture_group_interval.first || + named_index > non_participating_capture_group_interval.second) { + ReportError(RegExpError::kDuplicateCaptureGroupName); + return false; + } + } + } else { + ReportError(RegExpError::kDuplicateCaptureGroupName); + return false; + } } } - named_captures_->emplace(capture); - + auto entry = named_captures_->try_emplace( + capture, zone()->template New<ZoneList<int>>(1, zone())); + entry.first->second->Add(index, zone()); return true; } @@ -1558,7 +1655,7 @@ bool RegExpParserImpl<CharT>::ParseNamedBackReference( builder->AddEmpty(); } else { RegExpBackReference* atom = - zone()->template New<RegExpBackReference>(builder->flags()); + zone()->template New<RegExpBackReference>(zone()); atom->set_name(name); builder->AddAtom(atom); @@ -1595,16 +1692,17 @@ void RegExpParserImpl<CharT>::PatchNamedBackReferences() { DCHECK_NULL(search_capture->name()); search_capture->set_name(ref->name()); - int index = -1; const auto& capture_it = named_captures_->find(search_capture); - if (capture_it != named_captures_->end()) { - index = (*capture_it)->index(); - } else { + if (capture_it == named_captures_->end()) { ReportError(RegExpError::kInvalidNamedCaptureReference); return; } - ref->set_capture(GetCapture(index)); + DCHECK_IMPLIES(!v8_flags.js_regexp_duplicate_named_groups, + capture_it->second->length() == 1); + for (int index : *capture_it->second) { + ref->add_capture(GetCapture(index), zone()); + } } } @@ -1627,13 +1725,22 @@ RegExpCapture* RegExpParserImpl<CharT>::GetCapture(int index) { } template <class CharT> -ZoneVector<RegExpCapture*>* RegExpParserImpl<CharT>::GetNamedCaptures() const { - if (named_captures_ == nullptr || named_captures_->empty()) { +ZoneVector<RegExpCapture*>* RegExpParserImpl<CharT>::GetNamedCaptures() { + if (named_captures_ == nullptr) { return nullptr; } + DCHECK(!named_captures_->empty()); - return zone()->template New<ZoneVector<RegExpCapture*>>( - named_captures_->begin(), named_captures_->end(), zone()); + ZoneVector<RegExpCapture*>* flattened_named_captures = + zone()->template New<ZoneVector<RegExpCapture*>>(zone()); + for (auto capture : *named_captures_) { + DCHECK_IMPLIES(!v8_flags.js_regexp_duplicate_named_groups, + capture.second->length() == 1); + for (int index : *capture.second) { + flattened_named_captures->push_back(GetCapture(index)); + } + } + return flattened_named_captures; } template <class CharT> @@ -1890,7 +1997,7 @@ bool LookupPropertyValueName(UProperty property, ExtractStringsFromUnicodeSet(set, result_strings, flags, zone); } const bool needs_case_folding = IsUnicodeSets(flags) && IsIgnoreCase(flags); - if (needs_case_folding) CharacterRange::UnicodeSimpleCloseOver(set); + if (needs_case_folding) set.closeOver(USET_SIMPLE_CASE_INSENSITIVE); set.removeAllStrings(); if (negate) set.complement(); for (int i = 0; i < set.getRangeCount(); i++) { @@ -2096,13 +2203,22 @@ bool RegExpParserImpl<CharT>::AddPropertyClassRange( if (!IsSupportedBinaryProperty(property, unicode_sets())) return false; if (!IsExactPropertyAlias(name, property)) return false; // Negation of properties with strings is not allowed. - // TODO(v8:11935): Change permalink once proposal is in stage 4. // See - // https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#sec-static-semantics-maycontainstrings + // https://tc39.es/ecma262/#sec-static-semantics-maycontainstrings if (negate && IsBinaryPropertyOfStrings(property)) return false; - return LookupPropertyValueName(property, negate ? "N" : "Y", false, - add_to_ranges, add_to_strings, flags(), - zone()); + if (unicode_sets()) { + // In /v mode we can't simple lookup the "false" binary property values, + // as the spec requires us to perform case folding before calculating the + // complement. + // See https://tc39.es/ecma262/#sec-compiletocharset + // UnicodePropertyValueExpression :: LoneUnicodePropertyNameOrValue + return LookupPropertyValueName(property, "Y", negate, add_to_ranges, + add_to_strings, flags(), zone()); + } else { + return LookupPropertyValueName(property, negate ? "N" : "Y", false, + add_to_ranges, add_to_strings, flags(), + zone()); + } } else { // Both property name and value name are specified. Attempt to interpret // the property name as enumerated property. @@ -2325,8 +2441,7 @@ base::uc32 RegExpParserImpl<CharT>::ParseCharacterEscape( return c; } -// TODO(v8:11935): Change permalink once proposal is in stage 4. -// https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassRanges +// https://tc39.es/ecma262/#prod-ClassRanges template <class CharT> RegExpTree* RegExpParserImpl<CharT>::ParseClassRanges( ZoneList<CharacterRange>* ranges, bool add_unicode_case_equivalents) { @@ -2475,8 +2590,7 @@ void AddClassString(ZoneList<base::uc32>* normalized_string, } // namespace -// TODO(v8:11935): Change permalink once proposal is in stage 4. -// https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassStringDisjunction +// https://tc39.es/ecma262/#prod-ClassStringDisjunction template <class CharT> RegExpTree* RegExpParserImpl<CharT>::ParseClassStringDisjunction( ZoneList<CharacterRange>* ranges, CharacterClassStrings* strings) { @@ -2526,8 +2640,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassStringDisjunction( return nullptr; } -// TODO(v8:11935): Change permalink once proposal is in stage 4. -// https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassSetOperand +// https://tc39.es/ecma262/#prod-ClassSetOperand // Tree returned based on type_out: // * kNestedClass: RegExpClassSetExpression // * For all other types: RegExpClassSetOperand @@ -2538,12 +2651,13 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassSetOperand( zone()->template New<ZoneList<CharacterRange>>(1, zone()); CharacterClassStrings* strings = zone()->template New<CharacterClassStrings>(zone()); - RegExpTree* tree = - ParseClassSetOperand(builder, type_out, ranges, strings CHECK_FAILED); + base::uc32 character; + RegExpTree* tree = ParseClassSetOperand(builder, type_out, ranges, strings, + &character CHECK_FAILED); DCHECK_IMPLIES(*type_out != ClassSetOperandType::kNestedClass, tree == nullptr); DCHECK_IMPLIES(*type_out == ClassSetOperandType::kClassSetCharacter, - ranges->length() == 1); + ranges->is_empty()); DCHECK_IMPLIES(*type_out == ClassSetOperandType::kClassSetCharacter, strings->empty()); DCHECK_IMPLIES(*type_out == ClassSetOperandType::kNestedClass, @@ -2558,21 +2672,27 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassSetOperand( // CharacterClassEscape includes \p{}, which can contain ranges, strings or // both and \P{}, which could contain nothing (i.e. \P{Any}). if (tree == nullptr) { + if (*type_out == ClassSetOperandType::kClassSetCharacter) { + AddMaybeSimpleCaseFoldedRange(ranges, + CharacterRange::Singleton(character)); + } tree = zone()->template New<RegExpClassSetOperand>(ranges, strings); } return tree; } -// TODO(v8:11935): Change permalink once proposal is in stage 4. -// https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassSetOperand -// Based on |type_out| either a tree is returned or ranges/strings modified. -// If a tree is returned, ranges/strings are not modified. -// If |type_out| is kNestedClass, a tree of type RegExpClassSetExpression is -// returned. For all other types, ranges is modified and nullptr is returned. +// https://tc39.es/ecma262/#prod-ClassSetOperand +// Based on |type_out| either a tree is returned or +// |ranges|/|strings|/|character| modified. If a tree is returned, +// ranges/strings are not modified. If |type_out| is kNestedClass, a tree of +// type RegExpClassSetExpression is returned. If | type_out| is +// kClassSetCharacter, |character| is set and nullptr returned. For all other +// types, |ranges|/|strings|/|character| is modified and nullptr is returned. template <class CharT> RegExpTree* RegExpParserImpl<CharT>::ParseClassSetOperand( const RegExpBuilder* builder, ClassSetOperandType* type_out, - ZoneList<CharacterRange>* ranges, CharacterClassStrings* strings) { + ZoneList<CharacterRange>* ranges, CharacterClassStrings* strings, + base::uc32* character) { DCHECK(unicode_sets()); base::uc32 c = current(); if (c == '\\') { @@ -2599,7 +2719,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassSetOperand( *type_out = ClassSetOperandType::kClassSetCharacter; c = ParseClassSetCharacter(CHECK_FAILED); - ranges->Add(CharacterRange::Singleton(c), zone()); + *character = c; return nullptr; } @@ -2653,13 +2773,28 @@ bool MayContainStrings(ClassSetOperandType type, RegExpTree* operand) { } // namespace -// TODO(v8:11935): Change permalink once proposal is in stage 4. -// https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassUnion +template <class CharT> +void RegExpParserImpl<CharT>::AddMaybeSimpleCaseFoldedRange( + ZoneList<CharacterRange>* ranges, CharacterRange new_range) { + DCHECK(unicode_sets()); + if (ignore_case()) { + ZoneList<CharacterRange>* new_ranges = + zone()->template New<ZoneList<CharacterRange>>(2, zone()); + new_ranges->Add(new_range, zone()); + CharacterRange::AddUnicodeCaseEquivalents(new_ranges, zone()); + ranges->AddAll(*new_ranges, zone()); + } else { + ranges->Add(new_range, zone()); + } + CharacterRange::Canonicalize(ranges); +} + +// https://tc39.es/ecma262/#prod-ClassUnion template <class CharT> RegExpTree* RegExpParserImpl<CharT>::ParseClassUnion( const RegExpBuilder* builder, bool is_negated, RegExpTree* first_operand, ClassSetOperandType first_operand_type, ZoneList<CharacterRange>* ranges, - CharacterClassStrings* strings) { + CharacterClassStrings* strings, base::uc32 character) { DCHECK(unicode_sets()); ZoneList<RegExpTree*>* operands = zone()->template New<ZoneList<RegExpTree*>>(2, zone()); @@ -2673,7 +2808,6 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassUnion( operands->Add(first_operand, zone()); } ClassSetOperandType last_type = first_operand_type; - const bool needs_case_folding = ignore_case(); while (has_more() && current() != ']') { if (current() == '-') { // Mix of ClassSetRange and ClassSubtraction is not allowed. @@ -2690,42 +2824,36 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassUnion( // represent a character range. // In case one of them is not a ClassSetCharacter, it is a syntax error, // as '-' can not be used unescaped within a class with /v. - // TODO(v8:11935): Change permalink once proposal is in stage 4. // See - // https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassSetRange + // https://tc39.es/ecma262/#prod-ClassSetRange if (last_type != ClassSetOperandType::kClassSetCharacter) { return ReportError(RegExpError::kInvalidCharacterClass); } - ParseClassSetOperand(builder, &last_type, ranges, strings CHECK_FAILED); + base::uc32 from = character; + ParseClassSetOperand(builder, &last_type, ranges, strings, + &character CHECK_FAILED); if (last_type != ClassSetOperandType::kClassSetCharacter) { return ReportError(RegExpError::kInvalidCharacterClass); } - // Remove the last two singleton characters added to ranges, and combine - // them into a range. - auto rhs_ranges = ranges->RemoveLast(); - auto lhs_ranges = ranges->RemoveLast(); - DCHECK(lhs_ranges.IsSingleton()); - DCHECK(rhs_ranges.IsSingleton()); - base::uc32 from = lhs_ranges.from(); - base::uc32 to = rhs_ranges.from(); - if (from > to) { + if (from > character) { return ReportError(RegExpError::kOutOfOrderCharacterClass); } - ranges->Add(CharacterRange::Range(from, to), zone()); + AddMaybeSimpleCaseFoldedRange(ranges, + CharacterRange::Range(from, character)); last_type = ClassSetOperandType::kClassSetRange; } else { DCHECK_NE(current(), '-'); - RegExpTree* operand = ParseClassSetOperand(builder, &last_type, ranges, - strings CHECK_FAILED); + if (last_type == ClassSetOperandType::kClassSetCharacter) { + AddMaybeSimpleCaseFoldedRange(ranges, + CharacterRange::Singleton(character)); + } + RegExpTree* operand = ParseClassSetOperand( + builder, &last_type, ranges, strings, &character CHECK_FAILED); if (operand != nullptr) { may_contain_strings |= MayContainStrings(last_type, operand); // Add the range we started building as operand and reset the current // range. if (!ranges->is_empty() || !strings->empty()) { - if (needs_case_folding) { - CharacterRange::Canonicalize(ranges); - CharacterRange::AddUnicodeCaseEquivalents(ranges, zone()); - } may_contain_strings |= !strings->empty(); operands->Add( zone()->template New<RegExpClassSetOperand>(ranges, strings), @@ -2742,12 +2870,12 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassUnion( return ReportError(RegExpError::kUnterminatedCharacterClass); } + if (last_type == ClassSetOperandType::kClassSetCharacter) { + AddMaybeSimpleCaseFoldedRange(ranges, CharacterRange::Singleton(character)); + } + // Add the range we started building as operand. if (!ranges->is_empty() || !strings->empty()) { - if (needs_case_folding) { - CharacterRange::Canonicalize(ranges); - CharacterRange::AddUnicodeCaseEquivalents(ranges, zone()); - } may_contain_strings |= !strings->empty(); operands->Add(zone()->template New<RegExpClassSetOperand>(ranges, strings), zone()); @@ -2773,8 +2901,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassUnion( may_contain_strings, operands); } -// TODO(v8:11935): Change permalink once proposal is in stage 4. -// https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassIntersection +// https://tc39.es/ecma262/#prod-ClassIntersection template <class CharT> RegExpTree* RegExpParserImpl<CharT>::ParseClassIntersection( const RegExpBuilder* builder, bool is_negated, RegExpTree* first_operand, @@ -2815,8 +2942,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseClassIntersection( may_contain_strings, operands); } -// TODO(v8:11935): Change permalink once proposal is in stage 4. -// https://arai-a.github.io/ecma262-compare/snapshot.html?pr=2418#prod-ClassSubtraction +// https://tc39.es/ecma262/#prod-ClassSubtraction template <class CharT> RegExpTree* RegExpParserImpl<CharT>::ParseClassSubtraction( const RegExpBuilder* builder, bool is_negated, RegExpTree* first_operand, @@ -2891,12 +3017,16 @@ RegExpTree* RegExpParserImpl<CharT>::ParseCharacterClass( ClassSetOperandType operand_type; CharacterClassStrings* strings = zone()->template New<CharacterClassStrings>(zone()); - RegExpTree* operand = ParseClassSetOperand(builder, &operand_type, ranges, - strings CHECK_FAILED); + base::uc32 character; + RegExpTree* operand = ParseClassSetOperand( + builder, &operand_type, ranges, strings, &character CHECK_FAILED); switch (current()) { case '-': if (Next() == '-') { if (operand == nullptr) { + if (operand_type == ClassSetOperandType::kClassSetCharacter) { + ranges->Add(CharacterRange::Singleton(character), zone()); + } operand = zone()->template New<RegExpClassSetOperand>(ranges, strings); } @@ -2908,6 +3038,9 @@ RegExpTree* RegExpParserImpl<CharT>::ParseCharacterClass( case '&': if (Next() == '&') { if (operand == nullptr) { + if (operand_type == ClassSetOperandType::kClassSetCharacter) { + ranges->Add(CharacterRange::Singleton(character), zone()); + } operand = zone()->template New<RegExpClassSetOperand>(ranges, strings); } @@ -2916,7 +3049,7 @@ RegExpTree* RegExpParserImpl<CharT>::ParseCharacterClass( } } return ParseClassUnion(builder, is_negated, operand, operand_type, ranges, - strings); + strings, character); } } @@ -3047,7 +3180,7 @@ bool RegExpBuilder::AddQuantifierToAtom( RegExpTree* atom = text_builder().PopLastAtom(); if (atom != nullptr) { FlushText(); - } else if (terms_.size() > 0) { + } else if (!terms_.empty()) { atom = terms_.back(); terms_.pop_back(); if (atom->IsLookaround()) { diff --git a/js/src/irregexp/imported/regexp.h b/js/src/irregexp/imported/regexp.h index 50269a4b71..5dc9070ed9 100644 --- a/js/src/irregexp/imported/regexp.h +++ b/js/src/irregexp/imported/regexp.h @@ -87,8 +87,8 @@ class RegExp final : public AllStatic { RegExpFlags flags, uint32_t backtrack_limit); // Ensures that a regexp is fully compiled and ready to be executed on a - // subject string. Returns true on success. Return false on failure, and - // then an exception will be pending. + // subject string. Returns true on success. Throw and return false on + // failure. V8_WARN_UNUSED_RESULT static bool EnsureFullyCompiled(Isolate* isolate, Handle<JSRegExp> re, Handle<String> subject); @@ -211,14 +211,16 @@ class RegExpResultsCache final : public AllStatic { // Attempt to retrieve a cached result. On failure, 0 is returned as a Smi. // On success, the returned result is guaranteed to be a COW-array. - static Object Lookup(Heap* heap, String key_string, Object key_pattern, - FixedArray* last_match_out, ResultsCacheType type); + static Tagged<Object> Lookup(Heap* heap, Tagged<String> key_string, + Tagged<Object> key_pattern, + Tagged<FixedArray>* last_match_out, + ResultsCacheType type); // Attempt to add value_array to the cache specified by type. On success, // value_array is turned into a COW-array. static void Enter(Isolate* isolate, Handle<String> key_string, Handle<Object> key_pattern, Handle<FixedArray> value_array, Handle<FixedArray> last_match_cache, ResultsCacheType type); - static void Clear(FixedArray cache); + static void Clear(Tagged<FixedArray> cache); static constexpr int kRegExpResultsCacheSize = 0x100; diff --git a/js/src/irregexp/imported/special-case.cc b/js/src/irregexp/imported/special-case.cc index f5a9928b3a..d40ada6bb9 100644 --- a/js/src/irregexp/imported/special-case.cc +++ b/js/src/irregexp/imported/special-case.cc @@ -82,29 +82,6 @@ const icu::UnicodeSet& RegExpCaseFolding::SpecialAddSet() { return set.Pointer()->set; } -icu::UnicodeSet BuildUnicodeNonSimpleCloseOverSet() { - icu::UnicodeSet set; - set.add(0x390); - set.add(0x3b0); - set.add(0x1fd3); - set.add(0x1fe3); - set.add(0xfb05, 0xfb06); - set.freeze(); - return set; -} - -struct UnicodeNonSimpleCloseOverSetData { - UnicodeNonSimpleCloseOverSetData() : set(BuildUnicodeNonSimpleCloseOverSet()) {} - const icu::UnicodeSet set; -}; - -//static -const icu::UnicodeSet& RegExpCaseFolding::UnicodeNonSimpleCloseOverSet() { - static base::LazyInstance<UnicodeNonSimpleCloseOverSetData>::type set = - LAZY_INSTANCE_INITIALIZER; - return set.Pointer()->set; -} - } // namespace internal } // namespace v8 diff --git a/js/src/irregexp/imported/special-case.h b/js/src/irregexp/imported/special-case.h index ea511af5a4..050d72a064 100644 --- a/js/src/irregexp/imported/special-case.h +++ b/js/src/irregexp/imported/special-case.h @@ -70,21 +70,11 @@ namespace internal { // another character. Characters that match no other characters in // their equivalence class are added to IgnoreSet. Characters that // match at least one other character are added to SpecialAddSet. -// -// For unicode ignoreCase ("iu" and "iv"), -// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) adds all characters that are in -// the same equivalence class. This includes characaters that are in the same -// equivalence class using full case folding. According to the spec, only -// simple case folding shall be considered. We therefore create -// UnicodeNonSimpleCloseOverSet containing all characters for which -// UnicodeSet::closeOver adds characters that are not simple case folds. This -// set should be used similar to IgnoreSet described above. class RegExpCaseFolding final : public AllStatic { public: static const icu::UnicodeSet& IgnoreSet(); static const icu::UnicodeSet& SpecialAddSet(); - static const icu::UnicodeSet& UnicodeNonSimpleCloseOverSet(); // This implements ECMAScript 2020 21.2.2.8.2 (Runtime Semantics: // Canonicalize) step 3, which is used to determine whether diff --git a/js/src/irregexp/moz.build b/js/src/irregexp/moz.build index ff030ad4bd..2c363ad349 100644 --- a/js/src/irregexp/moz.build +++ b/js/src/irregexp/moz.build @@ -14,9 +14,13 @@ include("../js-cxxflags.mozbuild") CXXFLAGS += ["-Wno-error=type-limits", "-Wno-error=return-type"] -# Suppress spurious warnings in third-party code. See bug 1810584. +# Suppress spurious warnings in third-party code. +# See bug 1810584 and bug 1879225. if CONFIG["CC_TYPE"] == "gcc": - CXXFLAGS += ["-Wno-error=nonnull"] + CXXFLAGS += ["-Wno-error=nonnull", "-Wno-narrowing"] +if CONFIG["CC_TYPE"] in ("clang", "clang-cl"): + CXXFLAGS += ["-Wno-c++11-narrowing"] + UNIFIED_SOURCES += [ "imported/regexp-bytecode-generator.cc", diff --git a/js/src/irregexp/moz.yaml b/js/src/irregexp/moz.yaml index e230a89cfd..ca44833c24 100644 --- a/js/src/irregexp/moz.yaml +++ b/js/src/irregexp/moz.yaml @@ -9,8 +9,8 @@ origin: description: A fast regular expression engine from V8 url: https://v8.dev - release: 30a887aeb92153885619d8bb9fa57cda7adf9276 (Thu Jul 06 11:42:30 2023). - revision: 30a887aeb92153885619d8bb9fa57cda7adf9276 + release: e50ab13bbfaaf72717fd73d9a01434e4c3c1a0a8 (Thu Feb 29 03:38:59 2024). + revision: e50ab13bbfaaf72717fd73d9a01434e4c3c1a0a8 license: BSD-3-Clause license-file: LICENSE.v8 diff --git a/js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock b/js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock index 129b13b7f6..756fc8e6ac 100644 --- a/js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock +++ b/js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock @@ -29,6 +29,12 @@ dependencies = [ ] [[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -193,7 +199,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "wasm-encoder" -version = "0.38.1" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" dependencies = [ "leb128", ] @@ -214,8 +222,11 @@ dependencies = [ [[package]] name = "wast" -version = "69.0.1" +version = "71.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647c3ac4354da32688537e8fc4d2fe6c578df51896298cb64727d98088a1fd26" dependencies = [ + "bumpalo", "leb128", "memchr", "unicode-width", diff --git a/js/src/jit-test/etc/wasm/generate-spectests/config.toml b/js/src/jit-test/etc/wasm/generate-spectests/config.toml index 48e5be6e0d..d058a79986 100644 --- a/js/src/jit-test/etc/wasm/generate-spectests/config.toml +++ b/js/src/jit-test/etc/wasm/generate-spectests/config.toml @@ -1,6 +1,6 @@ # Standard 'directives.txt' prologues for jit-tests harness_directive = "|jit-test| skip-if: true" -directive = "|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js" +directive = "|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js" # Failing tests across all testsuites excluded_tests = [ @@ -43,7 +43,7 @@ url = "https://github.com/WebAssembly/exception-handling" branch = "main" parent = "spec" # Skip in jit-test when it's not enabled -directive = "; --wasm-exceptions; --wasm-exnref; skip-if: !wasmExceptionsEnabled()" +directive = "; --setpref=wasm_exnref=true; skip-if: !wasmExnRefEnabled()" excluded_tests = [ # harness doesn't support exnref, because JS-API globals can't use it "ref_null.wast.js" @@ -53,7 +53,7 @@ excluded_tests = [ name = "memory64" url = "https://github.com/mozilla-spidermonkey/memory64" branch = "test-cases" -directive = "; skip-if: !wasmMemory64Enabled()" +directive = "; --setpref=wasm_memory64=true; skip-if: !wasmMemory64Enabled()" excluded_tests = [] [[repos]] @@ -61,7 +61,7 @@ name = "function-references" url = "https://github.com/WebAssembly/function-references" branch = "main" parent = "spec" -directive = "; --wasm-function-references; skip-if: !wasmFunctionReferencesEnabled()" +directive = "; --setpref=wasm_gc=true; skip-if: !wasmGcEnabled()" excluded_tests = [ # duplicate tail calls tests "return_call.wast", @@ -87,7 +87,7 @@ name = "relaxed-simd" url = "https://github.com/WebAssembly/relaxed-simd" branch = "main" parent = "spec" -directive = "; --wasm-relaxed-simd; skip-if: !wasmRelaxedSimdEnabled()" +directive = "; --setpref=wasm_relaxed_simd=true; skip-if: !wasmRelaxedSimdEnabled()" excluded_tests = [] [[repos]] @@ -95,7 +95,7 @@ name = "extended-const" url = "https://github.com/WebAssembly/extended-const" branch = "main" parent = "spec" -directive = "; --wasm-extended-const; --no-wasm-gc; skip-if: !wasmExtendedConstEnabled()" +directive = "; --setpref=wasm_gc=false" excluded_tests = [] [[repos]] @@ -103,7 +103,7 @@ name = "tail-call" url = "https://github.com/WebAssembly/tail-call" branch = "main" parent = "spec" -directive = "; --wasm-tail-calls; skip-if: !wasmTailCallsEnabled()" +directive = "; --setpref=wasm_tail_calls=true; skip-if: !wasmTailCallsEnabled()" excluded_tests = [] [[repos]] @@ -111,7 +111,7 @@ name = "multi-memory" url = "https://github.com/WebAssembly/multi-memory" branch = "main" parent = "spec" -directive = "; --wasm-multi-memory; skip-if: !wasmMultiMemoryEnabled()" +directive = "; --setpref=wasm_multi_memory=true; skip-if: !wasmMultiMemoryEnabled()" excluded_tests = [ # Empty test fails parsing "memory_copy1.wast", @@ -122,7 +122,7 @@ name = "gc" url = "https://github.com/WebAssembly/gc" branch = "main" parent = "function-references" -directive = "; --wasm-gc; skip-if: !wasmGcEnabled()" +directive = "; --setpref=wasm_gc=true; skip-if: !wasmGcEnabled()" excluded_tests = [ # tail call tests that snuck in "return_call.wast", diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml index b55ae458df..1c7c3174e9 100644 --- a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml +++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] anyhow = "1.0.19" -wast = { path = "../../../../../../../../wasm-tools/crates/wast" } +wast = "71.0.1" diff --git a/js/src/jit-test/etc/wasm/spec-tests.patch b/js/src/jit-test/etc/wasm/spec-tests.patch index a3c0e15676..cdd0d1aebc 100644 --- a/js/src/jit-test/etc/wasm/spec-tests.patch +++ b/js/src/jit-test/etc/wasm/spec-tests.patch @@ -78,6 +78,40 @@ diff --git a/js/src/jit-test/tests/wasm/spec/memory64/memory_trap64.wast.js b/js + value("i64", 7523094288207667809n), + ]); +} +diff --git a/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js b/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js +--- a/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js ++++ b/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js +@@ -27,17 +27,19 @@ let $2 = instantiate(`(module (memory i64 1 256))`); + // ./test/core/memory64.wast:6 + let $3 = instantiate(`(module (memory i64 0 65536))`); + +-// ./test/core/memory64.wast:8 +-assert_invalid( +- () => instantiate(`(module (memory i64 0) (memory i64 0))`), +- `multiple memories`, +-); +- +-// ./test/core/memory64.wast:9 +-assert_invalid( +- () => instantiate(`(module (memory (import "spectest" "memory") i64 0) (memory i64 0))`), +- `multiple memories`, +-); ++if (!wasmMultiMemoryEnabled()) { ++ // ./test/core/memory64.wast:8 ++ assert_invalid( ++ () => instantiate(`(module (memory i64 0) (memory i64 0))`), ++ `multiple memories`, ++ ); ++ ++ // ./test/core/memory64.wast:9 ++ assert_invalid( ++ () => instantiate(`(module (memory (import "spectest" "memory") i64 0) (memory i64 0))`), ++ `multiple memories`, ++ ); ++} + + // ./test/core/memory64.wast:11 + let $4 = instantiate(`(module (memory i64 (data)) (func (export "memsize") (result i64) (memory.size)))`); diff --git a/js/src/jit-test/tests/wasm/spec/multi-memory/simd_load.wast.js b/js/src/jit-test/tests/wasm/spec/multi-memory/simd_load.wast.js --- a/js/src/jit-test/tests/wasm/spec/multi-memory/simd_load.wast.js +++ b/js/src/jit-test/tests/wasm/spec/multi-memory/simd_load.wast.js @@ -105,6 +139,94 @@ diff --git a/js/src/jit-test/tests/wasm/spec/multi-memory/simd_store.wast.js b/j /* Copyright 2021 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); +diff --git a/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js b/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js +--- a/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js ++++ b/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js +@@ -19,6 +19,15 @@ if (!wasmIsSupported()) { + quit(); + } + ++function partialOobWriteMayWritePartialData() { ++ let arm_native = getBuildConfiguration("arm") && !getBuildConfiguration("arm-simulator"); ++ let arm64_native = getBuildConfiguration("arm64") && !getBuildConfiguration("arm64-simulator"); ++ return arm_native || arm64_native; ++} ++ ++let native_arm = getBuildConfiguration("arm") && !getBuildConfiguration("arm-simulator"); ++let native_arm64 = getBuildConfiguration("arm64") && !getBuildConfiguration("arm64-simulator"); ++ + function bytes(type, bytes) { + var typedBuffer = new Uint8Array(bytes); + return wasmGlobalFromArrayBuffer(type, typedBuffer.buffer); +diff --git a/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js b/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js +--- a/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js ++++ b/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js +@@ -562,11 +562,15 @@ assert_trap(() => invoke($0, `i64.load32_u`, [-3]), `out of bounds memory access + // ./test/core/multi-memory/memory_trap1.wast:234 + assert_trap(() => invoke($0, `i64.load32_u`, [-4]), `out of bounds memory access`); + +-// ./test/core/multi-memory/memory_trap1.wast:237 +-assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 7523094288207667809n)]); ++// Bug 1842293 - do not observe the partial store caused by bug 1666747 on ++// some native platforms. ++if (!partialOobWriteMayWritePartialData()) { ++ // ./test/core/multi-memory/memory_trap1.wast:237 ++ assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 7523094288207667809n)]); + +-// ./test/core/multi-memory/memory_trap1.wast:238 +-assert_return(() => invoke($0, `i64.load`, [0]), [value("i64", 7523094288207667809n)]); ++ // ./test/core/multi-memory/memory_trap1.wast:238 ++ assert_return(() => invoke($0, `i64.load`, [0]), [value("i64", 7523094288207667809n)]); ++} + + // ./test/core/multi-memory/memory_trap1.wast:242 + assert_return(() => invoke($0, `i64.store`, [65528, 0n]), []); +@@ -574,14 +578,18 @@ assert_return(() => invoke($0, `i64.store`, [65528, 0n]), []); + // ./test/core/multi-memory/memory_trap1.wast:243 + assert_trap(() => invoke($0, `i32.store`, [65533, 305419896]), `out of bounds memory access`); + +-// ./test/core/multi-memory/memory_trap1.wast:244 +-assert_return(() => invoke($0, `i32.load`, [65532]), [value("i32", 0)]); ++if (!partialOobWriteMayWritePartialData()) { ++ // ./test/core/multi-memory/memory_trap1.wast:244 ++ assert_return(() => invoke($0, `i32.load`, [65532]), [value("i32", 0)]); ++} + + // ./test/core/multi-memory/memory_trap1.wast:245 + assert_trap(() => invoke($0, `i64.store`, [65529, 1311768467294899695n]), `out of bounds memory access`); + +-// ./test/core/multi-memory/memory_trap1.wast:246 +-assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 0n)]); ++if (!partialOobWriteMayWritePartialData()) { ++ // ./test/core/multi-memory/memory_trap1.wast:246 ++ assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 0n)]); ++} + + // ./test/core/multi-memory/memory_trap1.wast:247 + assert_trap( +@@ -589,8 +597,10 @@ assert_trap( + `out of bounds memory access`, + ); + +-// ./test/core/multi-memory/memory_trap1.wast:248 +-assert_return(() => invoke($0, `f32.load`, [65532]), [value("f32", 0)]); ++if (!partialOobWriteMayWritePartialData()) { ++ // ./test/core/multi-memory/memory_trap1.wast:248 ++ assert_return(() => invoke($0, `f32.load`, [65532]), [value("f32", 0)]); ++} + + // ./test/core/multi-memory/memory_trap1.wast:249 + assert_trap( +@@ -598,5 +608,7 @@ assert_trap( + `out of bounds memory access`, + ); + +-// ./test/core/multi-memory/memory_trap1.wast:250 +-assert_return(() => invoke($0, `f64.load`, [65528]), [value("f64", 0)]); ++if (!partialOobWriteMayWritePartialData()) { ++ // ./test/core/multi-memory/memory_trap1.wast:250 ++ assert_return(() => invoke($0, `f64.load`, [65528]), [value("f64", 0)]); ++} diff --git a/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js b/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js --- a/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js +++ b/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js @@ -132,6 +254,34 @@ diff --git a/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wa ), ], ); +diff --git a/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js b/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js +--- a/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js ++++ b/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js +@@ -33,14 +33,16 @@ let $4 = instantiate(`(module (memory 1 256))`); + // ./test/core/memory.wast:8 + let $5 = instantiate(`(module (memory 0 65536))`); + +-// ./test/core/memory.wast:10 +-assert_invalid(() => instantiate(`(module (memory 0) (memory 0))`), `multiple memories`); +- +-// ./test/core/memory.wast:11 +-assert_invalid( +- () => instantiate(`(module (memory (import "spectest" "memory") 0) (memory 0))`), +- `multiple memories`, +-); ++if (!wasmMultiMemoryEnabled()) { ++ // ./test/core/memory.wast:10 ++ assert_invalid(() => instantiate(`(module (memory 0) (memory 0))`), `multiple memories`); ++ ++ // ./test/core/memory.wast:11 ++ assert_invalid( ++ () => instantiate(`(module (memory (import "spectest" "memory") 0) (memory 0))`), ++ `multiple memories`, ++ ); ++} + + // ./test/core/memory.wast:13 + let $6 = instantiate(`(module (memory (data)) (func (export "memsize") (result i32) (memory.size)))`); diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js --- a/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js +++ b/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js @@ -632,7 +782,7 @@ index 3ea51a8cb0ff3..71739f4a1c8e4 100644 --- a/js/src/jit-test/tests/wasm/spec/function-references/return_call_ref.wast.js +++ b/js/src/jit-test/tests/wasm/spec/function-references/return_call_ref.wast.js @@ -1,3 +1,4 @@ -+// |jit-test| --wasm-tail-calls; skip-if: !wasmTailCallsEnabled() ++// |jit-test| --setpref=wasm_tail_calls=true; skip-if: !wasmTailCallsEnabled() /* Copyright 2021 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -640,7 +790,7 @@ diff --git a/js/src/jit-test/tests/wasm/spec/spec/global.wast.js b/js/src/jit-te --- a/js/src/jit-test/tests/wasm/spec/spec/global.wast.js +++ b/js/src/jit-test/tests/wasm/spec/spec/global.wast.js @@ -1,3 +1,4 @@ -+// |jit-test| --no-wasm-gc ++// |jit-test| --setpref=wasm_gc=false /* Copyright 2021 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-gen.js b/js/src/jit-test/lib/gen/wasm-gc-limits-gen.js new file mode 100644 index 0000000000..01fd527cc3 --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-gen.js @@ -0,0 +1,71 @@ +// Generates large .wasm files for use in ../limits.js. +// Make sure you are running this script from a release build or you will be sad. + +loadRelativeToScript("../wasm-binary.js"); + +function moduleNRecGroupNTypes(numRecs, numTypes) { + let types = []; + for (let i = 0; i < numTypes; i++) { + types.push({ kind: FuncCode, args: [], ret: [] }); + } + let recs = []; + for (let i = 0; i < numRecs; i++) { + recs.push(recGroup(types)); + } + return new Uint8Array(compressLZ4(new Uint8Array(moduleWithSections([typeSection(recs)])).buffer)); +} + +os.file.writeTypedArrayToFile("wasm-gc-limits-r1M-t1.wasm", moduleNRecGroupNTypes(1_000_000, 1)); +os.file.writeTypedArrayToFile("wasm-gc-limits-r1M1-t1.wasm", moduleNRecGroupNTypes(1_000_001, 1)); +os.file.writeTypedArrayToFile("wasm-gc-limits-r1-t1M.wasm", moduleNRecGroupNTypes(1, 1_000_000)); +os.file.writeTypedArrayToFile("wasm-gc-limits-r1-t1M1.wasm", moduleNRecGroupNTypes(1, 1_000_001)); +os.file.writeTypedArrayToFile("wasm-gc-limits-r2-t500K.wasm", moduleNRecGroupNTypes(2, 500_000)); +os.file.writeTypedArrayToFile("wasm-gc-limits-r2-t500K1.wasm", moduleNRecGroupNTypes(2, 500_001)); + +function moduleLargeStruct(size) { + let structInitializer = []; + for (let i = 0; i < size; i++) { + structInitializer.push(I64ConstCode); + structInitializer.push(...varU32(0)); + } + return new Uint8Array(compressLZ4(new Uint8Array(moduleWithSections([ + typeSection([ + { + kind: StructCode, + fields: Array(size).fill(I64Code) + }, + { + kind: FuncCode, + args: [], + ret: [AnyRefCode] + } + ]), + declSection([1, 1]), + exportSection([ + {name: "makeLargeStructDefault", funcIndex: 0}, + {name: "makeLargeStruct", funcIndex: 1} + ]), + bodySection([ + funcBody({ + locals: [], + body: [ + GcPrefix, + StructNewDefault, + ...varU32(0) + ], + }), + funcBody({ + locals: [], + body: [ + ...structInitializer, + GcPrefix, + StructNew, + ...varU32(0) + ], + }), + ]), + ])).buffer)); +} + +os.file.writeTypedArrayToFile("wasm-gc-limits-s10K.wasm", moduleLargeStruct(10_000)); +os.file.writeTypedArrayToFile("wasm-gc-limits-s10K1.wasm", moduleLargeStruct(10_001)); diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-r1-t1M.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-r1-t1M.wasm Binary files differnew file mode 100644 index 0000000000..f92d36f8e3 --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-r1-t1M.wasm diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-r1-t1M1.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-r1-t1M1.wasm Binary files differnew file mode 100644 index 0000000000..190b522472 --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-r1-t1M1.wasm diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-r1M-t1.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-r1M-t1.wasm Binary files differnew file mode 100644 index 0000000000..5424e907ff --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-r1M-t1.wasm diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-r1M1-t1.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-r1M1-t1.wasm Binary files differnew file mode 100644 index 0000000000..a96be86ef3 --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-r1M1-t1.wasm diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-r2-t500K.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-r2-t500K.wasm Binary files differnew file mode 100644 index 0000000000..b9eb6ea929 --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-r2-t500K.wasm diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-r2-t500K1.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-r2-t500K1.wasm Binary files differnew file mode 100644 index 0000000000..e44b6461fe --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-r2-t500K1.wasm diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-s10K.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-s10K.wasm Binary files differnew file mode 100644 index 0000000000..5b209ab5e4 --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-s10K.wasm diff --git a/js/src/jit-test/lib/gen/wasm-gc-limits-s10K1.wasm b/js/src/jit-test/lib/gen/wasm-gc-limits-s10K1.wasm Binary files differnew file mode 100644 index 0000000000..a2062abc52 --- /dev/null +++ b/js/src/jit-test/lib/gen/wasm-gc-limits-s10K1.wasm diff --git a/js/src/jit-test/lib/prologue.js b/js/src/jit-test/lib/prologue.js index 4e24b19836..a5c21a5a08 100644 --- a/js/src/jit-test/lib/prologue.js +++ b/js/src/jit-test/lib/prologue.js @@ -7,17 +7,32 @@ var appendToActual = function(s) { actual += s + ','; } -// Add dummy versions of missing functions and record whether they -// were originally present. +// Add dummy versions of missing functions and record whether they were +// originally present. +// +// This only affects the main global. Any globals created by the test will +// lack the function. let hasFunction = {}; -for (const name of ["gczeal", - "schedulegc", - "gcslice", - "selectforgc", - "verifyprebarriers", - "verifypostbarriers", - "gcPreserveCode", - "setMarkStackLimit"]) { +for (const name of [ + // Functions present if JS_GC_ZEAL defined: + "gczeal", + "unsetgczeal", + "schedulegc", + "selectforgc", + "verifyprebarriers", + "verifypostbarriers", + "currentgc", + "deterministicgc", + "dumpGCArenaInfo", + "setMarkStackLimit", + // Functions present if DEBUG or JS_OOM_BREAKPOINT defined: + "oomThreadTypes", + "oomAfterAllocations", + "oomAtAllocation", + "resetOOMFailure", + "oomTest", + "stackTest", + "interruptTest"]) { const present = name in this; if (!present) { this[name] = function() {}; diff --git a/js/src/jit-test/lib/wasm-binary.js b/js/src/jit-test/lib/wasm-binary.js index c55c8185f5..fdfd2d5732 100644 --- a/js/src/jit-test/lib/wasm-binary.js +++ b/js/src/jit-test/lib/wasm-binary.js @@ -44,6 +44,7 @@ const F64Code = 0x7c; const V128Code = 0x7b; const AnyFuncCode = 0x70; const ExternRefCode = 0x6f; +const AnyRefCode = 0x6e; const EqRefCode = 0x6d; const OptRefCode = 0x63; // (ref null $t), needs heap type immediate const RefCode = 0x64; // (ref $t), needs heap type immediate @@ -52,6 +53,9 @@ const StructCode = 0x5f; const ArrayCode = 0x5e; const VoidCode = 0x40; const BadType = 0x79; // reserved for testing +const RecGroupCode = 0x4e; +const SubFinalTypeCode = 0x4f; +const SubNoFinalTypeCode = 0x50; // Opcodes const UnreachableCode = 0x00 @@ -159,15 +163,17 @@ const MozPrefix = 0xff; const definedOpcodes = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - ...(wasmExceptionsEnabled() ? [0x06, 0x07, 0x08, 0x09] : []), + 0x06, 0x07, 0x08, 0x09, + ...(wasmExnRefEnabled() ? [0x0a] : []), 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, ...(wasmTailCallsEnabled() ? [0x12, 0x13] : []), - ...(wasmFunctionReferencesEnabled() ? [0x14] : []), + ...(wasmGcEnabled() ? [0x14] : []), ...(wasmTailCallsEnabled() && - wasmFunctionReferencesEnabled() ? [0x15] : []), - ...(wasmExceptionsEnabled() ? [0x18, 0x19] : []), + wasmGcEnabled() ? [0x15] : []), + 0x18, 0x19, 0x1a, 0x1b, 0x1c, + ...(wasmExnRefEnabled() ? [0x1f] : []), 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, @@ -218,6 +224,7 @@ const ElemDropCode = 0x0d; // Pending const TableCopyCode = 0x0e; // Pending const StructNew = 0x00; // UNOFFICIAL +const StructNewDefault = 0x01; // UNOFFICIAL const StructGet = 0x03; // UNOFFICIAL const StructSet = 0x06; // UNOFFICIAL @@ -232,8 +239,9 @@ const TagCode = 0x04; const HasMaximumFlag = 0x1; function toU8(array) { - for (let b of array) - assertEq(b < 256, true); + for (const [i, b] of array.entries()) { + assertEq(b < 256, true, `expected byte at index ${i} but got ${b}`); + } return Uint8Array.from(array); } @@ -284,12 +292,14 @@ function encodedString(name, len) { return varU32(len === undefined ? nameBytes.length : len).concat(nameBytes); } -function moduleWithSections(sectionArray) { - var bytes = moduleHeaderThen(); - for (let section of sectionArray) { +function moduleWithSections(sections) { + const bytes = moduleHeaderThen(); + for (const section of sections) { bytes.push(section.name); bytes.push(...varU32(section.body.length)); - bytes.push(...section.body); + for (let byte of section.body) { + bytes.push(byte); + } } return toU8(bytes); } @@ -385,13 +395,17 @@ function typeSection(types) { body.push(...varU32(types.length)); // technically a count of recursion groups for (const type of types) { if (type.isRecursionGroup) { - body.push(0x4f); + body.push(RecGroupCode); body.push(...varU32(type.types.length)); for (const t of type.types) { - body.push(..._encodeType(t)); + for (const byte of _encodeType(t)) { + body.push(byte); + } } } else { - body.push(..._encodeType(type)); + for (const byte of _encodeType(type)) { + body.push(byte); + } } } return { name: typeId, body }; @@ -439,12 +453,12 @@ function _encodeType(typeObj) { // Types are now final by default. const final = typeObj.final ?? true; if (typeObj.sub !== undefined) { - typeBytes.push(final ? 0x4e : 0x50); + typeBytes.push(final ? SubFinalTypeCode : SubNoFinalTypeCode); typeBytes.push(...varU32(1), ...varU32(typeObj.sub)); } else if (final == false) { // This type is extensible even if no supertype is defined. - typeBytes.push(0x50); + typeBytes.push(SubNoFinalTypeCode); typeBytes.push(0x00); } typeBytes.push(typeObj.kind); @@ -514,7 +528,9 @@ function funcBody(func, withEndCode=true) { var body = varU32(func.locals.length); for (let local of func.locals) body.push(...varU32(local)); - body = body.concat(...func.body); + for (let byte of func.body) { + body.push(byte); + } if (withEndCode) body.push(EndCode); body.splice(0, 0, ...varU32(body.length)); diff --git a/js/src/jit-test/lib/wasm.js b/js/src/jit-test/lib/wasm.js index 2b3374ebbe..a5721913e9 100644 --- a/js/src/jit-test/lib/wasm.js +++ b/js/src/jit-test/lib/wasm.js @@ -42,13 +42,12 @@ if (largeArrayBufferSupported()) { } var MaxPagesIn32BitMemory = Math.floor(MaxBytesIn32BitMemory / PageSizeInBytes); -function wasmEvalText(str, imports) { - let binary = wasmTextToBinary(str); - let valid = WebAssembly.validate(binary); +function wasmEvalBinary(binary, imports, compileOptions) { + let valid = WebAssembly.validate(binary, compileOptions); let m; try { - m = new WebAssembly.Module(binary); + m = new WebAssembly.Module(binary, compileOptions); assertEq(valid, true, "failed WebAssembly.validate but still compiled successfully"); } catch(e) { if (!e.toString().match(/out of memory/)) { @@ -60,8 +59,11 @@ function wasmEvalText(str, imports) { return new WebAssembly.Instance(m, imports); } -function wasmValidateText(str) { - let binary = wasmTextToBinary(str); +function wasmEvalText(str, imports, compileOptions) { + return wasmEvalBinary(wasmTextToBinary(str), imports, compileOptions); +} + +function wasmValidateBinary(binary) { let valid = WebAssembly.validate(binary); if (!valid) { new WebAssembly.Module(binary); @@ -70,12 +72,19 @@ function wasmValidateText(str) { assertEq(valid, true, "wasm module was invalid"); } -function wasmFailValidateText(str, pattern) { - let binary = wasmTextToBinary(str); +function wasmFailValidateBinary(binary, pattern) { assertEq(WebAssembly.validate(binary), false, "module passed WebAssembly.validate when it should not have"); assertErrorMessage(() => new WebAssembly.Module(binary), WebAssembly.CompileError, pattern, "module failed WebAssembly.validate but did not fail to compile as expected"); } +function wasmValidateText(str) { + return wasmValidateBinary(wasmTextToBinary(str)); +} + +function wasmFailValidateText(str, pattern) { + return wasmFailValidateBinary(wasmTextToBinary(str), pattern); +} + // Expected compilation failure can happen in a couple of ways: // // - The compiler can be available but not capable of recognizing some opcodes: diff --git a/js/src/jit-test/tests/Set/bug1729269.js b/js/src/jit-test/tests/Set/bug1729269.js index e97f6d2a12..61a7c0e535 100644 --- a/js/src/jit-test/tests/Set/bug1729269.js +++ b/js/src/jit-test/tests/Set/bug1729269.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var patchSet = new Set(); function checkSet(str) { diff --git a/js/src/jit-test/tests/arguments/1883837.js b/js/src/jit-test/tests/arguments/1883837.js new file mode 100644 index 0000000000..3fb7ac7944 --- /dev/null +++ b/js/src/jit-test/tests/arguments/1883837.js @@ -0,0 +1,10 @@ +let threw = false; +try { + ({ + a: arguments.length + } = 0); +} catch (error) { + assertEq(error instanceof ReferenceError, true); + threw = true; +} +assertEq(threw, true); diff --git a/js/src/jit-test/tests/arguments/argumentsNaming.js b/js/src/jit-test/tests/arguments/argumentsNaming.js new file mode 100644 index 0000000000..7532172873 --- /dev/null +++ b/js/src/jit-test/tests/arguments/argumentsNaming.js @@ -0,0 +1,3 @@ +let arguments = {} +arguments.length = () => { }; +assertEq(arguments.length.name, ""); diff --git a/js/src/jit-test/tests/arrays/from-async-oom.js b/js/src/jit-test/tests/arrays/from-async-oom.js index a68fd33299..5d7ada400f 100644 --- a/js/src/jit-test/tests/arrays/from-async-oom.js +++ b/js/src/jit-test/tests/arrays/from-async-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // Basic Smoke Test async function* asyncGen(n) { for (let i = 0; i < n; i++) { diff --git a/js/src/jit-test/tests/asm.js/bug1219954.js b/js/src/jit-test/tests/asm.js/bug1219954.js index 305369a936..c8553dde74 100644 --- a/js/src/jit-test/tests/asm.js/bug1219954.js +++ b/js/src/jit-test/tests/asm.js/bug1219954.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow "use strict"; let g = (function() { diff --git a/js/src/jit-test/tests/asm.js/bug1385428.js b/js/src/jit-test/tests/asm.js/bug1385428.js index c586ec8f3e..2044b52b96 100644 --- a/js/src/jit-test/tests/asm.js/bug1385428.js +++ b/js/src/jit-test/tests/asm.js/bug1385428.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - loadFile(` try { Array.prototype.splice.call({ get length() { diff --git a/js/src/jit-test/tests/asm.js/bug1421565.js b/js/src/jit-test/tests/asm.js/bug1421565.js index 04ab718305..54719e87f7 100644 --- a/js/src/jit-test/tests/asm.js/bug1421565.js +++ b/js/src/jit-test/tests/asm.js/bug1421565.js @@ -1,4 +1,4 @@ -// |jit-test| --ion-offthread-compile=off; skip-if: !isAsmJSCompilationAvailable() || !('oomTest' in this) +// |jit-test| --ion-offthread-compile=off; skip-if: !isAsmJSCompilationAvailable() load(libdir + "asm.js"); diff --git a/js/src/jit-test/tests/asm.js/oom-helper-thread-plus-validation-error.js b/js/src/jit-test/tests/asm.js/oom-helper-thread-plus-validation-error.js index 2d80e98d3c..71744c40be 100644 --- a/js/src/jit-test/tests/asm.js/oom-helper-thread-plus-validation-error.js +++ b/js/src/jit-test/tests/asm.js/oom-helper-thread-plus-validation-error.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this) - oomAfterAllocations(10, 2); evaluate(`function mod(stdlib, ffi, heap) { "use asm"; diff --git a/js/src/jit-test/tests/asm.js/oom-helper-thread.js b/js/src/jit-test/tests/asm.js/oom-helper-thread.js index 18165c01b3..6e7bfc7fd5 100644 --- a/js/src/jit-test/tests/asm.js/oom-helper-thread.js +++ b/js/src/jit-test/tests/asm.js/oom-helper-thread.js @@ -1,4 +1,4 @@ -// |jit-test| exitstatus: 3; skip-if: !('oomAfterAllocations' in this) +// |jit-test| exitstatus: 3 oomAfterAllocations(50, 2); eval("(function() {'use asm'; function f() { return +pow(.0, .0) })") diff --git a/js/src/jit-test/tests/asm.js/testBug1255954.js b/js/src/jit-test/tests/asm.js/testBug1255954.js index 004136bc18..2e1d38de12 100644 --- a/js/src/jit-test/tests/asm.js/testBug1255954.js +++ b/js/src/jit-test/tests/asm.js/testBug1255954.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow const USE_ASM = '"use asm";'; function asmCompile() { diff --git a/js/src/jit-test/tests/atomics/basic-tests.js b/js/src/jit-test/tests/atomics/basic-tests.js index 3712dd1b6f..97dc2a37af 100644 --- a/js/src/jit-test/tests/atomics/basic-tests.js +++ b/js/src/jit-test/tests/atomics/basic-tests.js @@ -1,3 +1,5 @@ +// |jit-test| --enable-arraybuffer-resizable + // Basic functional tests for the Atomics primitives. // // These do not test atomicity, just that calling and coercions and @@ -562,3 +564,21 @@ function runTests(SharedOrUnsharedArrayBuffer) { runTests(SharedArrayBuffer); runTests(ArrayBuffer); + +if (ArrayBuffer.prototype.resize) { + class ResizableArrayBuffer { + constructor(byteLength = 0) { + return new ArrayBuffer(byteLength, {maxByteLength: byteLength}); + } + } + runTests(ResizableArrayBuffer); +} + +if (SharedArrayBuffer.prototype.grow) { + class GrowableSharedArrayBuffer { + constructor(byteLength = 0) { + return new SharedArrayBuffer(byteLength, {maxByteLength: byteLength}); + } + } + runTests(GrowableSharedArrayBuffer); +} diff --git a/js/src/jit-test/tests/auto-regress/bug1263558.js b/js/src/jit-test/tests/auto-regress/bug1263558.js index 41705977ac..5a4d2df2dd 100644 --- a/js/src/jit-test/tests/auto-regress/bug1263558.js +++ b/js/src/jit-test/tests/auto-regress/bug1263558.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) +// |jit-test| skip-if: !hasFunction.oomTest evalcx(` eval('\ diff --git a/js/src/jit-test/tests/auto-regress/bug1263865.js b/js/src/jit-test/tests/auto-regress/bug1263865.js index e5b11769d9..4ce38d0dd9 100644 --- a/js/src/jit-test/tests/auto-regress/bug1263865.js +++ b/js/src/jit-test/tests/auto-regress/bug1263865.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - loadFile(""); loadFile(""); loadFile("Array.prototype.splice.call(1)"); diff --git a/js/src/jit-test/tests/auto-regress/bug1263879.js b/js/src/jit-test/tests/auto-regress/bug1263879.js index 1baba2dd3e..c2553b3124 100644 --- a/js/src/jit-test/tests/auto-regress/bug1263879.js +++ b/js/src/jit-test/tests/auto-regress/bug1263879.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var lines = ` diff --git a/js/src/jit-test/tests/auto-regress/bug1264823.js b/js/src/jit-test/tests/auto-regress/bug1264823.js index afac37c3c6..5005296dc7 100644 --- a/js/src/jit-test/tests/auto-regress/bug1264823.js +++ b/js/src/jit-test/tests/auto-regress/bug1264823.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - loadFile(""); loadFile(""); loadFile(` function lalala() {} diff --git a/js/src/jit-test/tests/auto-regress/bug1268034.js b/js/src/jit-test/tests/auto-regress/bug1268034.js index 43f2a661fd..f48cbd0278 100644 --- a/js/src/jit-test/tests/auto-regress/bug1268034.js +++ b/js/src/jit-test/tests/auto-regress/bug1268034.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { offThreadCompileToStencil(""); }); diff --git a/js/src/jit-test/tests/auto-regress/bug1269074.js b/js/src/jit-test/tests/auto-regress/bug1269074.js index 6e2cc33035..74d5fde822 100644 --- a/js/src/jit-test/tests/auto-regress/bug1269074.js +++ b/js/src/jit-test/tests/auto-regress/bug1269074.js @@ -1,3 +1,3 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom; skip-if: !hasFunction.oomTest evalcx('oomTest(function() { Array(...""); })', newGlobal()); diff --git a/js/src/jit-test/tests/auto-regress/bug1375446.js b/js/src/jit-test/tests/auto-regress/bug1375446.js index ef8ae4c640..b48475a96b 100644 --- a/js/src/jit-test/tests/auto-regress/bug1375446.js +++ b/js/src/jit-test/tests/auto-regress/bug1375446.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom loadFile(` disassemble(function() { diff --git a/js/src/jit-test/tests/auto-regress/bug1462341.js b/js/src/jit-test/tests/auto-regress/bug1462341.js index 37c4f2129c..3f77b43ba0 100644 --- a/js/src/jit-test/tests/auto-regress/bug1462341.js +++ b/js/src/jit-test/tests/auto-regress/bug1462341.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom loadFile(` switch (0) { diff --git a/js/src/jit-test/tests/auto-regress/bug1466626-1.js b/js/src/jit-test/tests/auto-regress/bug1466626-1.js index f82c4de48e..8815fafc66 100644 --- a/js/src/jit-test/tests/auto-regress/bug1466626-1.js +++ b/js/src/jit-test/tests/auto-regress/bug1466626-1.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { for (var i = 0; i < 10; ++i) { Promise.resolve().then(); diff --git a/js/src/jit-test/tests/auto-regress/bug1466626-2.js b/js/src/jit-test/tests/auto-regress/bug1466626-2.js index 056ea075e2..9c1ce593a8 100644 --- a/js/src/jit-test/tests/auto-regress/bug1466626-2.js +++ b/js/src/jit-test/tests/auto-regress/bug1466626-2.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var globals = []; for (var i = 0; i < 24; ++i) { var g = newGlobal(); diff --git a/js/src/jit-test/tests/auto-regress/bug1466626-3.js b/js/src/jit-test/tests/auto-regress/bug1466626-3.js index bffecfce73..2907e8ce2f 100644 --- a/js/src/jit-test/tests/auto-regress/bug1466626-3.js +++ b/js/src/jit-test/tests/auto-regress/bug1466626-3.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var g = newGlobal(); var i = 0; diff --git a/js/src/jit-test/tests/auto-regress/bug1466626-4.js b/js/src/jit-test/tests/auto-regress/bug1466626-4.js index aa02a3ba08..8efc52727c 100644 --- a/js/src/jit-test/tests/auto-regress/bug1466626-4.js +++ b/js/src/jit-test/tests/auto-regress/bug1466626-4.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var source = "{"; for (var i = 0; i < 120; ++i) source += `function f${i}(){}` diff --git a/js/src/jit-test/tests/auto-regress/bug1562102.js b/js/src/jit-test/tests/auto-regress/bug1562102.js index 78f5ef9010..5efb6fce13 100644 --- a/js/src/jit-test/tests/auto-regress/bug1562102.js +++ b/js/src/jit-test/tests/auto-regress/bug1562102.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom; allow-unhandlable-oom oomTest( function() { evaluate(` diff --git a/js/src/jit-test/tests/auto-regress/bug1652148.js b/js/src/jit-test/tests/auto-regress/bug1652148.js index 232957edb6..51a026b92f 100644 --- a/js/src/jit-test/tests/auto-regress/bug1652148.js +++ b/js/src/jit-test/tests/auto-regress/bug1652148.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { new AggregateError([]); }); diff --git a/js/src/jit-test/tests/auto-regress/bug1652153.js b/js/src/jit-test/tests/auto-regress/bug1652153.js index 875949c7ea..0eb4c52f3e 100644 --- a/js/src/jit-test/tests/auto-regress/bug1652153.js +++ b/js/src/jit-test/tests/auto-regress/bug1652153.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - x = "x"; lFile(x); diff --git a/js/src/jit-test/tests/auto-regress/bug1670378.js b/js/src/jit-test/tests/auto-regress/bug1670378.js index da3c1e93dd..ddda1f9710 100644 --- a/js/src/jit-test/tests/auto-regress/bug1670378.js +++ b/js/src/jit-test/tests/auto-regress/bug1670378.js @@ -1,4 +1,4 @@ -// |jit-test| allow-unhandlable-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-unhandlable-oom const otherGlobalNewCompartment = newGlobal({newCompartment: true}); diff --git a/js/src/jit-test/tests/auto-regress/bug1791401.js b/js/src/jit-test/tests/auto-regress/bug1791401.js index 6b3a7a5dbd..27cf95b0c0 100644 --- a/js/src/jit-test/tests/auto-regress/bug1791401.js +++ b/js/src/jit-test/tests/auto-regress/bug1791401.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { var f = Function(` // Don't actually enter the loop. This still causes the original bug and diff --git a/js/src/jit-test/tests/auto-regress/bug1798883.js b/js/src/jit-test/tests/auto-regress/bug1798883.js index 9ebe5daa2e..e0c8b52dd7 100644 --- a/js/src/jit-test/tests/auto-regress/bug1798883.js +++ b/js/src/jit-test/tests/auto-regress/bug1798883.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // String with an initial part which doesn't need to be normalised and a tail // which gets normalised to "\u05E9\u05BC\u05C1". var s = "a".repeat(32) + String.fromCharCode(0xFB2C); @@ -8,4 +6,4 @@ oomTest(function() { // |normalize()| needs to be called at least twice to trigger the bug. s.normalize(); s.normalize(); -});
\ No newline at end of file +}); diff --git a/js/src/jit-test/tests/auto-regress/bug1879688.js b/js/src/jit-test/tests/auto-regress/bug1879688.js index a05ae548b5..fecd582eff 100644 --- a/js/src/jit-test/tests/auto-regress/bug1879688.js +++ b/js/src/jit-test/tests/auto-regress/bug1879688.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - let x = 0; oomTest(function () { let y = x++; diff --git a/js/src/jit-test/tests/baseline/bug1209585.js b/js/src/jit-test/tests/baseline/bug1209585.js index 6873fe37d5..39f6316c26 100644 --- a/js/src/jit-test/tests/baseline/bug1209585.js +++ b/js/src/jit-test/tests/baseline/bug1209585.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: helperThreadCount() === 0 || !('oomAtAllocation' in this) +// |jit-test| skip-if: helperThreadCount() === 0 if ("gczeal" in this) gczeal(0); diff --git a/js/src/jit-test/tests/baseline/bug1344334.js b/js/src/jit-test/tests/baseline/bug1344334.js index 8245148833..b771da02fc 100644 --- a/js/src/jit-test/tests/baseline/bug1344334.js +++ b/js/src/jit-test/tests/baseline/bug1344334.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function f(s) { s + "x"; s.indexOf("y") === 0; diff --git a/js/src/jit-test/tests/baseline/bug1491337.js b/js/src/jit-test/tests/baseline/bug1491337.js index 107507155f..c297d6c9f3 100644 --- a/js/src/jit-test/tests/baseline/bug1491337.js +++ b/js/src/jit-test/tests/baseline/bug1491337.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(new Function(` let kJSEmbeddingMaxTypes = 1000000; let kJSEmbeddingMaxFunctions = 1000000; diff --git a/js/src/jit-test/tests/baseline/bug1491350.js b/js/src/jit-test/tests/baseline/bug1491350.js index 697a39c50c..404ed8bb1e 100644 --- a/js/src/jit-test/tests/baseline/bug1491350.js +++ b/js/src/jit-test/tests/baseline/bug1491350.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(new Function(` var a = ['p', 'q', 'r', 's', 't']; var o = {p:1, q:2, r:3, s:4, t:5}; diff --git a/js/src/jit-test/tests/basic/bug-1198090.js b/js/src/jit-test/tests/basic/bug-1198090.js index 2ee6b5a2ed..d6445639cd 100644 --- a/js/src/jit-test/tests/basic/bug-1198090.js +++ b/js/src/jit-test/tests/basic/bug-1198090.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAtAllocation' in this) +// |jit-test| allow-oom for (let a of [ null, function() {}, function() {}, null, function() {}, function() {}, diff --git a/js/src/jit-test/tests/basic/bug-1271507.js b/js/src/jit-test/tests/basic/bug-1271507.js index 88097aca8d..e053da62e9 100644 --- a/js/src/jit-test/tests/basic/bug-1271507.js +++ b/js/src/jit-test/tests/basic/bug-1271507.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: typeof oomAfterAllocations !== 'function' +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations lfcode = new Array(); oomAfterAllocations(100); loadFile(file); diff --git a/js/src/jit-test/tests/basic/bug-1665583.js b/js/src/jit-test/tests/basic/bug-1665583.js index 012dc1d043..d7350053ef 100644 --- a/js/src/jit-test/tests/basic/bug-1665583.js +++ b/js/src/jit-test/tests/basic/bug-1665583.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function parseModule(source) { offThreadCompileModuleToStencil(source); var stencil = finishOffThreadStencil(); diff --git a/js/src/jit-test/tests/basic/bug1207863.js b/js/src/jit-test/tests/basic/bug1207863.js index ef079a3c29..e452694e99 100644 --- a/js/src/jit-test/tests/basic/bug1207863.js +++ b/js/src/jit-test/tests/basic/bug1207863.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !("oomAtAllocation" in this && "resetOOMFailure" in this) +// |jit-test| allow-oom; allow-unhandlable-oom function oomTest(f) { var i = 1; diff --git a/js/src/jit-test/tests/basic/bug1219128-1.js b/js/src/jit-test/tests/basic/bug1219128-1.js index 7a81d73f0a..4724059bca 100644 --- a/js/src/jit-test/tests/basic/bug1219128-1.js +++ b/js/src/jit-test/tests/basic/bug1219128-1.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - evaluate(` x = evalcx("lazy"); oomTest(function () { diff --git a/js/src/jit-test/tests/basic/bug1219128-2.js b/js/src/jit-test/tests/basic/bug1219128-2.js index 7208fc3260..1a17f5abeb 100644 --- a/js/src/jit-test/tests/basic/bug1219128-2.js +++ b/js/src/jit-test/tests/basic/bug1219128-2.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - a = evalcx("lazy") oomTest(() => a.toString) diff --git a/js/src/jit-test/tests/basic/bug1219128-3.js b/js/src/jit-test/tests/basic/bug1219128-3.js index feca3eb55c..854be23213 100644 --- a/js/src/jit-test/tests/basic/bug1219128-3.js +++ b/js/src/jit-test/tests/basic/bug1219128-3.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - x = evalcx('lazy'); oomTest(function() { x.eval diff --git a/js/src/jit-test/tests/basic/bug1219128-4.js b/js/src/jit-test/tests/basic/bug1219128-4.js index 41f8b9757a..6329b3f198 100644 --- a/js/src/jit-test/tests/basic/bug1219128-4.js +++ b/js/src/jit-test/tests/basic/bug1219128-4.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - x = evalcx("lazy"); oomTest((function() { evalcx("({", x); diff --git a/js/src/jit-test/tests/basic/bug1219128-5.js b/js/src/jit-test/tests/basic/bug1219128-5.js index 30dc56e5ce..9ce2c7ab5c 100644 --- a/js/src/jit-test/tests/basic/bug1219128-5.js +++ b/js/src/jit-test/tests/basic/bug1219128-5.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - x = evalcx("lazy"); oomTest(function() { x.of(new(delete y)); diff --git a/js/src/jit-test/tests/basic/bug1219128-6.js b/js/src/jit-test/tests/basic/bug1219128-6.js index cb9f4c7170..bd02653202 100644 --- a/js/src/jit-test/tests/basic/bug1219128-6.js +++ b/js/src/jit-test/tests/basic/bug1219128-6.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - a = evalcx("lazy") oomTest(function() { a.b diff --git a/js/src/jit-test/tests/basic/bug1219128-7.js b/js/src/jit-test/tests/basic/bug1219128-7.js index 87ec092f87..e5f660cc77 100644 --- a/js/src/jit-test/tests/basic/bug1219128-7.js +++ b/js/src/jit-test/tests/basic/bug1219128-7.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow function main() { const v1 = this.newGlobal(); diff --git a/js/src/jit-test/tests/basic/bug1219128-8.js b/js/src/jit-test/tests/basic/bug1219128-8.js index a957b879dc..d8852b423f 100644 --- a/js/src/jit-test/tests/basic/bug1219128-8.js +++ b/js/src/jit-test/tests/basic/bug1219128-8.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - a = evalcx('lazy') oomTest(() => a < 0) diff --git a/js/src/jit-test/tests/basic/bug1234414.js b/js/src/jit-test/tests/basic/bug1234414.js index 2aeda2c897..5f8af8e0b6 100644 --- a/js/src/jit-test/tests/basic/bug1234414.js +++ b/js/src/jit-test/tests/basic/bug1234414.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { var max = 400; function f(b) { diff --git a/js/src/jit-test/tests/basic/bug1240502.js b/js/src/jit-test/tests/basic/bug1240502.js index 08d6619460..5baed067dc 100644 --- a/js/src/jit-test/tests/basic/bug1240502.js +++ b/js/src/jit-test/tests/basic/bug1240502.js @@ -1,2 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(() => eval(`Array(..."ABC")`)); diff --git a/js/src/jit-test/tests/basic/bug1263868.js b/js/src/jit-test/tests/basic/bug1263868.js index ba678d71d5..6375cfdd65 100644 --- a/js/src/jit-test/tests/basic/bug1263868.js +++ b/js/src/jit-test/tests/basic/bug1263868.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) function g(f, params) { entryPoints(params); } diff --git a/js/src/jit-test/tests/basic/bug1264954.js b/js/src/jit-test/tests/basic/bug1264954.js index e9d6422798..ca1a8fb179 100644 --- a/js/src/jit-test/tests/basic/bug1264954.js +++ b/js/src/jit-test/tests/basic/bug1264954.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) function f(x) { oomTest(() => eval(x)); } diff --git a/js/src/jit-test/tests/basic/bug1265693.js b/js/src/jit-test/tests/basic/bug1265693.js index 9922999d1b..d25713cf97 100644 --- a/js/src/jit-test/tests/basic/bug1265693.js +++ b/js/src/jit-test/tests/basic/bug1265693.js @@ -1,2 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(Function("Function.hasOwnProperty(1.1)")); diff --git a/js/src/jit-test/tests/basic/bug1278839.js b/js/src/jit-test/tests/basic/bug1278839.js index 3756e95d68..cff252cc7a 100644 --- a/js/src/jit-test/tests/basic/bug1278839.js +++ b/js/src/jit-test/tests/basic/bug1278839.js @@ -1,3 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) for (var i=0; i<2; i++) oomTest(() => eval("setJitCompilerOption(eval + Function, 0);")); diff --git a/js/src/jit-test/tests/basic/bug1296249.js b/js/src/jit-test/tests/basic/bug1296249.js index 7c8b7c5df4..c5fb49bdbe 100644 --- a/js/src/jit-test/tests/basic/bug1296249.js +++ b/js/src/jit-test/tests/basic/bug1296249.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow function f(x) { new Int32Array(x); } diff --git a/js/src/jit-test/tests/basic/bug1300904.js b/js/src/jit-test/tests/basic/bug1300904.js index 2274129366..15e62ad818 100644 --- a/js/src/jit-test/tests/basic/bug1300904.js +++ b/js/src/jit-test/tests/basic/bug1300904.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) Object.getOwnPropertyNames(this); oomTest(function() { this[0] = null; diff --git a/js/src/jit-test/tests/basic/bug1316557.js b/js/src/jit-test/tests/basic/bug1316557.js new file mode 100644 index 0000000000..73edb23893 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1316557.js @@ -0,0 +1 @@ +assertEq(Math.pow(-999, -999), -0); diff --git a/js/src/jit-test/tests/basic/bug1344265.js b/js/src/jit-test/tests/basic/bug1344265.js index 2ffc2f7e5c..da5a882e14 100644 --- a/js/src/jit-test/tests/basic/bug1344265.js +++ b/js/src/jit-test/tests/basic/bug1344265.js @@ -1,3 +1,3 @@ -// |jit-test| allow-unhandlable-oom; allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-unhandlable-oom; allow-oom oomAfterAllocations(1); newString("a", {external: true}); diff --git a/js/src/jit-test/tests/basic/bug1348407.js b/js/src/jit-test/tests/basic/bug1348407.js index 133a49018c..51371fefcd 100644 --- a/js/src/jit-test/tests/basic/bug1348407.js +++ b/js/src/jit-test/tests/basic/bug1348407.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) x = evalcx("lazy"); oomTest(function () { x.eval("1"); diff --git a/js/src/jit-test/tests/basic/bug1411294.js b/js/src/jit-test/tests/basic/bug1411294.js index 327c808bcf..2c748768bc 100644 --- a/js/src/jit-test/tests/basic/bug1411294.js +++ b/js/src/jit-test/tests/basic/bug1411294.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(function() { eval(`var clonebuffer = serialize("abc"); clonebuffer.clonebuffer = "\ diff --git a/js/src/jit-test/tests/basic/bug1447996.js b/js/src/jit-test/tests/basic/bug1447996.js index ec7cc5a25a..82c152ac15 100644 --- a/js/src/jit-test/tests/basic/bug1447996.js +++ b/js/src/jit-test/tests/basic/bug1447996.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('stackTest' in this) - var x = 0; function f() { var s = "abcdef(((((((a|b)a|b)a|b)a|b)a|b)a|b)a|b)" + x; diff --git a/js/src/jit-test/tests/basic/bug1459258.js b/js/src/jit-test/tests/basic/bug1459258.js index d29231a34a..b68ad115a0 100644 --- a/js/src/jit-test/tests/basic/bug1459258.js +++ b/js/src/jit-test/tests/basic/bug1459258.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(function() { return [0, Math.PI, NaN, Infinity, true, false, Symbol(), Math.tan, Reflect, Proxy, print, assertEq, Array, String, Boolean, Number, parseInt, diff --git a/js/src/jit-test/tests/basic/bug1493627.js b/js/src/jit-test/tests/basic/bug1493627.js index ee0525128c..b8952313ff 100644 --- a/js/src/jit-test/tests/basic/bug1493627.js +++ b/js/src/jit-test/tests/basic/bug1493627.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('stackTest' in this) stackTest(function() { eval(`var g = newGlobal(); recomputeWrappers(this, g);`); }); diff --git a/js/src/jit-test/tests/basic/bug1516406.js b/js/src/jit-test/tests/basic/bug1516406.js index 9a513fc1a2..dbee2feacb 100644 --- a/js/src/jit-test/tests/basic/bug1516406.js +++ b/js/src/jit-test/tests/basic/bug1516406.js @@ -1,2 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(() => dumpScopeChain(eval(`b => 1`))); diff --git a/js/src/jit-test/tests/basic/bug1532265.js b/js/src/jit-test/tests/basic/bug1532265.js index 500b6e4a64..bf8416b78d 100644 --- a/js/src/jit-test/tests/basic/bug1532265.js +++ b/js/src/jit-test/tests/basic/bug1532265.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom ignoreUnhandledRejections(); diff --git a/js/src/jit-test/tests/basic/bug1548759-1.js b/js/src/jit-test/tests/basic/bug1548759-1.js index 6e6f795cad..cac4953f06 100644 --- a/js/src/jit-test/tests/basic/bug1548759-1.js +++ b/js/src/jit-test/tests/basic/bug1548759-1.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) (function() { oomTest(async function() { x; diff --git a/js/src/jit-test/tests/basic/bug1548759-2.js b/js/src/jit-test/tests/basic/bug1548759-2.js index 5e0eef54cc..6fad23f92b 100644 --- a/js/src/jit-test/tests/basic/bug1548759-2.js +++ b/js/src/jit-test/tests/basic/bug1548759-2.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(function() { return { x: async function() { diff --git a/js/src/jit-test/tests/basic/bug1574725.js b/js/src/jit-test/tests/basic/bug1574725.js index f7ccb27d92..03cdf897aa 100644 --- a/js/src/jit-test/tests/basic/bug1574725.js +++ b/js/src/jit-test/tests/basic/bug1574725.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 for (let i = 0; i < 15; ++i) { evalInWorker("for (var i = 0; i < 100; i++) {}"); } diff --git a/js/src/jit-test/tests/basic/bug1644839-2.js b/js/src/jit-test/tests/basic/bug1644839-2.js index cf0f7d8981..5c2e06258d 100644 --- a/js/src/jit-test/tests/basic/bug1644839-2.js +++ b/js/src/jit-test/tests/basic/bug1644839-2.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) var code = ` (\`\${key}: \${(args[1]?.toString)?.()}\`) `; diff --git a/js/src/jit-test/tests/basic/bug1644839.js b/js/src/jit-test/tests/basic/bug1644839.js index b83b662358..44fdee1ddf 100644 --- a/js/src/jit-test/tests/basic/bug1644839.js +++ b/js/src/jit-test/tests/basic/bug1644839.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) var code = ` (\`\${key}: \${args[1]?.toString()}\`) `; diff --git a/js/src/jit-test/tests/basic/bug1666856.js b/js/src/jit-test/tests/basic/bug1666856.js index ea6e6942d2..50d8505eb6 100644 --- a/js/src/jit-test/tests/basic/bug1666856.js +++ b/js/src/jit-test/tests/basic/bug1666856.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !this.oomTest - let i = 10000; oomTest(() => { let arr = []; diff --git a/js/src/jit-test/tests/basic/bug1877586.js b/js/src/jit-test/tests/basic/bug1877586.js index d6ff5b1ae3..9d64f110af 100644 --- a/js/src/jit-test/tests/basic/bug1877586.js +++ b/js/src/jit-test/tests/basic/bug1877586.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomAtAllocation' in this); allow-oom +// |jit-test| allow-oom try { for (let i = 0; i < 5; i++) { WebAssembly.instantiateStreaming( diff --git a/js/src/jit-test/tests/basic/bug1883828.js b/js/src/jit-test/tests/basic/bug1883828.js new file mode 100644 index 0000000000..3c63a00d2b --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1883828.js @@ -0,0 +1,5 @@ +const arr = []; +arr[Symbol.toPrimitive] = quit; +const stack = {stack: saveStack(), cause: arr}; +const bound = bindToAsyncStack(function() {}, stack); +bound(); diff --git a/js/src/jit-test/tests/basic/bug1884706.js b/js/src/jit-test/tests/basic/bug1884706.js new file mode 100644 index 0000000000..9bf7c1b52d --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1884706.js @@ -0,0 +1,5 @@ +const arr = new Int32Array(1 << 26); +try { + for (const key in arr) { + } +} catch {} diff --git a/js/src/jit-test/tests/basic/date-getLocale-oom.js b/js/src/jit-test/tests/basic/date-getLocale-oom.js index 7c0b1a7190..dc6371a35b 100644 --- a/js/src/jit-test/tests/basic/date-getLocale-oom.js +++ b/js/src/jit-test/tests/basic/date-getLocale-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function () { new Date(NaN).toString(); }, {keepFailing: true}); diff --git a/js/src/jit-test/tests/basic/date-late-weekday-warning.js b/js/src/jit-test/tests/basic/date-late-weekday-warning.js deleted file mode 100644 index ca6730e56c..0000000000 --- a/js/src/jit-test/tests/basic/date-late-weekday-warning.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Test deprecation warning for late weekday in Date.parse - */ - -function testWarn(date) { - const g = newGlobal(); - g.eval(`Date.parse("${date}")`); - const warning = getLastWarning(); - assertEq(warning !== null, true, `warning should be caught for ${date}`); - assertEq(warning.name, "Warning", warning.name); - - clearLastWarning(); - - g.eval(`Date.parse("${date}")`); - assertEq(getLastWarning(), null, "warning should not be caught for 2nd ocurrence"); -} - -function testNoWarn(date) { - Date.parse(date); - assertEq(getLastWarning(), null, `warning should not be caught for ${date}`); -} - -enableLastWarning(); - -testWarn("Sep 26 1995 Tues"); -testWarn("Sep 26 Tues 1995"); -testWarn("Sep 26 Tues 1995 Tues"); -testWarn("Sep 26 1995 10:Tues:00"); - -testNoWarn("Sep 26 1995"); -testNoWarn("Tues Sep 26 1995"); -testNoWarn("Sep Tues 26 1995"); - -disableLastWarning(); diff --git a/js/src/jit-test/tests/basic/dictionary-add-prop-oom.js b/js/src/jit-test/tests/basic/dictionary-add-prop-oom.js index e393cfca6b..10e09ed06f 100644 --- a/js/src/jit-test/tests/basic/dictionary-add-prop-oom.js +++ b/js/src/jit-test/tests/basic/dictionary-add-prop-oom.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) enableShapeConsistencyChecks(); oomTest(() => { var obj = {a: 1, b: 2, c: 3}; diff --git a/js/src/jit-test/tests/basic/dumpValue.js b/js/src/jit-test/tests/basic/dumpValue.js index 6bf4ffcb8d..b97c9e89d8 100644 --- a/js/src/jit-test/tests/basic/dumpValue.js +++ b/js/src/jit-test/tests/basic/dumpValue.js @@ -1,6 +1,4 @@ -// |jit-test| skip-if: typeof dumpValue !== 'function' || getBuildConfiguration("windows") - -// FIXME: Fix backslash handling on windows (bug 1880003). +// |jit-test| skip-if: typeof dumpValue !== 'function' // Try the dumpValue and dumpValueToString shell functions on various types of // values, and make sure theyit don't crash, and the result is valid JSON. diff --git a/js/src/jit-test/tests/basic/inflate-oom.js b/js/src/jit-test/tests/basic/inflate-oom.js index 645980789f..52d73d66cb 100644 --- a/js/src/jit-test/tests/basic/inflate-oom.js +++ b/js/src/jit-test/tests/basic/inflate-oom.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) function test() { function foo() { return 1; diff --git a/js/src/jit-test/tests/basic/property-error-message-fix-disabled.js b/js/src/jit-test/tests/basic/property-error-message-fix-disabled.js index 542bada65f..a011605ed9 100644 --- a/js/src/jit-test/tests/basic/property-error-message-fix-disabled.js +++ b/js/src/jit-test/tests/basic/property-error-message-fix-disabled.js @@ -1,4 +1,4 @@ -// |jit-test| --disable-property-error-message-fix; skip-if: getBuildConfiguration('pbl') +// |jit-test| --setpref=property_error_message_fix=false; skip-if: getBuildConfiguration('pbl') function check(f, message) { let caught = false; diff --git a/js/src/jit-test/tests/basic/property-error-message-fix.js b/js/src/jit-test/tests/basic/property-error-message-fix.js index 6f27416496..32fe8a7408 100644 --- a/js/src/jit-test/tests/basic/property-error-message-fix.js +++ b/js/src/jit-test/tests/basic/property-error-message-fix.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: getBuildConfiguration('pbl') +// |jit-test| --setpref=property_error_message_fix=true; skip-if: getBuildConfiguration('pbl') function check(f, message) { let caught = false; diff --git a/js/src/jit-test/tests/basic/string-substring-latin1rope-with-twobyte-children.js b/js/src/jit-test/tests/basic/string-substring-latin1rope-with-twobyte-children.js new file mode 100644 index 0000000000..3a1d889073 --- /dev/null +++ b/js/src/jit-test/tests/basic/string-substring-latin1rope-with-twobyte-children.js @@ -0,0 +1,12 @@ +let right = newRope("b", "012345678901234567890123456789"); +let latin1Rope = newRope("a", right); +let twoByteRope = newRope("\u221e", right); + +// Flattening |twoByteRope| changes |right| from a Latin-1 rope into a two-byte +// dependent string. At this point, |latin1Rope| has the Latin-1 flag set, but +// also has a two-byte rope child. +ensureLinearString(twoByteRope); + +let result = latin1Rope.substring(0, 3); + +assertEq(result, "ab0"); diff --git a/js/src/jit-test/tests/basic/testBug756919.js b/js/src/jit-test/tests/basic/testBug756919.js index a72f46bece..9bb0d9a2cc 100644 --- a/js/src/jit-test/tests/basic/testBug756919.js +++ b/js/src/jit-test/tests/basic/testBug756919.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) function test(x) { var upvar = ""; function f() { upvar += ""; } diff --git a/js/src/jit-test/tests/basic/testNeutering.js b/js/src/jit-test/tests/basic/testDetach.js index fc49e3f99e..fc49e3f99e 100644 --- a/js/src/jit-test/tests/basic/testNeutering.js +++ b/js/src/jit-test/tests/basic/testDetach.js diff --git a/js/src/jit-test/tests/basic/testNativeArgsRooting.js b/js/src/jit-test/tests/basic/testNativeArgsRooting.js index 1ce8259f2d..5e3e8f5f12 100644 --- a/js/src/jit-test/tests/basic/testNativeArgsRooting.js +++ b/js/src/jit-test/tests/basic/testNativeArgsRooting.js @@ -1,4 +1,3 @@ -if ('gczeal' in this) (function () { (eval("\ (function () {\ diff --git a/js/src/jit-test/tests/bug1636306.js b/js/src/jit-test/tests/bug1636306.js index e39dc84f80..ab538831fe 100644 --- a/js/src/jit-test/tests/bug1636306.js +++ b/js/src/jit-test/tests/bug1636306.js @@ -1,4 +1,4 @@ -// |jit-test| --no-ion; skip-if: !('oomTest' in this) +// |jit-test| --no-ion oomTest(() => { eval(` function getCallee() { return getCallee.caller; } diff --git a/js/src/jit-test/tests/bug1681258.js b/js/src/jit-test/tests/bug1681258.js index 4f15a8283e..d0af22c322 100644 --- a/js/src/jit-test/tests/bug1681258.js +++ b/js/src/jit-test/tests/bug1681258.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this);--fast-warmup;--blinterp-warmup-threshold=10 +// |jit-test| --fast-warmup;--blinterp-warmup-threshold=10 ignoreUnhandledRejections(); oomTest(async function() { diff --git a/js/src/jit-test/tests/bug1787730.js b/js/src/jit-test/tests/bug1787730.js index a828527583..09a44496d8 100644 --- a/js/src/jit-test/tests/bug1787730.js +++ b/js/src/jit-test/tests/bug1787730.js @@ -1,3 +1,3 @@ -// |jit-test| --delazification-mode=concurrent-df+on-demand; skip-if: !('oomTest' in this) || isLcovEnabled() +// |jit-test| --delazification-mode=concurrent-df+on-demand; skip-if: isLcovEnabled() oomTest(() => evalcx(0)); diff --git a/js/src/jit-test/tests/bug1878098-serialization-log-oom.js b/js/src/jit-test/tests/bug1878098-serialization-log-oom.js new file mode 100644 index 0000000000..d752337cfa --- /dev/null +++ b/js/src/jit-test/tests/bug1878098-serialization-log-oom.js @@ -0,0 +1,8 @@ +// |jit-test| skip-if: !('oomTest' in this) + +x = []; +x.keepFailing = []; +oomTest(function () { + y = { z: [] }; + makeSerializable().log; +}, x); diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-bytelength-with-sab.js b/js/src/jit-test/tests/dataview/resizable-dataview-bytelength-with-sab.js new file mode 100644 index 0000000000..008657d6e3 --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-bytelength-with-sab.js @@ -0,0 +1,29 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +function testResizableArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new DataView(sab, 0, i); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteLength, i); + + sab.grow(i + j + 1); + assertEq(ta.byteLength, i); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBuffer(); + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new DataView(sab); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteLength, i + j); + + sab.grow(i + j + 1); + assertEq(ta.byteLength, i + j + 1); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-bytelength.js b/js/src/jit-test/tests/dataview/resizable-dataview-bytelength.js new file mode 100644 index 0000000000..851011032f --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-bytelength.js @@ -0,0 +1,43 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +load(libdir + "asserts.js"); + +function testResizableArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new DataView(ab, 0, i); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteLength, i); + + ab.resize(i + 1); + assertEq(ta.byteLength, i); + + if (i > 0) { + ab.resize(i - 1); + assertThrowsInstanceOf(() => ta.byteLength, TypeError); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBuffer(); + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new DataView(ab); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteLength, i); + + ab.resize(i + 1); + assertEq(ta.byteLength, i + 1); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ta.byteLength, i - 1); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-byteoffset-sab.js b/js/src/jit-test/tests/dataview/resizable-dataview-byteoffset-sab.js new file mode 100644 index 0000000000..e671700586 --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-byteoffset-sab.js @@ -0,0 +1,43 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new DataView(sab); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteOffset, 0); + + sab.grow(i + j + 1); + assertEq(ta.byteOffset, 0); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); + +function testResizableArrayBufferAutoLengthNonZeroOffset() { + for (let i = 1; i < 4 + 1; ++i) { + let sab = new SharedArrayBuffer(i + 1, {maxByteLength: i + 100 + 1}); + let ta = new DataView(sab, 1); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteOffset, 1); + + sab.grow(i + j + 2); + assertEq(ta.byteOffset, 1); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLengthNonZeroOffset(); + +function testResizableArrayBufferNonZeroOffset() { + for (let i = 2; i < 4 + 2; ++i) { + let sab = new SharedArrayBuffer(i + 2, {maxByteLength: i + 100 + 2}); + let ta = new DataView(sab, 1, 1); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteOffset, 1); + + sab.grow(i + j + 3); + assertEq(ta.byteOffset, 1); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferNonZeroOffset(); diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-byteoffset.js b/js/src/jit-test/tests/dataview/resizable-dataview-byteoffset.js new file mode 100644 index 0000000000..3f0afa1169 --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-byteoffset.js @@ -0,0 +1,67 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +load(libdir + "asserts.js"); + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new DataView(ab); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteOffset, 0); + + ab.resize(i + 1); + assertEq(ta.byteOffset, 0); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ta.byteOffset, 0); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); + +function testResizableArrayBufferAutoLengthNonZeroOffset() { + for (let i = 1; i < 4 + 1; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new DataView(ab, 1); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteOffset, 1); + + ab.resize(i + 1); + assertEq(ta.byteOffset, 1); + + ab.resize(i - 1); + if (i > 1) { + assertEq(ta.byteOffset, 1); + } else { + assertThrowsInstanceOf(() => ta.byteOffset, TypeError); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLengthNonZeroOffset(); + +function testResizableArrayBufferNonZeroOffset() { + for (let i = 2; i < 4 + 2; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new DataView(ab, 1, 1); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteOffset, 1); + + ab.resize(i + 1); + assertEq(ta.byteOffset, 1); + + ab.resize(i - 1); + if (i > 2) { + assertEq(ta.byteOffset, 1); + } else { + assertThrowsInstanceOf(() => ta.byteOffset, TypeError); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferNonZeroOffset(); diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-get-elem-with-sab.js b/js/src/jit-test/tests/dataview/resizable-dataview-get-elem-with-sab.js new file mode 100644 index 0000000000..ea8ca1666c --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-get-elem-with-sab.js @@ -0,0 +1,48 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +load(libdir + "dataview.js"); + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new SharedArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + for (let i = 0; i < length; ++i) { + actual[i] = type(i * i); + expected[i] = type(i * i); + } + + let dv = new DataView(rab); + for (let i = 0; i < 200; ++i) { + let index = i % length; + let byteIndex = index * TA.BYTES_PER_ELEMENT; + + assertEq(dv.getElem(byteIndex, nativeIsLittleEndian), expected[index]); + } +} + +for (let TA of TypedArrays) { + let getter = "get" + typeName(TA); + + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`.replaceAll("getElem", getter))(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-get-elem.js b/js/src/jit-test/tests/dataview/resizable-dataview-get-elem.js new file mode 100644 index 0000000000..1762f9f5ee --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-get-elem.js @@ -0,0 +1,48 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +load(libdir + "dataview.js"); + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new ArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + for (let i = 0; i < length; ++i) { + actual[i] = type(i * i); + expected[i] = type(i * i); + } + + let dv = new DataView(rab); + for (let i = 0; i < 200; ++i) { + let index = i % length; + let byteIndex = index * TA.BYTES_PER_ELEMENT; + + assertEq(dv.getElem(byteIndex, nativeIsLittleEndian), expected[index]); + } +} + +for (let TA of TypedArrays) { + let getter = "get" + typeName(TA); + + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`.replaceAll("getElem", getter))(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-set-elem-with-sab.js b/js/src/jit-test/tests/dataview/resizable-dataview-set-elem-with-sab.js new file mode 100644 index 0000000000..52d9ddaf4c --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-set-elem-with-sab.js @@ -0,0 +1,47 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +load(libdir + "dataview.js"); + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new SharedArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + let dv = new DataView(rab); + for (let i = 0; i < 200; ++i) { + let index = i % length; + let byteIndex = index * TA.BYTES_PER_ELEMENT; + + let v = type(i); + dv.setElem(byteIndex, v, nativeIsLittleEndian); + expected[index] = v; + + assertEq(actual[index], expected[index]); + } +} + +for (let TA of TypedArrays) { + let setter = "set" + typeName(TA); + + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`.replaceAll("setElem", setter))(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/dataview/resizable-dataview-set-elem.js b/js/src/jit-test/tests/dataview/resizable-dataview-set-elem.js new file mode 100644 index 0000000000..0359d1a8b3 --- /dev/null +++ b/js/src/jit-test/tests/dataview/resizable-dataview-set-elem.js @@ -0,0 +1,47 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +load(libdir + "dataview.js"); + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new ArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + let dv = new DataView(rab); + for (let i = 0; i < 200; ++i) { + let index = i % length; + let byteIndex = index * TA.BYTES_PER_ELEMENT; + + let v = type(i); + dv.setElem(byteIndex, v, nativeIsLittleEndian); + expected[index] = v; + + assertEq(actual[index], expected[index]); + } +} + +for (let TA of TypedArrays) { + let setter = "set" + typeName(TA); + + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`.replaceAll("setElem", setter))(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/debug/Debugger-findScripts-26.js b/js/src/jit-test/tests/debug/Debugger-findScripts-26.js index 1c1510aa4f..57709782ab 100644 --- a/js/src/jit-test/tests/debug/Debugger-findScripts-26.js +++ b/js/src/jit-test/tests/debug/Debugger-findScripts-26.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) var g = newGlobal({newCompartment: true}); var dbg = new Debugger(); var gw = dbg.addDebuggee(g); diff --git a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-18.js b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-18.js index 6ab17714e6..781e300f93 100644 --- a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-18.js +++ b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-18.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) - // Test drainAllocationsLog() entries' inNursery flag. gczeal(0); diff --git a/js/src/jit-test/tests/debug/Memory-takeCensus-06.js b/js/src/jit-test/tests/debug/Memory-takeCensus-06.js index 02f8de30be..9a49140638 100644 --- a/js/src/jit-test/tests/debug/Memory-takeCensus-06.js +++ b/js/src/jit-test/tests/debug/Memory-takeCensus-06.js @@ -106,3 +106,29 @@ Pattern({ other: { by: 'count', label: 'other' } } })); + +try { + const breakdown = { by: "objectClass" }; + breakdown.then = breakdown; + dbg.memory.takeCensus({ breakdown }); + assertEq(true, false, "should not reach here"); +} catch (e) { + assertEq(e.message, "takeCensus breakdown 'by' value nested within itself: \"objectClass\""); +} + +try { + const breakdown = { by: "objectClass", then: { by: "objectClass" } }; + dbg.memory.takeCensus({ breakdown }); + assertEq(true, false, "should not reach here"); +} catch (e) { + assertEq(e.message, "takeCensus breakdown 'by' value nested within itself: \"objectClass\""); +} + +try { + const breakdown = { by: "coarseType", scripts: { by: "filename" } }; + breakdown.scripts.noFilename = breakdown; + dbg.memory.takeCensus({ breakdown }); + assertEq(true, false, "should not reach here"); +} catch (e) { + assertEq(e.message, "takeCensus breakdown 'by' value nested within itself: \"coarseType\""); +} diff --git a/js/src/jit-test/tests/debug/Object-getPromiseReactions-07.js b/js/src/jit-test/tests/debug/Object-getPromiseReactions-07.js new file mode 100644 index 0000000000..a79b2dc2ef --- /dev/null +++ b/js/src/jit-test/tests/debug/Object-getPromiseReactions-07.js @@ -0,0 +1,14 @@ +async function f(arg) { + await arg; + + const g = newGlobal({ sameZoneAs: {} }); + const dbg = g.Debugger({}); + const promise = dbg.getNewestFrame().asyncPromise; + dbg.removeAllDebuggees(); + + // getPromiseReactions should return an empty array after removing debuggee. + assertEq(promise.getPromiseReactions().length, 0); +} + +const p = f(); +f(p); diff --git a/js/src/jit-test/tests/debug/Object-isSameNativeWithJitInfo.js b/js/src/jit-test/tests/debug/Object-isSameNativeWithJitInfo.js new file mode 100644 index 0000000000..648847ae49 --- /dev/null +++ b/js/src/jit-test/tests/debug/Object-isSameNativeWithJitInfo.js @@ -0,0 +1,32 @@ +var g = newGlobal({newCompartment: true}); +var dbg = Debugger(g); +var gdbg = dbg.addDebuggee(g); + +assertEq(gdbg.getProperty("print").return.isSameNativeWithJitInfo(print), true); +assertEq(gdbg.getProperty("print").return.isSameNativeWithJitInfo(newGlobal), false); + +// FakeDOMObject's accessor shares the single native functions, with +// different JSJitInfo for each. + +gdbg.executeInGlobal(` +var fun1 = Object.getOwnPropertyDescriptor(FakeDOMObject.prototype, "x").get; +var fun2 = Object.getOwnPropertyDescriptor(FakeDOMObject.prototype, "slot").get; +`); + +var g_fun1 = gdbg.executeInGlobal("fun1").return; +var g_fun2 = gdbg.executeInGlobal("fun2").return; + +var fun1 = Object.getOwnPropertyDescriptor(FakeDOMObject.prototype, "x").get; +var fun2 = Object.getOwnPropertyDescriptor(FakeDOMObject.prototype, "slot").get; + +// isSameNative doesn't distinguish between fun1 and fun2. +assertEq(g_fun1.isSameNative(fun1), true); +assertEq(g_fun1.isSameNative(fun2), true); +assertEq(g_fun2.isSameNative(fun1), true); +assertEq(g_fun2.isSameNative(fun2), true); + +// isSameNativeWithJitInfo can distinguish between fun1 and fun2. +assertEq(g_fun1.isSameNativeWithJitInfo(fun1), true); +assertEq(g_fun1.isSameNativeWithJitInfo(fun2), false); +assertEq(g_fun2.isSameNativeWithJitInfo(fun1), false); +assertEq(g_fun2.isSameNativeWithJitInfo(fun2), true); diff --git a/js/src/jit-test/tests/debug/breakpoint-oom-01.js b/js/src/jit-test/tests/debug/breakpoint-oom-01.js index 4fd709ccd0..cbbbd7ffda 100644 --- a/js/src/jit-test/tests/debug/breakpoint-oom-01.js +++ b/js/src/jit-test/tests/debug/breakpoint-oom-01.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) +// |jit-test| skip-if: !hasFunction.oomTest // Test for OOM hitting a breakpoint in a generator. // diff --git a/js/src/jit-test/tests/debug/bug-1238610.js b/js/src/jit-test/tests/debug/bug-1238610.js index 91562443bd..79fe5b3c2c 100644 --- a/js/src/jit-test/tests/debug/bug-1238610.js +++ b/js/src/jit-test/tests/debug/bug-1238610.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) || helperThreadCount() === 0 +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations || helperThreadCount() === 0 lfcode = new Array(); dbg = Debugger(); diff --git a/js/src/jit-test/tests/debug/bug-1248162.js b/js/src/jit-test/tests/debug/bug-1248162.js index 825b3376e4..6deadfdc07 100644 --- a/js/src/jit-test/tests/debug/bug-1248162.js +++ b/js/src/jit-test/tests/debug/bug-1248162.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom // Adapted from randomly chosen test: js/src/jit-test/tests/debug/Debugger-onNewGlobalObject-01.js for (var i = 0; i < 9; ++i) { diff --git a/js/src/jit-test/tests/debug/bug-1260725.js b/js/src/jit-test/tests/debug/bug-1260725.js index ce1c263f6a..7f8895e589 100644 --- a/js/src/jit-test/tests/debug/bug-1260725.js +++ b/js/src/jit-test/tests/debug/bug-1260725.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var dbg = new Debugger; dbg.onNewGlobalObject = function(global) { dbg.memory.takeCensus({}); diff --git a/js/src/jit-test/tests/debug/bug-1565275.js b/js/src/jit-test/tests/debug/bug-1565275.js index 242bce3a85..9e600162a4 100644 --- a/js/src/jit-test/tests/debug/bug-1565275.js +++ b/js/src/jit-test/tests/debug/bug-1565275.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - Object.defineProperty(this, "fuzzutils", { value: { evaluate: function() {}, diff --git a/js/src/jit-test/tests/debug/bug-1576862-2.js b/js/src/jit-test/tests/debug/bug-1576862-2.js index 6f7c31e98a..e1922f4a19 100644 --- a/js/src/jit-test/tests/debug/bug-1576862-2.js +++ b/js/src/jit-test/tests/debug/bug-1576862-2.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('stackTest' in this) // Failure to rewrap an exception in Completion::fromJSResult should be propagated. var dbgGlobal = newGlobal({ newCompartment: true }); diff --git a/js/src/jit-test/tests/debug/bug-1584195.js b/js/src/jit-test/tests/debug/bug-1584195.js index 93aca7291d..dd74597b1b 100644 --- a/js/src/jit-test/tests/debug/bug-1584195.js +++ b/js/src/jit-test/tests/debug/bug-1584195.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) // Bug 1584195: Debugger.Frame finalizer should't try to apply // IsAboutToBeFinalized to cells of other alloc kinds, whose arenas may have // been turned over to fresh allocations. diff --git a/js/src/jit-test/tests/debug/bug1216261.js b/js/src/jit-test/tests/debug/bug1216261.js index 0d98327256..6051d69c95 100644 --- a/js/src/jit-test/tests/debug/bug1216261.js +++ b/js/src/jit-test/tests/debug/bug1216261.js @@ -1,4 +1,4 @@ -// |jit-test| exitstatus: 3; skip-if: !('oomAfterAllocations' in this) +// |jit-test| exitstatus: 3 var g = newGlobal(); var dbg = new Debugger(g); diff --git a/js/src/jit-test/tests/debug/bug1219905.js b/js/src/jit-test/tests/debug/bug1219905.js index 5ed51a2423..5e5595a4e7 100644 --- a/js/src/jit-test/tests/debug/bug1219905.js +++ b/js/src/jit-test/tests/debug/bug1219905.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom // We need allow-oom here because the debugger reports an uncaught exception if // it hits OOM calling the exception unwind hook. This causes the shell to exit diff --git a/js/src/jit-test/tests/debug/bug1240546.js b/js/src/jit-test/tests/debug/bug1240546.js index 6d548d8b92..797af37988 100644 --- a/js/src/jit-test/tests/debug/bug1240546.js +++ b/js/src/jit-test/tests/debug/bug1240546.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations var g = newGlobal(); g.debuggeeGlobal = this; diff --git a/js/src/jit-test/tests/debug/bug1240803.js b/js/src/jit-test/tests/debug/bug1240803.js index ab1e0fb641..74eb34ed61 100644 --- a/js/src/jit-test/tests/debug/bug1240803.js +++ b/js/src/jit-test/tests/debug/bug1240803.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations (function() { g = newGlobal({newCompartment: true}) diff --git a/js/src/jit-test/tests/debug/bug1242111.js b/js/src/jit-test/tests/debug/bug1242111.js index dae0efcdab..ebfc2eec70 100644 --- a/js/src/jit-test/tests/debug/bug1242111.js +++ b/js/src/jit-test/tests/debug/bug1242111.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations var g = newGlobal(); g.debuggeeGlobal = []; diff --git a/js/src/jit-test/tests/debug/bug1245862.js b/js/src/jit-test/tests/debug/bug1245862.js index 274903bc40..83793a7306 100644 --- a/js/src/jit-test/tests/debug/bug1245862.js +++ b/js/src/jit-test/tests/debug/bug1245862.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations var g = newGlobal({newCompartment: true}); var dbg = new Debugger; diff --git a/js/src/jit-test/tests/debug/bug1251919.js b/js/src/jit-test/tests/debug/bug1251919.js index 79fd05f890..9e5ccac82f 100644 --- a/js/src/jit-test/tests/debug/bug1251919.js +++ b/js/src/jit-test/tests/debug/bug1251919.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // jsfunfuzz-generated fullcompartmentchecks(true); // Adapted from randomly chosen test: js/src/jit-test/tests/debug/bug-1248162.js diff --git a/js/src/jit-test/tests/debug/bug1254123.js b/js/src/jit-test/tests/debug/bug1254123.js index 72f36ef3ec..c96753b305 100644 --- a/js/src/jit-test/tests/debug/bug1254123.js +++ b/js/src/jit-test/tests/debug/bug1254123.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - evaluate(` function ERROR(msg) { throw new Error("boom"); diff --git a/js/src/jit-test/tests/debug/bug1254190.js b/js/src/jit-test/tests/debug/bug1254190.js index 3d3572b469..74a4799c39 100644 --- a/js/src/jit-test/tests/debug/bug1254190.js +++ b/js/src/jit-test/tests/debug/bug1254190.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this); allow-oom +// |jit-test| slow; allow-oom var g = newGlobal({newCompartment: true}); var dbg = new Debugger(g); diff --git a/js/src/jit-test/tests/debug/bug1254578.js b/js/src/jit-test/tests/debug/bug1254578.js index f36bcef601..9592adb67a 100644 --- a/js/src/jit-test/tests/debug/bug1254578.js +++ b/js/src/jit-test/tests/debug/bug1254578.js @@ -1,5 +1,3 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) - var g = newGlobal({newCompartment: true}); g.debuggeeGlobal = this; g.eval("(" + function() { diff --git a/js/src/jit-test/tests/debug/bug1264961.js b/js/src/jit-test/tests/debug/bug1264961.js index c43a29504d..2047768aeb 100644 --- a/js/src/jit-test/tests/debug/bug1264961.js +++ b/js/src/jit-test/tests/debug/bug1264961.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow loadFile(` var o = {} diff --git a/js/src/jit-test/tests/debug/bug1272908.js b/js/src/jit-test/tests/debug/bug1272908.js index b1e1c5aeaf..397de9a8eb 100644 --- a/js/src/jit-test/tests/debug/bug1272908.js +++ b/js/src/jit-test/tests/debug/bug1272908.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow // Adapted from randomly chosen test: js/src/jit-test/tests/modules/bug-1233915.js g = newGlobal({newCompartment: true}); diff --git a/js/src/jit-test/tests/debug/bug1370905.js b/js/src/jit-test/tests/debug/bug1370905.js index 8f8143132e..7237e55420 100644 --- a/js/src/jit-test/tests/debug/bug1370905.js +++ b/js/src/jit-test/tests/debug/bug1370905.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom function x() { var global = newGlobal({sameZoneAs: this}); diff --git a/js/src/jit-test/tests/debug/bug1404710.js b/js/src/jit-test/tests/debug/bug1404710.js index 78a8bbb5b8..69aa078333 100644 --- a/js/src/jit-test/tests/debug/bug1404710.js +++ b/js/src/jit-test/tests/debug/bug1404710.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('stackTest' in this) stackTest(new Function(` var g = newGlobal(); var dbg = new Debugger(g); diff --git a/js/src/jit-test/tests/debug/bug1434391.js b/js/src/jit-test/tests/debug/bug1434391.js index 86efe7970b..d3231b6e15 100644 --- a/js/src/jit-test/tests/debug/bug1434391.js +++ b/js/src/jit-test/tests/debug/bug1434391.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var g = newGlobal({newCompartment: true}); var dbg = new Debugger(); var gw = dbg.addDebuggee(g); diff --git a/js/src/jit-test/tests/debug/bug1647309.js b/js/src/jit-test/tests/debug/bug1647309.js index e4ffcfc349..520e505b5c 100644 --- a/js/src/jit-test/tests/debug/bug1647309.js +++ b/js/src/jit-test/tests/debug/bug1647309.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - const g = newGlobal({ newCompartment: true }); const dbg = new Debugger(g); diff --git a/js/src/jit-test/tests/debug/bug1878511.js b/js/src/jit-test/tests/debug/bug1878511.js index f9143b9961..a6a550c4b7 100644 --- a/js/src/jit-test/tests/debug/bug1878511.js +++ b/js/src/jit-test/tests/debug/bug1878511.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) var c = 0; var dbg = new Debugger(); oomTest(function () { diff --git a/js/src/jit-test/tests/debug/job-queue-04.js b/js/src/jit-test/tests/debug/job-queue-04.js index 76cf241e8d..040f5874c0 100644 --- a/js/src/jit-test/tests/debug/job-queue-04.js +++ b/js/src/jit-test/tests/debug/job-queue-04.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) // Bug 1527862: Don't assert that the Debugger drained its job queue unless we // actually saved the debuggee's queue. diff --git a/js/src/jit-test/tests/debug/wasm-14.js b/js/src/jit-test/tests/debug/wasm-14.js index 023c16dca4..b47b540d72 100644 --- a/js/src/jit-test/tests/debug/wasm-14.js +++ b/js/src/jit-test/tests/debug/wasm-14.js @@ -1,4 +1,4 @@ -// |jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-function-references --wasm-gc; skip-if: !wasmDebuggingEnabled() || !wasmGcEnabled(); skip-if: true +// |jit-test| test-also=--wasm-compiler=optimizing; test-also=--setpref=wasm_gc=true; skip-if: !wasmDebuggingEnabled() || !wasmGcEnabled(); skip-if: true // An extension of wasm-10.js, testing that wasm GC objects are inspectable in locals. // As of bug 1825098, this test is disabled. (skip-if: true) diff --git a/js/src/jit-test/tests/debug/wasm-15.js b/js/src/jit-test/tests/debug/wasm-15.js index 90b1fbea3b..c2af3bdc17 100644 --- a/js/src/jit-test/tests/debug/wasm-15.js +++ b/js/src/jit-test/tests/debug/wasm-15.js @@ -63,7 +63,7 @@ wasmRunWithDebugger( ); // Checking if enter/leave frame at return_call_ref. -wasmFunctionReferencesEnabled() && wasmRunWithDebugger( +wasmGcEnabled() && wasmRunWithDebugger( '(module (type $t (func)) (elem declare func 0) (func) (func (return_call_ref $t (ref.func 0))) (func (call 1)) (export "test" (func 2)))', undefined, function ({dbg}) { diff --git a/js/src/jit-test/tests/fields/private-proxy-oom.js b/js/src/jit-test/tests/fields/private-proxy-oom.js index dd5200d9a6..ff6bf4632b 100644 --- a/js/src/jit-test/tests/fields/private-proxy-oom.js +++ b/js/src/jit-test/tests/fields/private-proxy-oom.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this); // Check for proxy expando OOM issues. function assertThrowsTypeError(f) { @@ -45,4 +44,4 @@ function testing() { assertThrowsTypeError(() => A.gf(target)); } -oomTest(testing);
\ No newline at end of file +oomTest(testing); diff --git a/js/src/jit-test/tests/fuses/with.js b/js/src/jit-test/tests/fuses/with.js new file mode 100644 index 0000000000..8a76e5c09b --- /dev/null +++ b/js/src/jit-test/tests/fuses/with.js @@ -0,0 +1,9 @@ +let iterProto = [].values().__proto__ +with (newGlobal()) { + const v3 = [].values(); + Object.defineProperty(v3.__proto__, "return", {}); + const v18 = []; + for (let i = 0; i < 500; i++) { + [] = v18; + } +} diff --git a/js/src/jit-test/tests/gc/bug-1108007.js b/js/src/jit-test/tests/gc/bug-1108007.js index 35daaefc91..487b765c87 100644 --- a/js/src/jit-test/tests/gc/bug-1108007.js +++ b/js/src/jit-test/tests/gc/bug-1108007.js @@ -1,4 +1,4 @@ -// |jit-test| --no-threads; --no-ion; --no-baseline; skip-if: !('gczeal' in this) +// |jit-test| --no-threads; --no-ion; --no-baseline gczeal(2); (function() { diff --git a/js/src/jit-test/tests/gc/bug-1155455.js b/js/src/jit-test/tests/gc/bug-1155455.js index c9b8040884..47b2912804 100644 --- a/js/src/jit-test/tests/gc/bug-1155455.js +++ b/js/src/jit-test/tests/gc/bug-1155455.js @@ -1,4 +1,4 @@ -// |jit-test| error: TypeError; skip-if: !('gczeal' in this) +// |jit-test| error: TypeError var g = newGlobal(); gczeal(10, 2) var dbg = Debugger(g); diff --git a/js/src/jit-test/tests/gc/bug-1161968.js b/js/src/jit-test/tests/gc/bug-1161968.js index 5f83aa510c..dc78749cd5 100644 --- a/js/src/jit-test/tests/gc/bug-1161968.js +++ b/js/src/jit-test/tests/gc/bug-1161968.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) - // This test case is a simplified version of debug/Source-invisible.js. gczeal(2,21); diff --git a/js/src/jit-test/tests/gc/bug-1165966.js b/js/src/jit-test/tests/gc/bug-1165966.js index 79c58274cd..8b0bca873c 100644 --- a/js/src/jit-test/tests/gc/bug-1165966.js +++ b/js/src/jit-test/tests/gc/bug-1165966.js @@ -1,4 +1,4 @@ -// |jit-test| --no-ion; skip-if: !('oomTest' in this) +// |jit-test| --no-ion var g = newGlobal(); oomTest(function() { diff --git a/js/src/jit-test/tests/gc/bug-1171909.js b/js/src/jit-test/tests/gc/bug-1171909.js index 755c6ff89d..9ad1767016 100644 --- a/js/src/jit-test/tests/gc/bug-1171909.js +++ b/js/src/jit-test/tests/gc/bug-1171909.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest((function(x) { assertEq(x + y + ex, 25); })); diff --git a/js/src/jit-test/tests/gc/bug-1175755.js b/js/src/jit-test/tests/gc/bug-1175755.js index 1b6960fb8c..3c07184fe8 100644 --- a/js/src/jit-test/tests/gc/bug-1175755.js +++ b/js/src/jit-test/tests/gc/bug-1175755.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; allow-unhandlable-oom setGCCallback({ action: "majorGC", diff --git a/js/src/jit-test/tests/gc/bug-1191576.js b/js/src/jit-test/tests/gc/bug-1191576.js index 6346905256..ba74bfe142 100644 --- a/js/src/jit-test/tests/gc/bug-1191576.js +++ b/js/src/jit-test/tests/gc/bug-1191576.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('gczeal' in this && 'oomAfterAllocations' in this) +// |jit-test| allow-oom var lfcode = new Array(); gczeal(14); diff --git a/js/src/jit-test/tests/gc/bug-1206677.js b/js/src/jit-test/tests/gc/bug-1206677.js index a0d2ff3a1f..0052ef0ad1 100644 --- a/js/src/jit-test/tests/gc/bug-1206677.js +++ b/js/src/jit-test/tests/gc/bug-1206677.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 var lfGlobal = newGlobal(); for (lfLocal in this) { diff --git a/js/src/jit-test/tests/gc/bug-1208994.js b/js/src/jit-test/tests/gc/bug-1208994.js index 12c24f62af..4f28b0c0eb 100644 --- a/js/src/jit-test/tests/gc/bug-1208994.js +++ b/js/src/jit-test/tests/gc/bug-1208994.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => getBacktrace({args: oomTest[load+1], locals: true, thisprops: true})); diff --git a/js/src/jit-test/tests/gc/bug-1209001.js b/js/src/jit-test/tests/gc/bug-1209001.js index a737224d0d..3dfb087156 100644 --- a/js/src/jit-test/tests/gc/bug-1209001.js +++ b/js/src/jit-test/tests/gc/bug-1209001.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => parseModule('import v from "mod";')); diff --git a/js/src/jit-test/tests/gc/bug-1210607.js b/js/src/jit-test/tests/gc/bug-1210607.js index 15312c810a..d304f24d7d 100644 --- a/js/src/jit-test/tests/gc/bug-1210607.js +++ b/js/src/jit-test/tests/gc/bug-1210607.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom var g = newGlobal({newCompartment: true}); x = Debugger(g); diff --git a/js/src/jit-test/tests/gc/bug-1214006.js b/js/src/jit-test/tests/gc/bug-1214006.js index ed2c6468dc..3b150fbaed 100644 --- a/js/src/jit-test/tests/gc/bug-1214006.js +++ b/js/src/jit-test/tests/gc/bug-1214006.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom function f() { eval("(function() y)()"); diff --git a/js/src/jit-test/tests/gc/bug-1214781.js b/js/src/jit-test/tests/gc/bug-1214781.js index d18845812c..b8a5bb87bb 100644 --- a/js/src/jit-test/tests/gc/bug-1214781.js +++ b/js/src/jit-test/tests/gc/bug-1214781.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom try { gcparam("maxBytes", gcparam("gcBytes")); diff --git a/js/src/jit-test/tests/gc/bug-1214846.js b/js/src/jit-test/tests/gc/bug-1214846.js index 23b5b9fe94..4ba8b3a78f 100644 --- a/js/src/jit-test/tests/gc/bug-1214846.js +++ b/js/src/jit-test/tests/gc/bug-1214846.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: !hasFunction.oomTest || helperThreadCount() === 0 enableGeckoProfiling(); var s = newGlobal(); diff --git a/js/src/jit-test/tests/gc/bug-1215363-1.js b/js/src/jit-test/tests/gc/bug-1215363-1.js index 3ed21e1f9a..fe9eb8c9a2 100644 --- a/js/src/jit-test/tests/gc/bug-1215363-1.js +++ b/js/src/jit-test/tests/gc/bug-1215363-1.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => parseModule(10)); diff --git a/js/src/jit-test/tests/gc/bug-1215363-2.js b/js/src/jit-test/tests/gc/bug-1215363-2.js index 4b51a5a96d..afe0af7ac1 100644 --- a/js/src/jit-test/tests/gc/bug-1215363-2.js +++ b/js/src/jit-test/tests/gc/bug-1215363-2.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var lfcode = new Array(); oomTest((function(x) { assertEq(...Object); diff --git a/js/src/jit-test/tests/gc/bug-1215363-3.js b/js/src/jit-test/tests/gc/bug-1215363-3.js index 33495af2e1..0022507763 100644 --- a/js/src/jit-test/tests/gc/bug-1215363-3.js +++ b/js/src/jit-test/tests/gc/bug-1215363-3.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - var lfcode = new Array(); oomTest(() => getBacktrace({})); diff --git a/js/src/jit-test/tests/gc/bug-1216607.js b/js/src/jit-test/tests/gc/bug-1216607.js index 1afac7faab..46530c113c 100644 --- a/js/src/jit-test/tests/gc/bug-1216607.js +++ b/js/src/jit-test/tests/gc/bug-1216607.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - enableGeckoProfilingWithSlowAssertions(); try { (function() { diff --git a/js/src/jit-test/tests/gc/bug-1221359.js b/js/src/jit-test/tests/gc/bug-1221359.js index dcbafeb446..96b323edd0 100644 --- a/js/src/jit-test/tests/gc/bug-1221359.js +++ b/js/src/jit-test/tests/gc/bug-1221359.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => getBacktrace({ locals: true, thisprops: true diff --git a/js/src/jit-test/tests/gc/bug-1221747.js b/js/src/jit-test/tests/gc/bug-1221747.js index 5ff33dd64e..12d31df2fb 100644 --- a/js/src/jit-test/tests/gc/bug-1221747.js +++ b/js/src/jit-test/tests/gc/bug-1221747.js @@ -1,4 +1,4 @@ -// |jit-test| --dump-bytecode; skip-if: !('oomTest' in this) +// |jit-test| --dump-bytecode function f() { eval("(function() {})()"); diff --git a/js/src/jit-test/tests/gc/bug-1223021.js b/js/src/jit-test/tests/gc/bug-1223021.js index bbc40aa1fb..a5a85f3104 100644 --- a/js/src/jit-test/tests/gc/bug-1223021.js +++ b/js/src/jit-test/tests/gc/bug-1223021.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function f() { return this === null; }; diff --git a/js/src/jit-test/tests/gc/bug-1224710.js b/js/src/jit-test/tests/gc/bug-1224710.js index 9cb9aa7cd3..68809d7f36 100644 --- a/js/src/jit-test/tests/gc/bug-1224710.js +++ b/js/src/jit-test/tests/gc/bug-1224710.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { eval("\ function g() {\ diff --git a/js/src/jit-test/tests/gc/bug-1226896.js b/js/src/jit-test/tests/gc/bug-1226896.js index 0b8c513cc8..8ecf66fd59 100644 --- a/js/src/jit-test/tests/gc/bug-1226896.js +++ b/js/src/jit-test/tests/gc/bug-1226896.js @@ -1,4 +1,4 @@ -// |jit-test| --ion-pruning=on; skip-if: !('oomTest' in this) +// |jit-test| --ion-pruning=on oomTest(() => { var g = newGlobal({sameZoneAs: this}); diff --git a/js/src/jit-test/tests/gc/bug-1231386.js b/js/src/jit-test/tests/gc/bug-1231386.js index c2dc55b734..01bbee81cf 100644 --- a/js/src/jit-test/tests/gc/bug-1231386.js +++ b/js/src/jit-test/tests/gc/bug-1231386.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow function f1() {} function f2() {} diff --git a/js/src/jit-test/tests/gc/bug-1232386.js b/js/src/jit-test/tests/gc/bug-1232386.js index 82a33a7ec4..b768176cf0 100644 --- a/js/src/jit-test/tests/gc/bug-1232386.js +++ b/js/src/jit-test/tests/gc/bug-1232386.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom var dbg = new Debugger; dbg.onNewGlobalObject = function(global) { diff --git a/js/src/jit-test/tests/gc/bug-1234410.js b/js/src/jit-test/tests/gc/bug-1234410.js index fe400f8013..79623a0a43 100644 --- a/js/src/jit-test/tests/gc/bug-1234410.js +++ b/js/src/jit-test/tests/gc/bug-1234410.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - enableGeckoProfiling(); oomTest(() => { try { diff --git a/js/src/jit-test/tests/gc/bug-1236473.js b/js/src/jit-test/tests/gc/bug-1236473.js index 0051e789a6..cef0bd1d25 100644 --- a/js/src/jit-test/tests/gc/bug-1236473.js +++ b/js/src/jit-test/tests/gc/bug-1236473.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { offThreadCompileToStencil(`try {} catch (NaN) {}`); var stencil = finishOffThreadStencil(); diff --git a/js/src/jit-test/tests/gc/bug-1238555.js b/js/src/jit-test/tests/gc/bug-1238555.js index 4b9963292e..f9e139a895 100644 --- a/js/src/jit-test/tests/gc/bug-1238555.js +++ b/js/src/jit-test/tests/gc/bug-1238555.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest( function x() { try { diff --git a/js/src/jit-test/tests/gc/bug-1238575-2.js b/js/src/jit-test/tests/gc/bug-1238575-2.js index 9fe011efa1..0c715d3998 100644 --- a/js/src/jit-test/tests/gc/bug-1238575-2.js +++ b/js/src/jit-test/tests/gc/bug-1238575-2.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => evalInWorker("1")); diff --git a/js/src/jit-test/tests/gc/bug-1238575.js b/js/src/jit-test/tests/gc/bug-1238575.js index 8e6a629d9f..fbce26ff52 100644 --- a/js/src/jit-test/tests/gc/bug-1238575.js +++ b/js/src/jit-test/tests/gc/bug-1238575.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: helperThreadCount() === 0 oomAfterAllocations(5) gcslice(11); diff --git a/js/src/jit-test/tests/gc/bug-1238582.js b/js/src/jit-test/tests/gc/bug-1238582.js index b5dad7a64d..9b1a267627 100644 --- a/js/src/jit-test/tests/gc/bug-1238582.js +++ b/js/src/jit-test/tests/gc/bug-1238582.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { let a = [2147483651]; [a[0], a[undefined]].sort(); }); diff --git a/js/src/jit-test/tests/gc/bug-1240503.js b/js/src/jit-test/tests/gc/bug-1240503.js index 167752962b..cdb93a0929 100644 --- a/js/src/jit-test/tests/gc/bug-1240503.js +++ b/js/src/jit-test/tests/gc/bug-1240503.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function arrayProtoOutOfRange() { for (let [] = () => r, get;;) var r = f(i % 2 ? a : b); diff --git a/js/src/jit-test/tests/gc/bug-1240527.js b/js/src/jit-test/tests/gc/bug-1240527.js index ca4e0e3eb6..1621fa5764 100644 --- a/js/src/jit-test/tests/gc/bug-1240527.js +++ b/js/src/jit-test/tests/gc/bug-1240527.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this) +// |jit-test| skip-if: helperThreadCount() === 0 offThreadCompileToStencil(` oomTest(() => "".search(/d/)); diff --git a/js/src/jit-test/tests/gc/bug-1241731.js b/js/src/jit-test/tests/gc/bug-1241731.js index 015c7f3e67..320dece892 100644 --- a/js/src/jit-test/tests/gc/bug-1241731.js +++ b/js/src/jit-test/tests/gc/bug-1241731.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => serialize(0, [{}])); diff --git a/js/src/jit-test/tests/gc/bug-1242812.js b/js/src/jit-test/tests/gc/bug-1242812.js index df4ae09998..b685d17928 100644 --- a/js/src/jit-test/tests/gc/bug-1242812.js +++ b/js/src/jit-test/tests/gc/bug-1242812.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - var lfcode = new Array(); oomTest(() => { let a = [2147483651]; [-1, 0, 1, 31, 32].sort(); }); diff --git a/js/src/jit-test/tests/gc/bug-1245520.js b/js/src/jit-test/tests/gc/bug-1245520.js index 1f59c3dbab..d951a071ac 100644 --- a/js/src/jit-test/tests/gc/bug-1245520.js +++ b/js/src/jit-test/tests/gc/bug-1245520.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - var t = {}; oomTest(() => serialize(t)); diff --git a/js/src/jit-test/tests/gc/bug-1252329.js b/js/src/jit-test/tests/gc/bug-1252329.js index 8f5b1ce282..100a1817cf 100644 --- a/js/src/jit-test/tests/gc/bug-1252329.js +++ b/js/src/jit-test/tests/gc/bug-1252329.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: helperThreadCount() == 0 || !('oomAfterAllocations' in this) +// |jit-test| allow-oom; skip-if: helperThreadCount() == 0 || !hasFunction.oomAfterAllocations var lfcode = new Array(); lfcode.push("5"); diff --git a/js/src/jit-test/tests/gc/bug-1253124.js b/js/src/jit-test/tests/gc/bug-1253124.js index 6949605b00..3302e120fb 100644 --- a/js/src/jit-test/tests/gc/bug-1253124.js +++ b/js/src/jit-test/tests/gc/bug-1253124.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - for (let i = 0; i < 10; i++) toPrimitive = Date.prototype[Symbol.toPrimitive]; assertThrowsInstanceOf(() => 0); diff --git a/js/src/jit-test/tests/gc/bug-1259306.js b/js/src/jit-test/tests/gc/bug-1259306.js index fba5f71b6a..bb92bb43f5 100644 --- a/js/src/jit-test/tests/gc/bug-1259306.js +++ b/js/src/jit-test/tests/gc/bug-1259306.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - let runCount = 0; oomTest(() => { if (runCount < 5) { diff --git a/js/src/jit-test/tests/gc/bug-1261329.js b/js/src/jit-test/tests/gc/bug-1261329.js index afa1db2c3d..35015917e4 100644 --- a/js/src/jit-test/tests/gc/bug-1261329.js +++ b/js/src/jit-test/tests/gc/bug-1261329.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) +// |jit-test| skip-if: !hasFunction.oomTest print = function() {} function k() { return dissrc(print); } diff --git a/js/src/jit-test/tests/gc/bug-1263862.js b/js/src/jit-test/tests/gc/bug-1263862.js index 955805047a..1b904d34c7 100644 --- a/js/src/jit-test/tests/gc/bug-1263862.js +++ b/js/src/jit-test/tests/gc/bug-1263862.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function loadFile(lfVarx) { oomTest(() => eval(lfVarx)); } diff --git a/js/src/jit-test/tests/gc/bug-1263871.js b/js/src/jit-test/tests/gc/bug-1263871.js index 6680affedf..92e6d3645b 100644 --- a/js/src/jit-test/tests/gc/bug-1263871.js +++ b/js/src/jit-test/tests/gc/bug-1263871.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - lfLogBuffer = `this[''] = function() {}`; loadFile(lfLogBuffer); loadFile(lfLogBuffer); diff --git a/js/src/jit-test/tests/gc/bug-1263884.js b/js/src/jit-test/tests/gc/bug-1263884.js index 949945e0a4..d203c06354 100644 --- a/js/src/jit-test/tests/gc/bug-1263884.js +++ b/js/src/jit-test/tests/gc/bug-1263884.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { eval(` var argObj = function () { return arguments }() diff --git a/js/src/jit-test/tests/gc/bug-1271110.js b/js/src/jit-test/tests/gc/bug-1271110.js index 12d1617c57..49a849045d 100644 --- a/js/src/jit-test/tests/gc/bug-1271110.js +++ b/js/src/jit-test/tests/gc/bug-1271110.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - gczeal(0); var x1 = []; diff --git a/js/src/jit-test/tests/gc/bug-1280588.js b/js/src/jit-test/tests/gc/bug-1280588.js index a6b2c4f075..b3391e6eef 100644 --- a/js/src/jit-test/tests/gc/bug-1280588.js +++ b/js/src/jit-test/tests/gc/bug-1280588.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var x = []; oomTest(() => setGCCallback({ action: "minorGC" })); oomTest(() => setGCCallback({ action: "majorGC" })); diff --git a/js/src/jit-test/tests/gc/bug-1282986.js b/js/src/jit-test/tests/gc/bug-1282986.js index 934cea5b61..215b2190af 100644 --- a/js/src/jit-test/tests/gc/bug-1282986.js +++ b/js/src/jit-test/tests/gc/bug-1282986.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var lfLogBuffer = ` evalInWorker(\` try { oomAfterAllocations(2); } catch(e) {} diff --git a/js/src/jit-test/tests/gc/bug-1287399.js b/js/src/jit-test/tests/gc/bug-1287399.js index c7e6b8f44d..c7345954f6 100644 --- a/js/src/jit-test/tests/gc/bug-1287399.js +++ b/js/src/jit-test/tests/gc/bug-1287399.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: typeof gczeal !== 'function' || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 var lfGlobal = newGlobal(); gczeal(4); diff --git a/js/src/jit-test/tests/gc/bug-1287869.js b/js/src/jit-test/tests/gc/bug-1287869.js index dc04345ccf..7175342655 100644 --- a/js/src/jit-test/tests/gc/bug-1287869.js +++ b/js/src/jit-test/tests/gc/bug-1287869.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) - gczeal(16); let a = []; for (let i = 0; i < 1000; i++) diff --git a/js/src/jit-test/tests/gc/bug-1292564.js b/js/src/jit-test/tests/gc/bug-1292564.js index f292e1682c..56918093f8 100644 --- a/js/src/jit-test/tests/gc/bug-1292564.js +++ b/js/src/jit-test/tests/gc/bug-1292564.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom oomTest(() => { let global = newGlobal({sameZoneAs: this}); diff --git a/js/src/jit-test/tests/gc/bug-1298356.js b/js/src/jit-test/tests/gc/bug-1298356.js index 4c8a213125..78a492649d 100644 --- a/js/src/jit-test/tests/gc/bug-1298356.js +++ b/js/src/jit-test/tests/gc/bug-1298356.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - /x/; oomTest(function(){ offThreadCompileToStencil(''); diff --git a/js/src/jit-test/tests/gc/bug-1303015.js b/js/src/jit-test/tests/gc/bug-1303015.js index d5b5e3a5bd..021db0568d 100644 --- a/js/src/jit-test/tests/gc/bug-1303015.js +++ b/js/src/jit-test/tests/gc/bug-1303015.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow var x = ``.split(); oomTest(function() { diff --git a/js/src/jit-test/tests/gc/bug-1305220.js b/js/src/jit-test/tests/gc/bug-1305220.js index b6dad199a9..0666530bb9 100644 --- a/js/src/jit-test/tests/gc/bug-1305220.js +++ b/js/src/jit-test/tests/gc/bug-1305220.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations s = newGlobal(); evalcx("\ diff --git a/js/src/jit-test/tests/gc/bug-1310589.js b/js/src/jit-test/tests/gc/bug-1310589.js index 2907c3c440..98b045050c 100644 --- a/js/src/jit-test/tests/gc/bug-1310589.js +++ b/js/src/jit-test/tests/gc/bug-1310589.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - a = o = s = r = [] o2 = s2 = r2 = g2 = f2 = m2 = Map e2 = Set diff --git a/js/src/jit-test/tests/gc/bug-1315946.js b/js/src/jit-test/tests/gc/bug-1315946.js index a0668d00b5..8e95a97d20 100644 --- a/js/src/jit-test/tests/gc/bug-1315946.js +++ b/js/src/jit-test/tests/gc/bug-1315946.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // Don't run a full oomTest because it takes ages - a few iterations are // sufficient to trigger the bug. let i = 0; diff --git a/js/src/jit-test/tests/gc/bug-1325551.js b/js/src/jit-test/tests/gc/bug-1325551.js index 700f61daf8..8c4499a01e 100644 --- a/js/src/jit-test/tests/gc/bug-1325551.js +++ b/js/src/jit-test/tests/gc/bug-1325551.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - let g = newGlobal({newCompartment: true}); let dbg = new Debugger; let gw = dbg.addDebuggee(g); diff --git a/js/src/jit-test/tests/gc/bug-1340010.js b/js/src/jit-test/tests/gc/bug-1340010.js index 30c3cb08b9..22ceafce6f 100644 --- a/js/src/jit-test/tests/gc/bug-1340010.js +++ b/js/src/jit-test/tests/gc/bug-1340010.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: helperThreadCount() === 0 || !('deterministicgc' in this) +// |jit-test| skip-if: helperThreadCount() === 0 gczeal(0); gc(); diff --git a/js/src/jit-test/tests/gc/bug-1384047.js b/js/src/jit-test/tests/gc/bug-1384047.js index 4ec6a5272d..162af6af42 100644 --- a/js/src/jit-test/tests/gc/bug-1384047.js +++ b/js/src/jit-test/tests/gc/bug-1384047.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - +// |jit-test| skip-if: !hasFunction.oomTest newGlobal(); evalcx("oomTest(newGlobal);", newGlobal()); diff --git a/js/src/jit-test/tests/gc/bug-1401141.js b/js/src/jit-test/tests/gc/bug-1401141.js index 6ed1bea611..2674f77c37 100644 --- a/js/src/jit-test/tests/gc/bug-1401141.js +++ b/js/src/jit-test/tests/gc/bug-1401141.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('gczeal' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 gczeal(15,1); setGCCallback({ diff --git a/js/src/jit-test/tests/gc/bug-1411302.js b/js/src/jit-test/tests/gc/bug-1411302.js index 20c051edd9..510648b742 100644 --- a/js/src/jit-test/tests/gc/bug-1411302.js +++ b/js/src/jit-test/tests/gc/bug-1411302.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - let lfPreamble = ` value:{ `; diff --git a/js/src/jit-test/tests/gc/bug-1435295.js b/js/src/jit-test/tests/gc/bug-1435295.js index 01214a6214..0f091b7d49 100644 --- a/js/src/jit-test/tests/gc/bug-1435295.js +++ b/js/src/jit-test/tests/gc/bug-1435295.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this) +// |jit-test| skip-if: helperThreadCount() === 0 oomTest(new Function(`function execOffThread(source) { offThreadCompileModuleToStencil(source); diff --git a/js/src/jit-test/tests/gc/bug-1449887.js b/js/src/jit-test/tests/gc/bug-1449887.js index ef7fa45c7f..c5732b0723 100644 --- a/js/src/jit-test/tests/gc/bug-1449887.js +++ b/js/src/jit-test/tests/gc/bug-1449887.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { x, 0, { z: function() {} } }); diff --git a/js/src/jit-test/tests/gc/bug-1456536.js b/js/src/jit-test/tests/gc/bug-1456536.js index adfe3f0c9b..b61f5ca09c 100644 --- a/js/src/jit-test/tests/gc/bug-1456536.js +++ b/js/src/jit-test/tests/gc/bug-1456536.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(new Function(`let a = grayRoot();`)); diff --git a/js/src/jit-test/tests/gc/bug-1462337.js b/js/src/jit-test/tests/gc/bug-1462337.js index 84a5392a1f..1eaafc4997 100644 --- a/js/src/jit-test/tests/gc/bug-1462337.js +++ b/js/src/jit-test/tests/gc/bug-1462337.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { grayRoot().x = Object.create((obj[name]++)); }); diff --git a/js/src/jit-test/tests/gc/bug-1472734.js b/js/src/jit-test/tests/gc/bug-1472734.js index f88f3af1c6..2d4432743d 100644 --- a/js/src/jit-test/tests/gc/bug-1472734.js +++ b/js/src/jit-test/tests/gc/bug-1472734.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 try { oomTest(function() { diff --git a/js/src/jit-test/tests/gc/bug-1490042.js b/js/src/jit-test/tests/gc/bug-1490042.js index b043f25486..937ae41ef3 100644 --- a/js/src/jit-test/tests/gc/bug-1490042.js +++ b/js/src/jit-test/tests/gc/bug-1490042.js @@ -1,4 +1,4 @@ -// |jit-test| --no-ion; --no-baseline; --no-blinterp; skip-if: !('gcstate' in this && 'oomAfterAllocations' in this) +// |jit-test| --no-ion; --no-baseline; --no-blinterp; skip-if: !('gcstate' in this && hasFunction.oomAfterAllocations) gczeal(0); diff --git a/js/src/jit-test/tests/gc/bug-1530643.js b/js/src/jit-test/tests/gc/bug-1530643.js index c85e6267c6..fa61063ea9 100644 --- a/js/src/jit-test/tests/gc/bug-1530643.js +++ b/js/src/jit-test/tests/gc/bug-1530643.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomAtAllocation' in this); error: Error +// |jit-test| skip-if: !hasFunction.oomAtAllocation; error: Error const THREAD_TYPE_WORKER = 10; diff --git a/js/src/jit-test/tests/gc/bug-1556155.js b/js/src/jit-test/tests/gc/bug-1556155.js index 3c0dd11251..9bf2648131 100644 --- a/js/src/jit-test/tests/gc/bug-1556155.js +++ b/js/src/jit-test/tests/gc/bug-1556155.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) a = []; minorgc(); Object.defineProperty(a, 12, {}).push(1); diff --git a/js/src/jit-test/tests/gc/bug-1568119.js b/js/src/jit-test/tests/gc/bug-1568119.js index 1aed85c325..ccba9e1a72 100644 --- a/js/src/jit-test/tests/gc/bug-1568119.js +++ b/js/src/jit-test/tests/gc/bug-1568119.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function allocateSomeStuff() { return {a: "a fish", b: [1, 2, 3]}; } diff --git a/js/src/jit-test/tests/gc/bug-1574877.js b/js/src/jit-test/tests/gc/bug-1574877.js index e53746bcb0..09b5b9e6b0 100644 --- a/js/src/jit-test/tests/gc/bug-1574877.js +++ b/js/src/jit-test/tests/gc/bug-1574877.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function parseModule(source) { offThreadCompileModuleToStencil(source); var stencil = finishOffThreadStencil(); diff --git a/js/src/jit-test/tests/gc/bug-1648901.js b/js/src/jit-test/tests/gc/bug-1648901.js index 13e4895068..2eddbd9a3c 100644 --- a/js/src/jit-test/tests/gc/bug-1648901.js +++ b/js/src/jit-test/tests/gc/bug-1648901.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) - gczeal(15); enableShellAllocationMetadataBuilder(); var registry = new FinalizationRegistry(x => 0); diff --git a/js/src/jit-test/tests/gc/bug-1654186.js b/js/src/jit-test/tests/gc/bug-1654186.js index 562938d8f8..e6db718bb2 100644 --- a/js/src/jit-test/tests/gc/bug-1654186.js +++ b/js/src/jit-test/tests/gc/bug-1654186.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom; skip-if: !hasFunction.oomAfterAllocations gczeal(14, 5); var g = newGlobal(); diff --git a/js/src/jit-test/tests/gc/bug-1657554.js b/js/src/jit-test/tests/gc/bug-1657554.js index f751d442b9..f8dd0282b6 100644 --- a/js/src/jit-test/tests/gc/bug-1657554.js +++ b/js/src/jit-test/tests/gc/bug-1657554.js @@ -1,2 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(() => eval("new WeakRef({});")); diff --git a/js/src/jit-test/tests/gc/bug-1660293.js b/js/src/jit-test/tests/gc/bug-1660293.js index a2c953c11f..c7e3f46c9a 100644 --- a/js/src/jit-test/tests/gc/bug-1660293.js +++ b/js/src/jit-test/tests/gc/bug-1660293.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this) - try { function varying(mapColor, keyColor) { enqueueMark(`set-color-${keyColor}`); diff --git a/js/src/jit-test/tests/gc/bug-1689039.js b/js/src/jit-test/tests/gc/bug-1689039.js index 4bfe9bd140..a5d3c71106 100644 --- a/js/src/jit-test/tests/gc/bug-1689039.js +++ b/js/src/jit-test/tests/gc/bug-1689039.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this) - gczeal(7); for (let i = 0; i < 9999; ++i) { undefined + "y"; diff --git a/js/src/jit-test/tests/gc/bug-1692221.js b/js/src/jit-test/tests/gc/bug-1692221.js index 6300788ad9..11bd4a3aea 100644 --- a/js/src/jit-test/tests/gc/bug-1692221.js +++ b/js/src/jit-test/tests/gc/bug-1692221.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAtAllocation' in this) +// |jit-test| allow-oom // Test TenuredChunk::decommitFreeArenasWithoutUnlocking updates chunk // metadata correctly. The data is checked by assertions so this test is about diff --git a/js/src/jit-test/tests/gc/bug-1791975.js b/js/src/jit-test/tests/gc/bug-1791975.js index a194a92dd0..f56f723db7 100644 --- a/js/src/jit-test/tests/gc/bug-1791975.js +++ b/js/src/jit-test/tests/gc/bug-1791975.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAtAllocation' in this) - gczeal(10, 10); try { throw 0; diff --git a/js/src/jit-test/tests/gc/bug-1802478.js b/js/src/jit-test/tests/gc/bug-1802478.js index 05559c3f6f..d37188d576 100644 --- a/js/src/jit-test/tests/gc/bug-1802478.js +++ b/js/src/jit-test/tests/gc/bug-1802478.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this) - enableTrackAllocations(); for (a of "x") { gczeal(2, 1); diff --git a/js/src/jit-test/tests/gc/bug-1804629.js b/js/src/jit-test/tests/gc/bug-1804629.js index 6ed62eec8c..84c473c347 100644 --- a/js/src/jit-test/tests/gc/bug-1804629.js +++ b/js/src/jit-test/tests/gc/bug-1804629.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('gczeal' in this); error: ReferenceError +// |jit-test| error: ReferenceError gczeal(0); setMarkStackLimit(1); diff --git a/js/src/jit-test/tests/gc/bug-1865597.js b/js/src/jit-test/tests/gc/bug-1865597.js index 4bc7ff3a4d..451b806294 100644 --- a/js/src/jit-test/tests/gc/bug-1865597.js +++ b/js/src/jit-test/tests/gc/bug-1865597.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { gcparam('parallelMarkingEnabled', false); assertEq(gcparam('parallelMarkingEnabled'), 0); diff --git a/js/src/jit-test/tests/gc/bug-1870925.js b/js/src/jit-test/tests/gc/bug-1870925.js new file mode 100644 index 0000000000..48182a04e4 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1870925.js @@ -0,0 +1,14 @@ +// |jit-test| --no-ggc + +let x = [[]]; +for (let i = 0; i < 25; i++) { + for (let j = 0; j < 25; j++) { + (function () { + x[i] | 0; + })(); + } +} + +verifyprebarriers(); +bailAfter(1); +verifyprebarriers(); diff --git a/js/src/jit-test/tests/gc/bug-1871186.js b/js/src/jit-test/tests/gc/bug-1871186.js index fc4620fa65..260356ab8b 100644 --- a/js/src/jit-test/tests/gc/bug-1871186.js +++ b/js/src/jit-test/tests/gc/bug-1871186.js @@ -1,4 +1,4 @@ -// |jit-test| --blinterp-eager; skip-if: !('oomTest' in this) +// |jit-test| --blinterp-eager gc(); function f(x) { diff --git a/js/src/jit-test/tests/gc/bug-1877406.js b/js/src/jit-test/tests/gc/bug-1877406.js index bcb26ed062..d412abe82c 100644 --- a/js/src/jit-test/tests/gc/bug-1877406.js +++ b/js/src/jit-test/tests/gc/bug-1877406.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this); --fuzzing-safe +// |jit-test| --fuzzing-safe oomTest(Debugger); oomTest(Debugger); diff --git a/js/src/jit-test/tests/gc/bug-1880870.js b/js/src/jit-test/tests/gc/bug-1880870.js new file mode 100644 index 0000000000..e9bffc9295 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1880870.js @@ -0,0 +1,6 @@ +var x = []; +function f() { + Object.entries(x); + Object.defineProperty(x, "", { enumerable: true, get: f }); +} +oomTest(f); diff --git a/js/src/jit-test/tests/gc/bug-1881417.js b/js/src/jit-test/tests/gc/bug-1881417.js new file mode 100644 index 0000000000..f79d7d5c33 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1881417.js @@ -0,0 +1,12 @@ +for (let x = 0; x < 2; (function() { x++; })()) {}; +function f() { + var y = new (function () {})(); + (function () { + Reflect.apply(y.toString, [], [0]); + })(); +} +f(); +var z = []; +z.keepFailing = []; +oomTest(f, z); +dumpHeap(); diff --git a/js/src/jit-test/tests/gc/bug-1884746.js b/js/src/jit-test/tests/gc/bug-1884746.js new file mode 100644 index 0000000000..5dab465dac --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1884746.js @@ -0,0 +1,7 @@ +var x = newGlobal().Int8Array; +for (let i = 0; i < 2; i++) { + function f() {} + oomTest(function() { + new x().__proto__ = f; + }); +} diff --git a/js/src/jit-test/tests/gc/bug-978802.js b/js/src/jit-test/tests/gc/bug-978802.js index 1e13b76e0e..42b3553488 100644 --- a/js/src/jit-test/tests/gc/bug-978802.js +++ b/js/src/jit-test/tests/gc/bug-978802.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { try { var max = 400; diff --git a/js/src/jit-test/tests/gc/bug1246607.js b/js/src/jit-test/tests/gc/bug1246607.js index 1fbe9e5208..222fd20ed3 100644 --- a/js/src/jit-test/tests/gc/bug1246607.js +++ b/js/src/jit-test/tests/gc/bug1246607.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: typeof oomTest !== 'function' || typeof Intl !== 'object' +// |jit-test| skip-if: typeof Intl !== 'object' oomTest(() => { try { diff --git a/js/src/jit-test/tests/gc/bug1326343-gcstats.js b/js/src/jit-test/tests/gc/bug1326343-gcstats.js index f29306af4c..fa0190284d 100644 --- a/js/src/jit-test/tests/gc/bug1326343-gcstats.js +++ b/js/src/jit-test/tests/gc/bug1326343-gcstats.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - setJitCompilerOption('baseline.warmup.trigger', 4); oomTest((function () { gcslice(0); diff --git a/js/src/jit-test/tests/gc/bug1337324.js b/js/src/jit-test/tests/gc/bug1337324.js index eaf4c080f0..5ca628fc1d 100644 --- a/js/src/jit-test/tests/gc/bug1337324.js +++ b/js/src/jit-test/tests/gc/bug1337324.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(function () { offThreadCompileModuleToStencil(''); var stencil = finishOffThreadStencil(); diff --git a/js/src/jit-test/tests/gc/bug1471949.js b/js/src/jit-test/tests/gc/bug1471949.js index 5f0f10f4df..30853bf0ff 100644 --- a/js/src/jit-test/tests/gc/bug1471949.js +++ b/js/src/jit-test/tests/gc/bug1471949.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom gczeal(15); oomAfterAllocations(5); diff --git a/js/src/jit-test/tests/gc/bug1704451.js b/js/src/jit-test/tests/gc/bug1704451.js index d4b4d14995..426506e9fc 100644 --- a/js/src/jit-test/tests/gc/bug1704451.js +++ b/js/src/jit-test/tests/gc/bug1704451.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) - enableShellAllocationMetadataBuilder(); gczeal(9,1); var o86 = {x76: 1, y86: 2}; diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom1.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom1.js index 753448a650..83295bb702 100644 --- a/js/src/jit-test/tests/gc/finalizationRegistry-oom1.js +++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom1.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // Don't test prototype initialization etc. new FinalizationRegistry(x => 0); diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom2.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom2.js index 9d9b2a7db8..c63307dfb0 100644 --- a/js/src/jit-test/tests/gc/finalizationRegistry-oom2.js +++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom2.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) let registry = new FinalizationRegistry(x => 0); let token = {}; oomTest(() => registry.register({}, 1, token)); diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom3.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom3.js index d606ad8ba8..bcde1cc6c6 100644 --- a/js/src/jit-test/tests/gc/finalizationRegistry-oom3.js +++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom3.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) let registry = new FinalizationRegistry(x => 0); registry.register({}, 1, {}); let token = {}; diff --git a/js/src/jit-test/tests/gc/finalizationRegistry-oom4.js b/js/src/jit-test/tests/gc/finalizationRegistry-oom4.js index 4b7ef66ba0..03defb8558 100644 --- a/js/src/jit-test/tests/gc/finalizationRegistry-oom4.js +++ b/js/src/jit-test/tests/gc/finalizationRegistry-oom4.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) let registry = new FinalizationRegistry(x => 0); let target = {}; let token = {}; diff --git a/js/src/jit-test/tests/gc/gcparam.js b/js/src/jit-test/tests/gc/gcparam.js index f1077e335f..05e0359088 100644 --- a/js/src/jit-test/tests/gc/gcparam.js +++ b/js/src/jit-test/tests/gc/gcparam.js @@ -52,12 +52,11 @@ testChangeParam("compactingEnabled"); testChangeParam("parallelMarkingEnabled"); testChangeParam("parallelMarkingThresholdMB"); testChangeParam("minLastDitchGCPeriod"); -testChangeParam("nurseryFreeThresholdForIdleCollection"); -testChangeParam("nurseryFreeThresholdForIdleCollectionPercent"); -testChangeParam("nurseryTimeoutForIdleCollectionMS"); +testChangeParam("nurseryEagerCollectionThresholdKB"); +testChangeParam("nurseryEagerCollectionThresholdPercent"); +testChangeParam("nurseryEagerCollectionTimeoutMS"); testChangeParam("zoneAllocDelayKB"); testChangeParam("mallocThresholdBase"); testChangeParam("urgentThreshold"); -testChangeParam("nurseryTimeoutForIdleCollectionMS"); testChangeParam("helperThreadRatio"); testChangeParam("maxHelperThreads"); diff --git a/js/src/jit-test/tests/gc/incremental-compacting.js b/js/src/jit-test/tests/gc/incremental-compacting.js index f051cf60ea..c60ec28722 100644 --- a/js/src/jit-test/tests/gc/incremental-compacting.js +++ b/js/src/jit-test/tests/gc/incremental-compacting.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !("gcstate" in this && "gczeal" in this)
-
// Exercise incremental compacting GC
// Run with MOZ_GCTIMER to see the timings
diff --git a/js/src/jit-test/tests/gc/oomInArrayProtoTest.js b/js/src/jit-test/tests/gc/oomInArrayProtoTest.js index b99669d92d..3e3365cde5 100644 --- a/js/src/jit-test/tests/gc/oomInArrayProtoTest.js +++ b/js/src/jit-test/tests/gc/oomInArrayProtoTest.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function arrayProtoOutOfRange() { function f(obj) { return typeof obj[15]; diff --git a/js/src/jit-test/tests/gc/oomInByteSize.js b/js/src/jit-test/tests/gc/oomInByteSize.js index 9566b9cb49..5f9aa0059a 100644 --- a/js/src/jit-test/tests/gc/oomInByteSize.js +++ b/js/src/jit-test/tests/gc/oomInByteSize.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => byteSize({})); oomTest(() => byteSize({ w: 1, x: 2, y: 3 })); oomTest(() => byteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2 })); diff --git a/js/src/jit-test/tests/gc/oomInDebugger.js b/js/src/jit-test/tests/gc/oomInDebugger.js index c1904f573a..959f2f7f4f 100644 --- a/js/src/jit-test/tests/gc/oomInDebugger.js +++ b/js/src/jit-test/tests/gc/oomInDebugger.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - var g = newGlobal(); oomTest(() => Debugger(g)); diff --git a/js/src/jit-test/tests/gc/oomInDtoa.js b/js/src/jit-test/tests/gc/oomInDtoa.js index 83ded51cbb..6f37137e51 100644 --- a/js/src/jit-test/tests/gc/oomInDtoa.js +++ b/js/src/jit-test/tests/gc/oomInDtoa.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { return 1e300; }) diff --git a/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js b/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js index d5c8f29b27..cdd20cd9b7 100644 --- a/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js +++ b/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(() => { let x = 0; try { diff --git a/js/src/jit-test/tests/gc/oomInFindPath.js b/js/src/jit-test/tests/gc/oomInFindPath.js index 4b3d95688c..cacf587699 100644 --- a/js/src/jit-test/tests/gc/oomInFindPath.js +++ b/js/src/jit-test/tests/gc/oomInFindPath.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var o = { w: { x: { y: { z: {} } } } }; oomTest(() => findPath(o, o.w.x.y.z)); diff --git a/js/src/jit-test/tests/gc/oomInFormatStackDump.js b/js/src/jit-test/tests/gc/oomInFormatStackDump.js index ce68e47f9e..59e4921f93 100644 --- a/js/src/jit-test/tests/gc/oomInFormatStackDump.js +++ b/js/src/jit-test/tests/gc/oomInFormatStackDump.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => getBacktrace({args: true, locals: true, thisprops: true})); diff --git a/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js b/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js index a568fc592f..8415c9ac6c 100644 --- a/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js +++ b/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => getBacktrace({thisprops: gc() && delete addDebuggee.enabled})); diff --git a/js/src/jit-test/tests/gc/oomInNewGlobal.js b/js/src/jit-test/tests/gc/oomInNewGlobal.js index c45737e143..92e8385063 100644 --- a/js/src/jit-test/tests/gc/oomInNewGlobal.js +++ b/js/src/jit-test/tests/gc/oomInNewGlobal.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(newGlobal); diff --git a/js/src/jit-test/tests/gc/oomInOffTheadCompile.js b/js/src/jit-test/tests/gc/oomInOffTheadCompile.js index d4e0d4135e..65a2fe443c 100644 --- a/js/src/jit-test/tests/gc/oomInOffTheadCompile.js +++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 oomTest(() => { offThreadCompileToStencil( diff --git a/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js b/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js index 1cac5ee859..de4ad4106d 100644 --- a/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js +++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 oomTest(() => { offThreadCompileToStencil("function a(x) {"); diff --git a/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js b/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js index 6535676b72..5e0816f575 100644 --- a/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js +++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 oomTest(() => { offThreadCompileToStencil(` diff --git a/js/src/jit-test/tests/gc/oomInParseAsmJS.js b/js/src/jit-test/tests/gc/oomInParseAsmJS.js index 72216e1c2c..e2d8441897 100644 --- a/js/src/jit-test/tests/gc/oomInParseAsmJS.js +++ b/js/src/jit-test/tests/gc/oomInParseAsmJS.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function parseAsmJS() { eval(`function m(stdlib) { diff --git a/js/src/jit-test/tests/gc/oomInParseFunction.js b/js/src/jit-test/tests/gc/oomInParseFunction.js index b1c1bd6297..c7471a7e60 100644 --- a/js/src/jit-test/tests/gc/oomInParseFunction.js +++ b/js/src/jit-test/tests/gc/oomInParseFunction.js @@ -1,3 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => eval("function f() {}")); diff --git a/js/src/jit-test/tests/gc/oomInRegExp.js b/js/src/jit-test/tests/gc/oomInRegExp.js index b58f0ac50d..6564fda35b 100644 --- a/js/src/jit-test/tests/gc/oomInRegExp.js +++ b/js/src/jit-test/tests/gc/oomInRegExp.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3)); oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false)); oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true)); diff --git a/js/src/jit-test/tests/gc/oomInRegExp2.js b/js/src/jit-test/tests/gc/oomInRegExp2.js index c35075b375..8783435424 100644 --- a/js/src/jit-test/tests/gc/oomInRegExp2.js +++ b/js/src/jit-test/tests/gc/oomInRegExp2.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3), {keepFailing: true}); oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false), {keepFailing: true}); oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true), {keepFailing: true}); diff --git a/js/src/jit-test/tests/gc/oomInWeakMap.js b/js/src/jit-test/tests/gc/oomInWeakMap.js index 522dc24738..0992907ed7 100644 --- a/js/src/jit-test/tests/gc/oomInWeakMap.js +++ b/js/src/jit-test/tests/gc/oomInWeakMap.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function () { eval(`var wm = new WeakMap(); wm.set({}, 'FOO').get(false);`); diff --git a/js/src/jit-test/tests/generators/bug1501722.js b/js/src/jit-test/tests/generators/bug1501722.js index 9ff2724a9f..9ff5df1792 100644 --- a/js/src/jit-test/tests/generators/bug1501722.js +++ b/js/src/jit-test/tests/generators/bug1501722.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - ignoreUnhandledRejections(); (function () { diff --git a/js/src/jit-test/tests/ion/array-push-multiple-frozen.js b/js/src/jit-test/tests/ion/array-push-multiple-frozen.js index 271f6cb07a..da2c7a9262 100644 --- a/js/src/jit-test/tests/ion/array-push-multiple-frozen.js +++ b/js/src/jit-test/tests/ion/array-push-multiple-frozen.js @@ -1,4 +1,4 @@ -// |jit-test| --no-threads; skip-if: !('oomAtAllocation' in this) +// |jit-test| --no-threads // This test case check's Ion ability to recover from an allocation failure in // the inlining of Array.prototype.push, when given multiple arguments. Note, diff --git a/js/src/jit-test/tests/ion/array-push-multiple-with-funapply.js b/js/src/jit-test/tests/ion/array-push-multiple-with-funapply.js index b09fa9c440..0d3f174d9d 100644 --- a/js/src/jit-test/tests/ion/array-push-multiple-with-funapply.js +++ b/js/src/jit-test/tests/ion/array-push-multiple-with-funapply.js @@ -1,4 +1,4 @@ -// |jit-test| --no-threads; skip-if: !('oomAtAllocation' in this) +// |jit-test| --no-threads // This test case check's Ion ability to inline Array.prototype.push, when // fun.apply is used and inlined with the set of arguments of the current diff --git a/js/src/jit-test/tests/ion/array-push-multiple.js b/js/src/jit-test/tests/ion/array-push-multiple.js index 19c1a93e70..8287d07c94 100644 --- a/js/src/jit-test/tests/ion/array-push-multiple.js +++ b/js/src/jit-test/tests/ion/array-push-multiple.js @@ -1,4 +1,4 @@ -// |jit-test| --no-threads; skip-if: !('oomAtAllocation' in this) +// |jit-test| --no-threads // This test case check's Ion ability to recover from an allocation failure in // the inlining of Array.prototype.push, when given multiple arguments. Note, diff --git a/js/src/jit-test/tests/ion/bailout-oom-01.js b/js/src/jit-test/tests/ion/bailout-oom-01.js index b56b323774..0408f44e98 100644 --- a/js/src/jit-test/tests/ion/bailout-oom-01.js +++ b/js/src/jit-test/tests/ion/bailout-oom-01.js @@ -1,4 +1,4 @@ -// |jit-test| --no-threads; --fast-warmup; skip-if: !('oomTest' in this) +// |jit-test| --no-threads; --fast-warmup setJitCompilerOption("ion.warmup.trigger", 20); gczeal(0); diff --git a/js/src/jit-test/tests/ion/bug1207413.js b/js/src/jit-test/tests/ion/bug1207413.js index aedb8ece30..e2d5652b33 100644 --- a/js/src/jit-test/tests/ion/bug1207413.js +++ b/js/src/jit-test/tests/ion/bug1207413.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this) - function first(a) { return a[0]; } diff --git a/js/src/jit-test/tests/ion/bug1216157.js b/js/src/jit-test/tests/ion/bug1216157.js index 1ec9497e40..01a654502b 100644 --- a/js/src/jit-test/tests/ion/bug1216157.js +++ b/js/src/jit-test/tests/ion/bug1216157.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this); allow-oom +// |jit-test| allow-oom gcslice(0); // Start IGC, but don't mark anything. function f(str) { diff --git a/js/src/jit-test/tests/ion/bug1233331.js b/js/src/jit-test/tests/ion/bug1233331.js index 11b8faafd1..b181dfb856 100644 --- a/js/src/jit-test/tests/ion/bug1233331.js +++ b/js/src/jit-test/tests/ion/bug1233331.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - x = 0; try { a; diff --git a/js/src/jit-test/tests/ion/bug1240521.js b/js/src/jit-test/tests/ion/bug1240521.js index 5955705b7a..3160be7a7d 100644 --- a/js/src/jit-test/tests/ion/bug1240521.js +++ b/js/src/jit-test/tests/ion/bug1240521.js @@ -1,4 +1,4 @@ -// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) +// |jit-test| allow-oom var egc = 138; function SwitchTest(value) { diff --git a/js/src/jit-test/tests/ion/bug1269756.js b/js/src/jit-test/tests/ion/bug1269756.js index 659805f16c..1e201f18a4 100644 --- a/js/src/jit-test/tests/ion/bug1269756.js +++ b/js/src/jit-test/tests/ion/bug1269756.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(function() { m = parseModule(`while (x && NaN) prototype; let x`); moduleLink(m); diff --git a/js/src/jit-test/tests/ion/bug1284491.js b/js/src/jit-test/tests/ion/bug1284491.js index eb8f15619b..f061536bb1 100644 --- a/js/src/jit-test/tests/ion/bug1284491.js +++ b/js/src/jit-test/tests/ion/bug1284491.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - loadFile(` function SwitchTest(){ switch(value) { diff --git a/js/src/jit-test/tests/ion/bug1394505.js b/js/src/jit-test/tests/ion/bug1394505.js index 84979c046c..49c24d7ec6 100644 --- a/js/src/jit-test/tests/ion/bug1394505.js +++ b/js/src/jit-test/tests/ion/bug1394505.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this) +// |jit-test| skip-if: helperThreadCount() === 0 for (let j = 0; j < 50; j++) { if (j === 1) diff --git a/js/src/jit-test/tests/ion/bug1479394.js b/js/src/jit-test/tests/ion/bug1479394.js index ef33e35b20..7123a280d9 100644 --- a/js/src/jit-test/tests/ion/bug1479394.js +++ b/js/src/jit-test/tests/ion/bug1479394.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('stackTest' in this) var dbgGlobal = newGlobal({newCompartment: true}); var dbg = new dbgGlobal.Debugger(this); function f1() { diff --git a/js/src/jit-test/tests/ion/bug1492574.js b/js/src/jit-test/tests/ion/bug1492574.js index c1ce0c000a..070d69eaf7 100644 --- a/js/src/jit-test/tests/ion/bug1492574.js +++ b/js/src/jit-test/tests/ion/bug1492574.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function foo() {} function foooooooooooooooooooooooooooooooo() {} function fn(s) { diff --git a/js/src/jit-test/tests/ion/bug1568397.js b/js/src/jit-test/tests/ion/bug1568397.js index c03bb0283d..68fd83a226 100644 --- a/js/src/jit-test/tests/ion/bug1568397.js +++ b/js/src/jit-test/tests/ion/bug1568397.js @@ -1,4 +1,4 @@ -// |jit-test| error:TypeError: can't access property +// |jit-test| --setpref=property_error_message_fix=true; error:TypeError: can't access property "x" let obj = {x: 1}; obj.x = 1.1; diff --git a/js/src/jit-test/tests/ion/bug1877357.js b/js/src/jit-test/tests/ion/bug1877357.js new file mode 100644 index 0000000000..b1e0d9c686 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1877357.js @@ -0,0 +1,17 @@ +function f(x) { + let y = x | 0; + z = 1; + Function((x | 0 ? 1 : 9999999999) ? (z ? y : 9999999999) : 1); +} +f(1); +f(1); +f(1); +f(1); +f(1); +f(1); +f(1); +f(1); +f(9999999999); +f(1); +f(); +f(9007199254740993); diff --git a/js/src/jit-test/tests/ion/bug1877709.js b/js/src/jit-test/tests/ion/bug1877709.js new file mode 100644 index 0000000000..1dac277a90 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1877709.js @@ -0,0 +1,22 @@ +function testMathyFunction (f, inputs) { + var results = []; + for (var j = 0; j < inputs.length; ++j) + for (var k = 0; k < inputs.length; ++k) + results.push(f(inputs[j], inputs[k])); +} +mathy1=(function(stdlib,foreign,heap){ + ff = foreign.ff; + Float32ArrayView = new stdlib.Float32Array(heap); + Uint32ArrayView = new stdlib.Uint32Array(heap); + function f(d0) { + var i2=0; + var i4; + i2=Float32ArrayView[2]; + i4=i2; + ff(2,0) ? f : 6; + Uint32ArrayView[!!d0] + [...[eval]] + return i4 ? 1 : 0; + } +return f +})(this,{ ff:(Function('')) },new SharedArrayBuffer(40)); +testMathyFunction(mathy1,[Math.N,Number.M,(2),Number.M]) diff --git a/js/src/jit-test/tests/ion/dce-with-rinstructions.js b/js/src/jit-test/tests/ion/dce-with-rinstructions.js index 481a1279ec..5fd530fde4 100644 --- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js +++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js @@ -1983,6 +1983,26 @@ function rnantozero_negzero(i) { return i; } +let uceFault_ratomicsislockfree_true = eval(`(${uceFault})`.replace('uceFault', 'uceFault_ratomicsislockfree_true')); +function ratomicsislockfree_true(i) { + var x = [1, 2, 4, 8][i & 3]; + var y = Atomics.isLockFree(x); + if (uceFault_ratomicsislockfree_true(i) || uceFault_ratomicsislockfree_true(i)) + assertEq(y, true); + assertRecoveredOnBailout(y, true); + return i; +} + +let uceFault_ratomicsislockfree_false = eval(`(${uceFault})`.replace('uceFault', 'uceFault_ratomicsislockfree_false')); +function ratomicsislockfree_false(i) { + var x = [-1, 0, 3, 1000][i & 3]; + var y = Atomics.isLockFree(x); + if (uceFault_ratomicsislockfree_false(i) || uceFault_ratomicsislockfree_false(i)) + assertEq(y, false); + assertRecoveredOnBailout(y, true); + return i; +} + for (j = 100 - max; j < 100; j++) { with({}){} // Do not Ion-compile this loop. let i = j < 2 ? (Math.abs(j) % 50) + 2 : j; @@ -2184,6 +2204,8 @@ for (j = 100 - max; j < 100; j++) { rnantozero_nan(i); rnantozero_poszero(i); rnantozero_negzero(i); + ratomicsislockfree_true(i); + ratomicsislockfree_false(i); } // Test that we can refer multiple time to the same recover instruction, as well diff --git a/js/src/jit-test/tests/ion/scalar-replacement-oom.js b/js/src/jit-test/tests/ion/scalar-replacement-oom.js index 32a3a2e5d5..19d0b026e7 100644 --- a/js/src/jit-test/tests/ion/scalar-replacement-oom.js +++ b/js/src/jit-test/tests/ion/scalar-replacement-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAtAllocation' in this) - var lfcode = new Array(); function k(a, f_arg, b, c) { for (var i = 0; i < 5; ++i) { diff --git a/js/src/jit-test/tests/modules/bug-1219044.js b/js/src/jit-test/tests/modules/bug-1219044.js index 3917d7ca9c..bcddf8fb95 100644 --- a/js/src/jit-test/tests/modules/bug-1219044.js +++ b/js/src/jit-test/tests/modules/bug-1219044.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => parseModule('import v from "mod";')); fullcompartmentchecks(true); diff --git a/js/src/jit-test/tests/modules/bug-1402535.js b/js/src/jit-test/tests/modules/bug-1402535.js index f8cf878260..2ff8e3716b 100644 --- a/js/src/jit-test/tests/modules/bug-1402535.js +++ b/js/src/jit-test/tests/modules/bug-1402535.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('stackTest' in this) - stackTest(function() { let m = parseModule(``); moduleLink(m); diff --git a/js/src/jit-test/tests/modules/bug-1402649.js b/js/src/jit-test/tests/modules/bug-1402649.js index 559a91a98f..8199cbeb31 100644 --- a/js/src/jit-test/tests/modules/bug-1402649.js +++ b/js/src/jit-test/tests/modules/bug-1402649.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - loadFile(` function parseAndEvaluate(source) { let m = parseModule(source); diff --git a/js/src/jit-test/tests/modules/bug-1420420-3.js b/js/src/jit-test/tests/modules/bug-1420420-3.js index 508afa41b1..5ccbf536ca 100644 --- a/js/src/jit-test/tests/modules/bug-1420420-3.js +++ b/js/src/jit-test/tests/modules/bug-1420420-3.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('stackTest' in this) - let a = parseModule(`throw new Error`); moduleLink(a); stackTest(function() { diff --git a/js/src/jit-test/tests/modules/bug-1435327.js b/js/src/jit-test/tests/modules/bug-1435327.js index bb9f18a220..5edb47bedd 100644 --- a/js/src/jit-test/tests/modules/bug-1435327.js +++ b/js/src/jit-test/tests/modules/bug-1435327.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - lfLogBuffer = ` let c = registerModule('c', parseModule("")); let d = registerModule('d', parseModule("import { a } from 'c'; a;")); diff --git a/js/src/jit-test/tests/modules/bug-1771090.js b/js/src/jit-test/tests/modules/bug-1771090.js index cb5a5a2bc6..ffc73f93f2 100644 --- a/js/src/jit-test/tests/modules/bug-1771090.js +++ b/js/src/jit-test/tests/modules/bug-1771090.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this) +// |jit-test| skip-if: !hasFunction.oomAfterAllocations asyncFunc1("geval0\n await ''") async function asyncFunc1(lfVarx) { diff --git a/js/src/jit-test/tests/modules/bug-1802479.js b/js/src/jit-test/tests/modules/bug-1802479.js index c00d0e0da6..fd6ca259da 100644 --- a/js/src/jit-test/tests/modules/bug-1802479.js +++ b/js/src/jit-test/tests/modules/bug-1802479.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this); slow - function test(lfVarx) { try { oomTest(function() { diff --git a/js/src/jit-test/tests/modules/bug1670236.js b/js/src/jit-test/tests/modules/bug1670236.js index 35192c2b58..9237b4c1b2 100644 --- a/js/src/jit-test/tests/modules/bug1670236.js +++ b/js/src/jit-test/tests/modules/bug1670236.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) o0=r=/x/; this.toString=(function() { evaluate("",({ element:o0 })); diff --git a/js/src/jit-test/tests/modules/bug1685992.js b/js/src/jit-test/tests/modules/bug1685992.js index 39fd4ce7fd..d1672e18f9 100644 --- a/js/src/jit-test/tests/modules/bug1685992.js +++ b/js/src/jit-test/tests/modules/bug1685992.js @@ -1,4 +1,4 @@ -// |jit-test| --ion-offthread-compile=off; skip-if: !('oomTest' in this) +// |jit-test| --ion-offthread-compile=off function oomModule(lfMod) { oomTest(function () { @@ -9,4 +9,4 @@ oomModule(` class B50 { #priv() {} } -`)
\ No newline at end of file +`) diff --git a/js/src/jit-test/tests/modules/bug1846247.js b/js/src/jit-test/tests/modules/bug1846247.js index 4d63f0cfb3..d7dcd61132 100644 --- a/js/src/jit-test/tests/modules/bug1846247.js +++ b/js/src/jit-test/tests/modules/bug1846247.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this); allow-unhandlable-oom +// |jit-test| allow-unhandlable-oom ignoreUnhandledRejections(); oomTest(() => { gc(); diff --git a/js/src/jit-test/tests/modules/dynamic-import-oom.js b/js/src/jit-test/tests/modules/dynamic-import-oom.js index 9682c7560a..561ccf761e 100644 --- a/js/src/jit-test/tests/modules/dynamic-import-oom.js +++ b/js/src/jit-test/tests/modules/dynamic-import-oom.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this); --ion-offthread-compile=off +// |jit-test| --ion-offthread-compile=off // // Note: without --ion-offthread-compile=off this test takes a long time and // may timeout on some platforms. See bug 1507721. diff --git a/js/src/jit-test/tests/modules/eval-module-oom.js b/js/src/jit-test/tests/modules/eval-module-oom.js index 5587670735..ad5564e5c8 100644 --- a/js/src/jit-test/tests/modules/eval-module-oom.js +++ b/js/src/jit-test/tests/modules/eval-module-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // OOM tests for module parsing. const sa = diff --git a/js/src/jit-test/tests/modules/import-meta-oom.js b/js/src/jit-test/tests/modules/import-meta-oom.js index 168f1102a1..a3856e90eb 100644 --- a/js/src/jit-test/tests/modules/import-meta-oom.js +++ b/js/src/jit-test/tests/modules/import-meta-oom.js @@ -1,3 +1,3 @@ -// |jit-test| module; skip-if: !('oomTest' in this) +// |jit-test| module oomTest(() => import.meta); diff --git a/js/src/jit-test/tests/modules/offthread-oom.js b/js/src/jit-test/tests/modules/offthread-oom.js index 97b783c1ba..0af7d745c3 100644 --- a/js/src/jit-test/tests/modules/offthread-oom.js +++ b/js/src/jit-test/tests/modules/offthread-oom.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 // Test Out-of-Memory handling when parsing modules off-thread diff --git a/js/src/jit-test/tests/parser/bug-1263355-44.js b/js/src/jit-test/tests/parser/bug-1263355-44.js index c2de49fd49..a10366ea49 100644 --- a/js/src/jit-test/tests/parser/bug-1263355-44.js +++ b/js/src/jit-test/tests/parser/bug-1263355-44.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // Adapted from randomly chosen test: js/src/jit-test/tests/profiler/bug1231925.js "use strict"; enableGeckoProfiling(); diff --git a/js/src/jit-test/tests/parser/bug-1324773-2.js b/js/src/jit-test/tests/parser/bug-1324773-2.js index bf485ee602..a95012b72a 100644 --- a/js/src/jit-test/tests/parser/bug-1324773-2.js +++ b/js/src/jit-test/tests/parser/bug-1324773-2.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) - var lfGlobal = newGlobal(); lfGlobal.evaluate(` for (var i = 0; i < 600; i++) diff --git a/js/src/jit-test/tests/parser/bug-1324773.js b/js/src/jit-test/tests/parser/bug-1324773.js index 1ab1a3fb9a..3cb86fe944 100644 --- a/js/src/jit-test/tests/parser/bug-1324773.js +++ b/js/src/jit-test/tests/parser/bug-1324773.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('gczeal' in this) - var lfGlobal = newGlobal(); lfGlobal.evaluate(` for (var i = 0; i < 600; i++) diff --git a/js/src/jit-test/tests/parser/bug-1433014.js b/js/src/jit-test/tests/parser/bug-1433014.js index efb8ab98d3..7787862f6c 100644 --- a/js/src/jit-test/tests/parser/bug-1433014.js +++ b/js/src/jit-test/tests/parser/bug-1433014.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this) +// |jit-test| skip-if: helperThreadCount() === 0 evaluate(` oomTest(() => { offThreadCompileToStencil(""); diff --git a/js/src/jit-test/tests/parser/bug-1576865-1.js b/js/src/jit-test/tests/parser/bug-1576865-1.js index f31539b8ea..6becf49905 100644 --- a/js/src/jit-test/tests/parser/bug-1576865-1.js +++ b/js/src/jit-test/tests/parser/bug-1576865-1.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) var sourceText = ` function Outer() { var X00, X01, X02, X03, X04, X05, X06, X07; diff --git a/js/src/jit-test/tests/parser/bug-1576865-2.js b/js/src/jit-test/tests/parser/bug-1576865-2.js index d053c24728..4e2b02bfe9 100644 --- a/js/src/jit-test/tests/parser/bug-1576865-2.js +++ b/js/src/jit-test/tests/parser/bug-1576865-2.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) var sourceText = ` function Outer() { function LazyFunction() { diff --git a/js/src/jit-test/tests/parser/bug-1662260.js b/js/src/jit-test/tests/parser/bug-1662260.js index 235737657e..82fc7056a0 100644 --- a/js/src/jit-test/tests/parser/bug-1662260.js +++ b/js/src/jit-test/tests/parser/bug-1662260.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function loadX(lfVarx) { oomTest(function() { let m55 = parseModule(lfVarx); diff --git a/js/src/jit-test/tests/parser/bug-1764737.js b/js/src/jit-test/tests/parser/bug-1764737.js index 0fcc39e276..66d57b0dcc 100644 --- a/js/src/jit-test/tests/parser/bug-1764737.js +++ b/js/src/jit-test/tests/parser/bug-1764737.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this); --fuzzing-safe; --ion-offthread-compile=off +// |jit-test| --fuzzing-safe; --ion-offthread-compile=off function r(src) { oomTest(function() { diff --git a/js/src/jit-test/tests/parser/bug1461034.js b/js/src/jit-test/tests/parser/bug1461034.js index 84d6ae88ca..1b7798a703 100644 --- a/js/src/jit-test/tests/parser/bug1461034.js +++ b/js/src/jit-test/tests/parser/bug1461034.js @@ -1,2 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(function(){s[-1]}); diff --git a/js/src/jit-test/tests/parser/bug1547655.js b/js/src/jit-test/tests/parser/bug1547655.js index 540e011d9a..7a84d55b89 100644 --- a/js/src/jit-test/tests/parser/bug1547655.js +++ b/js/src/jit-test/tests/parser/bug1547655.js @@ -1,2 +1,2 @@ -// |jit-test| allow-unhandlable-oom; allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-unhandlable-oom; allow-oom oomTest(() => evaluate(`meta: { with({}) {} }`)); diff --git a/js/src/jit-test/tests/parser/bug1661454.js b/js/src/jit-test/tests/parser/bug1661454.js index ca7792f4bc..7692431039 100644 --- a/js/src/jit-test/tests/parser/bug1661454.js +++ b/js/src/jit-test/tests/parser/bug1661454.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function oomTestEval(lfVarx) { oomTest(() => eval(lfVarx)); } diff --git a/js/src/jit-test/tests/parser/bug1764715.js b/js/src/jit-test/tests/parser/bug1764715.js index b203925aa8..6fe1a19451 100644 --- a/js/src/jit-test/tests/parser/bug1764715.js +++ b/js/src/jit-test/tests/parser/bug1764715.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(function() { let m = parseModule(`x = a?.b; x = a?.b; x = a?.b;`); }); diff --git a/js/src/jit-test/tests/parser/bug1835785.js b/js/src/jit-test/tests/parser/bug1835785.js index 25317757ec..ea5b642f20 100644 --- a/js/src/jit-test/tests/parser/bug1835785.js +++ b/js/src/jit-test/tests/parser/bug1835785.js @@ -1,4 +1,4 @@ -// |jit-test| allow-unhandlable-oom; allow-oom; skip-if: !('oomAtAllocation' in this) +// |jit-test| allow-unhandlable-oom; allow-oom; skip-if: !hasFunction.oomAtAllocation function main() { this oomAtAllocation(7); diff --git a/js/src/jit-test/tests/parser/compile-script.js b/js/src/jit-test/tests/parser/compile-script.js index 293d25632e..c752f0b518 100644 --- a/js/src/jit-test/tests/parser/compile-script.js +++ b/js/src/jit-test/tests/parser/compile-script.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - load(libdir + "asserts.js"); let stencil = compileToStencil('314;'); diff --git a/js/src/jit-test/tests/parser/off_thread_compile_oom.js b/js/src/jit-test/tests/parser/off_thread_compile_oom.js index 5a8e32eb37..e9fb4c677d 100644 --- a/js/src/jit-test/tests/parser/off_thread_compile_oom.js +++ b/js/src/jit-test/tests/parser/off_thread_compile_oom.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 // OOM during off-thread initialization shouldn't leak memory. eval('oomTest(function(){offThreadCompileToStencil("")})'); diff --git a/js/src/jit-test/tests/parser/warning-oom.js b/js/src/jit-test/tests/parser/warning-oom.js index baf91cb9c9..d784c642d4 100644 --- a/js/src/jit-test/tests/parser/warning-oom.js +++ b/js/src/jit-test/tests/parser/warning-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // OOM during reporting warning should be handled. oomTest(function(){ diff --git a/js/src/jit-test/tests/profiler/bug1211962.js b/js/src/jit-test/tests/profiler/bug1211962.js index d47d823ff2..6803888e36 100644 --- a/js/src/jit-test/tests/profiler/bug1211962.js +++ b/js/src/jit-test/tests/profiler/bug1211962.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| slow; skip-if: helperThreadCount() === 0 enableGeckoProfiling(); var lfGlobal = newGlobal(); diff --git a/js/src/jit-test/tests/profiler/bug1231925.js b/js/src/jit-test/tests/profiler/bug1231925.js index 87325b6763..0e0011feb6 100644 --- a/js/src/jit-test/tests/profiler/bug1231925.js +++ b/js/src/jit-test/tests/profiler/bug1231925.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - enableGeckoProfiling(); oomTest(function() { eval("(function() {})()") diff --git a/js/src/jit-test/tests/profiler/bug1242840.js b/js/src/jit-test/tests/profiler/bug1242840.js index 8770403409..4c2f72024f 100644 --- a/js/src/jit-test/tests/profiler/bug1242840.js +++ b/js/src/jit-test/tests/profiler/bug1242840.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - enableGeckoProfiling(); oomTest(() => { try { diff --git a/js/src/jit-test/tests/profiler/bug1563889.js b/js/src/jit-test/tests/profiler/bug1563889.js index c8f9776ada..64a4c56423 100644 --- a/js/src/jit-test/tests/profiler/bug1563889.js +++ b/js/src/jit-test/tests/profiler/bug1563889.js @@ -1,3 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) for (var i = 0; i < 20; i++) {} oomTest(enableGeckoProfiling); diff --git a/js/src/jit-test/tests/promise/unhandled-rejections-oom.js b/js/src/jit-test/tests/promise/unhandled-rejections-oom.js index 706eba0032..922161f5ed 100644 --- a/js/src/jit-test/tests/promise/unhandled-rejections-oom.js +++ b/js/src/jit-test/tests/promise/unhandled-rejections-oom.js @@ -1,3 +1,3 @@ -// |jit-test| allow-oom; skip-if: !('oomTest' in this) +// |jit-test| allow-oom oomTest(async function() {}, { keepFailing: true }); diff --git a/js/src/jit-test/tests/regexp/CheckRegExpSyntax.js b/js/src/jit-test/tests/regexp/CheckRegExpSyntax.js index 19471fdf50..d261e6ae07 100644 --- a/js/src/jit-test/tests/regexp/CheckRegExpSyntax.js +++ b/js/src/jit-test/tests/regexp/CheckRegExpSyntax.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - load(libdir + "asserts.js"); assertEq(checkRegExpSyntax("correct[reg]exp"), undefined); diff --git a/js/src/jit-test/tests/regexp/bug-1845715.js b/js/src/jit-test/tests/regexp/bug-1845715.js index 992a5a8d8a..d6ca6776a3 100644 --- a/js/src/jit-test/tests/regexp/bug-1845715.js +++ b/js/src/jit-test/tests/regexp/bug-1845715.js @@ -1,2 +1 @@ -// |jit-test| skip-if: !('oomTest' in this) oomTest(() => { gc(); /./d.exec(); }); diff --git a/js/src/jit-test/tests/regexp/bug1640475.js b/js/src/jit-test/tests/regexp/bug1640475.js index 58c092ec1d..07c04827af 100644 --- a/js/src/jit-test/tests/regexp/bug1640475.js +++ b/js/src/jit-test/tests/regexp/bug1640475.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var i = 0; oomTest(function() { for (var j = 0; j < 10; ++j) { diff --git a/js/src/jit-test/tests/regexp/bug1640479.js b/js/src/jit-test/tests/regexp/bug1640479.js index ff166d6451..a04b44a2a3 100644 --- a/js/src/jit-test/tests/regexp/bug1640479.js +++ b/js/src/jit-test/tests/regexp/bug1640479.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var failures = 0; var i = 0; diff --git a/js/src/jit-test/tests/regexp/bug1794317.js b/js/src/jit-test/tests/regexp/bug1794317.js index 1ecb21eb64..ef50fd0c97 100644 --- a/js/src/jit-test/tests/regexp/bug1794317.js +++ b/js/src/jit-test/tests/regexp/bug1794317.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - for (let i = 0; i < 2; i++) { oomTest(function () { RegExp("(?<name" + i + ">)").exec(); diff --git a/js/src/jit-test/tests/saved-stacks/bug-1445973-quick.js b/js/src/jit-test/tests/saved-stacks/bug-1445973-quick.js index e95315f71f..2e8a5cef34 100644 --- a/js/src/jit-test/tests/saved-stacks/bug-1445973-quick.js +++ b/js/src/jit-test/tests/saved-stacks/bug-1445973-quick.js @@ -1,4 +1,4 @@ -// |jit-test| --no-baseline; skip-if: !('oomTest' in this) +// |jit-test| --no-baseline // // For background, see the comments for LiveSavedFrameCache in js/src/vm/Stack.h. // diff --git a/js/src/jit-test/tests/saved-stacks/oom-in-save-stack-02.js b/js/src/jit-test/tests/saved-stacks/oom-in-save-stack-02.js index ed24db8f0f..e62a3e953e 100644 --- a/js/src/jit-test/tests/saved-stacks/oom-in-save-stack-02.js +++ b/js/src/jit-test/tests/saved-stacks/oom-in-save-stack-02.js @@ -1,4 +1,4 @@ -// |jit-test| --no-ion; --no-baseline; --no-blinterp; skip-if: !('oomAtAllocation' in this) +// |jit-test| --no-ion; --no-baseline; --no-blinterp // This shouldn't assert (bug 1516514). // // Disabled for ion and baseline because those introduce OOMs at some point that diff --git a/js/src/jit-test/tests/saved-stacks/oom-in-save-stack.js b/js/src/jit-test/tests/saved-stacks/oom-in-save-stack.js index c96bbc5c6b..3163646626 100644 --- a/js/src/jit-test/tests/saved-stacks/oom-in-save-stack.js +++ b/js/src/jit-test/tests/saved-stacks/oom-in-save-stack.js @@ -1,4 +1,2 @@ -// |jit-test| skip-if: !('oomTest' in this) - let s = saveStack(); oomTest(() => { saveStack(); }); diff --git a/js/src/jit-test/tests/self-hosting/oom-delazify.js b/js/src/jit-test/tests/self-hosting/oom-delazify.js index 2c9bfc71e9..eb8c64fc0f 100644 --- a/js/src/jit-test/tests/self-hosting/oom-delazify.js +++ b/js/src/jit-test/tests/self-hosting/oom-delazify.js @@ -1,4 +1,4 @@ -// |jit-test| --no-blinterp; skip-if: !('oomTest' in this) +// |jit-test| --no-blinterp // Disable the JITs to make oomTest more reliable diff --git a/js/src/jit-test/tests/self-hosting/oom-toplevel.js b/js/src/jit-test/tests/self-hosting/oom-toplevel.js index 60f2be4e1e..9562d87049 100644 --- a/js/src/jit-test/tests/self-hosting/oom-toplevel.js +++ b/js/src/jit-test/tests/self-hosting/oom-toplevel.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomAtAllocation' in this) +// |jit-test| skip-if: !hasFunction.oomAtAllocation function code(n) { return ` diff --git a/js/src/jit-test/tests/self-test/oom-test-bug1497906.js b/js/src/jit-test/tests/self-test/oom-test-bug1497906.js index da6a0a959d..2acca432d0 100644 --- a/js/src/jit-test/tests/self-test/oom-test-bug1497906.js +++ b/js/src/jit-test/tests/self-test/oom-test-bug1497906.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this && 'stackTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: !(hasFunction.oomTest && hasFunction.stackTest) || helperThreadCount() === 0 // Check that oomTest throws an exception on worker threads. diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-bytelength-with-non-growable-write.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-bytelength-with-non-growable-write.js new file mode 100644 index 0000000000..32005f4725 --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-bytelength-with-non-growable-write.js @@ -0,0 +1,102 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab, sab) { + var ta = new Int32Array(gsab); + var ta2 = new Int32Array(sab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta2, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + var sab = new SharedArrayBuffer(12); + + // Start the worker. + { + let buffers = [gsab, sab]; + + // Shared memory locations: + // + // 0: Number of buffers + // 1: Ready-Flag Worker + // 2: Ready-Flag Main + let sync = new Int32Array(new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT)); + sync[0] = buffers.length; + + setSharedObject(sync.buffer); + + evalInWorker(` + let buffers = []; + let sync = new Int32Array(getSharedObject()); + let n = sync[0]; + for (let i = 0; i < n; ++i) { + // Notify we're ready to receive. + Atomics.store(sync, 1, 1); + + // Wait until buffer is in mailbox. + while (Atomics.compareExchange(sync, 2, 1, 0) !== 1); + + buffers.push(getSharedObject()); + } + (${worker})(...buffers); + `); + + for (let buffer of buffers) { + // Wait until worker is ready. + while (Atomics.compareExchange(sync, 1, 1, 0) !== 1); + + setSharedObject(buffer); + + // Notify buffer is in mailbox. + Atomics.store(sync, 2, 1); + } + } + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return {gsab, sab}; +} + +function testGrowableSharedArrayBufferByteLength() { + var {gsab, sab} = setup(); + var ta2 = new Int32Array(sab); + var r = 0; + + // |gsab.byteLength| is a seq-cst load, so it must prevent reordering any + // other loads, including unordered loads like |ta2[2]|. + while (gsab.byteLength <= 12) { + // |ta2[2]| is an unordered load, so it's hoistable by default. + r += ta2[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "gsab.byteLength acts as a memory barrier, so ta2[2] can't be hoisted" + ); +} +testGrowableSharedArrayBufferByteLength(); diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-bytelength.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-bytelength.js new file mode 100644 index 0000000000..c2f27d967c --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-bytelength.js @@ -0,0 +1,67 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab) { + var ta = new Int32Array(gsab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + + // Pass |gsab| to the mailbox. + setSharedObject(gsab); + + // Start the worker. + evalInWorker(` + (${worker})(getSharedObject()); + `); + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return gsab; +} + +function testGrowableSharedArrayBufferByteLength() { + var gsab = setup(); + var ta = new Int32Array(gsab); + var r = 0; + + // |gsab.byteLength| is a seq-cst load, so it must prevent reordering any + // other loads, including unordered loads like |ta[2]|. + while (gsab.byteLength <= 12) { + // |ta[2]| is an unordered load, so it's hoistable by default. + r += ta[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "gsab.byteLength acts as a memory barrier, so ta[2] can't be hoisted" + ); +} +testGrowableSharedArrayBufferByteLength(); diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-dataview-bytelength-with-non-growable-write.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-dataview-bytelength-with-non-growable-write.js new file mode 100644 index 0000000000..fc05df3ab1 --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-dataview-bytelength-with-non-growable-write.js @@ -0,0 +1,103 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab, sab) { + var ta = new Int32Array(gsab); + var ta2 = new Int32Array(sab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta2, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + var sab = new SharedArrayBuffer(12); + + // Start the worker. + { + let buffers = [gsab, sab]; + + // Shared memory locations: + // + // 0: Number of buffers + // 1: Ready-Flag Worker + // 2: Ready-Flag Main + let sync = new Int32Array(new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT)); + sync[0] = buffers.length; + + setSharedObject(sync.buffer); + + evalInWorker(` + let buffers = []; + let sync = new Int32Array(getSharedObject()); + let n = sync[0]; + for (let i = 0; i < n; ++i) { + // Notify we're ready to receive. + Atomics.store(sync, 1, 1); + + // Wait until buffer is in mailbox. + while (Atomics.compareExchange(sync, 2, 1, 0) !== 1); + + buffers.push(getSharedObject()); + } + (${worker})(...buffers); + `); + + for (let buffer of buffers) { + // Wait until worker is ready. + while (Atomics.compareExchange(sync, 1, 1, 0) !== 1); + + setSharedObject(buffer); + + // Notify buffer is in mailbox. + Atomics.store(sync, 2, 1); + } + } + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return {gsab, sab}; +} + +function testDataViewByteLength() { + var {gsab, sab} = setup(); + var dv = new DataView(gsab); + var ta2 = new Int32Array(sab); + var r = 0; + + // |dv.byteLength| is a seq-cst load, so it must prevent reordering any other + // loads, including unordered loads like |ta2[2]|. + while (dv.byteLength <= 12) { + // |ta2[2]| is an unordered load, so it's hoistable by default. + r += ta2[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "dv.byteLength acts as a memory barrier, so ta2[2] can't be hoisted" + ); +} +testDataViewByteLength(); diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-dataview-bytelength.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-dataview-bytelength.js new file mode 100644 index 0000000000..51ed63f254 --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-dataview-bytelength.js @@ -0,0 +1,68 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab) { + var ta = new Int32Array(gsab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + + // Pass |gsab| to the mailbox. + setSharedObject(gsab); + + // Start the worker. + evalInWorker(` + (${worker})(getSharedObject()); + `); + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return gsab; +} + +function testDataViewByteLength() { + var gsab = setup(); + var ta = new Int32Array(gsab); + var dv = new DataView(gsab); + var r = 0; + + // |dv.byteLength| is a seq-cst load, so it must prevent reordering any other + // loads, including unordered loads like |ta[2]|. + while (dv.byteLength <= 12) { + // |ta[2]| is an unordered load, so it's hoistable by default. + r += ta[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "dv.byteLength acts as a memory barrier, so ta[2] can't be hoisted" + ); +} +testDataViewByteLength(); diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-bytelength-with-non-growable-write.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-bytelength-with-non-growable-write.js new file mode 100644 index 0000000000..fd92ff615a --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-bytelength-with-non-growable-write.js @@ -0,0 +1,103 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab, sab) { + var ta = new Int32Array(gsab); + var ta2 = new Int32Array(sab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta2, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + var sab = new SharedArrayBuffer(12); + + // Start the worker. + { + let buffers = [gsab, sab]; + + // Shared memory locations: + // + // 0: Number of buffers + // 1: Ready-Flag Worker + // 2: Ready-Flag Main + let sync = new Int32Array(new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT)); + sync[0] = buffers.length; + + setSharedObject(sync.buffer); + + evalInWorker(` + let buffers = []; + let sync = new Int32Array(getSharedObject()); + let n = sync[0]; + for (let i = 0; i < n; ++i) { + // Notify we're ready to receive. + Atomics.store(sync, 1, 1); + + // Wait until buffer is in mailbox. + while (Atomics.compareExchange(sync, 2, 1, 0) !== 1); + + buffers.push(getSharedObject()); + } + (${worker})(...buffers); + `); + + for (let buffer of buffers) { + // Wait until worker is ready. + while (Atomics.compareExchange(sync, 1, 1, 0) !== 1); + + setSharedObject(buffer); + + // Notify buffer is in mailbox. + Atomics.store(sync, 2, 1); + } + } + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return {gsab, sab}; +} + +function testTypedArrayByteLength() { + var {gsab, sab} = setup(); + var ta = new Int32Array(gsab); + var ta2 = new Int32Array(sab); + var r = 0; + + // |ta.byteLength| is a seq-cst load, so it must prevent reordering any other + // loads, including unordered loads like |ta2[2]|. + while (ta.byteLength <= 12) { + // |ta2[2]| is an unordered load, so it's hoistable by default. + r += ta2[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "ta.byteLength acts as a memory barrier, so ta2[2] can't be hoisted" + ); +} +testTypedArrayByteLength(); diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-bytelength.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-bytelength.js new file mode 100644 index 0000000000..843f7dce15 --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-bytelength.js @@ -0,0 +1,67 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab) { + var ta = new Int32Array(gsab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + + // Pass |gsab| to the mailbox. + setSharedObject(gsab); + + // Start the worker. + evalInWorker(` + (${worker})(getSharedObject()); + `); + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return gsab; +} + +function testTypedArrayByteLength() { + var gsab = setup(); + var ta = new Int32Array(gsab); + var r = 0; + + // |ta.byteLength| is a seq-cst load, so it must prevent reordering any other + // loads, including unordered loads like |ta[2]|. + while (ta.byteLength <= 12) { + // |ta[2]| is an unordered load, so it's hoistable by default. + r += ta[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "ta.byteLength acts as a memory barrier, so ta[2] can't be hoisted" + ); +} +testTypedArrayByteLength(); diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-length-with-non-growable-write.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-length-with-non-growable-write.js new file mode 100644 index 0000000000..4371dd2b00 --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-length-with-non-growable-write.js @@ -0,0 +1,103 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab, sab) { + var ta = new Int32Array(gsab); + var ta2 = new Int32Array(sab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta2, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + var sab = new SharedArrayBuffer(12); + + // Start the worker. + { + let buffers = [gsab, sab]; + + // Shared memory locations: + // + // 0: Number of buffers + // 1: Ready-Flag Worker + // 2: Ready-Flag Main + let sync = new Int32Array(new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT)); + sync[0] = buffers.length; + + setSharedObject(sync.buffer); + + evalInWorker(` + let buffers = []; + let sync = new Int32Array(getSharedObject()); + let n = sync[0]; + for (let i = 0; i < n; ++i) { + // Notify we're ready to receive. + Atomics.store(sync, 1, 1); + + // Wait until buffer is in mailbox. + while (Atomics.compareExchange(sync, 2, 1, 0) !== 1); + + buffers.push(getSharedObject()); + } + (${worker})(...buffers); + `); + + for (let buffer of buffers) { + // Wait until worker is ready. + while (Atomics.compareExchange(sync, 1, 1, 0) !== 1); + + setSharedObject(buffer); + + // Notify buffer is in mailbox. + Atomics.store(sync, 2, 1); + } + } + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return {gsab, sab}; +} + +function testTypedArrayLength() { + var {gsab, sab} = setup(); + var ta = new Int32Array(gsab); + var ta2 = new Int32Array(sab); + var r = 0; + + // |ta.length| is a seq-cst load, so it must prevent reordering any other + // loads, including unordered loads like |ta2[2]|. + while (ta.length <= 3) { + // |ta2[2]| is an unordered load, so it's hoistable by default. + r += ta2[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "ta.length acts as a memory barrier, so ta2[2] can't be hoisted" + ); +} +testTypedArrayLength(); diff --git a/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-length.js b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-length.js new file mode 100644 index 0000000000..b32af6ae78 --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/growable-sab-memory-barrier-typedarray-length.js @@ -0,0 +1,67 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer||helperThreadCount()===0 + +function setup() { + // Shared memory locations: + // + // 0: Lock + // 1: Sleep + // 2: Data + // 3: Unused + + function worker(gsab) { + var ta = new Int32Array(gsab); + + // Notify the main thread that the worker is ready. + Atomics.store(ta, 0, 1); + + // Sleep to give the main thread time to execute and tier-up the loop. + Atomics.wait(ta, 1, 0, 500); + + // Modify the memory read in the loop. + Atomics.store(ta, 2, 1); + + // Sleep again to give the main thread time to execute the loop. + Atomics.wait(ta, 1, 0, 100); + + // Grow the buffer. This modifies the loop condition. + gsab.grow(16); + } + + var gsab = new SharedArrayBuffer(12, {maxByteLength: 16}); + + // Pass |gsab| to the mailbox. + setSharedObject(gsab); + + // Start the worker. + evalInWorker(` + (${worker})(getSharedObject()); + `); + + // Wait until worker is ready. + var ta = new Int32Array(gsab); + while (Atomics.load(ta, 0) === 0); + + return gsab; +} + +function testTypedArrayLength() { + var gsab = setup(); + var ta = new Int32Array(gsab); + var r = 0; + + // |ta.length| is a seq-cst load, so it must prevent reordering any other + // loads, including unordered loads like |ta[2]|. + while (ta.length <= 3) { + // |ta[2]| is an unordered load, so it's hoistable by default. + r += ta[2]; + } + + // The memory location is first modified and then the buffer is grown, so we + // must observe reads of the modified memory location before exiting the loop. + assertEq( + r > 0, + true, + "ta.length acts as a memory barrier, so ta[2] can't be hoisted" + ); +} +testTypedArrayLength(); diff --git a/js/src/jit-test/tests/stream/bug-1513266.js b/js/src/jit-test/tests/stream/bug-1513266.js index 3db1e2d941..5511ce8000 100644 --- a/js/src/jit-test/tests/stream/bug-1513266.js +++ b/js/src/jit-test/tests/stream/bug-1513266.js @@ -1,4 +1,4 @@ -// |jit-test| --no-ion; --no-baseline; skip-if: !('oomTest' in this && this.hasOwnProperty("ReadableStream")) +// |jit-test| --no-ion; --no-baseline; skip-if: !this.hasOwnProperty("ReadableStream") ignoreUnhandledRejections(); diff --git a/js/src/jit-test/tests/stream/bug-1515816.js b/js/src/jit-test/tests/stream/bug-1515816.js index 44329b056d..6199eb9a31 100644 --- a/js/src/jit-test/tests/stream/bug-1515816.js +++ b/js/src/jit-test/tests/stream/bug-1515816.js @@ -1,4 +1,4 @@ -// |jit-test| --no-ion; --no-baseline; --no-blinterp; skip-if: !('oomAfterAllocations' in this && this.hasOwnProperty("ReadableStream")) +// |jit-test| --no-ion; --no-baseline; --no-blinterp; skip-if: !this.hasOwnProperty("ReadableStream") // Don't crash on OOM in ReadableStreamDefaultReader.prototype.read(). for (let n = 1; n < 1000; n++) { diff --git a/js/src/jit-test/tests/typedarray/construct-with-growable-sharedarraybuffer.js b/js/src/jit-test/tests/typedarray/construct-with-growable-sharedarraybuffer.js new file mode 100644 index 0000000000..29696f0860 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/construct-with-growable-sharedarraybuffer.js @@ -0,0 +1,81 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +// Test TypedArray constructor when called with growable SharedArrayBuffers. + +function testSharedArrayBuffer() { + function test() { + var N = 200; + var sab = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: (5 + N) * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < N; ++i) { + var ta = new Int32Array(sab); + assertEq(ta.length, 4 + i); + + // Ensure auto-length tracking works correctly. + sab.grow((5 + i) * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 5 + i); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testSharedArrayBuffer(); + +function testSharedArrayBufferAndByteOffset() { + function test() { + var N = 200; + var sab = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: (5 + N) * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < N; ++i) { + var ta = new Int32Array(sab, Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 3 + i); + + // Ensure auto-length tracking works correctly. + sab.grow((5 + i) * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 4 + i); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testSharedArrayBufferAndByteOffset(); + +function testSharedArrayBufferAndByteOffsetAndLength() { + function test() { + var N = 200; + var sab = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: (5 + N) * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < N; ++i) { + var ta = new Int32Array(sab, Int32Array.BYTES_PER_ELEMENT, 2); + assertEq(ta.length, 2); + + // Ensure length doesn't change when resizing the buffer. + sab.grow((5 + i) * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 2); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testSharedArrayBufferAndByteOffsetAndLength(); + +function testWrappedSharedArrayBuffer() { + var g = newGlobal(); + + function test() { + var N = 200; + var sab = new g.SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: (5 + N) * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < N; ++i) { + var ta = new Int32Array(sab); + assertEq(ta.length, 4 + i); + + // Ensure auto-length tracking works correctly. + sab.grow((5 + i) * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 5 + i); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testWrappedSharedArrayBuffer(); diff --git a/js/src/jit-test/tests/typedarray/construct-with-resizable-arraybuffer.js b/js/src/jit-test/tests/typedarray/construct-with-resizable-arraybuffer.js new file mode 100644 index 0000000000..33f7a4f6c6 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/construct-with-resizable-arraybuffer.js @@ -0,0 +1,102 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +// Test TypedArray constructor when called with resizable ArrayBuffers. + +function testArrayBuffer() { + function test() { + var ab = new ArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: 5 * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < 200; ++i) { + var ta = new Int32Array(ab); + assertEq(ta.length, 4); + + // Ensure auto-length tracking works correctly. + ab.resize(5 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 5); + + ab.resize(2 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 2); + + // Reset to original length. + ab.resize(4 * Int32Array.BYTES_PER_ELEMENT); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testArrayBuffer(); + +function testArrayBufferAndByteOffset() { + function test() { + var ab = new ArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: 5 * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < 200; ++i) { + var ta = new Int32Array(ab, Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 3); + + // Ensure auto-length tracking works correctly. + ab.resize(5 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 4); + + ab.resize(2 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 1); + + // Reset to original length. + ab.resize(4 * Int32Array.BYTES_PER_ELEMENT); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testArrayBufferAndByteOffset(); + +function testArrayBufferAndByteOffsetAndLength() { + function test() { + var ab = new ArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: 5 * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < 200; ++i) { + var ta = new Int32Array(ab, Int32Array.BYTES_PER_ELEMENT, 2); + assertEq(ta.length, 2); + + // Ensure length doesn't change when resizing the buffer. + ab.resize(5 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 2); + + // Returns zero when the TypedArray get out-of-bounds. + ab.resize(2 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 0); + + // Reset to original length. + ab.resize(4 * Int32Array.BYTES_PER_ELEMENT); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testArrayBufferAndByteOffsetAndLength(); + +function testWrappedArrayBuffer() { + var g = newGlobal(); + + function test() { + var ab = new g.ArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT, {maxByteLength: 5 * Int32Array.BYTES_PER_ELEMENT}); + for (var i = 0; i < 200; ++i) { + var ta = new Int32Array(ab); + assertEq(ta.length, 4); + + // Ensure auto-length tracking works correctly. + ab.resize(5 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 5); + + ab.resize(2 * Int32Array.BYTES_PER_ELEMENT); + assertEq(ta.length, 2); + + // Reset to original length. + ab.resize(4 * Int32Array.BYTES_PER_ELEMENT); + } + } + for (var i = 0; i < 2; ++i) { + test(); + } +} +testWrappedArrayBuffer(); diff --git a/js/src/jit-test/tests/typedarray/ensure-non-inline.js b/js/src/jit-test/tests/typedarray/ensure-non-inline.js index d8859fa627..a1321be88a 100644 --- a/js/src/jit-test/tests/typedarray/ensure-non-inline.js +++ b/js/src/jit-test/tests/typedarray/ensure-non-inline.js @@ -1,3 +1,5 @@ +// |jit-test| --enable-arraybuffer-resizable + const constructors = [ Int8Array, Uint8Array, @@ -73,6 +75,16 @@ function test() { const big = new ctor(ab); messWith(big, ab); } + + // With resizable buffer. + for (const ctor of constructors) { + let ab = new ArrayBuffer(32, {maxByteLength: 64}); + const small = new ctor(ab); + messWith(small, small); + ab = new ArrayBuffer(4000, {maxByteLength: 4096}); + const big = new ctor(ab); + messWith(big, big); + } } try { diff --git a/js/src/jit-test/tests/typedarray/growable-sharedarraybuffer-bytelength.js b/js/src/jit-test/tests/typedarray/growable-sharedarraybuffer-bytelength.js new file mode 100644 index 0000000000..421bf03475 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/growable-sharedarraybuffer-bytelength.js @@ -0,0 +1,14 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +function testGrowableSharedArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + for (let j = 0; j < 100; ++j) { + assertEq(sab.byteLength, i + j); + + sab.grow(i + j + 1); + assertEq(sab.byteLength, i + j + 1); + } + } +} +for (let i = 0; i < 2; ++i) testGrowableSharedArrayBuffer(); diff --git a/js/src/jit-test/tests/typedarray/indexed-integer-exotics.js b/js/src/jit-test/tests/typedarray/indexed-integer-exotics.js index 6331efd7c3..510e57c0f0 100644 --- a/js/src/jit-test/tests/typedarray/indexed-integer-exotics.js +++ b/js/src/jit-test/tests/typedarray/indexed-integer-exotics.js @@ -88,10 +88,10 @@ assertEq(undefined, f()); // These checks currently fail due to bug 1129202 not being implemented yet. // We should uncomment them once that bug landed. -//assertThrows('Object.defineProperty(new Int32Array(100), -1, {value: 1})'); +assertThrows('Object.defineProperty(new Int32Array(100), -1, {value: 1})'); // -0 gets converted to the string "0", so use "-0" instead. -//assertThrows('Object.defineProperty(new Int32Array(100), "-0", {value: 1})'); -//assertThrows('Object.defineProperty(new Int32Array(100), -10, {value: 1})'); -//assertThrows('Object.defineProperty(new Int32Array(), 4294967295, {value: 1})'); +assertThrows('Object.defineProperty(new Int32Array(100), "-0", {value: 1})'); +assertThrows('Object.defineProperty(new Int32Array(100), -10, {value: 1})'); +assertThrows('Object.defineProperty(new Int32Array(), 4294967295, {value: 1})'); check(); diff --git a/js/src/jit-test/tests/typedarray/oom-allocating-arraybuffer-contents.js b/js/src/jit-test/tests/typedarray/oom-allocating-arraybuffer-contents.js index 16d1bf976f..1362c8d14c 100644 --- a/js/src/jit-test/tests/typedarray/oom-allocating-arraybuffer-contents.js +++ b/js/src/jit-test/tests/typedarray/oom-allocating-arraybuffer-contents.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // Resolve ArrayBuffer before OOM-testing, so OOM-testing runs less code and is // less fragile. var AB = ArrayBuffer; diff --git a/js/src/jit-test/tests/typedarray/oom-allocating-copying-same-buffer-contents.js b/js/src/jit-test/tests/typedarray/oom-allocating-copying-same-buffer-contents.js index ec33e4ef8e..cdbc3d7215 100644 --- a/js/src/jit-test/tests/typedarray/oom-allocating-copying-same-buffer-contents.js +++ b/js/src/jit-test/tests/typedarray/oom-allocating-copying-same-buffer-contents.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var buffer = new ArrayBuffer(16); var i8 = new Int8Array(buffer); var i16 = new Int16Array(buffer); diff --git a/js/src/jit-test/tests/typedarray/resizable-arraybuffer-bytelength.js b/js/src/jit-test/tests/typedarray/resizable-arraybuffer-bytelength.js new file mode 100644 index 0000000000..7b50331a15 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-arraybuffer-bytelength.js @@ -0,0 +1,20 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +function testResizableArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ab.byteLength, i); + + ab.resize(i + 1); + assertEq(ab.byteLength, i + 1); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ab.byteLength, i - 1); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBuffer(); diff --git a/js/src/jit-test/tests/typedarray/resizable-buffer-inlined-data-moved.js b/js/src/jit-test/tests/typedarray/resizable-buffer-inlined-data-moved.js new file mode 100644 index 0000000000..1206edd5e5 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-buffer-inlined-data-moved.js @@ -0,0 +1,53 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +function fillArrayBuffer(rab) { + let fill = new Int8Array(rab); + for (let i = 0; i < fill.length; ++i) fill[i] = i + 1; +} + +function test() { + let rab = new ArrayBuffer(4, {maxByteLength: 4}); + let ta = new Int8Array(rab, 2, 2); + + fillArrayBuffer(rab); + + assertEq(ta[0], 3); + assertEq(ta[1], 4); + + // Shrink to make |ta| out-of-bounds. + rab.resize(3); + + // Request GC to move inline data. + gc(); + + // Grow to make |ta| in-bounds again. + rab.resize(4); + + assertEq(ta[0], 3); + assertEq(ta[1], 0); +} +test(); + + +function testAutoLength() { + let rab = new ArrayBuffer(4, {maxByteLength: 4}); + let ta = new Int8Array(rab, 2); + + fillArrayBuffer(rab); + + assertEq(ta[0], 3); + assertEq(ta[1], 4); + + // Shrink to make |ta| out-of-bounds. + rab.resize(1); + + // Request GC to move inline data. + gc(); + + // Grow to make |ta| in-bounds again. + rab.resize(4); + + assertEq(ta[0], 0); + assertEq(ta[1], 0); +} +testAutoLength(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-bytelength-with-sab.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-bytelength-with-sab.js new file mode 100644 index 0000000000..828e989b92 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-bytelength-with-sab.js @@ -0,0 +1,29 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +function testResizableArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new Int8Array(sab, 0, i); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteLength, i); + + sab.grow(i + j + 1); + assertEq(ta.byteLength, i); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBuffer(); + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new Int8Array(sab); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteLength, i + j); + + sab.grow(i + j + 1); + assertEq(ta.byteLength, i + j + 1); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-bytelength.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-bytelength.js new file mode 100644 index 0000000000..925750d186 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-bytelength.js @@ -0,0 +1,41 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +function testResizableArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new Int8Array(ab, 0, i); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteLength, i); + + ab.resize(i + 1); + assertEq(ta.byteLength, i); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ta.byteLength, 0); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBuffer(); + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new Int8Array(ab); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteLength, i); + + ab.resize(i + 1); + assertEq(ta.byteLength, i + 1); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ta.byteLength, i - 1); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-byteoffset-sab.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-byteoffset-sab.js new file mode 100644 index 0000000000..6142d75978 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-byteoffset-sab.js @@ -0,0 +1,43 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new Int8Array(sab); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteOffset, 0); + + sab.grow(i + j + 1); + assertEq(ta.byteOffset, 0); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); + +function testResizableArrayBufferAutoLengthNonZeroOffset() { + for (let i = 1; i < 4 + 1; ++i) { + let sab = new SharedArrayBuffer(i + 1, {maxByteLength: i + 100 + 1}); + let ta = new Int8Array(sab, 1); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteOffset, 1); + + sab.grow(i + j + 2); + assertEq(ta.byteOffset, 1); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLengthNonZeroOffset(); + +function testResizableArrayBufferNonZeroOffset() { + for (let i = 2; i < 4 + 2; ++i) { + let sab = new SharedArrayBuffer(i + 2, {maxByteLength: i + 100 + 2}); + let ta = new Int8Array(sab, 1, 1); + for (let j = 0; j < 100; ++j) { + assertEq(ta.byteOffset, 1); + + sab.grow(i + j + 3); + assertEq(ta.byteOffset, 1); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferNonZeroOffset(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-byteoffset.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-byteoffset.js new file mode 100644 index 0000000000..56552eeb7f --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-byteoffset.js @@ -0,0 +1,57 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new Int8Array(ab); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteOffset, 0); + + ab.resize(i + 1); + assertEq(ta.byteOffset, 0); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ta.byteOffset, 0); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); + +function testResizableArrayBufferAutoLengthNonZeroOffset() { + for (let i = 1; i < 4 + 1; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new Int8Array(ab, 1); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteOffset, 1); + + ab.resize(i + 1); + assertEq(ta.byteOffset, 1); + + ab.resize(i - 1); + assertEq(ta.byteOffset, i > 1 ? 1 : 0); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLengthNonZeroOffset(); + +function testResizableArrayBufferNonZeroOffset() { + for (let i = 2; i < 4 + 2; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new Int8Array(ab, 1, 1); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.byteOffset, 1); + + ab.resize(i + 1); + assertEq(ta.byteOffset, 1); + + ab.resize(i - 1); + assertEq(ta.byteOffset, i > 2 ? 1 : 0); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferNonZeroOffset(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-get-elem-with-sab.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-get-elem-with-sab.js new file mode 100644 index 0000000000..236641d9a8 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-get-elem-with-sab.js @@ -0,0 +1,49 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new SharedArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + for (let i = 0; i < length; ++i) { + actual[i] = type(i * i); + expected[i] = type(i * i); + } + + // In-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % length; + assertEq(actual[index], expected[index]); + } + + // Out-of-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % (length + 4); + assertEq(actual[index], expected[index]); + } +} + +for (let TA of TypedArrays) { + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`)(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-get-elem.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-get-elem.js new file mode 100644 index 0000000000..902841e526 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-get-elem.js @@ -0,0 +1,49 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new ArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + for (let i = 0; i < length; ++i) { + actual[i] = type(i * i); + expected[i] = type(i * i); + } + + // In-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % length; + assertEq(actual[index], expected[index]); + } + + // Out-of-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % (length + 4); + assertEq(actual[index], expected[index]); + } +} + +for (let TA of TypedArrays) { + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`)(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-has-elem-with-sab.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-has-elem-with-sab.js new file mode 100644 index 0000000000..40d51ebfe2 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-has-elem-with-sab.js @@ -0,0 +1,36 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new SharedArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + + for (let i = 0; i < 200; ++i) { + let index = (i % (length + 4)); + assertEq(index in actual, index in expected); + } +} + +for (let TA of TypedArrays) { + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`)(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-has-elem.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-has-elem.js new file mode 100644 index 0000000000..07d44bf55b --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-has-elem.js @@ -0,0 +1,36 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new ArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + + for (let i = 0; i < 200; ++i) { + let index = (i % (length + 4)); + assertEq(index in actual, index in expected); + } +} + +for (let TA of TypedArrays) { + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`)(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-byteOffset.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-byteOffset.js new file mode 100644 index 0000000000..1b9f9a28e7 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-byteOffset.js @@ -0,0 +1,73 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +const TypedArrayByteOffset = getSelfHostedValue("TypedArrayByteOffset"); + +function testTypedArrayByteOffset() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab), + new Int8Array(ab, 1), + new Int8Array(ab, 2), + new Int8Array(ab, 3), + ]; + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayByteOffset(ta), i & 3); + } +} +testTypedArrayByteOffset(); + +function testTypedArrayByteOffsetOutOfBounds() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab, 0, 10), + new Int8Array(ab, 1, 10), + new Int8Array(ab, 2, 10), + new Int8Array(ab, 3, 10), + ]; + + // Resize to zero to make all views out-of-bounds. + ab.resize(0); + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayByteOffset(ta), i & 3); + } +} +testTypedArrayByteOffsetOutOfBounds(); + +function testTypedArrayByteOffsetDetached() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab, 0, 10), + new Int8Array(ab, 1, 10), + new Int8Array(ab, 2, 10), + new Int8Array(ab, 3, 10), + ]; + + // Detach the buffer. + ab.transfer(); + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayByteOffset(ta), 0); + } +} +testTypedArrayByteOffsetDetached(); + +function testTypedArrayByteOffsetWithShared() { + let ab = new SharedArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab), + new Int8Array(ab, 1), + new Int8Array(ab, 2), + new Int8Array(ab, 3), + ]; + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayByteOffset(ta), i & 3); + } +} +testTypedArrayByteOffsetWithShared(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-typedArrayLength.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-typedArrayLength.js new file mode 100644 index 0000000000..c36b4f997f --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-typedArrayLength.js @@ -0,0 +1,75 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +load(libdir + "asserts.js"); + +const TypedArrayLength = getSelfHostedValue("TypedArrayLength"); + +function testTypedArrayLength() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab), + new Int8Array(ab, 1), + new Int8Array(ab, 2), + new Int8Array(ab, 3), + ]; + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayLength(ta), 100 - (i & 3)); + } +} +testTypedArrayLength(); + +function testTypedArrayLengthOutOfBounds() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab, 0, 10), + new Int8Array(ab, 1, 10), + new Int8Array(ab, 2, 10), + new Int8Array(ab, 3, 10), + ]; + + // Resize to zero to make all views out-of-bounds. + ab.resize(0); + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertThrowsInstanceOf(() => TypedArrayLength(ta), TypeError); + } +} +testTypedArrayLengthOutOfBounds(); + +function testTypedArrayLengthDetached() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab, 0, 10), + new Int8Array(ab, 1, 10), + new Int8Array(ab, 2, 10), + new Int8Array(ab, 3, 10), + ]; + + // Detach the buffer. + ab.transfer(); + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayLength(ta), 0); + } +} +testTypedArrayLengthDetached(); + +function testTypedArrayLengthWithShared() { + let ab = new SharedArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab), + new Int8Array(ab, 1), + new Int8Array(ab, 2), + new Int8Array(ab, 3), + ]; + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayLength(ta), 100 - (i & 3)); + } +} +testTypedArrayLengthWithShared(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-typedArrayLengthZeroOnOutOfBounds.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-typedArrayLengthZeroOnOutOfBounds.js new file mode 100644 index 0000000000..a272d7d8c2 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-intrinsic-typedArrayLengthZeroOnOutOfBounds.js @@ -0,0 +1,75 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +load(libdir + "asserts.js"); + +const TypedArrayLengthZeroOnOutOfBounds = getSelfHostedValue("TypedArrayLengthZeroOnOutOfBounds"); + +function testTypedArrayLength() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab), + new Int8Array(ab, 1), + new Int8Array(ab, 2), + new Int8Array(ab, 3), + ]; + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayLengthZeroOnOutOfBounds(ta), 100 - (i & 3)); + } +} +testTypedArrayLength(); + +function testTypedArrayLengthOutOfBounds() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab, 0, 10), + new Int8Array(ab, 1, 10), + new Int8Array(ab, 2, 10), + new Int8Array(ab, 3, 10), + ]; + + // Resize to zero to make all views out-of-bounds. + ab.resize(0); + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayLengthZeroOnOutOfBounds(ta), 0); + } +} +testTypedArrayLengthOutOfBounds(); + +function testTypedArrayLengthDetached() { + let ab = new ArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab, 0, 10), + new Int8Array(ab, 1, 10), + new Int8Array(ab, 2, 10), + new Int8Array(ab, 3, 10), + ]; + + // Detach the buffer. + ab.transfer(); + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayLengthZeroOnOutOfBounds(ta), 0); + } +} +testTypedArrayLengthDetached(); + +function testTypedArrayLengthWithShared() { + let ab = new SharedArrayBuffer(100, {maxByteLength: 100}); + let typedArrays = [ + new Int8Array(ab), + new Int8Array(ab, 1), + new Int8Array(ab, 2), + new Int8Array(ab, 3), + ]; + + for (let i = 0; i < 200; ++i) { + let ta = typedArrays[i & 3]; + assertEq(TypedArrayLengthZeroOnOutOfBounds(ta), 100 - (i & 3)); + } +} +testTypedArrayLengthWithShared(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-length-with-sab.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-length-with-sab.js new file mode 100644 index 0000000000..9b1a9d41e8 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-length-with-sab.js @@ -0,0 +1,29 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +function testResizableArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new Int8Array(sab, 0, i); + for (let j = 0; j < 100; ++j) { + assertEq(ta.length, i); + + sab.grow(i + j + 1); + assertEq(ta.length, i); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBuffer(); + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let sab = new SharedArrayBuffer(i, {maxByteLength: i + 100}); + let ta = new Int8Array(sab); + for (let j = 0; j < 100; ++j) { + assertEq(ta.length, i + j); + + sab.grow(i + j + 1); + assertEq(ta.length, i + j + 1); + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-length.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-length.js new file mode 100644 index 0000000000..96bbc9752e --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-length.js @@ -0,0 +1,41 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +function testResizableArrayBuffer() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new Int8Array(ab, 0, i); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.length, i); + + ab.resize(i + 1); + assertEq(ta.length, i); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ta.length, 0); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBuffer(); + +function testResizableArrayBufferAutoLength() { + for (let i = 0; i < 4; ++i) { + let ab = new ArrayBuffer(i, {maxByteLength: i + 1}); + let ta = new Int8Array(ab); + for (let j = 0; j < 100; ++j) { + ab.resize(i); + assertEq(ta.length, i); + + ab.resize(i + 1); + assertEq(ta.length, i + 1); + + if (i > 0) { + ab.resize(i - 1); + assertEq(ta.length, i - 1); + } + } + } +} +for (let i = 0; i < 2; ++i) testResizableArrayBufferAutoLength(); diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-set-elem-with-sab.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-set-elem-with-sab.js new file mode 100644 index 0000000000..67a15e7714 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-set-elem-with-sab.js @@ -0,0 +1,54 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize||!this.SharedArrayBuffer + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new SharedArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + // In-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % length; + + let v = type(i); + actual[index] = v; + expected[index] = v; + + assertEq(actual[index], expected[index]); + } + + // Out-of-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % (length + 4); + + let v = type(i); + actual[index] = v; + expected[index] = v; + + assertEq(actual[index], expected[index]); + } +} + +for (let TA of TypedArrays) { + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`)(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/typedarray/resizable-typedarray-set-elem.js b/js/src/jit-test/tests/typedarray/resizable-typedarray-set-elem.js new file mode 100644 index 0000000000..91f5c7b048 --- /dev/null +++ b/js/src/jit-test/tests/typedarray/resizable-typedarray-set-elem.js @@ -0,0 +1,54 @@ +// |jit-test| --enable-arraybuffer-resizable; skip-if: !ArrayBuffer.prototype.resize + +const TypedArrays = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +function test(TA) { + const length = 4; + const byteLength = length * TA.BYTES_PER_ELEMENT; + + let rab = new ArrayBuffer(byteLength, {maxByteLength: byteLength}); + let actual = new TA(rab); + let expected = new TA(length); + let type = expected[0].constructor; + + // In-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % length; + + let v = type(i); + actual[index] = v; + expected[index] = v; + + assertEq(actual[index], expected[index]); + } + + // Out-of-bounds access + for (let i = 0; i < 200; ++i) { + let index = i % (length + 4); + + let v = type(i); + actual[index] = v; + expected[index] = v; + + assertEq(actual[index], expected[index]); + } +} + +for (let TA of TypedArrays) { + // Copy test function to ensure monomorphic ICs. + let copy = Function(`return ${test}`)(); + + copy(TA); +} diff --git a/js/src/jit-test/tests/warp/bug1665303.js b/js/src/jit-test/tests/warp/bug1665303.js index b2c26e01cf..3b0bc75079 100644 --- a/js/src/jit-test/tests/warp/bug1665303.js +++ b/js/src/jit-test/tests/warp/bug1665303.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this); --fast-warmup +// |jit-test| --fast-warmup // Prevent slowness with --ion-eager. setJitCompilerOption("ion.warmup.trigger", 100); diff --git a/js/src/jit-test/tests/warp/bug1667685.js b/js/src/jit-test/tests/warp/bug1667685.js index 2b9e392d24..99699b88d5 100644 --- a/js/src/jit-test/tests/warp/bug1667685.js +++ b/js/src/jit-test/tests/warp/bug1667685.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this); --fast-warmup +// |jit-test| --fast-warmup // Prevent slowness with --ion-eager. setJitCompilerOption("ion.warmup.trigger", 100); diff --git a/js/src/jit-test/tests/warp/bug1668197.js b/js/src/jit-test/tests/warp/bug1668197.js index 2dcd6cb376..24e1becec0 100644 --- a/js/src/jit-test/tests/warp/bug1668197.js +++ b/js/src/jit-test/tests/warp/bug1668197.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) function f(x, y) { return ~Math.hypot(x >>> 0, 2 - x >>> 0); } diff --git a/js/src/jit-test/tests/warp/bug1871089.js b/js/src/jit-test/tests/warp/bug1871089.js new file mode 100644 index 0000000000..8abddb6574 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1871089.js @@ -0,0 +1,13 @@ +// |jit-test| --fast-warmup +var i = 0; +function a() { + if (i++ > 50) { + return; + } + function b() { + new a("abcdefghijklm"); + } + [new b]; +} +gczeal(4); +a(); diff --git a/js/src/jit-test/tests/warp/trial-inline-gc-4.js b/js/src/jit-test/tests/warp/trial-inline-gc-4.js new file mode 100644 index 0000000000..2aecd4d371 --- /dev/null +++ b/js/src/jit-test/tests/warp/trial-inline-gc-4.js @@ -0,0 +1,42 @@ +// 1) Trial inline f => g1 => h. +// 2) Make f => g1 call site polymorphic by calling f => g2. +// This gets rid of the ICScript::inlinedChildren_ edge. +// 3) Restore f => g1. +// 4) Trigger a shrinking GC from f => g1 => h (h not trial-inlined; h preserves Baseline code) +// This purges h's inlined ICScript. +// 5) Call f => g1 => h (trial inlined). This must not use the discarded ICScript. +function h(i, x) { + if (i === 900) { + // Step 4. + gc(this, "shrinking"); + } + return x + 1; +} +function g2(i, x) { + if (i === 820) { + // Step 3. + callee = g1; + } + return h(i, x) + x; +} +function g1(i, x) { + if (i === 800) { + // Step 2. + callee = g2; + } + if (i === 900) { + // Step 4. + h(i, x); + } + return h(i, x) + x; +} + +var callee = g1; + +function f() { + for (var i = 0; i < 1000; i++) { + callee(i, i); + callee(i, "foo"); + } +} +f(); diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index 5a59330768..f7d60a56ce 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -119,7 +119,7 @@ wasmEval(moduleWithSections([tableSection0()])); wasmEval(moduleWithSections([memorySection(0)])); -function invalidMemorySection2() { +function memorySection2() { var body = []; body.push(...varU32(2)); // number of memories body.push(...varU32(0x0)); @@ -130,7 +130,11 @@ function invalidMemorySection2() { } wasmEval(moduleWithSections([memorySection0()])); -assertErrorMessage(() => wasmEval(moduleWithSections([invalidMemorySection2()])), CompileError, /number of memories must be at most one/); +if (wasmMultiMemoryEnabled()) { + wasmEval(moduleWithSections([memorySection2()])); +} else { + assertErrorMessage(() => wasmEval(moduleWithSections([memorySection2()])), CompileError, /number of memories must be at most one/); +} // Test early 'end' const bodyMismatch = /(function body length mismatch)|(operators remaining after end of function)/; diff --git a/js/src/jit-test/tests/wasm/bug1858423.js b/js/src/jit-test/tests/wasm/bug1858423.js index a90d308906..f6296c03d9 100644 --- a/js/src/jit-test/tests/wasm/bug1858423.js +++ b/js/src/jit-test/tests/wasm/bug1858423.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-gc; skip-if: !wasmCachingEnabled() || !wasmGcEnabled() +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmCachingEnabled() || !wasmGcEnabled() const code = wasmTextToBinary(`(module (type $t (struct (field i32) (field anyref))) diff --git a/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/directives.txt b/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/directives.txt index 46c9c504f4..7c458ede07 100644 --- a/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/directives.txt +++ b/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/directives.txt @@ -1 +1 @@ -|jit-test| --wasm-moz-intgemm; skip-if: (!getBuildConfiguration("x64") && !getBuildConfiguration("x86") && !getBuildConfiguration("arm64")) || getBuildConfiguration("simulator") || !wasmMozIntGemmEnabled() +|jit-test| --setpref=wasm_moz_intgemm=true; skip-if: (!getBuildConfiguration("x64") && !getBuildConfiguration("x86") && !getBuildConfiguration("arm64")) || getBuildConfiguration("simulator") || !wasmMozIntGemmEnabled() diff --git a/js/src/jit-test/tests/wasm/builtin-modules/js-string/basic.js b/js/src/jit-test/tests/wasm/builtin-modules/js-string/basic.js index dce6204fee..2c0ecb89c5 100644 --- a/js/src/jit-test/tests/wasm/builtin-modules/js-string/basic.js +++ b/js/src/jit-test/tests/wasm/builtin-modules/js-string/basic.js @@ -1,110 +1,126 @@ // |jit-test| skip-if: !wasmJSStringBuiltinsEnabled(); -let testModule = wasmTextToBinary(`(module +let testModule = `(module + (type $arrayMutI16 (array (mut i16))) + (func - (import "wasm:js-string" "fromWTF16Array") - (param anyref i32 i32) - (result externref) + (import "wasm:js-string" "test") + (param externref) + (result i32) + ) + (export "test" (func 0)) + + (func + (import "wasm:js-string" "cast") + (param externref) + (result (ref extern)) + ) + (export "cast" (func 1)) + + (func + (import "wasm:js-string" "fromCharCodeArray") + (param (ref null $arrayMutI16) i32 i32) + (result (ref extern)) ) - (export "fromWTF16Array" (func 0)) + (export "fromCharCodeArray" (func 2)) (func - (import "wasm:js-string" "toWTF16Array") - (param externref anyref i32) + (import "wasm:js-string" "intoCharCodeArray") + (param externref (ref null $arrayMutI16) i32) (result i32) ) - (export "toWTF16Array" (func 1)) + (export "intoCharCodeArray" (func 3)) (func (import "wasm:js-string" "fromCharCode") (param i32) (result externref) ) - (export "fromCharCode" (func 2)) + (export "fromCharCode" (func 4)) (func (import "wasm:js-string" "fromCodePoint") (param i32) (result externref) ) - (export "fromCodePoint" (func 3)) + (export "fromCodePoint" (func 5)) (func (import "wasm:js-string" "charCodeAt") (param externref i32) (result i32) ) - (export "charCodeAt" (func 4)) + (export "charCodeAt" (func 6)) (func (import "wasm:js-string" "codePointAt") (param externref i32) (result i32) ) - (export "codePointAt" (func 5)) + (export "codePointAt" (func 7)) (func (import "wasm:js-string" "length") (param externref) (result i32) ) - (export "length" (func 6)) + (export "length" (func 8)) (func - (import "wasm:js-string" "concatenate") + (import "wasm:js-string" "concat") (param externref externref) (result externref) ) - (export "concatenate" (func 7)) + (export "concat" (func 9)) (func (import "wasm:js-string" "substring") (param externref i32 i32) (result externref) ) - (export "substring" (func 8)) + (export "substring" (func 10)) (func (import "wasm:js-string" "equals") (param externref externref) (result i32) ) - (export "equals" (func 9)) + (export "equals" (func 11)) (func (import "wasm:js-string" "compare") (param externref externref) (result i32) ) - (export "compare" (func 10)) -)`); + (export "compare" (func 12)) +)`; let { - createArray, + createArrayMutI16, arrayLength, arraySet, arrayGet } = wasmEvalText(`(module - (type $i16Array (array (mut i16))) - (func (export "createArray") (param i32) (result anyref) + (type $arrayMutI16 (array (mut i16))) + (func (export "createArrayMutI16") (param i32) (result anyref) i32.const 0 local.get 0 - array.new $i16Array + array.new $arrayMutI16 ) (func (export "arrayLength") (param arrayref) (result i32) local.get 0 array.len ) - (func (export "arraySet") (param (ref $i16Array) i32 i32) + (func (export "arraySet") (param (ref $arrayMutI16) i32 i32) local.get 0 local.get 1 local.get 2 - array.set $i16Array + array.set $arrayMutI16 ) - (func (export "arrayGet") (param (ref $i16Array) i32) (result i32) + (func (export "arrayGet") (param (ref $arrayMutI16) i32) (result i32) local.get 0 local.get 1 - array.get_u $i16Array + array.get_u $arrayMutI16 ) )`).exports; @@ -114,9 +130,23 @@ function throwIfNotString(a) { } } let polyFillImports = { - fromWTF16Array: (array, arrayStart, arrayCount) => { - arrayStart |= 0; - arrayCount |= 0; + test: (string) => { + if (string === null || + typeof string !== "string") { + return 0; + } + return 1; + }, + cast: (string) => { + if (string === null || + typeof string !== "string") { + throw new WebAssembly.RuntimeError(); + } + return string; + }, + fromCharCodeArray: (array, arrayStart, arrayCount) => { + arrayStart >>>= 0; + arrayCount >>>= 0; let length = arrayLength(array); if (BigInt(arrayStart) + BigInt(arrayCount) > BigInt(length)) { throw new WebAssembly.RuntimeError(); @@ -127,8 +157,8 @@ let polyFillImports = { } return result; }, - toWTF16Array: (string, arr, arrayStart) => { - arrayStart |= 0; + intoCharCodeArray: (string, arr, arrayStart) => { + arrayStart >>>= 0; throwIfNotString(string); let arrLength = arrayLength(arr); let stringLength = string.length; @@ -141,22 +171,22 @@ let polyFillImports = { return stringLength; }, fromCharCode: (charCode) => { - charCode |= 0; + charCode >>>= 0; return String.fromCharCode(charCode); }, fromCodePoint: (codePoint) => { - codePoint |= 0; + codePoint >>>= 0; return String.fromCodePoint(codePoint); }, charCodeAt: (string, stringIndex) => { - stringIndex |= 0; + stringIndex >>>= 0; throwIfNotString(string); if (stringIndex >= string.length) throw new WebAssembly.RuntimeError(); return string.charCodeAt(stringIndex); }, codePointAt: (string, stringIndex) => { - stringIndex |= 0; + stringIndex >>>= 0; throwIfNotString(string); if (stringIndex >= string.length) throw new WebAssembly.RuntimeError(); @@ -166,14 +196,14 @@ let polyFillImports = { throwIfNotString(string); return string.length; }, - concatenate: (stringA, stringB) => { + concat: (stringA, stringB) => { throwIfNotString(stringA); throwIfNotString(stringB); return stringA + stringB; }, substring: (string, startIndex, endIndex) => { - startIndex |= 0; - endIndex |= 0; + startIndex >>>= 0; + endIndex >>>= 0; throwIfNotString(string); if (startIndex > string.length, endIndex > string.length, @@ -217,7 +247,6 @@ function assertSameBehavior(funcA, funcB, ...params) { if (errA || errB) { assertEq(errA === null, errB === null, errA ? errA.message : errB.message); assertEq(Object.getPrototypeOf(errA), Object.getPrototypeOf(errB)); - assertEq(errA.message, errB.message); } assertEq(resultA, resultB); @@ -227,13 +256,30 @@ function assertSameBehavior(funcA, funcB, ...params) { return resultA; } -let builtinExports = new WebAssembly.Instance(new WebAssembly.Module(testModule, {builtins: ["js-string"]}), {}).exports; -let polyfillExports = new WebAssembly.Instance(new WebAssembly.Module(testModule), { 'wasm:js-string': polyFillImports }).exports; +let builtinExports = wasmEvalText(testModule, {}, {builtins: ["js-string"]}).exports; +let polyfillExports = wasmEvalText(testModule, { 'wasm:js-string': polyFillImports }).exports; let testStrings = ["", "a", "1", "ab", "hello, world", "\n", "☺", "☺smiley", String.fromCodePoint(0x10000, 0x10001)]; let testCharCodes = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff]; let testCodePoints = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff, 0x10000, 0x10001]; +for (let a of WasmExternrefValues) { + assertSameBehavior( + builtinExports['test'], + polyfillExports['test'], + a + ); + try { + assertSameBehavior( + builtinExports['cast'], + polyfillExports['cast'], + a + ); + } catch (err) { + assertEq(err instanceof WebAssembly.RuntimeError, true); + } +} + for (let a of testCharCodes) { assertSameBehavior( builtinExports['fromCharCode'], @@ -272,16 +318,16 @@ for (let a of testStrings) { ); } - let array = createArray(length); + let arrayMutI16 = createArrayMutI16(length); assertSameBehavior( - builtinExports['toWTF16Array'], - polyfillExports['toWTF16Array'], - a, array, 0 + builtinExports['intoCharCodeArray'], + polyfillExports['intoCharCodeArray'], + a, arrayMutI16, 0 ); assertSameBehavior( - builtinExports['fromWTF16Array'], - polyfillExports['fromWTF16Array'], - array, 0, length + builtinExports['fromCharCodeArray'], + polyfillExports['fromCharCodeArray'], + arrayMutI16, 0, length ); for (let i = 0; i < length; i++) { @@ -298,8 +344,8 @@ for (let a of testStrings) { for (let a of testStrings) { for (let b of testStrings) { assertSameBehavior( - builtinExports['concatenate'], - polyfillExports['concatenate'], + builtinExports['concat'], + polyfillExports['concat'], a, b ); assertSameBehavior( @@ -314,3 +360,13 @@ for (let a of testStrings) { ); } } + +// fromCharCodeArray length is an unsigned integer +{ + let arrayMutI16 = createArrayMutI16(1); + assertErrorMessage(() => assertSameBehavior( + builtinExports['fromCharCodeArray'], + polyfillExports['fromCharCodeArray'], + arrayMutI16, 1, -1 + ), WebAssembly.RuntimeError, /./); +} diff --git a/js/src/jit-test/tests/wasm/builtin-modules/js-string/directives.txt b/js/src/jit-test/tests/wasm/builtin-modules/js-string/directives.txt index 8c5e6882eb..408d4dd01a 100644 --- a/js/src/jit-test/tests/wasm/builtin-modules/js-string/directives.txt +++ b/js/src/jit-test/tests/wasm/builtin-modules/js-string/directives.txt @@ -1 +1 @@ -|jit-test| --wasm-gc; --wasm-js-string-builtins; test-also=--wasm-compiler=optimizing; include:wasm.js +|jit-test| --setpref=wasm_js_string_builtins=true; test-also=--wasm-compiler=optimizing; include:wasm.js diff --git a/js/src/jit-test/tests/wasm/builtin-modules/oom-test.js b/js/src/jit-test/tests/wasm/builtin-modules/oom-test.js index 32aa1b2515..aaf8d1e5c3 100644 --- a/js/src/jit-test/tests/wasm/builtin-modules/oom-test.js +++ b/js/src/jit-test/tests/wasm/builtin-modules/oom-test.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { const module = wasmBuiltinI8VecMul(); WebAssembly.Module.imports(module); diff --git a/js/src/jit-test/tests/wasm/directiveless/bug1877358.js b/js/src/jit-test/tests/wasm/directiveless/bug1877358.js index 1f8fad0e43..10cb54398a 100644 --- a/js/src/jit-test/tests/wasm/directiveless/bug1877358.js +++ b/js/src/jit-test/tests/wasm/directiveless/bug1877358.js @@ -1,4 +1,4 @@ -// |jit-test| --no-wasm-exceptions; include:wasm.js +// |jit-test| -P wasm_exceptions=false; include:wasm.js let {test} = wasmEvalText(`(module (func $m (import "" "m")) diff --git a/js/src/jit-test/tests/wasm/directives.txt b/js/src/jit-test/tests/wasm/directives.txt index 15c7511171..4a2413125a 100644 --- a/js/src/jit-test/tests/wasm/directives.txt +++ b/js/src/jit-test/tests/wasm/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); test-also=--wasm-test-serialization; test-also=--wasm-compiler=optimizing --no-avx; skip-variant-if: --wasm-compiler=optimizing --no-avx, !getBuildConfiguration("x86") && !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:wasm.js +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); test-also=--setpref=wasm_test_serialization=true; test-also=--wasm-compiler=optimizing --no-avx; skip-variant-if: --wasm-compiler=optimizing --no-avx, !getBuildConfiguration("x86") && !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:wasm.js diff --git a/js/src/jit-test/tests/wasm/exceptions/bug-1751699.js b/js/src/jit-test/tests/wasm/exceptions/bug-1751699.js index 38268e7905..c60a94a8ed 100644 --- a/js/src/jit-test/tests/wasm/exceptions/bug-1751699.js +++ b/js/src/jit-test/tests/wasm/exceptions/bug-1751699.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { wasmEvalText(` (import "" "" (func $d)) diff --git a/js/src/jit-test/tests/wasm/exceptions/bug-1788213.js b/js/src/jit-test/tests/wasm/exceptions/bug-1788213.js index 4d0b4abcdc..7c0710c117 100644 --- a/js/src/jit-test/tests/wasm/exceptions/bug-1788213.js +++ b/js/src/jit-test/tests/wasm/exceptions/bug-1788213.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { wasmEvalText(`(import "" "" (tag $undef)) (func throw 0) (func (try (do)))`); }); diff --git a/js/src/jit-test/tests/wasm/exceptions/bug-1791361.js b/js/src/jit-test/tests/wasm/exceptions/bug-1791361.js index 9c4432de91..0e09a8a746 100644 --- a/js/src/jit-test/tests/wasm/exceptions/bug-1791361.js +++ b/js/src/jit-test/tests/wasm/exceptions/bug-1791361.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { wasmEvalText(` (tag $d) diff --git a/js/src/jit-test/tests/wasm/exceptions/directives.txt b/js/src/jit-test/tests/wasm/exceptions/directives.txt index 84f10ac2b3..c9c2613039 100644 --- a/js/src/jit-test/tests/wasm/exceptions/directives.txt +++ b/js/src/jit-test/tests/wasm/exceptions/directives.txt @@ -1 +1 @@ -|jit-test| --wasm-exceptions; test-also=--wasm-exnref; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmExceptionsEnabled() +|jit-test| test-also=--setpref=wasm_exnref=true; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; include:wasm.js diff --git a/js/src/jit-test/tests/wasm/exceptions/oom-construct-message.js b/js/src/jit-test/tests/wasm/exceptions/oom-construct-message.js index a7820876bc..8eb515ea9b 100644 --- a/js/src/jit-test/tests/wasm/exceptions/oom-construct-message.js +++ b/js/src/jit-test/tests/wasm/exceptions/oom-construct-message.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - const tag = new WebAssembly.Tag({ parameters: ["i32"] }); oomTest(() => { new WebAssembly.Exception(tag, []); diff --git a/js/src/jit-test/tests/wasm/exceptions/oom-create-exception-data.js b/js/src/jit-test/tests/wasm/exceptions/oom-create-exception-data.js index 0aa1e75698..51d4558da8 100644 --- a/js/src/jit-test/tests/wasm/exceptions/oom-create-exception-data.js +++ b/js/src/jit-test/tests/wasm/exceptions/oom-create-exception-data.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - const tag = new WebAssembly.Tag({ parameters: ["i32", "i32", "i32", "i32"] }); const params = [0, 0, 0, 0]; oomTest(() => { diff --git a/js/src/jit-test/tests/wasm/exceptions/unreachable.js b/js/src/jit-test/tests/wasm/exceptions/unreachable.js index f0091bc617..2dc2c8b3b1 100644 --- a/js/src/jit-test/tests/wasm/exceptions/unreachable.js +++ b/js/src/jit-test/tests/wasm/exceptions/unreachable.js @@ -1,4 +1,4 @@ -// |jit-test| test-also=--wasm-function-references --wasm-gc --wasm-compiler=optimizing; test-also=--wasm-function-references --wasm-gc --wasm-compiler=baseline; +// |jit-test| test-also=--setpref=wasm_gc=true --wasm-compiler=optimizing; test-also=--setpref=wasm_gc=true --wasm-compiler=baseline; wasmFailValidateText(`(module (tag) diff --git a/js/src/jit-test/tests/wasm/exnref/bug1883865.js b/js/src/jit-test/tests/wasm/exnref/bug1883865.js new file mode 100644 index 0000000000..d6418d1285 --- /dev/null +++ b/js/src/jit-test/tests/wasm/exnref/bug1883865.js @@ -0,0 +1,25 @@ +// Checks proper padding for nested tryNotes. + +new WebAssembly.Module(wasmTextToBinary(`(module + (func + try_table $l3 + try_table $l4 + try_table $l5 + end + end + end + ) +)`)); + +new WebAssembly.Module(wasmTextToBinary(`(module + (func + try_table $l3 + try_table $l4 + try_table $l5 + end + try_table $l5a + end + end + end + ) +)`)); diff --git a/js/src/jit-test/tests/wasm/exnref/directives.txt b/js/src/jit-test/tests/wasm/exnref/directives.txt index bc17009ea8..b993dbd1d5 100644 --- a/js/src/jit-test/tests/wasm/exnref/directives.txt +++ b/js/src/jit-test/tests/wasm/exnref/directives.txt @@ -1 +1 @@ -|jit-test| --wasm-exnref; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmExnRefEnabled() +|jit-test| --setpref=wasm_exnref=true; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmExnRefEnabled() diff --git a/js/src/jit-test/tests/wasm/extended-const/basic.js b/js/src/jit-test/tests/wasm/extended-const/basic.js index bf0ce460d5..6b71385cb6 100644 --- a/js/src/jit-test/tests/wasm/extended-const/basic.js +++ b/js/src/jit-test/tests/wasm/extended-const/basic.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !wasmExtendedConstEnabled() - function testPrivateGlobal(valtype, expr, result) { // Immutable private globals have a single cell for wasm. let { get } = wasmEvalText(`(module diff --git a/js/src/jit-test/tests/wasm/extended-const/directives.txt b/js/src/jit-test/tests/wasm/extended-const/directives.txt index 0d16de6524..c4b5e420f1 100644 --- a/js/src/jit-test/tests/wasm/extended-const/directives.txt +++ b/js/src/jit-test/tests/wasm/extended-const/directives.txt @@ -1 +1 @@ -|jit-test| --wasm-extended-const; test-also=--wasm-compiler=optimizing; test-also=--wasm-test-serialization; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; include:wasm.js +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--setpref=wasm_test_serialization=true; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; include:wasm.js diff --git a/js/src/jit-test/tests/wasm/extended-const/disabled.js b/js/src/jit-test/tests/wasm/extended-const/disabled.js deleted file mode 100644 index 01e64f6c44..0000000000 --- a/js/src/jit-test/tests/wasm/extended-const/disabled.js +++ /dev/null @@ -1,22 +0,0 @@ -// |jit-test| skip-if: wasmExtendedConstEnabled() - -const { CompileError, validate } = WebAssembly; - -const DISABLED = /extended constant expressions not enabled|unrecognized opcode/; - -let tests = [ - "(module (global i32 i32.const 0 i32.const 0 i32.add))", - "(module (global i32 i32.const 0 i32.const 0 i32.sub))", - "(module (global i32 i32.const 0 i32.const 0 i32.mul))", - "(module (global i64 i64.const 0 i64.const 0 i64.add))", - "(module (global i64 i64.const 0 i64.const 0 i64.sub))", - "(module (global i64 i64.const 0 i64.const 0 i64.mul))", -]; - -// Test that use of extended constants fails when disabled. - -for (let src of tests) { - let bin = wasmTextToBinary(src); - assertEq(validate(bin), false); - wasmCompilationShouldFail(bin, DISABLED); -} diff --git a/js/src/jit-test/tests/wasm/extended-const/pathological.js b/js/src/jit-test/tests/wasm/extended-const/pathological.js index e3695f3625..8a87b25be4 100644 --- a/js/src/jit-test/tests/wasm/extended-const/pathological.js +++ b/js/src/jit-test/tests/wasm/extended-const/pathological.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !wasmExtendedConstEnabled() - // Let's calculate zero in some elaborate ways. function testFancyZeroOffset(fancyZero, memType = 'i32') { try { diff --git a/js/src/jit-test/tests/wasm/features.js b/js/src/jit-test/tests/wasm/features.js index 904dd03e76..3292334ee6 100644 --- a/js/src/jit-test/tests/wasm/features.js +++ b/js/src/jit-test/tests/wasm/features.js @@ -1,5 +1,3 @@ -// |jit-test| test-also=--wasm-extended-const; test-also=--wasm-exceptions; - // Test that if a feature is 'experimental' then we must be in a nightly build, // and if a feature is 'released' then it must be enabled on release and beta. // @@ -68,26 +66,16 @@ for (let [name, enabled, test] of releasedFeaturesMaybeDisabledAnyway) { let releasedFeatures = [ ['threads', wasmThreadsEnabled(), `(module (memory 1 1 shared))`], [ - 'exceptions', - wasmExceptionsEnabled(), - `(module (type (func)) (tag (type 0)))` - ], - [ - 'extended-const', - wasmExtendedConstEnabled(), - `(module - (global i32 - i32.const 0 - i32.const 0 - i32.add - ) - )` - ], - [ 'tail-calls', wasmTailCallsEnabled(), `(module (func) (func (return_call 0)))` ], + ['gc', wasmGcEnabled(), `(module (type (struct)))`], + [ + 'multi-memory', + wasmMultiMemoryEnabled(), + `(module (memory 0) (memory 0))`, + ], ]; for (let [name, enabled, test] of releasedFeatures) { diff --git a/js/src/jit-test/tests/wasm/function-references/as-non-null.js b/js/src/jit-test/tests/wasm/function-references/as-non-null.js index 1280c8ee06..6fd02d61ac 100644 --- a/js/src/jit-test/tests/wasm/function-references/as-non-null.js +++ b/js/src/jit-test/tests/wasm/function-references/as-non-null.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() let {checkNonNull} = wasmEvalText(`(module (func (export "checkNonNull") (param externref) (result (ref extern)) diff --git a/js/src/jit-test/tests/wasm/function-references/binary.js b/js/src/jit-test/tests/wasm/function-references/binary.js index 91cf807dc4..0e64f0723e 100644 --- a/js/src/jit-test/tests/wasm/function-references/binary.js +++ b/js/src/jit-test/tests/wasm/function-references/binary.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() load(libdir + "wasm-binary.js"); diff --git a/js/src/jit-test/tests/wasm/function-references/br-non-null.js b/js/src/jit-test/tests/wasm/function-references/br-non-null.js index 371cdaa40a..6d359105fb 100644 --- a/js/src/jit-test/tests/wasm/function-references/br-non-null.js +++ b/js/src/jit-test/tests/wasm/function-references/br-non-null.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() // br_on_non_null from constant wasmValidateText(`(module diff --git a/js/src/jit-test/tests/wasm/function-references/br-null.js b/js/src/jit-test/tests/wasm/function-references/br-null.js index 26d1013de2..08728a3644 100644 --- a/js/src/jit-test/tests/wasm/function-references/br-null.js +++ b/js/src/jit-test/tests/wasm/function-references/br-null.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() // br_on_null from constant wasmValidateText(`(module diff --git a/js/src/jit-test/tests/wasm/function-references/call_ref.js b/js/src/jit-test/tests/wasm/function-references/call_ref.js index 514c3145d6..50bdfb6441 100644 --- a/js/src/jit-test/tests/wasm/function-references/call_ref.js +++ b/js/src/jit-test/tests/wasm/function-references/call_ref.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() let { plusOne } = wasmEvalText(`(module (; forward declaration so that ref.func works ;) diff --git a/js/src/jit-test/tests/wasm/function-references/directives.txt b/js/src/jit-test/tests/wasm/function-references/directives.txt index f7d2135421..293724e57a 100644 --- a/js/src/jit-test/tests/wasm/function-references/directives.txt +++ b/js/src/jit-test/tests/wasm/function-references/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing --wasm-function-references; test-also=--wasm-compiler=baseline --wasm-function-references; include:wasm.js +|jit-test| test-also=--setpref=wasm_gc=true; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; include:wasm.js diff --git a/js/src/jit-test/tests/wasm/function-references/disabled.js b/js/src/jit-test/tests/wasm/function-references/disabled.js index 70a66a937d..6e67d2ce7f 100644 --- a/js/src/jit-test/tests/wasm/function-references/disabled.js +++ b/js/src/jit-test/tests/wasm/function-references/disabled.js @@ -1,8 +1,8 @@ -// |jit-test| skip-if: wasmFunctionReferencesEnabled() +// |jit-test| skip-if: wasmGcEnabled() const { CompileError, validate } = WebAssembly; -const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|bad type|\(ref T\) types not enabled/; +const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|bad type|gc not enabled/; let simpleTests = [ `(module (func (param (ref 0)) (unreachable)))`, diff --git a/js/src/jit-test/tests/wasm/function-references/nnl-test.js b/js/src/jit-test/tests/wasm/function-references/nnl-test.js index 9436b970d5..df79a748a9 100644 --- a/js/src/jit-test/tests/wasm/function-references/nnl-test.js +++ b/js/src/jit-test/tests/wasm/function-references/nnl-test.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() // Generates combinations of different block types and operations for // non-defaultable locals (local.set / .tee / .get). diff --git a/js/src/jit-test/tests/wasm/function-references/non-nullable-table.js b/js/src/jit-test/tests/wasm/function-references/non-nullable-table.js index 97ab04713c..4efdfcd642 100644 --- a/js/src/jit-test/tests/wasm/function-references/non-nullable-table.js +++ b/js/src/jit-test/tests/wasm/function-references/non-nullable-table.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() // non-null table initialization var { get1, get2, get3, get4 } = wasmEvalText(`(module @@ -40,25 +40,33 @@ for (let i of [ )`, /(type mismatch|table with non-nullable references requires initializer)/); } -var t1 = new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }}, sampleWasmFunction); +let values = "10 funcref (ref.func $dummy)"; +let t1 = new wasmEvalText(`(module (func $dummy) (table (export "t1") ${values}))`).exports.t1; assertEq(t1.get(2) != null, true); -assertThrows(() => { - new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }}); -}); -assertThrows(() => { - new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }}, null); -}); -var t2 = new WebAssembly.Table({initial: 6, maximum: 20, element: {ref: 'extern', nullable: false }}, {foo: "bar"}); -assertEq(t2.get(1).foo, "bar"); +wasmFailValidateText(`(module + (table $t 10 (ref func)) +)`, /table with non-nullable references requires initializer/); + +wasmFailValidateText(` +(module + (func $dummy) + (table (export "t") 10 funcref (ref.null none)) +)`, /type mismatch/); + +const foo = "bar"; +const { t2, get } = wasmEvalText(` +(module + (global (import "" "foo") externref) + (table (export "t2") 6 20 externref (global.get 0)) +)`, { "": { "foo": foo } }).exports; + +assertEq(t2.get(5), "bar"); assertThrows(() => { t2.get(7) }); -assertThrows(() => { t2.grow(9, null) }); +assertThrows(() => { t2.grow(30, null) }); t2.grow(8, {t: "test"}); -assertEq(t2.get(3).foo, "bar"); +assertEq(t2.get(3), "bar"); assertEq(t2.get(7).t, "test"); -assertThrows(() => { - new WebAssembly.Table({initial: 10, element: {ref: 'extern', nullable: false }}, null); -}); // Fail because tables come before globals in the binary format, so tables // cannot refer to globals. diff --git a/js/src/jit-test/tests/wasm/function-references/non-nullable.js b/js/src/jit-test/tests/wasm/function-references/non-nullable.js index afe1d3cb43..653bbe7ab6 100644 --- a/js/src/jit-test/tests/wasm/function-references/non-nullable.js +++ b/js/src/jit-test/tests/wasm/function-references/non-nullable.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() // non-null values are subtype of null values wasmValidateText(`(module diff --git a/js/src/jit-test/tests/wasm/function-references/reftype-parse.js b/js/src/jit-test/tests/wasm/function-references/reftype-parse.js index 643f753ec8..f4cca7cb74 100644 --- a/js/src/jit-test/tests/wasm/function-references/reftype-parse.js +++ b/js/src/jit-test/tests/wasm/function-references/reftype-parse.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmFunctionReferencesEnabled() +// |jit-test| skip-if: !wasmGcEnabled() // RefType/ValueType as a simple string const t01 = new WebAssembly.Table({element: 'funcref', initial: 3}); @@ -16,36 +16,6 @@ assertErrorMessage( assertErrorMessage( () => new WebAssembly.Table({element: true, initial: 1}), TypeError, /bad value type/); - -// RefType/ValueType can be specified as an {ref: 'func', ...} object -const t11 = new WebAssembly.Table({element: {ref: 'func', nullable: true}, initial: 3}); -const t12 = new WebAssembly.Table({element: {ref: 'extern', nullable: true}, initial: 3}); -const t13 = new WebAssembly.Table({element: {ref: 'extern', nullable: false}, initial: 3}, {}); - -assertErrorMessage( - () => new WebAssembly.Table({element: {ref: 'func', nullable: false}, initial: 1}, null), - TypeError, /cannot pass null to non-nullable WebAssembly reference/); -assertErrorMessage( - () => new WebAssembly.Table({element: {ref: 'extern', nullable: false}, initial: 1}, null), - TypeError, /cannot pass null to non-nullable WebAssembly reference/); - -assertErrorMessage( - () => new WebAssembly.Table({element: {ref: 'bar', nullable: true}, initial: 1}), - TypeError, /bad value type/); - -const g11 = new WebAssembly.Global({value: {ref: 'func', nullable: true}, mutable: true}); -const g12 = new WebAssembly.Global({value: {ref: 'extern', nullable: true}, mutable: true}); -const g13 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, {}); -const g14 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}); -const g15 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, void 0); - -assertErrorMessage( - () => new WebAssembly.Global({value: {ref: 'func', nullable: false}, mutable: true}), - TypeError, /cannot pass null to non-nullable WebAssembly reference/); -assertErrorMessage( - () => new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, null), - TypeError, /cannot pass null to non-nullable WebAssembly reference/); - assertErrorMessage( () => new WebAssembly.Global({value: {ref: 'bar', nullable: true}, mutable: true}), TypeError, /bad value type/); diff --git a/js/src/jit-test/tests/wasm/gc/arrays.js b/js/src/jit-test/tests/wasm/gc/arrays.js index b3f03151bb..cb61bb1b07 100644 --- a/js/src/jit-test/tests/wasm/gc/arrays.js +++ b/js/src/jit-test/tests/wasm/gc/arrays.js @@ -604,6 +604,51 @@ assertErrorMessage(() => wasmEvalText(`(module },WebAssembly.RuntimeError, /index out of bounds/); } +// run: zero-length copies are allowed +{ + let { newData } = wasmEvalText(`(module + (type $a (array i8)) + (data $d "1337") + (func (export "newData") (result eqref) + (; offset=0 into data ;) i32.const 0 + (; size=0 into data ;) i32.const 0 + array.new_data $a $d + ) + )`).exports; + let arr = newData(); + assertEq(wasmGcArrayLength(arr), 0); +} + +// run: a zero-length copy from the end is allowed +{ + let { newData } = wasmEvalText(`(module + (type $a (array i8)) + (data $d "1337") + (func (export "newData") (result eqref) + (; offset=4 into data ;) i32.const 4 + (; size=0 into data ;) i32.const 0 + array.new_data $a $d + ) + )`).exports; + let arr = newData(); + assertEq(wasmGcArrayLength(arr), 0); +} + +// run: even empty data segments are allowed +{ + let { newData } = wasmEvalText(`(module + (type $a (array i8)) + (data $d "") + (func (export "newData") (result eqref) + (; offset=0 into data ;) i32.const 0 + (; size=0 into data ;) i32.const 0 + array.new_data $a $d + ) + )`).exports; + let arr = newData(); + assertEq(wasmGcArrayLength(arr), 0); +} + // run: resulting array is as expected { let { newData } = wasmEvalText(`(module @@ -802,6 +847,59 @@ assertErrorMessage(() => wasmEvalText(`(module },WebAssembly.RuntimeError, /index out of bounds/); } +// run: zero-length copies are allowed +{ + let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module + (type $a (array funcref)) + (elem $e func $f1 $f2 $f3 $f4) + (func $f1 (export "f1")) + (func $f2 (export "f2")) + (func $f3 (export "f3")) + (func $f4 (export "f4")) + (func (export "newElem") (result eqref) + (; offset=0 into elem ;) i32.const 0 + (; size=0 into elem ;) i32.const 0 + array.new_elem $a $e + ) + )`).exports; + let arr = newElem(); + assertEq(wasmGcArrayLength(arr), 0); +} + +// run: a zero-length copy from the end is allowed +{ + let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module + (type $a (array funcref)) + (elem $e func $f1 $f2 $f3 $f4) + (func $f1 (export "f1")) + (func $f2 (export "f2")) + (func $f3 (export "f3")) + (func $f4 (export "f4")) + (func (export "newElem") (result eqref) + (; offset=4 into elem ;) i32.const 4 + (; size=0 into elem ;) i32.const 0 + array.new_elem $a $e + ) + )`).exports; + let arr = newElem(); + assertEq(wasmGcArrayLength(arr), 0); +} + +// run: even empty elem segments are allowed +{ + let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module + (type $a (array funcref)) + (elem $e func) + (func (export "newElem") (result eqref) + (; offset=0 into elem ;) i32.const 0 + (; size=0 into elem ;) i32.const 0 + array.new_elem $a $e + ) + )`).exports; + let arr = newElem(); + assertEq(wasmGcArrayLength(arr), 0); +} + // run: resulting array is as expected { let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module @@ -1130,6 +1228,29 @@ assertErrorMessage(() => wasmEvalText(`(module },WebAssembly.RuntimeError, /index out of bounds/); } +// run: zeroes everywhere +{ + let { initData } = wasmEvalText(`(module + (type $a (array (mut i8))) + (data $d "") + (func (export "initData") (result eqref) + (local $arr (ref $a)) + (local.set $arr (array.new_default $a (i32.const 0))) + + (; array to init ;) local.get $arr + (; offset=0 into array ;) i32.const 0 + (; offset=0 into data ;) i32.const 0 + (; size=0 elements ;) i32.const 0 + array.init_data $a $d + + local.get $arr + ) + (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 + )`).exports; + let arr = initData(); + assertEq(wasmGcArrayLength(arr), 0); +} + // run: resulting array is as expected { let { initData } = wasmEvalText(`(module @@ -1488,6 +1609,28 @@ assertErrorMessage(() => wasmEvalText(`(module },WebAssembly.RuntimeError, /index out of bounds/); } +// run: zeroes everywhere +{ + let { initElem, f1, f2, f3, f4 } = wasmEvalText(`(module + (type $a (array (mut funcref))) + (elem $e func) + (func (export "initElem") (result eqref) + (local $arr (ref $a)) + (local.set $arr (array.new_default $a (i32.const 0))) + + (; array to init ;) local.get $arr + (; offset=0 into array ;) i32.const 0 + (; offset=0 into elem ;) i32.const 0 + (; size=0 into elem ;) i32.const 0 + array.init_elem $a $e + + local.get $arr + ) + )`).exports; + let arr = initElem(); + assertEq(wasmGcArrayLength(arr), 0); +} + // run: resulting array is as expected { let { initElem, f1, f2, f3, f4 } = wasmEvalText(`(module diff --git a/js/src/jit-test/tests/wasm/gc/binary.js b/js/src/jit-test/tests/wasm/gc/binary.js index 1ef4a586a8..e16af2bfbf 100644 --- a/js/src/jit-test/tests/wasm/gc/binary.js +++ b/js/src/jit-test/tests/wasm/gc/binary.js @@ -2,14 +2,16 @@ load(libdir + "wasm-binary.js"); -const v2vSig = {args:[], ret:VoidCode}; -const v2vSigSection = sigSection([v2vSig]); - function checkInvalid(body, errorMessage) { assertErrorMessage(() => new WebAssembly.Module( - moduleWithSections([v2vSigSection, declSection([0]), bodySection([body])])), - WebAssembly.CompileError, - errorMessage); + moduleWithSections([ + typeSection([ + { kind: FuncCode, args: [], ret: [] }, + ]), + declSection([0]), + bodySection([body]), + ]) + ), WebAssembly.CompileError, errorMessage); } const invalidRefBlockType = funcBody({locals:[], body:[ @@ -23,7 +25,7 @@ checkInvalid(invalidRefBlockType, /heap type/); const invalidTooBigRefType = funcBody({locals:[], body:[ BlockCode, OptRefCode, - varU32(1000000), + ...varU32(1000000), EndCode, ]}); checkInvalid(invalidTooBigRefType, /heap type/); diff --git a/js/src/jit-test/tests/wasm/gc/bug-1843295.js b/js/src/jit-test/tests/wasm/gc/bug-1843295.js index 19a32263f6..765a9000a1 100644 --- a/js/src/jit-test/tests/wasm/gc/bug-1843295.js +++ b/js/src/jit-test/tests/wasm/gc/bug-1843295.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmGcEnabled(); --wasm-test-serialization +// |jit-test| skip-if: !wasmGcEnabled(); --setpref=wasm_test_serialization=true wasmEvalText(`(module (type (sub (array (mut i32)))) diff --git a/js/src/jit-test/tests/wasm/gc/bug-1845436.js b/js/src/jit-test/tests/wasm/gc/bug-1845436.js index a79c22d9a1..6ea8070f4d 100644 --- a/js/src/jit-test/tests/wasm/gc/bug-1845436.js +++ b/js/src/jit-test/tests/wasm/gc/bug-1845436.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmGcEnabled(); --wasm-test-serialization +// |jit-test| skip-if: !wasmGcEnabled(); --setpref=wasm_test_serialization=true // Test that serialization doesn't create a forward reference to the third // struct when serializing the reference to the first struct, which is diff --git a/js/src/jit-test/tests/wasm/gc/bug-1854007.js b/js/src/jit-test/tests/wasm/gc/bug-1854007.js index c9d6b25369..d3e5afa9cd 100644 --- a/js/src/jit-test/tests/wasm/gc/bug-1854007.js +++ b/js/src/jit-test/tests/wasm/gc/bug-1854007.js @@ -1,4 +1,4 @@ -// |jit-test| test-also=--wasm-test-serialization; skip-if: !wasmGcEnabled() +// |jit-test| test-also=--setpref=wasm_test_serialization=true; skip-if: !wasmGcEnabled() let {run} = wasmEvalText(`(module (rec (type $$t1 (func (result (ref null $$t1))))) diff --git a/js/src/jit-test/tests/wasm/gc/bug-1879096.js b/js/src/jit-test/tests/wasm/gc/bug-1879096.js new file mode 100644 index 0000000000..e71d2bac27 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/bug-1879096.js @@ -0,0 +1,65 @@ +// |jit-test| test-also=--setpref=wasm_test_serialization; skip-if: !wasmGcEnabled() + +// Conditional branch instructions need to rewrite their stack types according +// to the destination label types. This loses information but is mandated by +// the spec. + +// br_if +wasmFailValidateText(`(module + (func (result anyref) + ref.null array ;; stack: [arrayref] + ref.null struct ;; stack: [arrayref structref] + i32.const 0 ;; stack: [arrayref structref i32] + br_if 0 ;; stack: [arrayref anyref] + ref.eq ;; should fail (anyref is not eq) + unreachable + ) +)`, /type mismatch: expression has type anyref but expected eqref/); + +// br_on_null +wasmFailValidateText(`(module + (func (param externref) (result anyref) + ref.null array ;; stack: [arrayref] + local.get 0 ;; stack: [arrayref externref] + br_on_null 0 ;; stack: [anyref (ref extern)] + drop ;; stack: [anyref] + array.len ;; should fail + unreachable + ) +)`, /type mismatch: expression has type anyref but expected arrayref/); + +// br_on_non_null +wasmFailValidateText(`(module + (func (param externref) (result anyref (ref extern)) + ref.null array ;; stack: [arrayref] + ref.null struct ;; stack: [arrayref structref] + local.get 0 ;; stack: [arrayref structref externref] + br_on_non_null 0 ;; stack: [arrayref anyref] + ref.eq ;; should fail (anyref is not eq) + unreachable + ) +)`, /type mismatch: expression has type anyref but expected eqref/); + +// br_on_cast +wasmFailValidateText(`(module + (type $s (struct)) + (func (result anyref (ref $s)) + ref.null array ;; stack: [arrayref] + ref.null struct ;; stack: [arrayref structref] + br_on_cast 0 structref (ref $s) ;; stack: [anyref structref] + ref.eq ;; should fail (anyref is not eq) + unreachable + ) +)`, /type mismatch: expression has type anyref but expected eqref/); + +// br_on_cast_fail +wasmFailValidateText(`(module + (type $s (struct)) + (func (result anyref anyref) + ref.null array ;; stack: [arrayref] + ref.null struct ;; stack: [arrayref structref] + br_on_cast_fail 0 structref (ref $s) ;; stack: [anyref (ref $s)] + ref.eq ;; should fail (anyref is not eq) + unreachable + ) +)`, /type mismatch: expression has type anyref but expected eqref/); diff --git a/js/src/jit-test/tests/wasm/gc/call-indirect-subtyping.js b/js/src/jit-test/tests/wasm/gc/call-indirect-subtyping.js index 4301621a8c..d83f2ed624 100644 --- a/js/src/jit-test/tests/wasm/gc/call-indirect-subtyping.js +++ b/js/src/jit-test/tests/wasm/gc/call-indirect-subtyping.js @@ -1,4 +1,4 @@ -// |jit-test| test-also=--wasm-tail-calls; skip-if: !wasmGcEnabled() +// |jit-test| test-also=--setpref=wasm_tail_calls=true; skip-if: !wasmGcEnabled() // Test that call_indirect will respect subtyping by defining a bunch of types // and checking every combination of (expected, actual) type. diff --git a/js/src/jit-test/tests/wasm/gc/directives.txt b/js/src/jit-test/tests/wasm/gc/directives.txt index e6d978cc44..293724e57a 100644 --- a/js/src/jit-test/tests/wasm/gc/directives.txt +++ b/js/src/jit-test/tests/wasm/gc/directives.txt @@ -1 +1 @@ -|jit-test| --wasm-gc; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; include:wasm.js +|jit-test| test-also=--setpref=wasm_gc=true; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; include:wasm.js diff --git a/js/src/jit-test/tests/wasm/gc/disabled.js b/js/src/jit-test/tests/wasm/gc/disabled.js index 791c6ff25e..206d32b1c4 100644 --- a/js/src/jit-test/tests/wasm/gc/disabled.js +++ b/js/src/jit-test/tests/wasm/gc/disabled.js @@ -2,7 +2,7 @@ const { CompileError, validate } = WebAssembly; -const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|(Structure|reference|gc) types not enabled|invalid heap type|invalid inline block type|bad type|\(ref T\) types not enabled|Invalid type|invalid function type/; +const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|gc not enabled|invalid heap type|invalid inline block type|bad type|Invalid type|invalid function type/; let simpleTests = [ "(module (func (drop (ref.null eq))))", diff --git a/js/src/jit-test/tests/wasm/gc/ion-and-baseline.js b/js/src/jit-test/tests/wasm/gc/ion-and-baseline.js index 5a4951c585..2c67bbce8b 100644 --- a/js/src/jit-test/tests/wasm/gc/ion-and-baseline.js +++ b/js/src/jit-test/tests/wasm/gc/ion-and-baseline.js @@ -10,7 +10,7 @@ // actually testing something here. // // Some logging with printf confirms that refmod is baseline-compiled and -// nonrefmod is ion-compiled at present, with --wasm-gc enabled. +// nonrefmod is ion-compiled at present, with --setpref=wasm_gc=true enabled. var refmod = new WebAssembly.Module(wasmTextToBinary( `(module diff --git a/js/src/jit-test/tests/wasm/gc/limits.js b/js/src/jit-test/tests/wasm/gc/limits.js deleted file mode 100644 index e6f21b5d6b..0000000000 --- a/js/src/jit-test/tests/wasm/gc/limits.js +++ /dev/null @@ -1,69 +0,0 @@ -// |jit-test| skip-if: !wasmGcEnabled() || getBuildConfiguration("tsan") - -// This test has a timeout on TSAN configurations due to the large -// allocations. - -// Limit of 1 million recursion groups -wasmValidateText(`(module - ${`(rec (type (func)))`.repeat(1_000_000)} - )`); -wasmFailValidateText(`(module - ${`(rec (type (func)))`.repeat(1_000_001)} - )`, /too many/); - -// Limit of 1 million types (across all recursion groups) -wasmValidateText(`(module - (rec ${`(type (func))`.repeat(1_000_000)}) - )`); -wasmValidateText(`(module - (rec ${`(type (func))`.repeat(500_000)}) - (rec ${`(type (func))`.repeat(500_000)}) - )`); -wasmFailValidateText(`(module - (rec ${`(type (func))`.repeat(1_000_001)}) - )`, /too many/); -wasmFailValidateText(`(module - (rec ${`(type (func))`.repeat(500_000)}) - (rec ${`(type (func))`.repeat(500_001)}) - )`, /too many/); - -// Limit of subtyping hierarchy 63 deep -function testSubtypingModule(depth) { - let types = '(type (sub (func)))'; - for (let i = 1; i <= depth; i++) { - types += `(type (sub ${i - 1} (func)))`; - } - return `(module - ${types} - )`; -} -wasmValidateText(testSubtypingModule(63)); -wasmFailValidateText(testSubtypingModule(64), /too deep/); - -// Limit of 10_000 struct fields -wasmFailValidateText(`(module - (type (struct ${'(field i64)'.repeat(10_001)})) -)`, /too many/); - -{ - let {makeLargeStructDefault, makeLargeStruct} = wasmEvalText(`(module - (type $s (struct ${'(field i64)'.repeat(10_000)})) - (func (export "makeLargeStructDefault") (result anyref) - struct.new_default $s - ) - (func (export "makeLargeStruct") (result anyref) - ${'i64.const 0 '.repeat(10_000)} - struct.new $s - ) - )`).exports; - let largeStructDefault = makeLargeStructDefault(); - let largeStruct = makeLargeStruct(); -} - -// array.new_fixed has limit of 10_000 operands -wasmFailValidateText(`(module - (type $a (array i32)) - (func - array.new_fixed $a 10001 - ) -)`, /too many/); diff --git a/js/src/jit-test/tests/wasm/gc/limits/array-new-fixed.js b/js/src/jit-test/tests/wasm/gc/limits/array-new-fixed.js new file mode 100644 index 0000000000..4b0600c724 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/array-new-fixed.js @@ -0,0 +1,9 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; + +// array.new_fixed has limit of 10_000 operands +wasmFailValidateText(`(module + (type $a (array i32)) + (func + array.new_fixed $a 10001 + ) +)`, /too many/); diff --git a/js/src/jit-test/tests/wasm/gc/limits/load-mod.js b/js/src/jit-test/tests/wasm/gc/limits/load-mod.js new file mode 100644 index 0000000000..cd972ceb65 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/load-mod.js @@ -0,0 +1,5 @@ +// Files for some of these tests are pre-generated and located in js/src/jit-test/lib/gen. +// There you will also find the script to update these files. +function loadMod(name) { + return decompressLZ4(os.file.readFile(libdir + "gen/" + name, "binary").buffer) +} diff --git a/js/src/jit-test/tests/wasm/gc/limits/rec-groups-1.js b/js/src/jit-test/tests/wasm/gc/limits/rec-groups-1.js new file mode 100644 index 0000000000..489bf89cd4 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/rec-groups-1.js @@ -0,0 +1,6 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; + +loadRelativeToScript("load-mod.js"); + +// Limit of 1 million recursion groups +wasmValidateBinary(loadMod("wasm-gc-limits-r1M-t1.wasm")); diff --git a/js/src/jit-test/tests/wasm/gc/limits/rec-groups-2.js b/js/src/jit-test/tests/wasm/gc/limits/rec-groups-2.js new file mode 100644 index 0000000000..40c020c4b5 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/rec-groups-2.js @@ -0,0 +1,6 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; + +loadRelativeToScript("load-mod.js"); + +// Limit of 1 million recursion groups +wasmFailValidateBinary(loadMod("wasm-gc-limits-r1M1-t1.wasm"), /too many/); diff --git a/js/src/jit-test/tests/wasm/gc/limits/struct-fields.js b/js/src/jit-test/tests/wasm/gc/limits/struct-fields.js new file mode 100644 index 0000000000..ae60f38d57 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/struct-fields.js @@ -0,0 +1,11 @@ +// |jit-test| --setpref=wasm_gc; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; include:wasm.js; + +loadRelativeToScript("load-mod.js"); + +// Limit of 10_000 struct fields +wasmFailValidateBinary(loadMod("wasm-gc-limits-s10K1.wasm"), /too many/); +{ + let {makeLargeStructDefault, makeLargeStruct} = wasmEvalBinary(loadMod("wasm-gc-limits-s10K.wasm")).exports; + let largeStructDefault = makeLargeStructDefault(); + let largeStruct = makeLargeStruct(); +} diff --git a/js/src/jit-test/tests/wasm/gc/limits/subtyping-depth.js b/js/src/jit-test/tests/wasm/gc/limits/subtyping-depth.js new file mode 100644 index 0000000000..2d70215ee9 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/subtyping-depth.js @@ -0,0 +1,13 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; include: wasm-binary.js; + +// Limit of subtyping hierarchy 63 deep +function moduleSubtypingDepth(depth) { + let types = []; + types.push({final: false, kind: FuncCode, args: [], ret: []}); + for (let i = 1; i <= depth; i++) { + types.push({final: false, sub: i - 1, kind: FuncCode, args: [], ret: []}); + } + return moduleWithSections([typeSection(types)]); +} +wasmValidateBinary(moduleSubtypingDepth(63)); +wasmFailValidateBinary(moduleSubtypingDepth(64), /too deep/); diff --git a/js/src/jit-test/tests/wasm/gc/limits/types-1.js b/js/src/jit-test/tests/wasm/gc/limits/types-1.js new file mode 100644 index 0000000000..c097907e79 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/types-1.js @@ -0,0 +1,6 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; + +loadRelativeToScript("load-mod.js"); + +// Limit of 1 million types (across all recursion groups) +wasmValidateBinary(loadMod("wasm-gc-limits-r1-t1M.wasm")); diff --git a/js/src/jit-test/tests/wasm/gc/limits/types-2.js b/js/src/jit-test/tests/wasm/gc/limits/types-2.js new file mode 100644 index 0000000000..5e81bdf6ab --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/types-2.js @@ -0,0 +1,6 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; + +loadRelativeToScript("load-mod.js"); + +// Limit of 1 million types (across all recursion groups) +wasmValidateBinary(loadMod("wasm-gc-limits-r2-t500K.wasm")); diff --git a/js/src/jit-test/tests/wasm/gc/limits/types-3.js b/js/src/jit-test/tests/wasm/gc/limits/types-3.js new file mode 100644 index 0000000000..e9effa4bfa --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/types-3.js @@ -0,0 +1,6 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; + +loadRelativeToScript("load-mod.js"); + +// Limit of 1 million types (across all recursion groups) +wasmFailValidateBinary(loadMod("wasm-gc-limits-r1-t1M1.wasm"), /too many/); diff --git a/js/src/jit-test/tests/wasm/gc/limits/types-4.js b/js/src/jit-test/tests/wasm/gc/limits/types-4.js new file mode 100644 index 0000000000..dd413ed4e9 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/limits/types-4.js @@ -0,0 +1,6 @@ +// |jit-test| --setpref=wasm_gc; include:wasm.js; + +loadRelativeToScript("load-mod.js"); + +// Limit of 1 million types (across all recursion groups) +wasmFailValidateBinary(loadMod("wasm-gc-limits-r2-t500K1.wasm"), /too many/); diff --git a/js/src/jit-test/tests/wasm/gc/ref.js b/js/src/jit-test/tests/wasm/gc/ref.js index a55b0c8f02..2bf76daf52 100644 --- a/js/src/jit-test/tests/wasm/gc/ref.js +++ b/js/src/jit-test/tests/wasm/gc/ref.js @@ -173,7 +173,7 @@ assertErrorMessage(() => wasmEvalText(` `), WebAssembly.CompileError, /expression has type \(ref null.*\) but expected \(ref null.*\)/); -if (!wasmFunctionReferencesEnabled()) { +if (!wasmGcEnabled()) { // Ref type can't reference a function type assertErrorMessage(() => wasmEvalText(` diff --git a/js/src/jit-test/tests/wasm/gc/regress-1754701.js b/js/src/jit-test/tests/wasm/gc/regress-1754701.js index 656aa5d625..1a2307fa87 100644 --- a/js/src/jit-test/tests/wasm/gc/regress-1754701.js +++ b/js/src/jit-test/tests/wasm/gc/regress-1754701.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmGcEnabled() || !('oomTest' in this) +// |jit-test| skip-if: !wasmGcEnabled() let { testArray, testStructInline, testStructOutline } = wasmEvalText(` (module diff --git a/js/src/jit-test/tests/wasm/gc/regress-1884767.js b/js/src/jit-test/tests/wasm/gc/regress-1884767.js new file mode 100644 index 0000000000..54a168d657 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/regress-1884767.js @@ -0,0 +1,13 @@ +// |jit-test| skip-if: !wasmGcEnabled() + +const { test } = wasmEvalText(`(module + (type $a (array i32)) + (func (export "test") (result anyref) + try (result anyref) + (array.new_default $a (i32.const 999999999)) + catch_all + unreachable + end + ) +)`).exports; +assertErrorMessage(() => test(), WebAssembly.RuntimeError, /too many array elements/); diff --git a/js/src/jit-test/tests/wasm/gc/structs.js b/js/src/jit-test/tests/wasm/gc/structs.js index 0ff0cbd4b4..15dab873e9 100644 --- a/js/src/jit-test/tests/wasm/gc/structs.js +++ b/js/src/jit-test/tests/wasm/gc/structs.js @@ -665,46 +665,6 @@ assertErrorMessage(() => new WebAssembly.Module(bad), let exports = wasmEvalText(txt).exports; } -////////////////////////////////////////////////////////////////////////////// -// -// Checks for requests to create structs with more than MaxStructFields, where -// MaxStructFields == 1000. - -function structNewOfManyFields(numFields) { - let defString = "(type $s (struct "; - for (i = 0; i < numFields; i++) { - defString += "(field i32) "; - } - defString += "))"; - - let insnString = "(struct.new $s "; - for (i = 0; i < numFields; i++) { - insnString += "(i32.const 1337) "; - } - insnString += ")"; - - return "(module " + - defString + - " (func (export \"create\") (result eqref) " + - insnString + - "))"; -} - -{ - // 10_000 fields is allowable - let exports = wasmEvalText(structNewOfManyFields(10000)).exports; - let s = exports.create(); - assertEq(s, s); -} -{ - // but 10_001 is not - assertErrorMessage(() => wasmEvalText(structNewOfManyFields(10001)), - WebAssembly.CompileError, - /too many fields in struct/); -} - -// FIXME: also check struct.new_default, once it is available in both compilers. - // Exercise stack maps and GC { // Zeal will cause us to allocate structs via instance call, requiring live registers diff --git a/js/src/jit-test/tests/wasm/globals.js b/js/src/jit-test/tests/wasm/globals.js index 5a6d83e348..bbba095bb9 100644 --- a/js/src/jit-test/tests/wasm/globals.js +++ b/js/src/jit-test/tests/wasm/globals.js @@ -1,5 +1,3 @@ -// |jit-test| test-also=--wasm-extended-const; test-also=--no-wasm-extended-const - const { Instance, Module, LinkError } = WebAssembly; // Locally-defined globals @@ -44,68 +42,65 @@ testInner('i32', 13, 37, x => x|0); testInner('f32', 13.37, 0.1989, Math.fround); testInner('f64', 13.37, 0.1989, x => +x); -// Extended const stuff -if (wasmExtendedConstEnabled()) { - // Basic global shenanigans - { - const module = wasmEvalText(`(module - ;; -2 * (5 - (-10 + 20)) = 10 - (global i32 (i32.mul (i32.const -2) (i32.sub (i32.const 5) (i32.add (i32.const -10) (i32.const 20))))) - ;; ((1 + 2) - (3 * 4)) = -9 - (global i64 (i64.sub (i64.add (i64.const 1) (i64.const 2)) (i64.mul (i64.const 3) (i64.const 4)))) - - (func (export "get0") (result i32) global.get 0) - (func (export "get1") (result i64) global.get 1) - )`).exports; - - assertEq(module.get0(), 10); - assertEq(module.get1(), -9n); - } +// Basic global shenanigans +{ + const module = wasmEvalText(`(module + ;; -2 * (5 - (-10 + 20)) = 10 + (global i32 (i32.mul (i32.const -2) (i32.sub (i32.const 5) (i32.add (i32.const -10) (i32.const 20))))) + ;; ((1 + 2) - (3 * 4)) = -9 + (global i64 (i64.sub (i64.add (i64.const 1) (i64.const 2)) (i64.mul (i64.const 3) (i64.const 4)))) + + (func (export "get0") (result i32) global.get 0) + (func (export "get1") (result i64) global.get 1) + )`).exports; - // Example use of dynamic linking - { - // Make a memory for two dynamically-linked modules to share. Each module gets five pages. - const mem = new WebAssembly.Memory({ initial: 15, maximum: 15 }); - - const mod1 = new WebAssembly.Module(wasmTextToBinary(`(module - (memory (import "env" "memory") 15 15) - (global $memBase (import "env" "__memory_base") i32) - (data (offset (global.get $memBase)) "Hello from module 1.") - (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 1.") - )`)); - const instance1 = new WebAssembly.Instance(mod1, { - env: { - memory: mem, - __memory_base: 65536 * 5, // this module's memory starts at page 5 - }, - }); - - const mod2 = new WebAssembly.Module(wasmTextToBinary(`(module - (memory (import "env" "memory") 15 15) - (global $memBase (import "env" "__memory_base") i32) - (data (offset (global.get $memBase)) "Hello from module 2.") - (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 2.") - )`)); - const instance2 = new WebAssembly.Instance(mod2, { - env: { - memory: mem, - __memory_base: 65536 * 10, // this module's memory starts at page 10 - }, - }); - - // All four strings should now be present in the memory. - - function assertStringInMem(mem, str, addr) { - const bytes = new Uint8Array(mem.buffer).slice(addr, addr + str.length); - let memStr = String.fromCharCode(...bytes); - assertEq(memStr, str); - } + assertEq(module.get0(), 10); + assertEq(module.get1(), -9n); +} - assertStringInMem(mem, "Hello from module 1.", 65536 * 5); - assertStringInMem(mem, "Goodbye from module 1.", 65536 * 6); - assertStringInMem(mem, "Hello from module 2.", 65536 * 10); - assertStringInMem(mem, "Goodbye from module 2.", 65536 * 11); +// Example use of dynamic linking +{ + // Make a memory for two dynamically-linked modules to share. Each module gets five pages. + const mem = new WebAssembly.Memory({ initial: 15, maximum: 15 }); + + const mod1 = new WebAssembly.Module(wasmTextToBinary(`(module + (memory (import "env" "memory") 15 15) + (global $memBase (import "env" "__memory_base") i32) + (data (offset (global.get $memBase)) "Hello from module 1.") + (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 1.") + )`)); + const instance1 = new WebAssembly.Instance(mod1, { + env: { + memory: mem, + __memory_base: 65536 * 5, // this module's memory starts at page 5 + }, + }); + + const mod2 = new WebAssembly.Module(wasmTextToBinary(`(module + (memory (import "env" "memory") 15 15) + (global $memBase (import "env" "__memory_base") i32) + (data (offset (global.get $memBase)) "Hello from module 2.") + (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 2.") + )`)); + const instance2 = new WebAssembly.Instance(mod2, { + env: { + memory: mem, + __memory_base: 65536 * 10, // this module's memory starts at page 10 + }, + }); + + // All four strings should now be present in the memory. + + function assertStringInMem(mem, str, addr) { + const bytes = new Uint8Array(mem.buffer).slice(addr, addr + str.length); + let memStr = String.fromCharCode(...bytes); + assertEq(memStr, str); } + + assertStringInMem(mem, "Hello from module 1.", 65536 * 5); + assertStringInMem(mem, "Goodbye from module 1.", 65536 * 6); + assertStringInMem(mem, "Hello from module 2.", 65536 * 10); + assertStringInMem(mem, "Goodbye from module 2.", 65536 * 11); } // Semantic errors. diff --git a/js/src/jit-test/tests/wasm/import-export.js b/js/src/jit-test/tests/wasm/import-export.js index 4845269f24..ecd02ca143 100644 --- a/js/src/jit-test/tests/wasm/import-export.js +++ b/js/src/jit-test/tests/wasm/import-export.js @@ -401,9 +401,10 @@ wasmFailValidateText('(module (export "a" (memory 0)))', /exported memory index wasmFailValidateText('(module (export "a" (table 0)))', /exported table index out of bounds/); // Default memory/table rules - -wasmFailValidateText('(module (import "a" "b" (memory 1 1)) (memory 1 1))', /already have default memory/); -wasmFailValidateText('(module (import "a" "b" (memory 1 1)) (import "x" "y" (memory 2 2)))', /already have default memory/); +if (!wasmMultiMemoryEnabled()) { + wasmFailValidateText('(module (import "a" "b" (memory 1 1)) (memory 1 1))', /already have default memory/); + wasmFailValidateText('(module (import "a" "b" (memory 1 1)) (import "x" "y" (memory 2 2)))', /already have default memory/); +} // Data segments on imports diff --git a/js/src/jit-test/tests/wasm/memory-control/directives.txt b/js/src/jit-test/tests/wasm/memory-control/directives.txt index 1092e20d8a..a3daf19d97 100644 --- a/js/src/jit-test/tests/wasm/memory-control/directives.txt +++ b/js/src/jit-test/tests/wasm/memory-control/directives.txt @@ -1 +1 @@ -|jit-test| include:wasm.js; test-also=--wasm-compiler=optimizing --wasm-memory-control; test-also=--wasm-compiler=baseline --wasm-memory-control; test-also=--wasm-compiler=optimizing --no-wasm-memory64 --wasm-memory-control; test-also=--wasm-compiler=baseline --no-wasm-memory64 --wasm-memory-control +|jit-test| include:wasm.js; test-also=--wasm-compiler=optimizing --setpref=wasm_memory_control=true; test-also=--wasm-compiler=baseline --setpref=wasm_memory_control=true; test-also=--wasm-compiler=optimizing --setpref=wasm_memory64=false --setpref=wasm_memory_control=true; test-also=--wasm-compiler=baseline --setpref=wasm_memory64=false --setpref=wasm_memory_control=true diff --git a/js/src/jit-test/tests/wasm/memory-control/memory-discard.js b/js/src/jit-test/tests/wasm/memory-control/memory-discard.js index 87f2ae625a..8d2c63ad8a 100644 --- a/js/src/jit-test/tests/wasm/memory-control/memory-discard.js +++ b/js/src/jit-test/tests/wasm/memory-control/memory-discard.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmMemoryControlEnabled(); test-also=--wasm-memory64; test-also=--no-wasm-memory64 +// |jit-test| skip-if: !wasmMemoryControlEnabled(); test-also=--setpref=wasm_memory64=true; test-also=--setpref=wasm_memory64=false // This tests memory.discard and WebAssembly.Memory.discard() by placing data // (the alphabet) halfway across a page boundary, then discarding the first diff --git a/js/src/jit-test/tests/wasm/memory64/directives.txt b/js/src/jit-test/tests/wasm/memory64/directives.txt index 98d92a7afc..5fc5acabf0 100644 --- a/js/src/jit-test/tests/wasm/memory64/directives.txt +++ b/js/src/jit-test/tests/wasm/memory64/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmMemory64Enabled() +|jit-test| test-also=--setpref=wasm_memory64=true; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmMemory64Enabled() diff --git a/js/src/jit-test/tests/wasm/multi-memory/directives.txt b/js/src/jit-test/tests/wasm/multi-memory/directives.txt index 44374e8ceb..2ed5e3dd2a 100644 --- a/js/src/jit-test/tests/wasm/multi-memory/directives.txt +++ b/js/src/jit-test/tests/wasm/multi-memory/directives.txt @@ -1,2 +1,2 @@ -|jit-test| --wasm-multi-memory; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; include:wasm.js; skip-if: !wasmMultiMemoryEnabled() +|jit-test| --setpref=wasm_multi_memory=true; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; include:wasm.js; skip-if: !wasmMultiMemoryEnabled() diff --git a/js/src/jit-test/tests/wasm/multi-value/directives.txt b/js/src/jit-test/tests/wasm/multi-value/directives.txt index f636e648ec..b5a203297f 100644 --- a/js/src/jit-test/tests/wasm/multi-value/directives.txt +++ b/js/src/jit-test/tests/wasm/multi-value/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); include:wasm.js +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); include:wasm.js diff --git a/js/src/jit-test/tests/wasm/oom/breakpoints.js b/js/src/jit-test/tests/wasm/oom/breakpoints.js index a90f97739a..eabb660e0f 100644 --- a/js/src/jit-test/tests/wasm/oom/breakpoints.js +++ b/js/src/jit-test/tests/wasm/oom/breakpoints.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - var dbgGlobal = newGlobal({newCompartment: true}); var dbg = new dbgGlobal.Debugger(); dbg.addDebuggee(this); diff --git a/js/src/jit-test/tests/wasm/oom/exports.js b/js/src/jit-test/tests/wasm/oom/exports.js index 391850fda7..f75ada6199 100644 --- a/js/src/jit-test/tests/wasm/oom/exports.js +++ b/js/src/jit-test/tests/wasm/oom/exports.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { let text = `(module (type (func (param i32) (result i32))) diff --git a/js/src/jit-test/tests/wasm/oom/jsapi-prototype.js b/js/src/jit-test/tests/wasm/oom/jsapi-prototype.js index 4888a70db0..f5f319a8c5 100644 --- a/js/src/jit-test/tests/wasm/oom/jsapi-prototype.js +++ b/js/src/jit-test/tests/wasm/oom/jsapi-prototype.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - oomTest(() => { let memory = new WebAssembly.Memory({initial: 0}); assertEq(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype, "prototype"); diff --git a/js/src/jit-test/tests/wasm/ref-types/directives.txt b/js/src/jit-test/tests/wasm/ref-types/directives.txt index ed90b6bd5f..f9c573abef 100644 --- a/js/src/jit-test/tests/wasm/ref-types/directives.txt +++ b/js/src/jit-test/tests/wasm/ref-types/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); test-also=--wasm-test-serialization; include:wasm.js +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); test-also=--setpref=wasm_test_serialization=true; include:wasm.js diff --git a/js/src/jit-test/tests/wasm/regress/bug1708124.js b/js/src/jit-test/tests/wasm/regress/bug1708124.js index dc035b02e5..3cccd59dc0 100644 --- a/js/src/jit-test/tests/wasm/regress/bug1708124.js +++ b/js/src/jit-test/tests/wasm/regress/bug1708124.js @@ -1,4 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) // Ensure that trap reporting mechanism doesn't crash under OOM conditions. oomTest( diff --git a/js/src/jit-test/tests/wasm/regress/bug1839065.js b/js/src/jit-test/tests/wasm/regress/bug1839065.js index ecc4c1b90c..e3d84589b6 100644 --- a/js/src/jit-test/tests/wasm/regress/bug1839065.js +++ b/js/src/jit-test/tests/wasm/regress/bug1839065.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-gc; --wasm-function-references; skip-if: !wasmGcEnabled() || !wasmFunctionReferencesEnabled() +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmGcEnabled() || !wasmGcEnabled() function wasmEvalText(str, imports) { let binary = wasmTextToBinary(str); m = new WebAssembly.Module(binary); diff --git a/js/src/jit-test/tests/wasm/regress/bug1839142.js b/js/src/jit-test/tests/wasm/regress/bug1839142.js index 339c1d7cb9..49ca0117e9 100644 --- a/js/src/jit-test/tests/wasm/regress/bug1839142.js +++ b/js/src/jit-test/tests/wasm/regress/bug1839142.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-gc; --wasm-function-references; skip-if: !wasmSimdEnabled() || !wasmGcEnabled() || !wasmFunctionReferencesEnabled() +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmSimdEnabled() || !wasmGcEnabled() || !wasmGcEnabled() var wasm_code = new Uint8Array([0,97,115,109,1,0,0,0,1,152,128,128,128,0,4,80,0,95,1,126,0,80,0,94,124,1,80,0,96,3,127,127,127,1,127,96,0,0,3,130,128,128,128,0,1,2,4,133,128,128,128,0,1,112,1,1,1,5,132,128,128,128,0,1,1,16,32,13,131,128,128,128,0,1,0,3,6,204,131,128,128,0,62,100,107,0,66,197,129,131,134,140,152,176,224,64,251,0,0,11,127,0,65,196,129,131,134,124,11,100,107,0,66,192,129,131,134,204,132,137,146,36,251,0,0,11,124,1,68,0,0,0,0,0,0,0,0,11,124,1,68,0,0,0,0,0,0,0,0,11,124,1,68,0,0,0,0,0,0,0,0,11,124,1,68,0,0,0,0,0,0,0,0,11,100,107,0,66,192,129,131,134,140,216,53,251,0,0,11,100,107,1,66,210,164,201,146,165,202,148,169,210,0,66,210,164,201,146,165,202,212,156,218,0,66,192,129,131,134,140,152,176,224,64,66,192,129,131,134,140,152,176,224,64,126,125,66,192,129,131,128,130,152,176,224,64,125,66,192,129,131,190,130,152,176,224,36,125,66,164,200,0,125,125,66,0,125,66,0,125,66,0,125,251,0,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,127,0,65,0,11,7,136,128,128,128,0,1,4,109,97,105,110,0,0,9,139,128,128,128,0,1,6,0,65,0,11,112,1,210,0,11,10,141,133,128,128,0,1,138,5,0,65,238,235,177,226,126,253,15,253,83,32,0,65,235,146,246,155,122,65,244,231,246,248,124,253,15,253,164,1,65,230,152,157,154,7,253,15,253,164,1,118,65,167,184,218,133,127,253,15,253,164,1,118,118,66,149,131,127,66,164,128,218,132,206,227,209,231,254,0,65,230,133,189,200,126,65,252,208,237,164,5,254,32,0,132,245,241,222,13,27,254,71,2,211,226,246,158,7,66,243,213,226,237,209,166,141,199,0,68,76,189,205,180,194,110,195,89,36,3,131,253,18,253,127,253,127,253,127,253,127,253,164,1,65,138,173,198,47,65,138,248,237,203,120,65,205,162,146,252,5,65,190,148,192,156,5,254,53,0,200,229,139,195,9,65,167,139,216,173,5,65,215,146,221,45,254,53,0,169,255,135,252,1,254,53,0,193,209,131,217,7,40,2,134,242,184,197,3,65,228,191,145,146,6,65,142,162,226,169,4,254,53,0,168,178,151,189,15,113,109,71,109,107,254,46,0,191,232,145,230,9,67,66,84,34,11,67,88,147,220,200,91,68,233,240,20,66,52,37,190,38,182,187,182,187,182,187,182,187,182,187,57,3,168,169,148,198,10,65,226,162,208,167,7,65,221,226,226,242,120,107,65,140,215,139,233,5,65,141,151,153,19,107,107,65,188,134,175,165,5,65,183,219,200,136,121,107,65,250,197,157,214,123,65,139,168,173,167,126,107,107,107,42,1,249,156,171,169,13,187,182,187,182,187,182,187,182,187,182,65,191,253,243,170,122,253,15,65,203,195,202,169,122,253,15,65,179,204,244,234,123,253,15,253,119,65,166,184,138,186,122,253,15,65,129,140,243,163,6,253,15,253,119,65,229,139,254,233,121,253,15,65,183,191,195,183,122,253,15,253,119,253,119,65,151,211,231,151,122,253,15,253,119,253,119,253,119,65,192,156,192,215,3,65,178,193,209,198,7,107,65,240,157,246,199,6,65,221,225,148,169,1,107,65,145,183,142,141,127,65,188,218,139,244,7,107,107,65,236,243,250,169,127,65,146,241,174,181,120,107,65,139,147,232,229,124,65,255,203,253,217,3,107,107,65,250,197,224,140,2,65,202,242,215,181,3,107,65,135,244,246,28,65,140,170,229,200,123,107,107,107,65,154,217,196,153,1,65,137,128,243,231,123,107,107,107,107,65,227,146,143,180,126,40,1,245,130,139,196,13,40,2,244,172,225,238,10,40,0,216,160,178,215,11,40,1,197,193,230,178,3,40,2,195,241,223,254,2,65,158,240,247,204,124,40,2,140,190,218,180,14,40,2,215,128,167,146,8,40,0,141,143,157,196,10,40,0,147,146,185,143,13,40,1,195,168,134,179,5,107,107,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,253,15,253,119,253,31,0,91,254,46,0,203,243,148,239,8,11]); var wasm_module = new WebAssembly.Module(wasm_code); var wasm_instance = new WebAssembly.Instance(wasm_module); diff --git a/js/src/jit-test/tests/wasm/regress/bug1856733.js b/js/src/jit-test/tests/wasm/regress/bug1856733.js index 0f3e59dcfe..57e1380780 100644 --- a/js/src/jit-test/tests/wasm/regress/bug1856733.js +++ b/js/src/jit-test/tests/wasm/regress/bug1856733.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-gc; skip-if: !wasmGcEnabled() +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmGcEnabled() // Validates if imported globals are accounted for in init expressions. diff --git a/js/src/jit-test/tests/wasm/regress/bug1857829.js b/js/src/jit-test/tests/wasm/regress/bug1857829.js index 037855a4b1..df0598f678 100644 --- a/js/src/jit-test/tests/wasm/regress/bug1857829.js +++ b/js/src/jit-test/tests/wasm/regress/bug1857829.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-gc; skip-if: !wasmGcEnabled() +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmGcEnabled() // Tests if i31ref global value is normalized. var ins = wasmEvalText(`(module diff --git a/js/src/jit-test/tests/wasm/regress/bug1858982.js b/js/src/jit-test/tests/wasm/regress/bug1858982.js index 574c4bfe24..c67ae317f1 100644 --- a/js/src/jit-test/tests/wasm/regress/bug1858982.js +++ b/js/src/jit-test/tests/wasm/regress/bug1858982.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-tail-calls; --wasm-gc; skip-if: !wasmGcEnabled() || !wasmTailCallsEnabled() +// |jit-test| --setpref=wasm_tail_calls=true; --setpref=wasm_gc=true; skip-if: !wasmGcEnabled() || !wasmTailCallsEnabled() // Tests if instance registers were restored properly when call_ref is used // with tail calls. diff --git a/js/src/jit-test/tests/wasm/regress/bug1878673.js b/js/src/jit-test/tests/wasm/regress/bug1878673.js new file mode 100644 index 0000000000..edf2e67187 --- /dev/null +++ b/js/src/jit-test/tests/wasm/regress/bug1878673.js @@ -0,0 +1,13 @@ +// Check proper handling of OOM in SIMD loads. + +oomTest(function () { + let x = wasmTextToBinary(`(module + (memory 1 1) + (func + i32.const 16 + v128.load8x8_s + i16x8.abs + drop) + )`); + new WebAssembly.Module(x); +}); diff --git a/js/src/jit-test/tests/wasm/regress/bug1880770.js b/js/src/jit-test/tests/wasm/regress/bug1880770.js new file mode 100644 index 0000000000..0e748137d2 --- /dev/null +++ b/js/src/jit-test/tests/wasm/regress/bug1880770.js @@ -0,0 +1,20 @@ +// Check proper handling of OOM during segments creation. + +var x = {}; +Object.defineProperty(x, "", { + enumerable: true, + get: function () { + new WebAssembly.Instance( + new WebAssembly.Module( + wasmTextToBinary( + '(func $f (result f32) f32.const 0)(table (export "g") 1 funcref) (elem (i32.const 0) $f)' + ) + ) + ).exports.g + .get(0) + .type(WebAssembly, "", WebAssembly.Module, {}); + }, +}); +oomTest(function () { + Object.values(x); +}); diff --git a/js/src/jit-test/tests/wasm/regress/oom-eval.js b/js/src/jit-test/tests/wasm/regress/oom-eval.js index 1ce7c26df1..2f70bf8803 100644 --- a/js/src/jit-test/tests/wasm/regress/oom-eval.js +++ b/js/src/jit-test/tests/wasm/regress/oom-eval.js @@ -1,4 +1,4 @@ -// |jit-test| slow; allow-oom; skip-if: !wasmIsSupported() || !('oomTest' in this) +// |jit-test| slow; allow-oom; skip-if: !wasmIsSupported() function foo() { var g = newGlobal({sameZoneAs: this}); diff --git a/js/src/jit-test/tests/wasm/regress/oom-init.js b/js/src/jit-test/tests/wasm/regress/oom-init.js index f08b088107..4c6d0206b6 100644 --- a/js/src/jit-test/tests/wasm/regress/oom-init.js +++ b/js/src/jit-test/tests/wasm/regress/oom-init.js @@ -1,4 +1,4 @@ -// |jit-test| slow; allow-oom; skip-if: !wasmIsSupported() || !('oomTest' in this) +// |jit-test| slow; allow-oom; skip-if: !wasmIsSupported() || !hasFunction.oomTest Object.getOwnPropertyNames(this); s = newGlobal(); diff --git a/js/src/jit-test/tests/wasm/regress/oom-masm-baseline.js b/js/src/jit-test/tests/wasm/regress/oom-masm-baseline.js index 3f74666d0a..f2026a6788 100644 --- a/js/src/jit-test/tests/wasm/regress/oom-masm-baseline.js +++ b/js/src/jit-test/tests/wasm/regress/oom-masm-baseline.js @@ -1,4 +1,4 @@ -// |jit-test| slow; skip-if: !('oomTest' in this) +// |jit-test| slow // Test baseline compiler only. if (typeof wasmCompileMode === 'undefined' || wasmCompileMode() != 'baseline') diff --git a/js/src/jit-test/tests/wasm/regress/oom-wasm-streaming.js b/js/src/jit-test/tests/wasm/regress/oom-wasm-streaming.js index 5d7e719912..8a3c8e65d4 100644 --- a/js/src/jit-test/tests/wasm/regress/oom-wasm-streaming.js +++ b/js/src/jit-test/tests/wasm/regress/oom-wasm-streaming.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomAfterAllocations' in this) - ignoreUnhandledRejections(); try { diff --git a/js/src/jit-test/tests/wasm/regress/oom-wasmtexttobinary-block.js b/js/src/jit-test/tests/wasm/regress/oom-wasmtexttobinary-block.js index 6f3b666873..aaa7ff121f 100644 --- a/js/src/jit-test/tests/wasm/regress/oom-wasmtexttobinary-block.js +++ b/js/src/jit-test/tests/wasm/regress/oom-wasmtexttobinary-block.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - try { oomTest((function () { wasmTextToBinary("(module(func(loop $label1 $label0)))"); diff --git a/js/src/jit-test/tests/wasm/regress/oom-wrong-argument-number-for-import-call.js b/js/src/jit-test/tests/wasm/regress/oom-wrong-argument-number-for-import-call.js index 2d41b466ea..d4eb2da5a3 100644 --- a/js/src/jit-test/tests/wasm/regress/oom-wrong-argument-number-for-import-call.js +++ b/js/src/jit-test/tests/wasm/regress/oom-wrong-argument-number-for-import-call.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - function f() { // Too many results returned. return [52, 10, 0, 0]; diff --git a/js/src/jit-test/tests/wasm/simd/directives.txt b/js/src/jit-test/tests/wasm/simd/directives.txt index 651282ecb5..aced6c31a7 100644 --- a/js/src/jit-test/tests/wasm/simd/directives.txt +++ b/js/src/jit-test/tests/wasm/simd/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=baseline; test-also=--wasm-compiler=optimizing; test-also=--wasm-test-serialization; test-also=--wasm-compiler=optimizing --no-avx; skip-variant-if: --wasm-compiler=optimizing --no-avx, !getBuildConfiguration("x86") && !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:wasm.js +|jit-test| test-also=--wasm-compiler=baseline; test-also=--wasm-compiler=optimizing; test-also=--setpref=wasm_test_serialization=true; test-also=--wasm-compiler=optimizing --no-avx; skip-variant-if: --wasm-compiler=optimizing --no-avx, !getBuildConfiguration("x86") && !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:wasm.js diff --git a/js/src/jit-test/tests/wasm/simd/experimental.js b/js/src/jit-test/tests/wasm/simd/experimental.js index 3076cce80b..ded45928e6 100644 --- a/js/src/jit-test/tests/wasm/simd/experimental.js +++ b/js/src/jit-test/tests/wasm/simd/experimental.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-relaxed-simd; skip-if: !wasmRelaxedSimdEnabled() +// |jit-test| --setpref=wasm_relaxed_simd=true; skip-if: !wasmRelaxedSimdEnabled() // Experimental opcodes. We have no text parsing support for these yet. The // tests will be cleaned up and moved into ad-hack.js if the opcodes are @@ -77,7 +77,7 @@ for ( let [opcode, xs, ys, as, operator] of [[F32x4RelaxedMaddCode, fxs, fys, fa body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), ...V128Load(48), - SimdPrefix, varU32(opcode)])]})])])); + SimdPrefix, ...varU32(opcode)])]})])])); var mem = new (k == 4 ? Float32Array : Float64Array)(ins.exports.mem.buffer); set(mem, k, xs); @@ -97,7 +97,7 @@ for ( let [opcode, xs, ys, as, operator] of [[F32x4RelaxedMaddCode, fxs, fys, fa funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(0), ...V128Load(0), - SimdPrefix, varU32(opcode)])]})])]))); + SimdPrefix, ...varU32(opcode)])]})])]))); } // Relaxed swizzle, https://github.com/WebAssembly/relaxed-simd/issues/22 @@ -112,7 +112,7 @@ var ins = wasmValidateAndEval(moduleWithSections([ funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), - SimdPrefix, varU32(I8x16RelaxedSwizzleCode)])]})])])); + SimdPrefix, ...varU32(I8x16RelaxedSwizzleCode)])]})])])); var mem = new Uint8Array(ins.exports.mem.buffer); var test = [1, 4, 3, 7, 123, 0, 8, 222]; set(mem, 16, test); @@ -134,7 +134,7 @@ assertEq(false, WebAssembly.validate(moduleWithSections([ bodySection([ funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), - SimdPrefix, varU32(I8x16RelaxedSwizzleCode)])]})])]))); + SimdPrefix, ...varU32(I8x16RelaxedSwizzleCode)])]})])]))); // Relaxed MIN/MAX, https://github.com/WebAssembly/relaxed-simd/issues/33 @@ -164,11 +164,11 @@ for (let k of [4, 2]) { funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), - SimdPrefix, varU32(minOpcode)])]}), + SimdPrefix, ...varU32(minOpcode)])]}), funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), - SimdPrefix, varU32(maxOpcode)])]})])])); + SimdPrefix, ...varU32(maxOpcode)])]})])])); for (let i = 0; i < minMaxTests.length; i++) { var Ty = k == 4 ? Float32Array : Float64Array; var mem = new Ty(ins.exports.mem.buffer); @@ -198,7 +198,7 @@ for (let k of [4, 2]) { bodySection([ funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(0), - SimdPrefix, varU32(op)])]})])]))); + SimdPrefix, ...varU32(op)])]})])]))); } } @@ -216,16 +216,16 @@ var ins = wasmValidateAndEval(moduleWithSections([ bodySection([ funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), - SimdPrefix, varU32(I32x4RelaxedTruncSSatF32x4Code)])]}), + SimdPrefix, ...varU32(I32x4RelaxedTruncSSatF32x4Code)])]}), funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), - SimdPrefix, varU32(I32x4RelaxedTruncUSatF32x4Code)])]}), + SimdPrefix, ...varU32(I32x4RelaxedTruncUSatF32x4Code)])]}), funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), - SimdPrefix, varU32(I32x4RelaxedTruncSatF64x2SZeroCode)])]}), + SimdPrefix, ...varU32(I32x4RelaxedTruncSatF64x2SZeroCode)])]}), funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), - SimdPrefix, varU32(I32x4RelaxedTruncSatF64x2UZeroCode)])]})])])); + SimdPrefix, ...varU32(I32x4RelaxedTruncSatF64x2UZeroCode)])]})])])); var mem = ins.exports.mem.buffer; set(new Float32Array(mem), 4, [0, 2.3, -3.4, 100000]); @@ -260,7 +260,7 @@ for (let op of [I32x4RelaxedTruncSSatF32x4Code, I32x4RelaxedTruncUSatF32x4Code, exportSection([]), bodySection([ funcBody({locals:[], - body: [...V128StoreExpr(0, [SimdPrefix, varU32(op)])]})])]))); + body: [...V128StoreExpr(0, [SimdPrefix, ...varU32(op)])]})])]))); } // Relaxed blend / laneselect, https://github.com/WebAssembly/relaxed-simd/issues/17 @@ -281,7 +281,7 @@ for (let [k, opcode, AT] of [[1, I8x16RelaxedLaneSelectCode, Int8Array], body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), ...V128Load(48), - SimdPrefix, varU32(opcode)])]})])])); + SimdPrefix, ...varU32(opcode)])]})])])); var mem = ins.exports.mem.buffer; var mem8 = new Uint8Array(mem); @@ -310,7 +310,7 @@ for (let [k, opcode, AT] of [[1, I8x16RelaxedLaneSelectCode, Int8Array], funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(0), ...V128Load(0), - SimdPrefix, varU32(opcode)])]})])]))); + SimdPrefix, ...varU32(opcode)])]})])]))); } @@ -325,7 +325,7 @@ var ins = wasmValidateAndEval(moduleWithSections([ funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), - SimdPrefix, varU32(I16x8RelaxedQ15MulrSCode)])]})])])); + SimdPrefix, ...varU32(I16x8RelaxedQ15MulrSCode)])]})])])); var mem16 = new Int16Array(ins.exports.mem.buffer); for (let [as, bs] of cross([ @@ -355,7 +355,7 @@ var ins = wasmValidateAndEval(moduleWithSections([ funcBody({locals:[], body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), - SimdPrefix, varU32(I16x8DotI8x16I7x16SCode)])]})])])); + SimdPrefix, ...varU32(I16x8DotI8x16I7x16SCode)])]})])])); var mem8 = new Int8Array(ins.exports.mem.buffer); var mem16 = new Int16Array(ins.exports.mem.buffer); var test7bit = [1, 2, 3, 4, 5, 64, 65, 127, 127, 0, 0, @@ -385,7 +385,7 @@ var ins = wasmValidateAndEval(moduleWithSections([ body: [...V128StoreExpr(0, [...V128Load(16), ...V128Load(32), ...V128Load(48), - SimdPrefix, varU32(I32x4DotI8x16I7x16AddSCode)])]})])])); + SimdPrefix, ...varU32(I32x4DotI8x16I7x16AddSCode)])]})])])); var mem8 = new Int8Array(ins.exports.mem.buffer); var mem32 = new Int32Array(ins.exports.mem.buffer); var test7bit = [1, 2, 3, 4, 5, 64, 65, 127, 127, 0, 0, diff --git a/js/src/jit-test/tests/wasm/spec/exception-handling/directives.txt b/js/src/jit-test/tests/wasm/spec/exception-handling/directives.txt index 51f58354cb..77f4127908 100644 --- a/js/src/jit-test/tests/wasm/spec/exception-handling/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/exception-handling/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --wasm-exceptions; --wasm-exnref; skip-if: !wasmExceptionsEnabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_exnref=true; skip-if: !wasmExnRefEnabled()
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/extended-const/directives.txt b/js/src/jit-test/tests/wasm/spec/extended-const/directives.txt index 5b3d5f6d83..e93d8c8df0 100644 --- a/js/src/jit-test/tests/wasm/spec/extended-const/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/extended-const/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --wasm-extended-const; --no-wasm-gc; skip-if: !wasmExtendedConstEnabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_gc=false
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/function-references/directives.txt b/js/src/jit-test/tests/wasm/spec/function-references/directives.txt index bb76560525..a9cf6401e1 100644 --- a/js/src/jit-test/tests/wasm/spec/function-references/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/function-references/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --wasm-function-references; skip-if: !wasmFunctionReferencesEnabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_gc=true; skip-if: !wasmGcEnabled()
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/function-references/return_call_ref.wast.js b/js/src/jit-test/tests/wasm/spec/function-references/return_call_ref.wast.js index 3ea51a8cb0..df24798146 100644 --- a/js/src/jit-test/tests/wasm/spec/function-references/return_call_ref.wast.js +++ b/js/src/jit-test/tests/wasm/spec/function-references/return_call_ref.wast.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-tail-calls; skip-if: !wasmTailCallsEnabled() +// |jit-test| --setpref=wasm_tail_calls=true; skip-if: !wasmTailCallsEnabled() /* Copyright 2021 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/js/src/jit-test/tests/wasm/spec/gc/directives.txt b/js/src/jit-test/tests/wasm/spec/gc/directives.txt index c071d8b980..a9cf6401e1 100644 --- a/js/src/jit-test/tests/wasm/spec/gc/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/gc/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --wasm-gc; skip-if: !wasmGcEnabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_gc=true; skip-if: !wasmGcEnabled()
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/memory64/directives.txt b/js/src/jit-test/tests/wasm/spec/memory64/directives.txt index bbc47fb788..740ef217b9 100644 --- a/js/src/jit-test/tests/wasm/spec/memory64/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/memory64/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; skip-if: !wasmMemory64Enabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_memory64=true; skip-if: !wasmMemory64Enabled()
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js b/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js index 341742ab86..0af2f021bb 100644 --- a/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js +++ b/js/src/jit-test/tests/wasm/spec/memory64/memory64.wast.js @@ -27,17 +27,19 @@ let $2 = instantiate(`(module (memory i64 1 256))`); // ./test/core/memory64.wast:6 let $3 = instantiate(`(module (memory i64 0 65536))`); -// ./test/core/memory64.wast:8 -assert_invalid( - () => instantiate(`(module (memory i64 0) (memory i64 0))`), - `multiple memories`, -); - -// ./test/core/memory64.wast:9 -assert_invalid( - () => instantiate(`(module (memory (import "spectest" "memory") i64 0) (memory i64 0))`), - `multiple memories`, -); +if (!wasmMultiMemoryEnabled()) { + // ./test/core/memory64.wast:8 + assert_invalid( + () => instantiate(`(module (memory i64 0) (memory i64 0))`), + `multiple memories`, + ); + + // ./test/core/memory64.wast:9 + assert_invalid( + () => instantiate(`(module (memory (import "spectest" "memory") i64 0) (memory i64 0))`), + `multiple memories`, + ); +} // ./test/core/memory64.wast:11 let $4 = instantiate(`(module (memory i64 (data)) (func (export "memsize") (result i64) (memory.size)))`); diff --git a/js/src/jit-test/tests/wasm/spec/multi-memory/directives.txt b/js/src/jit-test/tests/wasm/spec/multi-memory/directives.txt index 9211583549..7c18d36751 100644 --- a/js/src/jit-test/tests/wasm/spec/multi-memory/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/multi-memory/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --wasm-multi-memory; skip-if: !wasmMultiMemoryEnabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_multi_memory=true; skip-if: !wasmMultiMemoryEnabled()
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js b/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js index a96781e8ed..e03b3f35db 100644 --- a/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js +++ b/js/src/jit-test/tests/wasm/spec/multi-memory/harness/harness.js @@ -19,6 +19,15 @@ if (!wasmIsSupported()) { quit(); } +function partialOobWriteMayWritePartialData() { + let arm_native = getBuildConfiguration("arm") && !getBuildConfiguration("arm-simulator"); + let arm64_native = getBuildConfiguration("arm64") && !getBuildConfiguration("arm64-simulator"); + return arm_native || arm64_native; +} + +let native_arm = getBuildConfiguration("arm") && !getBuildConfiguration("arm-simulator"); +let native_arm64 = getBuildConfiguration("arm64") && !getBuildConfiguration("arm64-simulator"); + function bytes(type, bytes) { var typedBuffer = new Uint8Array(bytes); return wasmGlobalFromArrayBuffer(type, typedBuffer.buffer); diff --git a/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js b/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js index ef68a1c0ec..35b8bd5a80 100644 --- a/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js +++ b/js/src/jit-test/tests/wasm/spec/multi-memory/memory_trap1.wast.js @@ -562,11 +562,15 @@ assert_trap(() => invoke($0, `i64.load32_u`, [-3]), `out of bounds memory access // ./test/core/multi-memory/memory_trap1.wast:234 assert_trap(() => invoke($0, `i64.load32_u`, [-4]), `out of bounds memory access`); -// ./test/core/multi-memory/memory_trap1.wast:237 -assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 7523094288207667809n)]); +// Bug 1842293 - do not observe the partial store caused by bug 1666747 on +// some native platforms. +if (!partialOobWriteMayWritePartialData()) { + // ./test/core/multi-memory/memory_trap1.wast:237 + assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 7523094288207667809n)]); -// ./test/core/multi-memory/memory_trap1.wast:238 -assert_return(() => invoke($0, `i64.load`, [0]), [value("i64", 7523094288207667809n)]); + // ./test/core/multi-memory/memory_trap1.wast:238 + assert_return(() => invoke($0, `i64.load`, [0]), [value("i64", 7523094288207667809n)]); +} // ./test/core/multi-memory/memory_trap1.wast:242 assert_return(() => invoke($0, `i64.store`, [65528, 0n]), []); @@ -574,14 +578,18 @@ assert_return(() => invoke($0, `i64.store`, [65528, 0n]), []); // ./test/core/multi-memory/memory_trap1.wast:243 assert_trap(() => invoke($0, `i32.store`, [65533, 305419896]), `out of bounds memory access`); -// ./test/core/multi-memory/memory_trap1.wast:244 -assert_return(() => invoke($0, `i32.load`, [65532]), [value("i32", 0)]); +if (!partialOobWriteMayWritePartialData()) { + // ./test/core/multi-memory/memory_trap1.wast:244 + assert_return(() => invoke($0, `i32.load`, [65532]), [value("i32", 0)]); +} // ./test/core/multi-memory/memory_trap1.wast:245 assert_trap(() => invoke($0, `i64.store`, [65529, 1311768467294899695n]), `out of bounds memory access`); -// ./test/core/multi-memory/memory_trap1.wast:246 -assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 0n)]); +if (!partialOobWriteMayWritePartialData()) { + // ./test/core/multi-memory/memory_trap1.wast:246 + assert_return(() => invoke($0, `i64.load`, [65528]), [value("i64", 0n)]); +} // ./test/core/multi-memory/memory_trap1.wast:247 assert_trap( @@ -589,8 +597,10 @@ assert_trap( `out of bounds memory access`, ); -// ./test/core/multi-memory/memory_trap1.wast:248 -assert_return(() => invoke($0, `f32.load`, [65532]), [value("f32", 0)]); +if (!partialOobWriteMayWritePartialData()) { + // ./test/core/multi-memory/memory_trap1.wast:248 + assert_return(() => invoke($0, `f32.load`, [65532]), [value("f32", 0)]); +} // ./test/core/multi-memory/memory_trap1.wast:249 assert_trap( @@ -598,5 +608,7 @@ assert_trap( `out of bounds memory access`, ); -// ./test/core/multi-memory/memory_trap1.wast:250 -assert_return(() => invoke($0, `f64.load`, [65528]), [value("f64", 0)]); +if (!partialOobWriteMayWritePartialData()) { + // ./test/core/multi-memory/memory_trap1.wast:250 + assert_return(() => invoke($0, `f64.load`, [65528]), [value("f64", 0)]); +} diff --git a/js/src/jit-test/tests/wasm/spec/relaxed-simd/directives.txt b/js/src/jit-test/tests/wasm/spec/relaxed-simd/directives.txt index 625758af79..bbeff405b1 100644 --- a/js/src/jit-test/tests/wasm/spec/relaxed-simd/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/relaxed-simd/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --wasm-relaxed-simd; skip-if: !wasmRelaxedSimdEnabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_relaxed_simd=true; skip-if: !wasmRelaxedSimdEnabled()
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/spec/directives.txt b/js/src/jit-test/tests/wasm/spec/spec/directives.txt index 9fa4f75347..b26fb254a9 100644 --- a/js/src/jit-test/tests/wasm/spec/spec/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/spec/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; test-also=--no-avx; skip-variant-if: --no-avx, !getBuildConfiguration('x86') && !getBuildConfiguration('x64') || getBuildConfiguration('simulator')
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; test-also=--no-avx; skip-variant-if: --no-avx, !getBuildConfiguration('x86') && !getBuildConfiguration('x64') || getBuildConfiguration('simulator')
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/spec/global.wast.js b/js/src/jit-test/tests/wasm/spec/spec/global.wast.js index e3351d3421..7f2502cc27 100644 --- a/js/src/jit-test/tests/wasm/spec/spec/global.wast.js +++ b/js/src/jit-test/tests/wasm/spec/spec/global.wast.js @@ -1,4 +1,4 @@ -// |jit-test| --no-wasm-gc +// |jit-test| --setpref=wasm_gc=false /* Copyright 2021 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js b/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js index 831bb26d69..211f610b69 100644 --- a/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js +++ b/js/src/jit-test/tests/wasm/spec/spec/memory.wast.js @@ -33,14 +33,16 @@ let $4 = instantiate(`(module (memory 1 256))`); // ./test/core/memory.wast:8 let $5 = instantiate(`(module (memory 0 65536))`); -// ./test/core/memory.wast:10 -assert_invalid(() => instantiate(`(module (memory 0) (memory 0))`), `multiple memories`); - -// ./test/core/memory.wast:11 -assert_invalid( - () => instantiate(`(module (memory (import "spectest" "memory") 0) (memory 0))`), - `multiple memories`, -); +if (!wasmMultiMemoryEnabled()) { + // ./test/core/memory.wast:10 + assert_invalid(() => instantiate(`(module (memory 0) (memory 0))`), `multiple memories`); + + // ./test/core/memory.wast:11 + assert_invalid( + () => instantiate(`(module (memory (import "spectest" "memory") 0) (memory 0))`), + `multiple memories`, + ); +} // ./test/core/memory.wast:13 let $6 = instantiate(`(module (memory (data)) (func (export "memsize") (result i32) (memory.size)))`); diff --git a/js/src/jit-test/tests/wasm/spec/tail-call/directives.txt b/js/src/jit-test/tests/wasm/spec/tail-call/directives.txt index 223b6b843a..fbd3639abc 100644 --- a/js/src/jit-test/tests/wasm/spec/tail-call/directives.txt +++ b/js/src/jit-test/tests/wasm/spec/tail-call/directives.txt @@ -1 +1 @@ -|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --wasm-tail-calls; skip-if: !wasmTailCallsEnabled()
\ No newline at end of file +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; --setpref=wasm_tail_calls=true; skip-if: !wasmTailCallsEnabled()
\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/tail-calls/bug1862473.js b/js/src/jit-test/tests/wasm/tail-calls/bug1862473.js index 57e5ea2118..fd44aa8f26 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/bug1862473.js +++ b/js/src/jit-test/tests/wasm/tail-calls/bug1862473.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-gc; skip-if: !wasmGcEnabled() +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmGcEnabled() var ins = wasmEvalText(`(module (func $func1) diff --git a/js/src/jit-test/tests/wasm/tail-calls/bug1865044.js b/js/src/jit-test/tests/wasm/tail-calls/bug1865044.js index 042e641f32..bf984e7b87 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/bug1865044.js +++ b/js/src/jit-test/tests/wasm/tail-calls/bug1865044.js @@ -1,4 +1,4 @@ -// |jit-test| --more-compartments; skip-variant-if: --wasm-test-serialization, true; skip-variant-if: --wasm-compiler=ion, true; skip-if: !wasmGcEnabled() || !('Function' in WebAssembly) +// |jit-test| --more-compartments; skip-variant-if: --setpref=wasm_test_serialization=true, true; skip-variant-if: --wasm-compiler=ion, true; skip-if: !wasmGcEnabled() || !('Function' in WebAssembly) a = newGlobal(); a.b = this; diff --git a/js/src/jit-test/tests/wasm/tail-calls/bug1871605.js b/js/src/jit-test/tests/wasm/tail-calls/bug1871605.js index 84f37a3e42..3403cea102 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/bug1871605.js +++ b/js/src/jit-test/tests/wasm/tail-calls/bug1871605.js @@ -1,4 +1,4 @@ -// |jit-test| --more-compartments; skip-variant-if: --wasm-test-serialization, true; skip-variant-if: --wasm-compiler=ion, true; skip-if: !wasmGcEnabled() +// |jit-test| --more-compartments; skip-variant-if: --setpref=wasm_test_serialization=true, true; skip-variant-if: --wasm-compiler=ion, true; skip-if: !wasmGcEnabled() var dbg = newGlobal() dbg.parent = this diff --git a/js/src/jit-test/tests/wasm/tail-calls/bug1871606.js b/js/src/jit-test/tests/wasm/tail-calls/bug1871606.js index aa520d1f53..ec43665c62 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/bug1871606.js +++ b/js/src/jit-test/tests/wasm/tail-calls/bug1871606.js @@ -1,4 +1,4 @@ -// |jit-test| --more-compartments; skip-variant-if: --wasm-test-serialization, true; skip-variant-if: --wasm-compiler=ion, true +// |jit-test| --more-compartments; skip-variant-if: --setpref=wasm_test_serialization=true, true; skip-variant-if: --wasm-compiler=ion, true dbg = newGlobal(); dbg.b = this; dbg.eval("(" + function() { diff --git a/js/src/jit-test/tests/wasm/tail-calls/bug1871951.js b/js/src/jit-test/tests/wasm/tail-calls/bug1871951.js index 6b3e481d59..6dc8b69548 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/bug1871951.js +++ b/js/src/jit-test/tests/wasm/tail-calls/bug1871951.js @@ -1,4 +1,4 @@ -// |jit-test| skip-variant-if: --wasm-test-serialization, true; skip-if: !wasmGcEnabled() +// |jit-test| skip-variant-if: --setpref=wasm_test_serialization=true, true; skip-if: !wasmGcEnabled() gczeal(18) function a(str, imports) { diff --git a/js/src/jit-test/tests/wasm/tail-calls/directives.txt b/js/src/jit-test/tests/wasm/tail-calls/directives.txt index 66e8161b9d..eac1ba242f 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/directives.txt +++ b/js/src/jit-test/tests/wasm/tail-calls/directives.txt @@ -1 +1 @@ -|jit-test| --wasm-tail-calls; test-also=--wasm-compiler=baseline; test-also=--wasm-compiler=ion; test-also=--wasm-test-serialization; skip-if: !wasmTailCallsEnabled(); include:wasm.js +|jit-test| --setpref=wasm_tail_calls=true; test-also=--wasm-compiler=baseline; test-also=--wasm-compiler=ion; test-also=--setpref=wasm_test_serialization=true; skip-if: !wasmTailCallsEnabled(); include:wasm.js diff --git a/js/src/jit-test/tests/wasm/tail-calls/exceptions.js b/js/src/jit-test/tests/wasm/tail-calls/exceptions.js index fd05f37e53..c4c774a4f3 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/exceptions.js +++ b/js/src/jit-test/tests/wasm/tail-calls/exceptions.js @@ -1,5 +1,3 @@ -// |jit-test| --wasm-exceptions; skip-if: !wasmExceptionsEnabled() - // Simple test with return_call. var ins = wasmEvalText(`(module (tag $exn) diff --git a/js/src/jit-test/tests/wasm/tail-calls/gc.js b/js/src/jit-test/tests/wasm/tail-calls/gc.js index 10e5971e6d..7f4df4c69a 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/gc.js +++ b/js/src/jit-test/tests/wasm/tail-calls/gc.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-function-references; --wasm-gc; skip-if: !wasmGcEnabled() || getBuildConfiguration("simulator") +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmGcEnabled() || getBuildConfiguration("simulator") // Tests GC references passed as arguments during return calls. // Similar to js/src/jit-test/tests/wasm/gc/trailers-gc-stress.js diff --git a/js/src/jit-test/tests/wasm/tail-calls/litmus3.js b/js/src/jit-test/tests/wasm/tail-calls/litmus3.js index bdd0918717..4fe367fb29 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/litmus3.js +++ b/js/src/jit-test/tests/wasm/tail-calls/litmus3.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmTailCallsEnabled() || !wasmExceptionsEnabled() +// |jit-test| skip-if: !wasmTailCallsEnabled() // Mutually recursive functions implement a multi-entry loop using tail calls, // with exception handling. diff --git a/js/src/jit-test/tests/wasm/tail-calls/litmus4.js b/js/src/jit-test/tests/wasm/tail-calls/litmus4.js index c16f712aac..6562f76686 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/litmus4.js +++ b/js/src/jit-test/tests/wasm/tail-calls/litmus4.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmTailCallsEnabled() || !wasmExceptionsEnabled() +// |jit-test| skip-if: !wasmTailCallsEnabled() // Mutually recursive functions implement a multi-entry loop using indirect tail // calls, with exception handling. diff --git a/js/src/jit-test/tests/wasm/tail-calls/litmus8.js b/js/src/jit-test/tests/wasm/tail-calls/litmus8.js index 4602664488..126367a0a0 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/litmus8.js +++ b/js/src/jit-test/tests/wasm/tail-calls/litmus8.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmTailCallsEnabled() || !wasmExceptionsEnabled() +// |jit-test| skip-if: !wasmTailCallsEnabled() // Tail-call litmus test with multiple results // diff --git a/js/src/jit-test/tests/wasm/tail-calls/litmus9.js b/js/src/jit-test/tests/wasm/tail-calls/litmus9.js index 3bbde27111..513e5c058e 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/litmus9.js +++ b/js/src/jit-test/tests/wasm/tail-calls/litmus9.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !wasmTailCallsEnabled() || !wasmExceptionsEnabled() +// |jit-test| skip-if: !wasmTailCallsEnabled() // Tail-call litmus test with multiple results // diff --git a/js/src/jit-test/tests/wasm/tail-calls/return_call_ref.js b/js/src/jit-test/tests/wasm/tail-calls/return_call_ref.js index c5f250a41c..947da02ef5 100644 --- a/js/src/jit-test/tests/wasm/tail-calls/return_call_ref.js +++ b/js/src/jit-test/tests/wasm/tail-calls/return_call_ref.js @@ -1,4 +1,4 @@ -// |jit-test| --wasm-gc; skip-if: !wasmGcEnabled() +// |jit-test| --setpref=wasm_gc=true; skip-if: !wasmGcEnabled() var ins = wasmEvalText(`(module (type $t (func (param i64 i64 funcref) (result i64))) (elem declare func $fac-acc $fac-acc-broken) diff --git a/js/src/jit-test/tests/wasm/testing/directives.txt b/js/src/jit-test/tests/wasm/testing/directives.txt new file mode 100644 index 0000000000..01f722ba1e --- /dev/null +++ b/js/src/jit-test/tests/wasm/testing/directives.txt @@ -0,0 +1 @@ +|jit-test| include:wasm.js diff --git a/js/src/jit-test/tests/wasm/testing/global-lossless-invoke.js b/js/src/jit-test/tests/wasm/testing/global-lossless-invoke.js new file mode 100644 index 0000000000..66db0499d9 --- /dev/null +++ b/js/src/jit-test/tests/wasm/testing/global-lossless-invoke.js @@ -0,0 +1,13 @@ +// |jit-test| skip-if: !('wasmLosslessInvoke' in this) + +let bytecode = wasmTextToBinary(`(module + (func (export "f") (result i32) + i32.const 1 + ) +)`); +let g = newGlobal({sameCompartmentAs: wasmLosslessInvoke}); +let m = new g.WebAssembly.Module(bytecode); +let i = new g.WebAssembly.Instance(m); + +assertEq(i.exports.f(), 1); +assertEq(wasmLosslessInvoke(i.exports.f).value, 1); diff --git a/js/src/jit-test/tests/xdr/bug1390856.js b/js/src/jit-test/tests/xdr/bug1390856.js index b1dbbffdfa..bb8940eae6 100644 --- a/js/src/jit-test/tests/xdr/bug1390856.js +++ b/js/src/jit-test/tests/xdr/bug1390856.js @@ -1,4 +1,4 @@ -// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0 +// |jit-test| skip-if: helperThreadCount() === 0 // Test main thread encode/decode OOM oomTest(function() { diff --git a/js/src/jit-test/tests/xdr/bug1427860.js b/js/src/jit-test/tests/xdr/bug1427860.js index fd6d6f0411..befa6782ae 100644 --- a/js/src/jit-test/tests/xdr/bug1427860.js +++ b/js/src/jit-test/tests/xdr/bug1427860.js @@ -1,4 +1,4 @@ -// |jit-test| --code-coverage; skip-if: !('oomAtAllocation' in this) +// |jit-test| --code-coverage let x = cacheEntry("function inner() { return 3; }; inner()"); evaluate(x, { saveIncrementalBytecode: true }); diff --git a/js/src/jit-test/tests/xdr/incremental-oom.js b/js/src/jit-test/tests/xdr/incremental-oom.js index ef202d112d..a0e1cdb0a0 100644 --- a/js/src/jit-test/tests/xdr/incremental-oom.js +++ b/js/src/jit-test/tests/xdr/incremental-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // Delazify a function while encoding bytecode. oomTest(() => { let code = cacheEntry(` diff --git a/js/src/jit-test/tests/xdr/module-oom.js b/js/src/jit-test/tests/xdr/module-oom.js index 14aef8e0af..951b6212a6 100644 --- a/js/src/jit-test/tests/xdr/module-oom.js +++ b/js/src/jit-test/tests/xdr/module-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - // OOM tests for xdr module parsing. const sa = diff --git a/js/src/jit-test/tests/xdr/stencil-oom.js b/js/src/jit-test/tests/xdr/stencil-oom.js index f57e8f82f8..70b4398f21 100644 --- a/js/src/jit-test/tests/xdr/stencil-oom.js +++ b/js/src/jit-test/tests/xdr/stencil-oom.js @@ -1,5 +1,3 @@ -// |jit-test| skip-if: !('oomTest' in this) - const sa = ` function f(x, y) { return x + y } let a = 10, b = 20; diff --git a/js/src/jit/AtomicOp.h b/js/src/jit/AtomicOp.h index 90edb631cb..ed9a6dd74c 100644 --- a/js/src/jit/AtomicOp.h +++ b/js/src/jit/AtomicOp.h @@ -7,17 +7,19 @@ #ifndef jit_AtomicOp_h #define jit_AtomicOp_h +#include <stdint.h> + namespace js { namespace jit { // Types of atomic operation, shared by MIR and LIR. -enum AtomicOp { - AtomicFetchAddOp, - AtomicFetchSubOp, - AtomicFetchAndOp, - AtomicFetchOrOp, - AtomicFetchXorOp +enum class AtomicOp { + Add, + Sub, + And, + Or, + Xor, }; // Memory barrier types, shared by MIR and LIR. @@ -26,7 +28,7 @@ enum AtomicOp { // distinction (DSB vs DMB on ARM, SYNC vs parameterized SYNC on MIPS) // but there's been no reason to use it yet. -enum MemoryBarrierBits { +enum MemoryBarrierBits : uint8_t { MembarLoadLoad = 1, MembarLoadStore = 2, MembarStoreStore = 4, @@ -41,16 +43,16 @@ enum MemoryBarrierBits { static inline constexpr MemoryBarrierBits operator|(MemoryBarrierBits a, MemoryBarrierBits b) { - return MemoryBarrierBits(int(a) | int(b)); + return MemoryBarrierBits(static_cast<uint8_t>(a) | static_cast<uint8_t>(b)); } static inline constexpr MemoryBarrierBits operator&(MemoryBarrierBits a, MemoryBarrierBits b) { - return MemoryBarrierBits(int(a) & int(b)); + return MemoryBarrierBits(static_cast<uint8_t>(a) & static_cast<uint8_t>(b)); } static inline constexpr MemoryBarrierBits operator~(MemoryBarrierBits a) { - return MemoryBarrierBits(~int(a)); + return MemoryBarrierBits(~static_cast<uint8_t>(a)); } // Standard barrier bits for a full barrier. diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index 171771ed51..92490ef8b8 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -2170,8 +2170,13 @@ void ShapeListObject::trace(JSTracer* trc, JSObject* obj) { } bool ShapeListObject::traceWeak(JSTracer* trc) { + uint32_t length = getDenseInitializedLength(); + if (length == 0) { + return false; // Object may be uninitialized. + } + const HeapSlot* src = elements_; - const HeapSlot* end = src + getDenseInitializedLength(); + const HeapSlot* end = src + length; HeapSlot* dst = elements_; while (src != end) { Shape* shape = static_cast<Shape*>(src->toPrivate()); @@ -2184,7 +2189,7 @@ bool ShapeListObject::traceWeak(JSTracer* trc) { } MOZ_ASSERT(dst <= end); - size_t length = dst - elements_; + length = dst - elements_; setDenseInitializedLength(length); return length != 0; @@ -3446,6 +3451,9 @@ void BaselineCacheIRCompiler::createThis(Register argcReg, Register calleeReg, // Restore saved registers. masm.PopRegsInMask(liveNonGCRegs); + + // Restore ICStubReg. The stub might have been moved if CreateThisFromIC + // discarded JIT code. Address stubAddr(FramePointer, BaselineStubFrameLayout::ICStubOffsetFromFP); masm.loadPtr(stubAddr, ICStubReg); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index c2245e38b5..68dbd6bfee 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -520,9 +520,14 @@ enum class NativeGetPropKind { static NativeGetPropKind IsCacheableGetPropCall(NativeObject* obj, NativeObject* holder, - PropertyInfo prop) { + PropertyInfo prop, + jsbytecode* pc = nullptr) { MOZ_ASSERT(IsCacheableProtoChain(obj, holder)); + if (pc && JSOp(*pc) == JSOp::GetBoundName) { + return NativeGetPropKind::None; + } + if (!prop.isAccessorProperty()) { return NativeGetPropKind::None; } @@ -615,7 +620,7 @@ static NativeGetPropKind CanAttachNativeGetProp(JSContext* cx, JSObject* obj, return NativeGetPropKind::Slot; } - return IsCacheableGetPropCall(nobj, *holder, propInfo->ref()); + return IsCacheableGetPropCall(nobj, *holder, propInfo->ref(), pc); } if (!prop.isFound()) { @@ -1975,6 +1980,46 @@ AttachDecision GetPropIRGenerator::tryAttachProxy(HandleObject obj, MOZ_CRASH("Unexpected ProxyStubType"); } +const JSClass* js::jit::ClassFor(GuardClassKind kind) { + switch (kind) { + case GuardClassKind::Array: + return &ArrayObject::class_; + case GuardClassKind::PlainObject: + return &PlainObject::class_; + case GuardClassKind::FixedLengthArrayBuffer: + return &FixedLengthArrayBufferObject::class_; + case GuardClassKind::ResizableArrayBuffer: + return &ResizableArrayBufferObject::class_; + case GuardClassKind::FixedLengthSharedArrayBuffer: + return &FixedLengthSharedArrayBufferObject::class_; + case GuardClassKind::GrowableSharedArrayBuffer: + return &GrowableSharedArrayBufferObject::class_; + case GuardClassKind::FixedLengthDataView: + return &FixedLengthDataViewObject::class_; + case GuardClassKind::ResizableDataView: + return &ResizableDataViewObject::class_; + case GuardClassKind::MappedArguments: + return &MappedArgumentsObject::class_; + case GuardClassKind::UnmappedArguments: + return &UnmappedArgumentsObject::class_; + case GuardClassKind::WindowProxy: + // Caller needs to handle this case, see + // JSRuntime::maybeWindowProxyClass(). + break; + case GuardClassKind::JSFunction: + // Caller needs to handle this case. Can be either |js::FunctionClass| or + // |js::ExtendedFunctionClass|. + break; + case GuardClassKind::BoundFunction: + return &BoundFunctionObject::class_; + case GuardClassKind::Set: + return &SetObject::class_; + case GuardClassKind::Map: + return &MapObject::class_; + } + MOZ_CRASH("unexpected kind"); +} + // Guards the class of an object. Because shape implies class, and a shape guard // is faster than a class guard, if this is our first time attaching a stub, we // instead generate a shape guard. @@ -1983,25 +2028,16 @@ void IRGenerator::emitOptimisticClassGuard(ObjOperandId objId, JSObject* obj, #ifdef DEBUG switch (kind) { case GuardClassKind::Array: - MOZ_ASSERT(obj->is<ArrayObject>()); - break; case GuardClassKind::PlainObject: - MOZ_ASSERT(obj->is<PlainObject>()); - break; case GuardClassKind::FixedLengthArrayBuffer: - MOZ_ASSERT(obj->is<FixedLengthArrayBufferObject>()); - break; + case GuardClassKind::ResizableArrayBuffer: case GuardClassKind::FixedLengthSharedArrayBuffer: - MOZ_ASSERT(obj->is<FixedLengthSharedArrayBufferObject>()); - break; + case GuardClassKind::GrowableSharedArrayBuffer: case GuardClassKind::FixedLengthDataView: - MOZ_ASSERT(obj->is<FixedLengthDataViewObject>()); - break; + case GuardClassKind::ResizableDataView: case GuardClassKind::Set: - MOZ_ASSERT(obj->is<SetObject>()); - break; case GuardClassKind::Map: - MOZ_ASSERT(obj->is<MapObject>()); + MOZ_ASSERT(obj->hasClass(ClassFor(kind))); break; case GuardClassKind::MappedArguments: @@ -2077,8 +2113,7 @@ AttachDecision GetPropIRGenerator::tryAttachObjectLength(HandleObject obj, AttachDecision GetPropIRGenerator::tryAttachTypedArray(HandleObject obj, ObjOperandId objId, HandleId id) { - // TODO: Support resizable typed arrays. (bug 1842999) - if (!obj->is<FixedLengthTypedArrayObject>()) { + if (!obj->is<TypedArrayObject>()) { return AttachDecision::NoAction; } @@ -2120,31 +2155,52 @@ AttachDecision GetPropIRGenerator::tryAttachTypedArray(HandleObject obj, } } - auto* tarr = &obj->as<FixedLengthTypedArrayObject>(); + auto* tarr = &obj->as<TypedArrayObject>(); maybeEmitIdGuard(id); // Emit all the normal guards for calling this native, but specialize // callNativeGetterResult. EmitCallGetterResultGuards(writer, tarr, holder, id, *prop, objId, mode_); if (isLength) { - if (tarr->length() <= INT32_MAX) { - writer.loadArrayBufferViewLengthInt32Result(objId); + size_t length = tarr->length().valueOr(0); + if (tarr->is<FixedLengthTypedArrayObject>()) { + if (length <= INT32_MAX) { + writer.loadArrayBufferViewLengthInt32Result(objId); + } else { + writer.loadArrayBufferViewLengthDoubleResult(objId); + } } else { - writer.loadArrayBufferViewLengthDoubleResult(objId); + if (length <= INT32_MAX) { + writer.resizableTypedArrayLengthInt32Result(objId); + } else { + writer.resizableTypedArrayLengthDoubleResult(objId); + } } trackAttached("GetProp.TypedArrayLength"); } else if (isByteOffset) { - if (tarr->byteOffset() <= INT32_MAX) { + // byteOffset doesn't need to use different code paths for fixed-length and + // resizable TypedArrays. + size_t byteOffset = tarr->byteOffset().valueOr(0); + if (byteOffset <= INT32_MAX) { writer.arrayBufferViewByteOffsetInt32Result(objId); } else { writer.arrayBufferViewByteOffsetDoubleResult(objId); } trackAttached("GetProp.TypedArrayByteOffset"); } else { - if (tarr->byteLength() <= INT32_MAX) { - writer.typedArrayByteLengthInt32Result(objId); + size_t byteLength = tarr->byteLength().valueOr(0); + if (tarr->is<FixedLengthTypedArrayObject>()) { + if (byteLength <= INT32_MAX) { + writer.typedArrayByteLengthInt32Result(objId); + } else { + writer.typedArrayByteLengthDoubleResult(objId); + } } else { - writer.typedArrayByteLengthDoubleResult(objId); + if (byteLength <= INT32_MAX) { + writer.resizableTypedArrayByteLengthInt32Result(objId); + } else { + writer.resizableTypedArrayByteLengthDoubleResult(objId); + } } trackAttached("GetProp.TypedArrayByteLength"); } @@ -2156,11 +2212,10 @@ AttachDecision GetPropIRGenerator::tryAttachTypedArray(HandleObject obj, AttachDecision GetPropIRGenerator::tryAttachDataView(HandleObject obj, ObjOperandId objId, HandleId id) { - // TODO: Support resizable dataviews. (bug 1842999) - if (!obj->is<FixedLengthDataViewObject>()) { + if (!obj->is<DataViewObject>()) { return AttachDecision::NoAction; } - auto* dv = &obj->as<FixedLengthDataViewObject>(); + auto* dv = &obj->as<DataViewObject>(); if (mode_ != ICState::Mode::Specialized) { return AttachDecision::NoAction; @@ -2181,6 +2236,12 @@ AttachDecision GetPropIRGenerator::tryAttachDataView(HandleObject obj, return AttachDecision::NoAction; } + // byteOffset and byteLength both throw when the ArrayBuffer is out-of-bounds. + if (dv->is<ResizableDataViewObject>() && + dv->as<ResizableDataViewObject>().isOutOfBounds()) { + return AttachDecision::NoAction; + } + NativeObject* holder = nullptr; Maybe<PropertyInfo> prop; NativeGetPropKind kind = @@ -2205,18 +2266,33 @@ AttachDecision GetPropIRGenerator::tryAttachDataView(HandleObject obj, // callNativeGetterResult. EmitCallGetterResultGuards(writer, dv, holder, id, *prop, objId, mode_); writer.guardHasAttachedArrayBuffer(objId); + if (dv->is<ResizableDataViewObject>()) { + writer.guardResizableArrayBufferViewInBounds(objId); + } if (isByteOffset) { - if (dv->byteOffset() <= INT32_MAX) { + // byteOffset doesn't need to use different code paths for fixed-length and + // resizable DataViews. + size_t byteOffset = dv->byteOffset().valueOr(0); + if (byteOffset <= INT32_MAX) { writer.arrayBufferViewByteOffsetInt32Result(objId); } else { writer.arrayBufferViewByteOffsetDoubleResult(objId); } trackAttached("GetProp.DataViewByteOffset"); } else { - if (dv->byteLength() <= INT32_MAX) { - writer.loadArrayBufferViewLengthInt32Result(objId); + size_t byteLength = dv->byteLength().valueOr(0); + if (dv->is<FixedLengthDataViewObject>()) { + if (byteLength <= INT32_MAX) { + writer.loadArrayBufferViewLengthInt32Result(objId); + } else { + writer.loadArrayBufferViewLengthDoubleResult(objId); + } } else { - writer.loadArrayBufferViewLengthDoubleResult(objId); + if (byteLength <= INT32_MAX) { + writer.resizableDataViewByteLengthInt32Result(objId); + } else { + writer.resizableDataViewByteLengthDoubleResult(objId); + } } trackAttached("GetProp.DataViewByteLength"); } @@ -2232,11 +2308,6 @@ AttachDecision GetPropIRGenerator::tryAttachArrayBufferMaybeShared( } auto* buf = &obj->as<ArrayBufferObjectMaybeShared>(); - // TODO: Support resizable buffers. (bug 1842999) - if (buf->isResizable()) { - return AttachDecision::NoAction; - } - if (mode_ != ICState::Mode::Specialized) { return AttachDecision::NoAction; } @@ -2273,10 +2344,18 @@ AttachDecision GetPropIRGenerator::tryAttachArrayBufferMaybeShared( // Emit all the normal guards for calling this native, but specialize // callNativeGetterResult. EmitCallGetterResultGuards(writer, buf, holder, id, *prop, objId, mode_); - if (buf->byteLength() <= INT32_MAX) { - writer.loadArrayBufferByteLengthInt32Result(objId); + if (!buf->is<GrowableSharedArrayBufferObject>()) { + if (buf->byteLength() <= INT32_MAX) { + writer.loadArrayBufferByteLengthInt32Result(objId); + } else { + writer.loadArrayBufferByteLengthDoubleResult(objId); + } } else { - writer.loadArrayBufferByteLengthDoubleResult(objId); + if (buf->byteLength() <= INT32_MAX) { + writer.growableSharedArrayBufferByteLengthInt32Result(objId); + } else { + writer.growableSharedArrayBufferByteLengthDoubleResult(objId); + } } writer.returnFromIC(); @@ -3044,9 +3123,8 @@ AttachDecision GetPropIRGenerator::tryAttachSparseElement( // For Uint32Array we let the stub return an Int32 if we have not seen a // double, to allow better codegen in Warp while avoiding bailout loops. -static bool ForceDoubleForUint32Array(FixedLengthTypedArrayObject* tarr, - uint64_t index) { - MOZ_ASSERT(index < tarr->length()); +static bool ForceDoubleForUint32Array(TypedArrayObject* tarr, uint64_t index) { + MOZ_ASSERT(index < tarr->length().valueOr(0)); if (tarr->type() != Scalar::Type::Uint32) { // Return value is only relevant for Uint32Array. @@ -3059,10 +3137,27 @@ static bool ForceDoubleForUint32Array(FixedLengthTypedArrayObject* tarr, return res.isDouble(); } +static ArrayBufferViewKind ToArrayBufferViewKind(const TypedArrayObject* obj) { + if (obj->is<FixedLengthTypedArrayObject>()) { + return ArrayBufferViewKind::FixedLength; + } + + MOZ_ASSERT(obj->is<ResizableTypedArrayObject>()); + return ArrayBufferViewKind::Resizable; +} + +static ArrayBufferViewKind ToArrayBufferViewKind(const DataViewObject* obj) { + if (obj->is<FixedLengthDataViewObject>()) { + return ArrayBufferViewKind::FixedLength; + } + + MOZ_ASSERT(obj->is<ResizableDataViewObject>()); + return ArrayBufferViewKind::Resizable; +} + AttachDecision GetPropIRGenerator::tryAttachTypedArrayElement( HandleObject obj, ObjOperandId objId) { - // TODO: Support resizable typed arrays. (bug 1842999) - if (!obj->is<FixedLengthTypedArrayObject>()) { + if (!obj->is<TypedArrayObject>()) { return AttachDecision::NoAction; } @@ -3070,12 +3165,12 @@ AttachDecision GetPropIRGenerator::tryAttachTypedArrayElement( return AttachDecision::NoAction; } - auto* tarr = &obj->as<FixedLengthTypedArrayObject>(); + auto* tarr = &obj->as<TypedArrayObject>(); bool handleOOB = false; int64_t indexInt64; if (!ValueIsInt64Index(idVal_, &indexInt64) || indexInt64 < 0 || - uint64_t(indexInt64) >= tarr->length()) { + uint64_t(indexInt64) >= tarr->length().valueOr(0)) { handleOOB = true; } @@ -3092,8 +3187,9 @@ AttachDecision GetPropIRGenerator::tryAttachTypedArrayElement( ValOperandId keyId = getElemKeyValueId(); IntPtrOperandId intPtrIndexId = guardToIntPtrIndex(idVal_, keyId, handleOOB); + auto viewKind = ToArrayBufferViewKind(tarr); writer.loadTypedArrayElementResult(objId, intPtrIndexId, tarr->type(), - handleOOB, forceDoubleForUint32); + handleOOB, forceDoubleForUint32, viewKind); writer.returnFromIC(); trackAttached("GetProp.TypedElement"); @@ -3376,7 +3472,7 @@ AttachDecision GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId, GlobalObject* global = &globalLexical->global(); - NativeGetPropKind kind = IsCacheableGetPropCall(global, holder, *prop); + NativeGetPropKind kind = IsCacheableGetPropCall(global, holder, *prop, pc_); if (kind != NativeGetPropKind::NativeGetter && kind != NativeGetPropKind::ScriptedGetter) { return AttachDecision::NoAction; @@ -3957,11 +4053,19 @@ AttachDecision HasPropIRGenerator::tryAttachNative(NativeObject* obj, return AttachDecision::Attach; } +static void EmitGuardTypedArray(CacheIRWriter& writer, TypedArrayObject* obj, + ObjOperandId objId) { + if (obj->is<FixedLengthTypedArrayObject>()) { + writer.guardIsFixedLengthTypedArray(objId); + } else { + writer.guardIsResizableTypedArray(objId); + } +} + AttachDecision HasPropIRGenerator::tryAttachTypedArray(HandleObject obj, ObjOperandId objId, ValOperandId keyId) { - // TODO: Support resizable typed arrays. (bug 1842999) - if (!obj->is<FixedLengthTypedArrayObject>()) { + if (!obj->is<TypedArrayObject>()) { return AttachDecision::NoAction; } @@ -3970,10 +4074,14 @@ AttachDecision HasPropIRGenerator::tryAttachTypedArray(HandleObject obj, return AttachDecision::NoAction; } - writer.guardIsFixedLengthTypedArray(objId); + auto* tarr = &obj->as<TypedArrayObject>(); + EmitGuardTypedArray(writer, tarr, objId); + IntPtrOperandId intPtrIndexId = guardToIntPtrIndex(idVal_, keyId, /* supportOOB = */ true); - writer.loadTypedArrayElementExistsResult(objId, intPtrIndexId); + + auto viewKind = ToArrayBufferViewKind(tarr); + writer.loadTypedArrayElementExistsResult(objId, intPtrIndexId, viewKind); writer.returnFromIC(); trackAttached("HasProp.TypedArrayObject"); @@ -4940,15 +5048,14 @@ AttachDecision SetPropIRGenerator::tryAttachAddOrUpdateSparseElement( AttachDecision SetPropIRGenerator::tryAttachSetTypedArrayElement( HandleObject obj, ObjOperandId objId, ValOperandId rhsId) { - // TODO: Support resizable typed arrays. (bug 1842999) - if (!obj->is<FixedLengthTypedArrayObject>()) { + if (!obj->is<TypedArrayObject>()) { return AttachDecision::NoAction; } if (!idVal_.isNumber()) { return AttachDecision::NoAction; } - auto* tarr = &obj->as<FixedLengthTypedArrayObject>(); + auto* tarr = &obj->as<TypedArrayObject>(); Scalar::Type elementType = tarr->type(); // Don't attach if the input type doesn't match the guard added below. @@ -4959,7 +5066,7 @@ AttachDecision SetPropIRGenerator::tryAttachSetTypedArrayElement( bool handleOOB = false; int64_t indexInt64; if (!ValueIsInt64Index(idVal_, &indexInt64) || indexInt64 < 0 || - uint64_t(indexInt64) >= tarr->length()) { + uint64_t(indexInt64) >= tarr->length().valueOr(0)) { handleOOB = true; } @@ -4980,8 +5087,9 @@ AttachDecision SetPropIRGenerator::tryAttachSetTypedArrayElement( ValOperandId keyId = setElemKeyValueId(); IntPtrOperandId indexId = guardToIntPtrIndex(idVal_, keyId, handleOOB); + auto viewKind = ToArrayBufferViewKind(tarr); writer.storeTypedArrayElement(objId, elementType, indexId, rhsValId, - handleOOB); + handleOOB, viewKind); writer.returnFromIC(); trackAttached(handleOOB ? "SetTypedElementOOB" : "SetTypedElement"); @@ -6470,9 +6578,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachArrayIsArray() { AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet( Scalar::Type type) { // Ensure |this| is a DataViewObject. - // TODO: Support resizable dataviews. (bug 1842999) - if (!thisval_.isObject() || - !thisval_.toObject().is<FixedLengthDataViewObject>()) { + if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) { return AttachDecision::NoAction; } @@ -6488,11 +6594,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet( return AttachDecision::NoAction; } - auto* dv = &thisval_.toObject().as<FixedLengthDataViewObject>(); + auto* dv = &thisval_.toObject().as<DataViewObject>(); // Bounds check the offset. - if (offsetInt64 < 0 || - !dv->offsetIsInBounds(Scalar::byteSize(type), offsetInt64)) { + size_t byteLength = dv->byteLength().valueOr(0); + if (offsetInt64 < 0 || !DataViewObject::offsetIsInBounds( + Scalar::byteSize(type), offsetInt64, byteLength)) { return AttachDecision::NoAction; } @@ -6501,7 +6608,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet( bool forceDoubleForUint32 = false; if (type == Scalar::Uint32) { bool isLittleEndian = argc_ > 1 && args_[1].toBoolean(); - uint32_t res = dv->read<uint32_t>(offsetInt64, isLittleEndian); + uint32_t res = dv->read<uint32_t>(offsetInt64, byteLength, isLittleEndian); forceDoubleForUint32 = res >= INT32_MAX; } @@ -6515,8 +6622,14 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet( ValOperandId thisValId = writer.loadArgumentFixedSlot(ArgumentKind::This, argc_); ObjOperandId objId = writer.guardToObject(thisValId); - emitOptimisticClassGuard(objId, &thisval_.toObject(), - GuardClassKind::FixedLengthDataView); + + if (dv->is<FixedLengthDataViewObject>()) { + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::FixedLengthDataView); + } else { + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::ResizableDataView); + } // Convert offset to intPtr. ValOperandId offsetId = @@ -6533,8 +6646,10 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet( boolLittleEndianId = writer.loadBooleanConstant(false); } + auto viewKind = ToArrayBufferViewKind(dv); writer.loadDataViewValueResult(objId, intPtrOffsetId, boolLittleEndianId, - type, forceDoubleForUint32); + type, forceDoubleForUint32, viewKind); + writer.returnFromIC(); trackAttached("DataViewGet"); @@ -6544,9 +6659,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet( AttachDecision InlinableNativeIRGenerator::tryAttachDataViewSet( Scalar::Type type) { // Ensure |this| is a DataViewObject. - // TODO: Support resizable dataviews. (bug 1842999) - if (!thisval_.isObject() || - !thisval_.toObject().is<FixedLengthDataViewObject>()) { + if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) { return AttachDecision::NoAction; } @@ -6565,11 +6678,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewSet( return AttachDecision::NoAction; } - auto* dv = &thisval_.toObject().as<FixedLengthDataViewObject>(); + auto* dv = &thisval_.toObject().as<DataViewObject>(); // Bounds check the offset. - if (offsetInt64 < 0 || - !dv->offsetIsInBounds(Scalar::byteSize(type), offsetInt64)) { + size_t byteLength = dv->byteLength().valueOr(0); + if (offsetInt64 < 0 || !DataViewObject::offsetIsInBounds( + Scalar::byteSize(type), offsetInt64, byteLength)) { return AttachDecision::NoAction; } @@ -6583,8 +6697,14 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewSet( ValOperandId thisValId = writer.loadArgumentFixedSlot(ArgumentKind::This, argc_); ObjOperandId objId = writer.guardToObject(thisValId); - emitOptimisticClassGuard(objId, &thisval_.toObject(), - GuardClassKind::FixedLengthDataView); + + if (dv->is<FixedLengthDataViewObject>()) { + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::FixedLengthDataView); + } else { + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::ResizableDataView); + } // Convert offset to intPtr. ValOperandId offsetId = @@ -6606,8 +6726,10 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewSet( boolLittleEndianId = writer.loadBooleanConstant(false); } + auto viewKind = ToArrayBufferViewKind(dv); writer.storeDataViewValueResult(objId, intPtrOffsetId, numericValueId, - boolLittleEndianId, type); + boolLittleEndianId, type, viewKind); + writer.returnFromIC(); trackAttached("DataViewSet"); @@ -6949,19 +7071,84 @@ AttachDecision InlinableNativeIRGenerator::tryAttachGuardToClass( return AttachDecision::Attach; } +AttachDecision InlinableNativeIRGenerator::tryAttachGuardToClass( + GuardClassKind kind) { + // Self-hosted code calls this with an object argument. + MOZ_ASSERT(argc_ == 1); + MOZ_ASSERT(args_[0].isObject()); + + // Class must match. + const JSClass* clasp = ClassFor(kind); + if (args_[0].toObject().getClass() != clasp) { + return AttachDecision::NoAction; + } + + // Initialize the input operand. + initializeInputOperand(); + + // Note: we don't need to call emitNativeCalleeGuard for intrinsics. + + // Guard that the argument is an object. + ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_); + ObjOperandId objId = writer.guardToObject(argId); + + // Guard that the object has the correct class. + writer.guardClass(objId, kind); + + // Return the object. + writer.loadObjectResult(objId); + writer.returnFromIC(); + + trackAttached("GuardToClass"); + return AttachDecision::Attach; +} + +AttachDecision InlinableNativeIRGenerator::tryAttachGuardToEitherClass( + GuardClassKind kind1, GuardClassKind kind2) { + MOZ_ASSERT(kind1 != kind2, + "prefer tryAttachGuardToClass for the same class case"); + + // Self-hosted code calls this with an object argument. + MOZ_ASSERT(argc_ == 1); + MOZ_ASSERT(args_[0].isObject()); + + // Class must match. + const JSClass* clasp1 = ClassFor(kind1); + const JSClass* clasp2 = ClassFor(kind2); + const JSClass* objClass = args_[0].toObject().getClass(); + if (objClass != clasp1 && objClass != clasp2) { + return AttachDecision::NoAction; + } + + // Initialize the input operand. + initializeInputOperand(); + + // Note: we don't need to call emitNativeCalleeGuard for intrinsics. + + // Guard that the argument is an object. + ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_); + ObjOperandId objId = writer.guardToObject(argId); + + // Guard that the object has the correct class. + writer.guardEitherClass(objId, kind1, kind2); + + // Return the object. + writer.loadObjectResult(objId); + writer.returnFromIC(); + + trackAttached("GuardToEitherClass"); + return AttachDecision::Attach; +} + AttachDecision InlinableNativeIRGenerator::tryAttachGuardToArrayBuffer() { - // TODO: Support resizable ArrayBuffers (bug 1842999), for now simply - // pass through to tryAttachGuardToClass which guards on - // FixedLengthArrayBufferObject. - return tryAttachGuardToClass(InlinableNative::IntrinsicGuardToArrayBuffer); + return tryAttachGuardToEitherClass(GuardClassKind::FixedLengthArrayBuffer, + GuardClassKind::ResizableArrayBuffer); } AttachDecision InlinableNativeIRGenerator::tryAttachGuardToSharedArrayBuffer() { - // TODO: Support resizable SharedArrayBuffers (bug 1842999), for now simply - // pass through to tryAttachGuardToClass which guards on - // FixedLengthSharedArrayBufferObject. - return tryAttachGuardToClass( - InlinableNative::IntrinsicGuardToSharedArrayBuffer); + return tryAttachGuardToEitherClass( + GuardClassKind::FixedLengthSharedArrayBuffer, + GuardClassKind::GrowableSharedArrayBuffer); } AttachDecision InlinableNativeIRGenerator::tryAttachHasClass( @@ -8924,7 +9111,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachReflectGetPrototypeOf() { return AttachDecision::Attach; } -static bool AtomicsMeetsPreconditions(FixedLengthTypedArrayObject* typedArray, +static bool AtomicsMeetsPreconditions(TypedArrayObject* typedArray, const Value& index) { switch (typedArray->type()) { case Scalar::Int8: @@ -8954,7 +9141,8 @@ static bool AtomicsMeetsPreconditions(FixedLengthTypedArrayObject* typedArray, if (!ValueIsInt64Index(index, &indexInt64)) { return false; } - if (indexInt64 < 0 || uint64_t(indexInt64) >= typedArray->length()) { + if (indexInt64 < 0 || + uint64_t(indexInt64) >= typedArray->length().valueOr(0)) { return false; } @@ -8971,17 +9159,15 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() { return AttachDecision::NoAction; } - // TODO: Support resizable typed arrays. (bug 1842999) // Arguments: typedArray, index (number), expected, replacement. - if (!args_[0].isObject() || - !args_[0].toObject().is<FixedLengthTypedArrayObject>()) { + if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) { return AttachDecision::NoAction; } if (!args_[1].isNumber()) { return AttachDecision::NoAction; } - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); if (!AtomicsMeetsPreconditions(typedArray, args_[1])) { return AttachDecision::NoAction; } @@ -9022,8 +9208,10 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() { OperandId numericReplacementId = emitNumericGuard(replacementId, args_[3], elementType); + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsCompareExchangeResult(objId, intPtrIndexId, numericExpectedId, - numericReplacementId, typedArray->type()); + numericReplacementId, typedArray->type(), + viewKind); writer.returnFromIC(); trackAttached("AtomicsCompareExchange"); @@ -9040,17 +9228,15 @@ bool InlinableNativeIRGenerator::canAttachAtomicsReadWriteModify() { return false; } - // TODO: Support resizable typed arrays. (bug 1842999) // Arguments: typedArray, index (number), value. - if (!args_[0].isObject() || - !args_[0].toObject().is<FixedLengthTypedArrayObject>()) { + if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) { return false; } if (!args_[1].isNumber()) { return false; } - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); if (!AtomicsMeetsPreconditions(typedArray, args_[1])) { return false; } @@ -9064,7 +9250,7 @@ InlinableNativeIRGenerator::AtomicsReadWriteModifyOperands InlinableNativeIRGenerator::emitAtomicsReadWriteModifyOperands() { MOZ_ASSERT(canAttachAtomicsReadWriteModify()); - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); // Initialize the input operand. initializeInputOperand(); @@ -9099,10 +9285,11 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsExchange() { auto [objId, intPtrIndexId, numericValueId] = emitAtomicsReadWriteModifyOperands(); - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsExchangeResult(objId, intPtrIndexId, numericValueId, - typedArray->type()); + typedArray->type(), viewKind); writer.returnFromIC(); trackAttached("AtomicsExchange"); @@ -9117,11 +9304,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsAdd() { auto [objId, intPtrIndexId, numericValueId] = emitAtomicsReadWriteModifyOperands(); - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); bool forEffect = ignoresResult(); + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsAddResult(objId, intPtrIndexId, numericValueId, - typedArray->type(), forEffect); + typedArray->type(), forEffect, viewKind); writer.returnFromIC(); trackAttached("AtomicsAdd"); @@ -9136,11 +9324,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsSub() { auto [objId, intPtrIndexId, numericValueId] = emitAtomicsReadWriteModifyOperands(); - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); bool forEffect = ignoresResult(); + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsSubResult(objId, intPtrIndexId, numericValueId, - typedArray->type(), forEffect); + typedArray->type(), forEffect, viewKind); writer.returnFromIC(); trackAttached("AtomicsSub"); @@ -9155,11 +9344,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsAnd() { auto [objId, intPtrIndexId, numericValueId] = emitAtomicsReadWriteModifyOperands(); - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); bool forEffect = ignoresResult(); + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsAndResult(objId, intPtrIndexId, numericValueId, - typedArray->type(), forEffect); + typedArray->type(), forEffect, viewKind); writer.returnFromIC(); trackAttached("AtomicsAnd"); @@ -9174,11 +9364,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsOr() { auto [objId, intPtrIndexId, numericValueId] = emitAtomicsReadWriteModifyOperands(); - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); bool forEffect = ignoresResult(); + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsOrResult(objId, intPtrIndexId, numericValueId, - typedArray->type(), forEffect); + typedArray->type(), forEffect, viewKind); writer.returnFromIC(); trackAttached("AtomicsOr"); @@ -9193,11 +9384,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsXor() { auto [objId, intPtrIndexId, numericValueId] = emitAtomicsReadWriteModifyOperands(); - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); bool forEffect = ignoresResult(); + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsXorResult(objId, intPtrIndexId, numericValueId, - typedArray->type(), forEffect); + typedArray->type(), forEffect, viewKind); writer.returnFromIC(); trackAttached("AtomicsXor"); @@ -9214,17 +9406,15 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsLoad() { return AttachDecision::NoAction; } - // TODO: Support resizable typed arrays. (bug 1842999) // Arguments: typedArray, index (number). - if (!args_[0].isObject() || - !args_[0].toObject().is<FixedLengthTypedArrayObject>()) { + if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) { return AttachDecision::NoAction; } if (!args_[1].isNumber()) { return AttachDecision::NoAction; } - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); if (!AtomicsMeetsPreconditions(typedArray, args_[1])) { return AttachDecision::NoAction; } @@ -9245,7 +9435,8 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsLoad() { IntPtrOperandId intPtrIndexId = guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false); - writer.atomicsLoadResult(objId, intPtrIndexId, typedArray->type()); + auto viewKind = ToArrayBufferViewKind(typedArray); + writer.atomicsLoadResult(objId, intPtrIndexId, typedArray->type(), viewKind); writer.returnFromIC(); trackAttached("AtomicsLoad"); @@ -9271,17 +9462,15 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsStore() { // obviously unused or if the argument is already Int32 and thus requires no // conversion. - // TODO: Support resizable typed arrays. (bug 1842999) // Arguments: typedArray, index (number), value. - if (!args_[0].isObject() || - !args_[0].toObject().is<FixedLengthTypedArrayObject>()) { + if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) { return AttachDecision::NoAction; } if (!args_[1].isNumber()) { return AttachDecision::NoAction; } - auto* typedArray = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* typedArray = &args_[0].toObject().as<TypedArrayObject>(); if (!AtomicsMeetsPreconditions(typedArray, args_[1])) { return AttachDecision::NoAction; } @@ -9323,8 +9512,9 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsStore() { numericValueId = emitNumericGuard(valueId, args_[2], elementType); } + auto viewKind = ToArrayBufferViewKind(typedArray); writer.atomicsStoreResult(objId, intPtrIndexId, numericValueId, - typedArray->type()); + typedArray->type(), viewKind); writer.returnFromIC(); trackAttached("AtomicsStore"); @@ -10182,12 +10372,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() { MOZ_ASSERT(args_[0].isObject()); MOZ_ASSERT(args_[0].toObject().is<TypedArrayObject>()); - // TODO: Support resizable typed arrays. (bug 1842999) - if (!args_[0].toObject().is<FixedLengthTypedArrayObject>()) { - return AttachDecision::NoAction; - } - - auto* tarr = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + auto* tarr = &args_[0].toObject().as<TypedArrayObject>(); // Initialize the input operand. initializeInputOperand(); @@ -10196,12 +10381,25 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() { ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_); ObjOperandId objArgId = writer.guardToObject(argId); - writer.guardIsFixedLengthTypedArray(objArgId); - if (tarr->byteOffset() <= INT32_MAX) { - writer.arrayBufferViewByteOffsetInt32Result(objArgId); + + EmitGuardTypedArray(writer, tarr, objArgId); + + size_t byteOffset = tarr->byteOffsetMaybeOutOfBounds(); + if (tarr->is<FixedLengthTypedArrayObject>()) { + if (byteOffset <= INT32_MAX) { + writer.arrayBufferViewByteOffsetInt32Result(objArgId); + } else { + writer.arrayBufferViewByteOffsetDoubleResult(objArgId); + } } else { - writer.arrayBufferViewByteOffsetDoubleResult(objArgId); + if (byteOffset <= INT32_MAX) { + writer.resizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result(objArgId); + } else { + writer.resizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult( + objArgId); + } } + writer.returnFromIC(); trackAttached("IntrinsicTypedArrayByteOffset"); @@ -10229,7 +10427,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayElementSize() { } AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength( - bool isPossiblyWrapped) { + bool isPossiblyWrapped, bool allowOutOfBounds) { // Self-hosted code calls this with a single, possibly wrapped, // TypedArrayObject argument. MOZ_ASSERT(argc_ == 1); @@ -10242,12 +10440,19 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength( MOZ_ASSERT(args_[0].toObject().is<TypedArrayObject>()); - // TODO: Support resizable typed arrays. (bug 1842999) - if (!args_[0].toObject().is<FixedLengthTypedArrayObject>()) { - return AttachDecision::NoAction; - } + auto* tarr = &args_[0].toObject().as<TypedArrayObject>(); - auto* tarr = &args_[0].toObject().as<FixedLengthTypedArrayObject>(); + // Don't optimize when a resizable TypedArray is out-of-bounds and + // out-of-bounds isn't allowed. + auto length = tarr->length(); + if (length.isNothing() && !tarr->hasDetachedBuffer()) { + MOZ_ASSERT(tarr->is<ResizableTypedArrayObject>()); + MOZ_ASSERT(tarr->isOutOfBounds()); + + if (!allowOutOfBounds) { + return AttachDecision::NoAction; + } + } // Initialize the input operand. initializeInputOperand(); @@ -10261,11 +10466,24 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength( writer.guardIsNotProxy(objArgId); } - writer.guardIsFixedLengthTypedArray(objArgId); - if (tarr->length() <= INT32_MAX) { - writer.loadArrayBufferViewLengthInt32Result(objArgId); + EmitGuardTypedArray(writer, tarr, objArgId); + + if (tarr->is<FixedLengthTypedArrayObject>()) { + if (length.valueOr(0) <= INT32_MAX) { + writer.loadArrayBufferViewLengthInt32Result(objArgId); + } else { + writer.loadArrayBufferViewLengthDoubleResult(objArgId); + } } else { - writer.loadArrayBufferViewLengthDoubleResult(objArgId); + if (!allowOutOfBounds) { + writer.guardResizableArrayBufferViewInBoundsOrDetached(objArgId); + } + + if (length.valueOr(0) <= INT32_MAX) { + writer.resizableTypedArrayLengthInt32Result(objArgId); + } else { + writer.resizableTypedArrayLengthDoubleResult(objArgId); + } } writer.returnFromIC(); @@ -10273,13 +10491,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength( return AttachDecision::Attach; } -AttachDecision -InlinableNativeIRGenerator::tryAttachTypedArrayLengthZeroOnOutOfBounds() { - // We don't yet inline resizable buffers, so this operation is equivalent to - // the inline code path for tryAttachTypedArrayLength(). - return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false); -} - AttachDecision InlinableNativeIRGenerator::tryAttachArrayBufferByteLength( bool isPossiblyWrapped) { // Self-hosted code calls this with a single, possibly wrapped, @@ -10296,11 +10507,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachArrayBufferByteLength( auto* buffer = &args_[0].toObject().as<ArrayBufferObject>(); - // TODO: Support resizable buffers. (bug 1842999) - if (buffer->isResizable()) { - return AttachDecision::NoAction; - } - // Initialize the input operand. initializeInputOperand(); @@ -10662,14 +10868,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayConstructor() { if (args_[0].isObject() && args_[0].toObject().is<ProxyObject>()) { return AttachDecision::NoAction; } - if (args_[0].isObject() && - args_[0].toObject().is<ResizableArrayBufferObject>()) { - return AttachDecision::NoAction; - } - if (args_[0].isObject() && - args_[0].toObject().is<GrowableSharedArrayBufferObject>()) { - return AttachDecision::NoAction; - } #ifdef JS_CODEGEN_X86 // Unfortunately NewTypedArrayFromArrayBufferResult needs more registers than @@ -10714,9 +10912,13 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayConstructor() { // From ArrayBuffer. if (obj->is<FixedLengthArrayBufferObject>()) { writer.guardClass(objId, GuardClassKind::FixedLengthArrayBuffer); - } else { - MOZ_ASSERT(obj->is<FixedLengthSharedArrayBufferObject>()); + } else if (obj->is<FixedLengthSharedArrayBufferObject>()) { writer.guardClass(objId, GuardClassKind::FixedLengthSharedArrayBuffer); + } else if (obj->is<ResizableArrayBufferObject>()) { + writer.guardClass(objId, GuardClassKind::ResizableArrayBuffer); + } else { + MOZ_ASSERT(obj->is<GrowableSharedArrayBufferObject>()); + writer.guardClass(objId, GuardClassKind::GrowableSharedArrayBuffer); } ValOperandId byteOffsetId; if (argc_ > 1) { @@ -11584,7 +11786,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { // Map intrinsics. case InlinableNative::IntrinsicGuardToMapObject: - return tryAttachGuardToClass(native); + return tryAttachGuardToClass(GuardClassKind::Map); case InlinableNative::IntrinsicGetNextMapEntryForIterator: return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ true); @@ -11612,7 +11814,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { // Set intrinsics. case InlinableNative::IntrinsicGuardToSetObject: - return tryAttachGuardToClass(native); + return tryAttachGuardToClass(GuardClassKind::Set); case InlinableNative::IntrinsicGetNextSetEntryForIterator: return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ false); @@ -11642,11 +11844,14 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { case InlinableNative::IntrinsicTypedArrayElementSize: return tryAttachTypedArrayElementSize(); case InlinableNative::IntrinsicTypedArrayLength: - return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false); + return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false, + /* allowOutOfBounds = */ false); case InlinableNative::IntrinsicTypedArrayLengthZeroOnOutOfBounds: - return tryAttachTypedArrayLengthZeroOnOutOfBounds(); + return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false, + /* allowOutOfBounds = */ true); case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength: - return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ true); + return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ true, + /* allowOutOfBounds = */ false); // Reflect natives. case InlinableNative::ReflectGetPrototypeOf: diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index b483257d12..9bedbb7ddc 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -515,8 +515,11 @@ enum class GuardClassKind : uint8_t { Array, PlainObject, FixedLengthArrayBuffer, + ResizableArrayBuffer, FixedLengthSharedArrayBuffer, + GrowableSharedArrayBuffer, FixedLengthDataView, + ResizableDataView, MappedArguments, UnmappedArguments, WindowProxy, @@ -526,6 +529,13 @@ enum class GuardClassKind : uint8_t { Map, }; +const JSClass* ClassFor(GuardClassKind kind); + +enum class ArrayBufferViewKind : uint8_t { + FixedLength, + Resizable, +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index 73f3831371..1467cebe08 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -18,7 +18,6 @@ #include "jsmath.h" #include "builtin/DataViewObject.h" -#include "builtin/MapObject.h" #include "builtin/Object.h" #include "gc/GCEnum.h" #include "gc/SweepingAPI.h" // js::gc::AutoLockStoreBuffer @@ -1365,6 +1364,8 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub, const CacheIRStubInfo* stubInfo) { using Type = StubField::Type; + bool isDead = false; + uint32_t field = 0; size_t offset = 0; while (true) { @@ -1375,7 +1376,7 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub, stubInfo->getStubField<T, Type::WeakShape>(stub, offset); auto r = TraceWeakEdge(trc, &shapeField, "cacheir-weak-shape"); if (r.isDead()) { - return false; + isDead = true; } break; } @@ -1384,7 +1385,7 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub, stubInfo->getStubField<T, Type::WeakObject>(stub, offset); auto r = TraceWeakEdge(trc, &objectField, "cacheir-weak-object"); if (r.isDead()) { - return false; + isDead = true; } break; } @@ -1393,7 +1394,7 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub, stubInfo->getStubField<T, Type::WeakBaseScript>(stub, offset); auto r = TraceWeakEdge(trc, &scriptField, "cacheir-weak-script"); if (r.isDead()) { - return false; + isDead = true; } break; } @@ -1403,12 +1404,13 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub, auto r = TraceWeakEdge(trc, &getterSetterField, "cacheir-weak-getter-setter"); if (r.isDead()) { - return false; + isDead = true; } break; } case Type::Limit: - return true; // Done. + // Done. + return !isDead; case Type::RawInt32: case Type::RawPointer: case Type::Shape: @@ -2148,6 +2150,30 @@ bool CacheIRCompiler::emitGuardNonDoubleType(ValOperandId inputId, return true; } +static const JSClass* ClassFor(JSContext* cx, GuardClassKind kind) { + switch (kind) { + case GuardClassKind::Array: + case GuardClassKind::PlainObject: + case GuardClassKind::FixedLengthArrayBuffer: + case GuardClassKind::ResizableArrayBuffer: + case GuardClassKind::FixedLengthSharedArrayBuffer: + case GuardClassKind::GrowableSharedArrayBuffer: + case GuardClassKind::FixedLengthDataView: + case GuardClassKind::ResizableDataView: + case GuardClassKind::MappedArguments: + case GuardClassKind::UnmappedArguments: + case GuardClassKind::Set: + case GuardClassKind::Map: + case GuardClassKind::BoundFunction: + return ClassFor(kind); + case GuardClassKind::WindowProxy: + return cx->runtime()->maybeWindowProxyClass(); + case GuardClassKind::JSFunction: + MOZ_CRASH("must be handled by caller"); + } + MOZ_CRASH("unexpected kind"); +} + bool CacheIRCompiler::emitGuardClass(ObjOperandId objId, GuardClassKind kind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); Register obj = allocator.useRegister(masm, objId); @@ -2169,44 +2195,7 @@ bool CacheIRCompiler::emitGuardClass(ObjOperandId objId, GuardClassKind kind) { return true; } - const JSClass* clasp = nullptr; - switch (kind) { - case GuardClassKind::Array: - clasp = &ArrayObject::class_; - break; - case GuardClassKind::PlainObject: - clasp = &PlainObject::class_; - break; - case GuardClassKind::FixedLengthArrayBuffer: - clasp = &FixedLengthArrayBufferObject::class_; - break; - case GuardClassKind::FixedLengthSharedArrayBuffer: - clasp = &FixedLengthSharedArrayBufferObject::class_; - break; - case GuardClassKind::FixedLengthDataView: - clasp = &FixedLengthDataViewObject::class_; - break; - case GuardClassKind::MappedArguments: - clasp = &MappedArgumentsObject::class_; - break; - case GuardClassKind::UnmappedArguments: - clasp = &UnmappedArgumentsObject::class_; - break; - case GuardClassKind::WindowProxy: - clasp = cx_->runtime()->maybeWindowProxyClass(); - break; - case GuardClassKind::Set: - clasp = &SetObject::class_; - break; - case GuardClassKind::Map: - clasp = &MapObject::class_; - break; - case GuardClassKind::BoundFunction: - clasp = &BoundFunctionObject::class_; - break; - case GuardClassKind::JSFunction: - MOZ_CRASH("JSFunction handled before switch"); - } + const JSClass* clasp = ClassFor(cx_, kind); MOZ_ASSERT(clasp); if (objectGuardNeedsSpectreMitigations(objId)) { @@ -2220,6 +2209,39 @@ bool CacheIRCompiler::emitGuardClass(ObjOperandId objId, GuardClassKind kind) { return true; } +bool CacheIRCompiler::emitGuardEitherClass(ObjOperandId objId, + GuardClassKind kind1, + GuardClassKind kind2) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + Register obj = allocator.useRegister(masm, objId); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // We don't yet need this case, so it's unsupported for now. + MOZ_ASSERT(kind1 != GuardClassKind::JSFunction && + kind2 != GuardClassKind::JSFunction); + + const JSClass* clasp1 = ClassFor(cx_, kind1); + MOZ_ASSERT(clasp1); + + const JSClass* clasp2 = ClassFor(cx_, kind2); + MOZ_ASSERT(clasp2); + + if (objectGuardNeedsSpectreMitigations(objId)) { + masm.branchTestObjClass(Assembler::NotEqual, obj, {clasp1, clasp2}, scratch, + obj, failure->label()); + } else { + masm.branchTestObjClassNoSpectreMitigations( + Assembler::NotEqual, obj, {clasp1, clasp2}, scratch, failure->label()); + } + + return true; +} + bool CacheIRCompiler::emitGuardNullProto(ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); Register obj = allocator.useRegister(masm, objId); @@ -2569,6 +2591,22 @@ bool CacheIRCompiler::emitGuardIsFixedLengthTypedArray(ObjOperandId objId) { return true; } +bool CacheIRCompiler::emitGuardIsResizableTypedArray(ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + Register obj = allocator.useRegister(masm, objId); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + masm.loadObjClassUnsafe(obj, scratch); + masm.branchIfClassIsNotResizableTypedArray(scratch, failure->label()); + return true; +} + bool CacheIRCompiler::emitGuardIsNotDOMProxy(ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); Register obj = allocator.useRegister(masm, objId); @@ -2796,7 +2834,7 @@ bool CacheIRCompiler::emitStringToAtom(StringOperandId stringId) { masm.branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()), Imm32(JSString::ATOM_BIT), &done); - masm.lookupStringInAtomCacheLastLookups(str, scratch, &vmCall); + masm.lookupStringInAtomCacheLastLookups(str, scratch, str, &vmCall); masm.jump(&done); masm.bind(&vmCall); @@ -4760,17 +4798,30 @@ bool CacheIRCompiler::emitLoadDenseElementHoleResult(ObjOperandId objId, } bool CacheIRCompiler::emitLoadTypedArrayElementExistsResult( - ObjOperandId objId, IntPtrOperandId indexId) { + ObjOperandId objId, IntPtrOperandId indexId, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); AutoOutputRegister output(*this); Register obj = allocator.useRegister(masm, objId); Register index = allocator.useRegister(masm, indexId); AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + Maybe<AutoScratchRegister> scratch2; + if (viewKind == ArrayBufferViewKind::Resizable) { + scratch2.emplace(allocator, masm); + } Label outOfBounds, done; // Bounds check. - masm.loadArrayBufferViewLengthIntPtr(obj, scratch); + if (viewKind == ArrayBufferViewKind::FixedLength) { + masm.loadArrayBufferViewLengthIntPtr(obj, scratch); + } else { + // Bounds check doesn't require synchronization. See IsValidIntegerIndex + // abstract operation which reads the underlying buffer byte length using + // "unordered" memory order. + auto sync = Synchronization::None(); + + masm.loadResizableTypedArrayLengthIntPtr(sync, obj, scratch, *scratch2); + } masm.branchPtr(Assembler::BelowOrEqual, scratch, index, &outOfBounds); EmitStoreBoolean(masm, true, output); masm.jump(&done); @@ -5039,6 +5090,46 @@ bool CacheIRCompiler::emitArrayBufferViewByteOffsetDoubleResult( return true; } +bool CacheIRCompiler:: + emitResizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, scratch1, + scratch2); + masm.guardNonNegativeIntPtrToInt32(scratch1, failure->label()); + masm.tagValue(JSVAL_TYPE_INT32, scratch1, output.valueReg()); + return true; +} + +bool CacheIRCompiler:: + emitResizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + Register obj = allocator.useRegister(masm, objId); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + + ScratchDoubleScope fpscratch(masm); + masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, scratch1, + scratch2); + masm.convertIntPtrToDouble(scratch1, fpscratch); + masm.boxDouble(fpscratch, output.valueReg(), fpscratch); + return true; +} + bool CacheIRCompiler::emitTypedArrayByteLengthInt32Result(ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); @@ -5081,6 +5172,100 @@ bool CacheIRCompiler::emitTypedArrayByteLengthDoubleResult(ObjOperandId objId) { return true; } +bool CacheIRCompiler::emitResizableTypedArrayByteLengthInt32Result( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadResizableTypedArrayLengthIntPtr(sync, obj, scratch1, scratch2); + masm.guardNonNegativeIntPtrToInt32(scratch1, failure->label()); + masm.typedArrayElementSize(obj, scratch2); + + masm.branchMul32(Assembler::Overflow, scratch2.get(), scratch1, + failure->label()); + + masm.tagValue(JSVAL_TYPE_INT32, scratch1, output.valueReg()); + return true; +} + +bool CacheIRCompiler::emitResizableTypedArrayByteLengthDoubleResult( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadResizableTypedArrayLengthIntPtr(sync, obj, scratch1, scratch2); + masm.typedArrayElementSize(obj, scratch2); + masm.mulPtr(scratch2, scratch1); + + ScratchDoubleScope fpscratch(masm); + masm.convertIntPtrToDouble(scratch1, fpscratch); + masm.boxDouble(fpscratch, output.valueReg(), fpscratch); + return true; +} + +bool CacheIRCompiler::emitResizableTypedArrayLengthInt32Result( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // Explicit |length| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadResizableTypedArrayLengthIntPtr(sync, obj, scratch1, scratch2); + masm.guardNonNegativeIntPtrToInt32(scratch1, failure->label()); + + masm.tagValue(JSVAL_TYPE_INT32, scratch1, output.valueReg()); + return true; +} + +bool CacheIRCompiler::emitResizableTypedArrayLengthDoubleResult( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + // Explicit |length| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadResizableTypedArrayLengthIntPtr(sync, obj, scratch1, scratch2); + + ScratchDoubleScope fpscratch(masm); + masm.convertIntPtrToDouble(scratch1, fpscratch); + masm.boxDouble(fpscratch, output.valueReg(), fpscratch); + return true; +} + bool CacheIRCompiler::emitTypedArrayElementSizeResult(ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); @@ -5093,6 +5278,92 @@ bool CacheIRCompiler::emitTypedArrayElementSizeResult(ObjOperandId objId) { return true; } +bool CacheIRCompiler::emitResizableDataViewByteLengthInt32Result( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadResizableDataViewByteLengthIntPtr(sync, obj, scratch1, scratch2); + masm.guardNonNegativeIntPtrToInt32(scratch1, failure->label()); + + masm.tagValue(JSVAL_TYPE_INT32, scratch1, output.valueReg()); + return true; +} + +bool CacheIRCompiler::emitResizableDataViewByteLengthDoubleResult( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegister scratch2(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadResizableDataViewByteLengthIntPtr(sync, obj, scratch1, scratch2); + + ScratchDoubleScope fpscratch(masm); + masm.convertIntPtrToDouble(scratch1, fpscratch); + masm.boxDouble(fpscratch, output.valueReg(), fpscratch); + return true; +} + +bool CacheIRCompiler::emitGrowableSharedArrayBufferByteLengthInt32Result( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + Register obj = allocator.useRegister(masm, objId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, scratch); + masm.guardNonNegativeIntPtrToInt32(scratch, failure->label()); + + masm.tagValue(JSVAL_TYPE_INT32, scratch, output.valueReg()); + return true; +} + +bool CacheIRCompiler::emitGrowableSharedArrayBufferByteLengthDoubleResult( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + Register obj = allocator.useRegister(masm, objId); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, scratch); + + ScratchDoubleScope fpscratch(masm); + masm.convertIntPtrToDouble(scratch, fpscratch); + masm.boxDouble(fpscratch, output.valueReg(), fpscratch); + return true; +} + bool CacheIRCompiler::emitGuardHasAttachedArrayBuffer(ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); @@ -5108,6 +5379,42 @@ bool CacheIRCompiler::emitGuardHasAttachedArrayBuffer(ObjOperandId objId) { return true; } +bool CacheIRCompiler::emitGuardResizableArrayBufferViewInBounds( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoScratchRegister scratch(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + masm.branchIfResizableArrayBufferViewOutOfBounds(obj, scratch, + failure->label()); + return true; +} + +bool CacheIRCompiler::emitGuardResizableArrayBufferViewInBoundsOrDetached( + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoScratchRegister scratch(allocator, masm); + Register obj = allocator.useRegister(masm, objId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + Label done; + masm.branchIfResizableArrayBufferViewInBounds(obj, scratch, &done); + masm.branchIfHasAttachedArrayBuffer(obj, scratch, failure->label()); + masm.bind(&done); + return true; +} + bool CacheIRCompiler::emitIsTypedArrayConstructorResult(ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); @@ -6220,8 +6527,8 @@ bool CacheIRCompiler::emitArrayPush(ObjOperandId objId, ValOperandId rhsId) { bool CacheIRCompiler::emitStoreTypedArrayElement(ObjOperandId objId, Scalar::Type elementType, IntPtrOperandId indexId, - uint32_t rhsId, - bool handleOOB) { + uint32_t rhsId, bool handleOOB, + ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); Register obj = allocator.useRegister(masm, objId); Register index = allocator.useRegister(masm, indexId); @@ -6261,7 +6568,8 @@ bool CacheIRCompiler::emitStoreTypedArrayElement(ObjOperandId objId, AutoScratchRegister scratch1(allocator, masm); Maybe<AutoScratchRegister> scratch2; Maybe<AutoSpectreBoundsScratchRegister> spectreScratch; - if (Scalar::isBigIntType(elementType)) { + if (Scalar::isBigIntType(elementType) || + viewKind == ArrayBufferViewKind::Resizable) { scratch2.emplace(allocator, masm); } else { spectreScratch.emplace(allocator, masm); @@ -6276,10 +6584,9 @@ bool CacheIRCompiler::emitStoreTypedArrayElement(ObjOperandId objId, // Bounds check. Label done; - Register spectreTemp = scratch2 ? scratch2->get() : spectreScratch->get(); - masm.loadArrayBufferViewLengthIntPtr(obj, scratch1); - masm.spectreBoundsCheckPtr(index, scratch1, spectreTemp, - handleOOB ? &done : failure->label()); + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch1, scratch2, + spectreScratch, + handleOOB ? &done : failure->label()); // Load the elements vector. masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), scratch1); @@ -6348,9 +6655,61 @@ static void EmitAllocateBigInt(MacroAssembler& masm, Register result, masm.bind(&done); } +void CacheIRCompiler::emitTypedArrayBoundsCheck(ArrayBufferViewKind viewKind, + Register obj, Register index, + Register scratch, + Register maybeScratch, + Register spectreScratch, + Label* fail) { + // |index| must not alias any scratch register. + MOZ_ASSERT(index != scratch); + MOZ_ASSERT(index != maybeScratch); + MOZ_ASSERT(index != spectreScratch); + + if (viewKind == ArrayBufferViewKind::FixedLength) { + masm.loadArrayBufferViewLengthIntPtr(obj, scratch); + masm.spectreBoundsCheckPtr(index, scratch, spectreScratch, fail); + } else { + if (maybeScratch == InvalidReg) { + // Spill |index| to use it as an additional scratch register. + masm.push(index); + + maybeScratch = index; + } else { + // Use |maybeScratch| when no explicit |spectreScratch| is present. + if (spectreScratch == InvalidReg) { + spectreScratch = maybeScratch; + } + } + + // Bounds check doesn't require synchronization. See IsValidIntegerIndex + // abstract operation which reads the underlying buffer byte length using + // "unordered" memory order. + auto sync = Synchronization::None(); + + masm.loadResizableTypedArrayLengthIntPtr(sync, obj, scratch, maybeScratch); + + if (maybeScratch == index) { + // Restore |index|. + masm.pop(index); + } + + masm.spectreBoundsCheckPtr(index, scratch, spectreScratch, fail); + } +} + +void CacheIRCompiler::emitTypedArrayBoundsCheck( + ArrayBufferViewKind viewKind, Register obj, Register index, + Register scratch, mozilla::Maybe<Register> maybeScratch, + mozilla::Maybe<Register> spectreScratch, Label* fail) { + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch, + maybeScratch.valueOr(InvalidReg), + spectreScratch.valueOr(InvalidReg), fail); +} + bool CacheIRCompiler::emitLoadTypedArrayElementResult( ObjOperandId objId, IntPtrOperandId indexId, Scalar::Type elementType, - bool handleOOB, bool forceDoubleForUint32) { + bool handleOOB, bool forceDoubleForUint32, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); AutoOutputRegister output(*this); Register obj = allocator.useRegister(masm, objId); @@ -6372,9 +6731,8 @@ bool CacheIRCompiler::emitLoadTypedArrayElementResult( // Bounds check. Label outOfBounds; - masm.loadArrayBufferViewLengthIntPtr(obj, scratch1); - masm.spectreBoundsCheckPtr(index, scratch1, scratch2, - handleOOB ? &outOfBounds : failure->label()); + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch1, scratch2, scratch2, + handleOOB ? &outOfBounds : failure->label()); // Allocate BigInt if needed. The code after this should be infallible. Maybe<Register> bigInt; @@ -6437,11 +6795,40 @@ bool CacheIRCompiler::emitLoadTypedArrayElementResult( return true; } -static void EmitDataViewBoundsCheck(MacroAssembler& masm, size_t byteSize, - Register obj, Register offset, - Register scratch, Label* fail) { +void CacheIRCompiler::emitDataViewBoundsCheck(ArrayBufferViewKind viewKind, + size_t byteSize, Register obj, + Register offset, Register scratch, + Register maybeScratch, + Label* fail) { + // |offset| must not alias any scratch register. + MOZ_ASSERT(offset != scratch); + MOZ_ASSERT(offset != maybeScratch); + + if (viewKind == ArrayBufferViewKind::FixedLength) { + masm.loadArrayBufferViewLengthIntPtr(obj, scratch); + } else { + if (maybeScratch == InvalidReg) { + // Spill |offset| to use it as an additional scratch register. + masm.push(offset); + + maybeScratch = offset; + } + + // Bounds check doesn't require synchronization. See GetViewValue and + // SetViewValue abstract operations which read the underlying buffer byte + // length using "unordered" memory order. + auto sync = Synchronization::None(); + + masm.loadResizableDataViewByteLengthIntPtr(sync, obj, scratch, + maybeScratch); + + if (maybeScratch == offset) { + // Restore |offset|. + masm.pop(offset); + } + } + // Ensure both offset < length and offset + (byteSize - 1) < length. - masm.loadArrayBufferViewLengthIntPtr(obj, scratch); if (byteSize == 1) { masm.spectreBoundsCheckPtr(offset, scratch, InvalidReg, fail); } else { @@ -6456,7 +6843,7 @@ static void EmitDataViewBoundsCheck(MacroAssembler& masm, size_t byteSize, bool CacheIRCompiler::emitLoadDataViewValueResult( ObjOperandId objId, IntPtrOperandId offsetId, BooleanOperandId littleEndianId, Scalar::Type elementType, - bool forceDoubleForUint32) { + bool forceDoubleForUint32, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); AutoOutputRegister output(*this); @@ -6469,6 +6856,18 @@ bool CacheIRCompiler::emitLoadDataViewValueResult( Register64 outputReg64 = output.valueReg().toRegister64(); Register outputScratch = outputReg64.scratchReg(); + Register boundsCheckScratch; +#ifndef JS_CODEGEN_X86 + Maybe<AutoScratchRegister> maybeBoundsCheckScratch; + if (viewKind == ArrayBufferViewKind::Resizable) { + maybeBoundsCheckScratch.emplace(allocator, masm); + boundsCheckScratch = *maybeBoundsCheckScratch; + } +#else + // Not enough registers on x86, so use the other part of outputReg64. + boundsCheckScratch = outputReg64.secondScratchReg(); +#endif + FailurePath* failure; if (!addFailurePath(&failure)) { return false; @@ -6476,8 +6875,8 @@ bool CacheIRCompiler::emitLoadDataViewValueResult( const size_t byteSize = Scalar::byteSize(elementType); - EmitDataViewBoundsCheck(masm, byteSize, obj, offset, outputScratch, - failure->label()); + emitDataViewBoundsCheck(viewKind, byteSize, obj, offset, outputScratch, + boundsCheckScratch, failure->label()); masm.loadPtr(Address(obj, DataViewObject::dataOffset()), outputScratch); @@ -6612,7 +7011,8 @@ bool CacheIRCompiler::emitLoadDataViewValueResult( bool CacheIRCompiler::emitStoreDataViewValueResult( ObjOperandId objId, IntPtrOperandId offsetId, uint32_t valueId, - BooleanOperandId littleEndianId, Scalar::Type elementType) { + BooleanOperandId littleEndianId, Scalar::Type elementType, + ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); AutoOutputRegister output(*this); @@ -6686,6 +7086,24 @@ bool CacheIRCompiler::emitStoreDataViewValueResult( } #endif + Register boundsCheckScratch; +#ifndef JS_CODEGEN_X86 + Maybe<AutoScratchRegister> maybeBoundsCheckScratch; + if (viewKind == ArrayBufferViewKind::Resizable) { + if (scratch2.constructed<AutoScratchRegister>()) { + boundsCheckScratch = scratch2.ref<AutoScratchRegister>().get(); + } else if (scratch2.constructed<AutoScratchRegister64>()) { + boundsCheckScratch = + scratch2.ref<AutoScratchRegister64>().get().scratchReg(); + } else { + maybeBoundsCheckScratch.emplace(allocator, masm); + boundsCheckScratch = *maybeBoundsCheckScratch; + } + } +#else + // Not enough registers on x86. +#endif + FailurePath* failure; if (!addFailurePath(&failure)) { return false; @@ -6693,8 +7111,8 @@ bool CacheIRCompiler::emitStoreDataViewValueResult( const size_t byteSize = Scalar::byteSize(elementType); - EmitDataViewBoundsCheck(masm, byteSize, obj, offset, scratch1, - failure->label()); + emitDataViewBoundsCheck(viewKind, byteSize, obj, offset, scratch1, + boundsCheckScratch, failure->label()); masm.loadPtr(Address(obj, DataViewObject::dataOffset()), scratch1); BaseIndex dest(scratch1, offset, TimesOne); @@ -8903,7 +9321,8 @@ bool CacheIRCompiler::emitGetFirstDollarIndexResult(StringOperandId strId) { bool CacheIRCompiler::emitAtomicsCompareExchangeResult( ObjOperandId objId, IntPtrOperandId indexId, uint32_t expectedId, - uint32_t replacementId, Scalar::Type elementType) { + uint32_t replacementId, Scalar::Type elementType, + ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); Maybe<AutoOutputRegister> output; @@ -8936,8 +9355,17 @@ bool CacheIRCompiler::emitAtomicsCompareExchangeResult( : callvm->outputValueReg().scratchReg(); MOZ_ASSERT(scratch != obj, "scratchReg must not be typeReg"); + Maybe<AutoScratchRegister> scratch2; + if (viewKind == ArrayBufferViewKind::Resizable) { +#ifdef JS_CODEGEN_X86 + // Not enough spare registers on x86. +#else + scratch2.emplace(allocator, masm); +#endif + } + // Not enough registers on X86. - Register spectreTemp = Register::Invalid(); + constexpr auto spectreTemp = mozilla::Nothing{}; FailurePath* failure; if (!addFailurePath(&failure)) { @@ -8950,8 +9378,8 @@ bool CacheIRCompiler::emitAtomicsCompareExchangeResult( MOZ_ASSERT(isBaseline(), "Can't use FailurePath with AutoCallVM in Ion ICs"); // Bounds check. - masm.loadArrayBufferViewLengthIntPtr(obj, scratch); - masm.spectreBoundsCheckPtr(index, scratch, spectreTemp, failure->label()); + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch, scratch2, + spectreTemp, failure->label()); // Atomic operations are highly platform-dependent, for example x86/x64 has // specific requirements on which registers are used; MIPS needs multiple @@ -8966,8 +9394,8 @@ bool CacheIRCompiler::emitAtomicsCompareExchangeResult( masm.Push(index); masm.Push(obj); - using Fn = BigInt* (*)(JSContext*, FixedLengthTypedArrayObject*, size_t, - const BigInt*, const BigInt*); + using Fn = BigInt* (*)(JSContext*, TypedArrayObject*, size_t, const BigInt*, + const BigInt*); callvm->call<Fn, jit::AtomicsCompareExchange64>(); return true; } @@ -9004,15 +9432,20 @@ bool CacheIRCompiler::emitAtomicsCompareExchangeResult( bool CacheIRCompiler::emitAtomicsReadModifyWriteResult( ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, - Scalar::Type elementType, AtomicsReadWriteModifyFn fn) { + Scalar::Type elementType, ArrayBufferViewKind viewKind, + AtomicsReadWriteModifyFn fn) { AutoOutputRegister output(*this); Register obj = allocator.useRegister(masm, objId); Register index = allocator.useRegister(masm, indexId); Register value = allocator.useRegister(masm, Int32OperandId(valueId)); AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + Maybe<AutoScratchRegisterMaybeOutputType> scratch2; + if (viewKind == ArrayBufferViewKind::Resizable) { + scratch2.emplace(allocator, masm, output); + } // Not enough registers on X86. - Register spectreTemp = Register::Invalid(); + constexpr auto spectreTemp = mozilla::Nothing{}; FailurePath* failure; if (!addFailurePath(&failure)) { @@ -9020,8 +9453,8 @@ bool CacheIRCompiler::emitAtomicsReadModifyWriteResult( } // Bounds check. - masm.loadArrayBufferViewLengthIntPtr(obj, scratch); - masm.spectreBoundsCheckPtr(index, scratch, spectreTemp, failure->label()); + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch, scratch2, + spectreTemp, failure->label()); // See comment in emitAtomicsCompareExchange for why we use an ABI call. { @@ -9054,15 +9487,20 @@ bool CacheIRCompiler::emitAtomicsReadModifyWriteResult( template <CacheIRCompiler::AtomicsReadWriteModify64Fn fn> bool CacheIRCompiler::emitAtomicsReadModifyWriteResult64( - ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId) { + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + ArrayBufferViewKind viewKind) { AutoCallVM callvm(masm, this, allocator); Register obj = allocator.useRegister(masm, objId); Register index = allocator.useRegister(masm, indexId); Register value = allocator.useRegister(masm, BigIntOperandId(valueId)); AutoScratchRegisterMaybeOutput scratch(allocator, masm, callvm.output()); + Maybe<AutoScratchRegisterMaybeOutputType> scratch2; + if (viewKind == ArrayBufferViewKind::Resizable) { + scratch2.emplace(allocator, masm, callvm.output()); + } // Not enough registers on X86. - Register spectreTemp = Register::Invalid(); + constexpr auto spectreTemp = mozilla::Nothing{}; FailurePath* failure; if (!addFailurePath(&failure)) { @@ -9075,8 +9513,8 @@ bool CacheIRCompiler::emitAtomicsReadModifyWriteResult64( MOZ_ASSERT(isBaseline(), "Can't use FailurePath with AutoCallVM in Ion ICs"); // Bounds check. - masm.loadArrayBufferViewLengthIntPtr(obj, scratch); - masm.spectreBoundsCheckPtr(index, scratch, spectreTemp, failure->label()); + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch, scratch2, + spectreTemp, failure->label()); // See comment in emitAtomicsCompareExchange for why we use a VM call. @@ -9093,95 +9531,88 @@ bool CacheIRCompiler::emitAtomicsReadModifyWriteResult64( bool CacheIRCompiler::emitAtomicsExchangeResult(ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, - Scalar::Type elementType) { + Scalar::Type elementType, + ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); if (Scalar::isBigIntType(elementType)) { return emitAtomicsReadModifyWriteResult64<jit::AtomicsExchange64>( - objId, indexId, valueId); + objId, indexId, valueId, viewKind); } return emitAtomicsReadModifyWriteResult(objId, indexId, valueId, elementType, + viewKind, AtomicsExchange(elementType)); } -bool CacheIRCompiler::emitAtomicsAddResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool CacheIRCompiler::emitAtomicsAddResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); if (Scalar::isBigIntType(elementType)) { - return emitAtomicsReadModifyWriteResult64<jit::AtomicsAdd64>(objId, indexId, - valueId); + return emitAtomicsReadModifyWriteResult64<jit::AtomicsAdd64>( + objId, indexId, valueId, viewKind); } return emitAtomicsReadModifyWriteResult(objId, indexId, valueId, elementType, - AtomicsAdd(elementType)); + viewKind, AtomicsAdd(elementType)); } -bool CacheIRCompiler::emitAtomicsSubResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool CacheIRCompiler::emitAtomicsSubResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); if (Scalar::isBigIntType(elementType)) { - return emitAtomicsReadModifyWriteResult64<jit::AtomicsSub64>(objId, indexId, - valueId); + return emitAtomicsReadModifyWriteResult64<jit::AtomicsSub64>( + objId, indexId, valueId, viewKind); } return emitAtomicsReadModifyWriteResult(objId, indexId, valueId, elementType, - AtomicsSub(elementType)); + viewKind, AtomicsSub(elementType)); } -bool CacheIRCompiler::emitAtomicsAndResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool CacheIRCompiler::emitAtomicsAndResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); if (Scalar::isBigIntType(elementType)) { - return emitAtomicsReadModifyWriteResult64<jit::AtomicsAnd64>(objId, indexId, - valueId); + return emitAtomicsReadModifyWriteResult64<jit::AtomicsAnd64>( + objId, indexId, valueId, viewKind); } return emitAtomicsReadModifyWriteResult(objId, indexId, valueId, elementType, - AtomicsAnd(elementType)); + viewKind, AtomicsAnd(elementType)); } -bool CacheIRCompiler::emitAtomicsOrResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool CacheIRCompiler::emitAtomicsOrResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); if (Scalar::isBigIntType(elementType)) { - return emitAtomicsReadModifyWriteResult64<jit::AtomicsOr64>(objId, indexId, - valueId); + return emitAtomicsReadModifyWriteResult64<jit::AtomicsOr64>( + objId, indexId, valueId, viewKind); } return emitAtomicsReadModifyWriteResult(objId, indexId, valueId, elementType, - AtomicsOr(elementType)); + viewKind, AtomicsOr(elementType)); } -bool CacheIRCompiler::emitAtomicsXorResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool CacheIRCompiler::emitAtomicsXorResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); if (Scalar::isBigIntType(elementType)) { - return emitAtomicsReadModifyWriteResult64<jit::AtomicsXor64>(objId, indexId, - valueId); + return emitAtomicsReadModifyWriteResult64<jit::AtomicsXor64>( + objId, indexId, valueId, viewKind); } return emitAtomicsReadModifyWriteResult(objId, indexId, valueId, elementType, - AtomicsXor(elementType)); + viewKind, AtomicsXor(elementType)); } bool CacheIRCompiler::emitAtomicsLoadResult(ObjOperandId objId, IntPtrOperandId indexId, - Scalar::Type elementType) { + Scalar::Type elementType, + ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); Maybe<AutoOutputRegister> output; @@ -9195,7 +9626,13 @@ bool CacheIRCompiler::emitAtomicsLoadResult(ObjOperandId objId, Register index = allocator.useRegister(masm, indexId); AutoScratchRegisterMaybeOutput scratch(allocator, masm, output ? *output : callvm->output()); - AutoSpectreBoundsScratchRegister spectreTemp(allocator, masm); + Maybe<AutoSpectreBoundsScratchRegister> spectreTemp; + Maybe<AutoScratchRegister> scratch2; + if (viewKind == ArrayBufferViewKind::FixedLength) { + spectreTemp.emplace(allocator, masm); + } else { + scratch2.emplace(allocator, masm); + } AutoAvailableFloatRegister floatReg(*this, FloatReg0); FailurePath* failure; @@ -9209,8 +9646,8 @@ bool CacheIRCompiler::emitAtomicsLoadResult(ObjOperandId objId, MOZ_ASSERT(isBaseline(), "Can't use FailurePath with AutoCallVM in Ion ICs"); // Bounds check. - masm.loadArrayBufferViewLengthIntPtr(obj, scratch); - masm.spectreBoundsCheckPtr(index, scratch, spectreTemp, failure->label()); + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch, scratch2, + spectreTemp, failure->label()); // Atomic operations are highly platform-dependent, for example x86/arm32 has // specific requirements on which registers are used. Therefore we're using a @@ -9221,7 +9658,7 @@ bool CacheIRCompiler::emitAtomicsLoadResult(ObjOperandId objId, masm.Push(index); masm.Push(obj); - using Fn = BigInt* (*)(JSContext*, FixedLengthTypedArrayObject*, size_t); + using Fn = BigInt* (*)(JSContext*, TypedArrayObject*, size_t); callvm->call<Fn, jit::AtomicsLoad64>(); return true; } @@ -9250,7 +9687,8 @@ bool CacheIRCompiler::emitAtomicsLoadResult(ObjOperandId objId, bool CacheIRCompiler::emitAtomicsStoreResult(ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, - Scalar::Type elementType) { + Scalar::Type elementType, + ArrayBufferViewKind viewKind) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); AutoOutputRegister output(*this); @@ -9264,9 +9702,13 @@ bool CacheIRCompiler::emitAtomicsStoreResult(ObjOperandId objId, valueBigInt.emplace(allocator.useRegister(masm, BigIntOperandId(valueId))); } AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + Maybe<AutoScratchRegisterMaybeOutputType> scratch2; + if (viewKind == ArrayBufferViewKind::Resizable) { + scratch2.emplace(allocator, masm, output); + } // Not enough registers on X86. - Register spectreTemp = Register::Invalid(); + constexpr auto spectreTemp = mozilla::Nothing{}; FailurePath* failure; if (!addFailurePath(&failure)) { @@ -9274,8 +9716,8 @@ bool CacheIRCompiler::emitAtomicsStoreResult(ObjOperandId objId, } // Bounds check. - masm.loadArrayBufferViewLengthIntPtr(obj, scratch); - masm.spectreBoundsCheckPtr(index, scratch, spectreTemp, failure->label()); + emitTypedArrayBoundsCheck(viewKind, obj, index, scratch, scratch2, + spectreTemp, failure->label()); if (!Scalar::isBigIntType(elementType)) { // Load the elements vector. @@ -9302,7 +9744,7 @@ bool CacheIRCompiler::emitAtomicsStoreResult(ObjOperandId objId, volatileRegs.takeUnchecked(scratch); masm.PushRegsInMask(volatileRegs); - using Fn = void (*)(FixedLengthTypedArrayObject*, size_t, const BigInt*); + using Fn = void (*)(TypedArrayObject*, size_t, const BigInt*); masm.setupUnalignedABICall(scratch); masm.passABIArg(obj); masm.passABIArg(index); diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h index 3b8941e242..69b1dd34ac 100644 --- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -25,7 +25,6 @@ class BigInt; namespace js { -class FixedLengthTypedArrayObject; class TypedArrayObject; enum class UnaryMathFunction : uint8_t; @@ -846,21 +845,37 @@ class MOZ_RAII CacheIRCompiler { bool emitDoubleIncDecResult(bool isInc, NumberOperandId inputId); - using AtomicsReadWriteModifyFn = int32_t (*)(FixedLengthTypedArrayObject*, - size_t, int32_t); + void emitTypedArrayBoundsCheck(ArrayBufferViewKind viewKind, Register obj, + Register index, Register scratch, + Register maybeScratch, Register spectreScratch, + Label* fail); + + void emitTypedArrayBoundsCheck(ArrayBufferViewKind viewKind, Register obj, + Register index, Register scratch, + mozilla::Maybe<Register> maybeScratch, + mozilla::Maybe<Register> spectreScratch, + Label* fail); + + void emitDataViewBoundsCheck(ArrayBufferViewKind viewKind, size_t byteSize, + Register obj, Register offset, Register scratch, + Register maybeScratch, Label* fail); + + using AtomicsReadWriteModifyFn = int32_t (*)(TypedArrayObject*, size_t, + int32_t); [[nodiscard]] bool emitAtomicsReadModifyWriteResult( ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, - Scalar::Type elementType, AtomicsReadWriteModifyFn fn); + Scalar::Type elementType, ArrayBufferViewKind viewKind, + AtomicsReadWriteModifyFn fn); - using AtomicsReadWriteModify64Fn = - JS::BigInt* (*)(JSContext*, FixedLengthTypedArrayObject*, size_t, - const JS::BigInt*); + using AtomicsReadWriteModify64Fn = JS::BigInt* (*)(JSContext*, + TypedArrayObject*, size_t, + const JS::BigInt*); template <AtomicsReadWriteModify64Fn fn> - [[nodiscard]] bool emitAtomicsReadModifyWriteResult64(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId); + [[nodiscard]] bool emitAtomicsReadModifyWriteResult64( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + ArrayBufferViewKind viewKind); void emitActivateIterator(Register objBeingIterated, Register iterObject, Register nativeIter, Register scratch, diff --git a/js/src/jit/CacheIRGenerator.h b/js/src/jit/CacheIRGenerator.h index 9880b82b71..2e15b2d8a6 100644 --- a/js/src/jit/CacheIRGenerator.h +++ b/js/src/jit/CacheIRGenerator.h @@ -636,6 +636,9 @@ class MOZ_RAII InlinableNativeIRGenerator { AttachDecision tryAttachIsConstructor(); AttachDecision tryAttachIsCrossRealmArrayConstructor(); AttachDecision tryAttachGuardToClass(InlinableNative native); + AttachDecision tryAttachGuardToClass(GuardClassKind kind); + AttachDecision tryAttachGuardToEitherClass(GuardClassKind kind1, + GuardClassKind kind2); AttachDecision tryAttachGuardToArrayBuffer(); AttachDecision tryAttachGuardToSharedArrayBuffer(); AttachDecision tryAttachHasClass(const JSClass* clasp, @@ -693,8 +696,8 @@ class MOZ_RAII InlinableNativeIRGenerator { AttachDecision tryAttachIsTypedArrayConstructor(); AttachDecision tryAttachTypedArrayByteOffset(); AttachDecision tryAttachTypedArrayElementSize(); - AttachDecision tryAttachTypedArrayLength(bool isPossiblyWrapped); - AttachDecision tryAttachTypedArrayLengthZeroOnOutOfBounds(); + AttachDecision tryAttachTypedArrayLength(bool isPossiblyWrapped, + bool allowOutOfBounds); AttachDecision tryAttachArrayBufferByteLength(bool isPossiblyWrapped); AttachDecision tryAttachIsConstructing(); AttachDecision tryAttachGetNextMapSetEntryForIterator(bool isMap); diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml index ccaf64d924..974404d5c0 100644 --- a/js/src/jit/CacheIROps.yaml +++ b/js/src/jit/CacheIROps.yaml @@ -272,6 +272,16 @@ obj: ObjId kind: GuardClassKindImm +# Guard per GuardClassKind. +- name: GuardEitherClass + shared: true + transpile: true + cost_estimate: 1 + args: + obj: ObjId + kind1: GuardClassKindImm + kind2: GuardClassKindImm + # Guard on a realm fuse. - name: GuardFuse shared: true @@ -472,6 +482,13 @@ args: obj: ObjId +- name: GuardIsResizableTypedArray + shared: true + transpile: true + cost_estimate: 1 + args: + obj: ObjId + - name: GuardHasProxyHandler shared: false transpile: true @@ -1189,6 +1206,20 @@ args: obj: ObjId +- name: ResizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: ResizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + - name: TypedArrayByteLengthInt32Result shared: true transpile: true @@ -1203,6 +1234,34 @@ args: obj: ObjId +- name: ResizableTypedArrayByteLengthInt32Result + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: ResizableTypedArrayByteLengthDoubleResult + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: ResizableTypedArrayLengthInt32Result + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: ResizableTypedArrayLengthDoubleResult + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + - name: TypedArrayElementSizeResult shared: true transpile: true @@ -1210,6 +1269,34 @@ args: obj: ObjId +- name: ResizableDataViewByteLengthInt32Result + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: ResizableDataViewByteLengthDoubleResult + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: GrowableSharedArrayBufferByteLengthInt32Result + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: GrowableSharedArrayBufferByteLengthDoubleResult + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + - name: GuardHasAttachedArrayBuffer shared: true transpile: true @@ -1217,6 +1304,20 @@ args: obj: ObjId +- name: GuardResizableArrayBufferViewInBounds + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + +- name: GuardResizableArrayBufferViewInBoundsOrDetached + shared: true + transpile: true + cost_estimate: 2 + args: + obj: ObjId + - name: NewArrayIteratorResult shared: true transpile: true @@ -1615,6 +1716,7 @@ index: IntPtrId rhs: RawId handleOOB: BoolImm + viewKind: ArrayBufferViewKindImm - name: AtomicsCompareExchangeResult shared: true @@ -1626,6 +1728,7 @@ expected: RawId replacement: RawId elementType: ScalarTypeImm + viewKind: ArrayBufferViewKindImm - name: AtomicsExchangeResult shared: true @@ -1636,6 +1739,7 @@ index: IntPtrId value: RawId elementType: ScalarTypeImm + viewKind: ArrayBufferViewKindImm - name: AtomicsAddResult shared: true @@ -1647,6 +1751,7 @@ value: RawId elementType: ScalarTypeImm forEffect: BoolImm + viewKind: ArrayBufferViewKindImm - name: AtomicsSubResult shared: true @@ -1658,6 +1763,7 @@ value: RawId elementType: ScalarTypeImm forEffect: BoolImm + viewKind: ArrayBufferViewKindImm - name: AtomicsAndResult shared: true @@ -1669,6 +1775,7 @@ value: RawId elementType: ScalarTypeImm forEffect: BoolImm + viewKind: ArrayBufferViewKindImm - name: AtomicsOrResult shared: true @@ -1680,6 +1787,7 @@ value: RawId elementType: ScalarTypeImm forEffect: BoolImm + viewKind: ArrayBufferViewKindImm - name: AtomicsXorResult shared: true @@ -1691,6 +1799,7 @@ value: RawId elementType: ScalarTypeImm forEffect: BoolImm + viewKind: ArrayBufferViewKindImm - name: AtomicsLoadResult shared: true @@ -1700,6 +1809,7 @@ obj: ObjId index: IntPtrId elementType: ScalarTypeImm + viewKind: ArrayBufferViewKindImm - name: AtomicsStoreResult shared: true @@ -1710,6 +1820,7 @@ index: IntPtrId value: RawId elementType: ScalarTypeImm + viewKind: ArrayBufferViewKindImm - name: AtomicsIsLockFreeResult shared: true @@ -2051,6 +2162,7 @@ args: obj: ObjId index: IntPtrId + viewKind: ArrayBufferViewKindImm - name: LoadDenseElementHoleExistsResult shared: true @@ -2070,6 +2182,7 @@ elementType: ScalarTypeImm handleOOB: BoolImm forceDoubleForUint32: BoolImm + viewKind: ArrayBufferViewKindImm - name: LoadDataViewValueResult shared: true @@ -2081,6 +2194,7 @@ littleEndian: BooleanId elementType: ScalarTypeImm forceDoubleForUint32: BoolImm + viewKind: ArrayBufferViewKindImm - name: StoreDataViewValueResult shared: true @@ -2092,6 +2206,7 @@ value: RawId littleEndian: BooleanId elementType: ScalarTypeImm + viewKind: ArrayBufferViewKindImm - name: LoadInt32ArrayLengthResult shared: true diff --git a/js/src/jit/CacheIRReader.h b/js/src/jit/CacheIRReader.h index affefdac01..54b298c999 100644 --- a/js/src/jit/CacheIRReader.h +++ b/js/src/jit/CacheIRReader.h @@ -96,6 +96,9 @@ class MOZ_RAII CacheIRReader { uint32_t stubOffset() { return buffer_.readByte() * sizeof(uintptr_t); } GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); } + ArrayBufferViewKind arrayBufferViewKind() { + return ArrayBufferViewKind(buffer_.readByte()); + } ValueType valueType() { return ValueType(buffer_.readByte()); } wasm::ValType::Kind wasmValType() { return wasm::ValType::Kind(buffer_.readByte()); diff --git a/js/src/jit/CacheIRSpewer.cpp b/js/src/jit/CacheIRSpewer.cpp index 921da75d61..613e0f7d85 100644 --- a/js/src/jit/CacheIRSpewer.cpp +++ b/js/src/jit/CacheIRSpewer.cpp @@ -106,6 +106,9 @@ class MOZ_RAII CacheIROpsJitSpewer { void spewGuardClassKindImm(const char* name, GuardClassKind kind) { out_.printf("%s GuardClassKind(%u)", name, unsigned(kind)); } + void spewArrayBufferViewKindImm(const char* name, ArrayBufferViewKind kind) { + out_.printf("%s ArrayBufferViewKind(%u)", name, unsigned(kind)); + } void spewWasmValTypeImm(const char* name, wasm::ValType::Kind kind) { out_.printf("%s WasmValTypeKind(%u)", name, unsigned(kind)); } @@ -251,6 +254,9 @@ class MOZ_RAII CacheIROpsJSONSpewer { void spewGuardClassKindImm(const char* name, GuardClassKind kind) { spewArgImpl(name, "Imm", unsigned(kind)); } + void spewArrayBufferViewKindImm(const char* name, ArrayBufferViewKind kind) { + spewArgImpl(name, "Imm", unsigned(kind)); + } void spewRealmFuseIndexImm(const char* name, RealmFuses::FuseIndex kind) { spewArgImpl(name, "Imm", unsigned(kind)); } diff --git a/js/src/jit/CacheIRWriter.h b/js/src/jit/CacheIRWriter.h index 454a1b2511..6a32885d7c 100644 --- a/js/src/jit/CacheIRWriter.h +++ b/js/src/jit/CacheIRWriter.h @@ -262,6 +262,11 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { "GuardClassKind must fit in a byte"); buffer_.writeByte(uint8_t(kind)); } + void writeArrayBufferViewKindImm(ArrayBufferViewKind kind) { + static_assert(sizeof(ArrayBufferViewKind) == sizeof(uint8_t), + "ArrayBufferViewKind must fit in a byte"); + buffer_.writeByte(uint8_t(kind)); + } void writeValueTypeImm(ValueType type) { static_assert(sizeof(ValueType) == sizeof(uint8_t), "ValueType must fit in uint8_t"); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 2c41acc736..10a69f0cb3 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2167,8 +2167,8 @@ class CreateDependentString { NotInlineString, Count }; - mozilla::EnumeratedArray<FallbackKind, FallbackKind::Count, Label> fallbacks_, - joins_; + mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)> + fallbacks_, joins_; public: CreateDependentString(CharEncoding encoding, Register string, Register temp1, @@ -4632,6 +4632,17 @@ void CodeGenerator::visitGuardIsFixedLengthTypedArray( bailoutFrom(&bail, guard->snapshot()); } +void CodeGenerator::visitGuardIsResizableTypedArray( + LGuardIsResizableTypedArray* guard) { + Register obj = ToRegister(guard->input()); + Register temp = ToRegister(guard->temp0()); + + Label bail; + masm.loadObjClassUnsafe(obj, temp); + masm.branchIfClassIsNotResizableTypedArray(temp, &bail); + bailoutFrom(&bail, guard->snapshot()); +} + void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) { Register obj = ToRegister(guard->input()); @@ -9660,6 +9671,68 @@ void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) { masm.typedArrayElementSize(obj, out); } +void CodeGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds( + LResizableTypedArrayByteOffsetMaybeOutOfBounds* lir) { + Register obj = ToRegister(lir->object()); + Register out = ToRegister(lir->output()); + Register temp = ToRegister(lir->temp0()); + + masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, out, temp); +} + +void CodeGenerator::visitResizableTypedArrayLength( + LResizableTypedArrayLength* lir) { + Register obj = ToRegister(lir->object()); + Register out = ToRegister(lir->output()); + Register temp = ToRegister(lir->temp0()); + + masm.loadResizableTypedArrayLengthIntPtr(lir->synchronization(), obj, out, + temp); +} + +void CodeGenerator::visitResizableDataViewByteLength( + LResizableDataViewByteLength* lir) { + Register obj = ToRegister(lir->object()); + Register out = ToRegister(lir->output()); + Register temp = ToRegister(lir->temp0()); + + masm.loadResizableDataViewByteLengthIntPtr(lir->synchronization(), obj, out, + temp); +} + +void CodeGenerator::visitGrowableSharedArrayBufferByteLength( + LGrowableSharedArrayBufferByteLength* lir) { + Register obj = ToRegister(lir->object()); + Register out = ToRegister(lir->output()); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto sync = Synchronization::Load(); + + masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out); +} + +void CodeGenerator::visitGuardResizableArrayBufferViewInBounds( + LGuardResizableArrayBufferViewInBounds* lir) { + Register obj = ToRegister(lir->object()); + Register temp = ToRegister(lir->temp0()); + + Label bail; + masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail); + bailoutFrom(&bail, lir->snapshot()); +} + +void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached( + LGuardResizableArrayBufferViewInBoundsOrDetached* lir) { + Register obj = ToRegister(lir->object()); + Register temp = ToRegister(lir->temp0()); + + Label done, bail; + masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done); + masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail); + masm.bind(&done); + bailoutFrom(&bail, lir->snapshot()); +} + void CodeGenerator::visitGuardHasAttachedArrayBuffer( LGuardHasAttachedArrayBuffer* lir) { Register obj = ToRegister(lir->object()); @@ -15039,15 +15112,19 @@ static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, // REG DUMP AREA, if any. size_t regDumpWords = 0; const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs(); - GeneralRegisterForwardIterator wasmAnyRefRegsIter(wasmAnyRefRegs); + const LiveGeneralRegisterSet slotsOrElementsRegs = + safepoint.slotsOrElementsRegs(); + const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union( + wasmAnyRefRegs.set(), slotsOrElementsRegs.set())); + GeneralRegisterForwardIterator refRegsIter(refRegs); switch (safepoint.wasmSafepointKind()) { case WasmSafepointKind::LirCall: case WasmSafepointKind::CodegenCall: { size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*); regDumpWords += spilledNumWords; - for (; wasmAnyRefRegsIter.more(); ++wasmAnyRefRegsIter) { - Register reg = *wasmAnyRefRegsIter; + for (; refRegsIter.more(); ++refRegsIter) { + Register reg = *refRegsIter; size_t offsetFromSpillBase = safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) / sizeof(void*); @@ -15055,9 +15132,13 @@ static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, offsetFromSpillBase <= spilledNumWords); size_t index = spilledNumWords - offsetFromSpillBase; - stackMap->set(index, wasm::StackMap::AnyRef); + if (wasmAnyRefRegs.has(reg)) { + stackMap->set(index, wasm::StackMap::AnyRef); + } else { + MOZ_ASSERT(slotsOrElementsRegs.has(reg)); + stackMap->set(index, wasm::StackMap::ArrayDataPointer); + } } - // Float and vector registers do not have to be handled; they cannot // contain wasm anyrefs, and they are spilled after general-purpose // registers. Gprs are therefore closest to the spill base and thus their @@ -15066,8 +15147,8 @@ static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, case WasmSafepointKind::Trap: { regDumpWords += trapExitLayoutNumWords; - for (; wasmAnyRefRegsIter.more(); ++wasmAnyRefRegsIter) { - Register reg = *wasmAnyRefRegsIter; + for (; refRegsIter.more(); ++refRegsIter) { + Register reg = *refRegsIter; size_t offsetFromTop = trapExitLayout.getOffset(reg); // If this doesn't hold, the associated register wasn't saved by @@ -15080,7 +15161,12 @@ static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, // offset up from the bottom of the (integer register) save area. size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop; - stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef); + if (wasmAnyRefRegs.has(reg)) { + stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef); + } else { + MOZ_ASSERT(slotsOrElementsRegs.has(reg)); + stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer); + } } } break; default: @@ -17263,25 +17349,20 @@ void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) { void CodeGenerator::visitLoadTypedArrayElementHole( LLoadTypedArrayElementHole* lir) { - Register object = ToRegister(lir->object()); + Register elements = ToRegister(lir->elements()); + Register index = ToRegister(lir->index()); + Register length = ToRegister(lir->length()); const ValueOperand out = ToOutValue(lir); - // Load the length. Register scratch = out.scratchReg(); - Register scratch2 = ToRegister(lir->temp0()); - Register index = ToRegister(lir->index()); - masm.loadArrayBufferViewLengthIntPtr(object, scratch); // Load undefined if index >= length. Label outOfBounds, done; - masm.spectreBoundsCheckPtr(index, scratch, scratch2, &outOfBounds); - - // Load the elements vector. - masm.loadPtr(Address(object, ArrayBufferViewObject::dataOffset()), scratch); + masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds); Scalar::Type arrayType = lir->mir()->arrayType(); Label fail; - BaseIndex source(scratch, index, ScaleFromScalarType(arrayType)); + BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); MacroAssembler::Uint32Mode uint32Mode = lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble : MacroAssembler::Uint32Mode::FailOnDouble; @@ -17301,37 +17382,38 @@ void CodeGenerator::visitLoadTypedArrayElementHole( void CodeGenerator::visitLoadTypedArrayElementHoleBigInt( LLoadTypedArrayElementHoleBigInt* lir) { - Register object = ToRegister(lir->object()); + Register elements = ToRegister(lir->elements()); + Register index = ToRegister(lir->index()); + Register length = ToRegister(lir->length()); const ValueOperand out = ToOutValue(lir); - // On x86 there are not enough registers. In that case reuse the output's - // type register as temporary. + Register temp = ToRegister(lir->temp()); + + // On x86 there are not enough registers. In that case reuse the output + // registers as temporaries. #ifdef JS_CODEGEN_X86 - MOZ_ASSERT(lir->temp()->isBogusTemp()); - Register temp = out.typeReg(); + MOZ_ASSERT(lir->temp64().isBogusTemp()); + Register64 temp64 = out.toRegister64(); #else - Register temp = ToRegister(lir->temp()); -#endif Register64 temp64 = ToRegister64(lir->temp64()); - - // Load the length. - Register scratch = out.scratchReg(); - Register index = ToRegister(lir->index()); - masm.loadArrayBufferViewLengthIntPtr(object, scratch); +#endif // Load undefined if index >= length. Label outOfBounds, done; - masm.spectreBoundsCheckPtr(index, scratch, temp, &outOfBounds); - - // Load the elements vector. - masm.loadPtr(Address(object, ArrayBufferViewObject::dataOffset()), scratch); + masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds); Scalar::Type arrayType = lir->mir()->arrayType(); - BaseIndex source(scratch, index, ScaleFromScalarType(arrayType)); + BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); masm.load64(source, temp64); +#ifdef JS_CODEGEN_X86 + Register bigInt = temp; + Register maybeTemp = InvalidReg; +#else Register bigInt = out.scratchReg(); - emitCreateBigInt(lir, arrayType, temp64, bigInt, temp); + Register maybeTemp = temp; +#endif + emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp); masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out); masm.jump(&done); @@ -17679,6 +17761,10 @@ void CodeGenerator::visitStoreTypedArrayElementHoleBigInt( masm.bind(&skip); } +void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { + masm.memoryBarrier(ins->type()); +} + void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) { Register value = ToRegister(lir->value()); Register output = ToRegister(lir->output()); @@ -18453,6 +18539,24 @@ void CodeGenerator::visitGuardToClass(LGuardToClass* ins) { bailoutFrom(¬Equal, ins->snapshot()); } +void CodeGenerator::visitGuardToEitherClass(LGuardToEitherClass* ins) { + Register lhs = ToRegister(ins->lhs()); + Register temp = ToRegister(ins->temp0()); + + // branchTestObjClass may zero the object register on speculative paths + // (we should have a defineReuseInput allocation in this case). + Register spectreRegToZero = lhs; + + Label notEqual; + + masm.branchTestObjClass(Assembler::NotEqual, lhs, + {ins->mir()->getClass1(), ins->mir()->getClass2()}, + temp, spectreRegToZero, ¬Equal); + + // Can't return null-return here, so bail. + bailoutFrom(¬Equal, ins->snapshot()); +} + void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) { Register lhs = ToRegister(ins->lhs()); Register temp = ToRegister(ins->temp0()); @@ -20133,7 +20237,8 @@ void CodeGenerator::visitToHashableString(LToHashableString* ins) { Address(input, JSString::offsetOfFlags()), Imm32(JSString::ATOM_BIT), &isAtom); - masm.lookupStringInAtomCacheLastLookups(input, output, ool->entry()); + masm.lookupStringInAtomCacheLastLookups(input, output, output, ool->entry()); + masm.jump(ool->rejoin()); masm.bind(&isAtom); masm.movePtr(input, output); masm.bind(ool->rejoin()); diff --git a/js/src/jit/Disassemble.cpp b/js/src/jit/Disassemble.cpp index 652c381ce7..df768d4fd1 100644 --- a/js/src/jit/Disassemble.cpp +++ b/js/src/jit/Disassemble.cpp @@ -22,6 +22,8 @@ # include "jit/arm64/vixl/Instructions-vixl.h" // vixl::Instruction # elif defined(JS_CODEGEN_ARM) # include "jit/arm/disasm/Disasm-arm.h" // js::jit::disasm::* +# elif defined(JS_CODEGEN_RISCV64) +# include "jit/riscv64/disasm/Disasm-riscv64.h" // js::jit::disasm::* # endif #endif @@ -99,6 +101,31 @@ void Disassemble(uint8_t* code, size_t length, InstrCallback callback) { } } +#elif defined(JS_JITSPEW) && defined(JS_CODEGEN_RISCV64) + +bool HasDisassembler() { return true; } + +void Disassemble(uint8_t* code, size_t length, InstrCallback callback) { + disasm::NameConverter converter; + disasm::Disassembler d(converter); + + uint8_t* instr = code; + uint8_t* end = code + length; + + while (instr < end) { + EmbeddedVector<char, ReasonableBufferSize> buffer; + buffer[0] = '\0'; + uint8_t* next_instr = instr + d.InstructionDecode(buffer, instr); + + JS::UniqueChars formatted = + JS_smprintf("0x%p %08x %s", instr, *reinterpret_cast<int32_t*>(instr), + buffer.start()); + callback(formatted.get()); + + instr = next_instr; + } +} + #else bool HasDisassembler() { return false; } diff --git a/js/src/jit/ExecutableAllocator.h b/js/src/jit/ExecutableAllocator.h index 85c01562c3..02c8727e85 100644 --- a/js/src/jit/ExecutableAllocator.h +++ b/js/src/jit/ExecutableAllocator.h @@ -72,7 +72,8 @@ class ExecutablePool { bool m_mark : 1; // Number of bytes currently allocated for each CodeKind. - mozilla::EnumeratedArray<CodeKind, CodeKind::Count, size_t> m_codeBytes; + mozilla::EnumeratedArray<CodeKind, size_t, size_t(CodeKind::Count)> + m_codeBytes; public: void release(bool willDestroy = false); diff --git a/js/src/jit/GenerateAtomicOperations.py b/js/src/jit/GenerateAtomicOperations.py index 8e37e5dcd6..9194b8b685 100644 --- a/js/src/jit/GenerateAtomicOperations.py +++ b/js/src/jit/GenerateAtomicOperations.py @@ -50,8 +50,6 @@ def gen_load(fun_name, cpp_type, size, barrier): # - MacroAssembler::wasmLoad if cpu_arch in ("x86", "x86_64"): insns = "" - if barrier: - insns += fmt_insn("mfence") if size == 8: insns += fmt_insn("movb (%[arg]), %[res]") elif size == 16: @@ -61,8 +59,6 @@ def gen_load(fun_name, cpp_type, size, barrier): else: assert size == 64 insns += fmt_insn("movq (%[arg]), %[res]") - if barrier: - insns += fmt_insn("mfence") return """ INLINE_ATTR %(cpp_type)s %(fun_name)s(const %(cpp_type)s* arg) { %(cpp_type)s res; @@ -78,8 +74,6 @@ def gen_load(fun_name, cpp_type, size, barrier): } if cpu_arch == "aarch64": insns = "" - if barrier: - insns += fmt_insn("dmb ish") if size == 8: insns += fmt_insn("ldrb %w[res], [%x[arg]]") elif size == 16: @@ -106,8 +100,6 @@ def gen_load(fun_name, cpp_type, size, barrier): } if cpu_arch == "arm": insns = "" - if barrier: - insns += fmt_insn("dmb sy") if size == 8: insns += fmt_insn("ldrb %[res], [%[arg]]") elif size == 16: @@ -141,8 +133,6 @@ def gen_store(fun_name, cpp_type, size, barrier): # - MacroAssembler::wasmStore if cpu_arch in ("x86", "x86_64"): insns = "" - if barrier: - insns += fmt_insn("mfence") if size == 8: insns += fmt_insn("movb %[val], (%[addr])") elif size == 16: diff --git a/js/src/jit/GenerateCacheIRFiles.py b/js/src/jit/GenerateCacheIRFiles.py index 5cecf82e64..d71c70b753 100644 --- a/js/src/jit/GenerateCacheIRFiles.py +++ b/js/src/jit/GenerateCacheIRFiles.py @@ -82,6 +82,7 @@ arg_writer_info = { "BoolImm": ("bool", "writeBoolImm"), "ByteImm": ("uint32_t", "writeByteImm"), # uint32_t to enable fits-in-byte asserts. "GuardClassKindImm": ("GuardClassKind", "writeGuardClassKindImm"), + "ArrayBufferViewKindImm": ("ArrayBufferViewKind", "writeArrayBufferViewKindImm"), "ValueTypeImm": ("ValueType", "writeValueTypeImm"), "JSWhyMagicImm": ("JSWhyMagic", "writeJSWhyMagicImm"), "CallFlagsImm": ("CallFlags", "writeCallFlagsImm"), @@ -184,6 +185,11 @@ arg_reader_info = { "BoolImm": ("bool", "", "reader.readBool()"), "ByteImm": ("uint8_t", "", "reader.readByte()"), "GuardClassKindImm": ("GuardClassKind", "", "reader.guardClassKind()"), + "ArrayBufferViewKindImm": ( + "ArrayBufferViewKind", + "", + "reader.arrayBufferViewKind()", + ), "ValueTypeImm": ("ValueType", "", "reader.valueType()"), "JSWhyMagicImm": ("JSWhyMagic", "", "reader.whyMagic()"), "CallFlagsImm": ("CallFlags", "", "reader.callFlags()"), @@ -272,6 +278,7 @@ arg_spewer_method = { "BoolImm": "spewBoolImm", "ByteImm": "spewByteImm", "GuardClassKindImm": "spewGuardClassKindImm", + "ArrayBufferViewKindImm": "spewArrayBufferViewKindImm", "ValueTypeImm": "spewValueTypeImm", "JSWhyMagicImm": "spewJSWhyMagicImm", "CallFlagsImm": "spewCallFlagsImm", @@ -415,6 +422,7 @@ arg_length = { "JSOpImm": 1, "ValueTypeImm": 1, "GuardClassKindImm": 1, + "ArrayBufferViewKindImm": 1, "JSWhyMagicImm": 1, "WasmValTypeImm": 1, "Int32Imm": 4, diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index a0c9a51c39..543ed0eb83 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -747,13 +747,13 @@ static bool IsDiamondPattern(MBasicBlock* initialBlock) { MTest* initialTest = ins->toTest(); MBasicBlock* trueBranch = initialTest->ifTrue(); - if (trueBranch->numPredecessors() != 1 || trueBranch->numSuccessors() != 1) { + if (trueBranch->numPredecessors() != 1 || !trueBranch->lastIns()->isGoto()) { return false; } MBasicBlock* falseBranch = initialTest->ifFalse(); if (falseBranch->numPredecessors() != 1 || - falseBranch->numSuccessors() != 1) { + !falseBranch->lastIns()->isGoto()) { return false; } @@ -2228,6 +2228,7 @@ bool TypeAnalyzer::adjustPhiInputs(MPhi* phi) { phi->replaceOperand(i, in->toBox()->input()); } else { MInstruction* replacement; + MBasicBlock* predecessor = phi->block()->getPredecessor(i); if (phiType == MIRType::Double && IsFloatType(in->type())) { // Convert int32 operands to double. @@ -2239,14 +2240,14 @@ bool TypeAnalyzer::adjustPhiInputs(MPhi* phi) { // See comment below if (in->type() != MIRType::Value) { MBox* box = MBox::New(alloc(), in); - in->block()->insertBefore(in->block()->lastIns(), box); + predecessor->insertAtEnd(box); in = box; } MUnbox* unbox = MUnbox::New(alloc(), in, MIRType::Double, MUnbox::Fallible); unbox->setBailoutKind(BailoutKind::SpeculativePhi); - in->block()->insertBefore(in->block()->lastIns(), unbox); + predecessor->insertAtEnd(unbox); replacement = MToFloat32::New(alloc(), in); } } else { @@ -2255,7 +2256,7 @@ bool TypeAnalyzer::adjustPhiInputs(MPhi* phi) { // below. if (in->type() != MIRType::Value) { MBox* box = MBox::New(alloc(), in); - in->block()->insertBefore(in->block()->lastIns(), box); + predecessor->insertAtEnd(box); in = box; } @@ -2265,7 +2266,7 @@ bool TypeAnalyzer::adjustPhiInputs(MPhi* phi) { } replacement->setBailoutKind(BailoutKind::SpeculativePhi); - in->block()->insertBefore(in->block()->lastIns(), replacement); + predecessor->insertAtEnd(replacement); phi->replaceOperand(i, replacement); } } @@ -4452,6 +4453,10 @@ static bool NeedsKeepAlive(MInstruction* slotsOrElements, MInstruction* use) { if (use->type() == MIRType::BigInt) { return true; } + if (use->isLoadTypedArrayElementHole() && + Scalar::isBigIntType(use->toLoadTypedArrayElementHole()->arrayType())) { + return true; + } MBasicBlock* block = use->block(); MInstructionIterator iter(block->begin(slotsOrElements)); diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h index e68dfaa124..92e4586131 100644 --- a/js/src/jit/IonOptimizationLevels.h +++ b/js/src/jit/IonOptimizationLevels.h @@ -181,8 +181,8 @@ class OptimizationInfo { class OptimizationLevelInfo { private: - mozilla::EnumeratedArray<OptimizationLevel, OptimizationLevel::Count, - OptimizationInfo> + mozilla::EnumeratedArray<OptimizationLevel, OptimizationInfo, + size_t(OptimizationLevel::Count)> infos_; public: diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 7b3cb1184e..176b988e05 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -20,7 +20,6 @@ #include "jit/JitRuntime.h" #include "jit/JitSpewer.h" #include "jit/LIR.h" -#include "jit/PcScriptCache.h" #include "jit/Recover.h" #include "jit/Safepoints.h" #include "jit/ScriptFromCalleeToken.h" @@ -922,32 +921,32 @@ static void TraceThisAndArguments(JSTracer* trc, const JSJitFrameIter& frame, return; } - size_t nargs = layout->numActualArgs(); - size_t nformals = 0; - JSFunction* fun = CalleeTokenToFunction(layout->calleeToken()); + + size_t numFormals = fun->nargs(); + size_t numArgs = std::max(layout->numActualArgs(), numFormals); + size_t firstArg = 0; + if (frame.type() != FrameType::JSJitToWasm && !frame.isExitFrameLayout<CalledFromJitExitFrameLayout>() && !fun->nonLazyScript()->mayReadFrameArgsDirectly()) { - nformals = fun->nargs(); + firstArg = numFormals; } - size_t newTargetOffset = std::max(nargs, fun->nargs()); - Value* argv = layout->thisAndActualArgs(); // Trace |this|. TraceRoot(trc, argv, "ion-thisv"); - // Trace actual arguments beyond the formals. Note + 1 for thisv. - for (size_t i = nformals + 1; i < nargs + 1; i++) { - TraceRoot(trc, &argv[i], "ion-argv"); + // Trace arguments. Note + 1 for thisv. + for (size_t i = firstArg; i < numArgs; i++) { + TraceRoot(trc, &argv[i + 1], "ion-argv"); } // Always trace the new.target from the frame. It's not in the snapshots. // +1 to pass |this| if (CalleeTokenIsConstructing(layout->calleeToken())) { - TraceRoot(trc, &argv[1 + newTargetOffset], "ion-newTarget"); + TraceRoot(trc, &argv[1 + numArgs], "ion-newTarget"); } } @@ -1539,90 +1538,6 @@ JSScript* GetTopJitJSScript(JSContext* cx) { return frame.script(); } -void GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes) { - JitSpew(JitSpew_IonSnapshots, "Recover PC & Script from the last frame."); - - // Recover the return address so that we can look it up in the - // PcScriptCache, as script/pc computation is expensive. - JitActivationIterator actIter(cx); - OnlyJSJitFrameIter it(actIter); - uint8_t* retAddr; - if (it.frame().isExitFrame()) { - ++it; - - // Skip baseline interpreter entry frames. - // Can exist before rectifier frames. - if (it.frame().isBaselineInterpreterEntry()) { - ++it; - } - - // Skip rectifier frames. - if (it.frame().isRectifier()) { - ++it; - MOZ_ASSERT(it.frame().isBaselineStub() || it.frame().isBaselineJS() || - it.frame().isIonJS()); - } - - // Skip Baseline/Ion stub and IC call frames. - if (it.frame().isBaselineStub()) { - ++it; - MOZ_ASSERT(it.frame().isBaselineJS()); - } else if (it.frame().isIonICCall()) { - ++it; - MOZ_ASSERT(it.frame().isIonJS()); - } - - MOZ_ASSERT(it.frame().isBaselineJS() || it.frame().isIonJS()); - - // Don't use the return address and the cache if the BaselineFrame is - // running in the Baseline Interpreter. In this case the bytecode pc is - // cheap to get, so we won't benefit from the cache, and the return address - // does not map to a single bytecode pc. - if (it.frame().isBaselineJS() && - it.frame().baselineFrame()->runningInInterpreter()) { - it.frame().baselineScriptAndPc(scriptRes, pcRes); - return; - } - - retAddr = it.frame().resumePCinCurrentFrame(); - } else { - MOZ_ASSERT(it.frame().isBailoutJS()); - retAddr = it.frame().returnAddress(); - } - - MOZ_ASSERT(retAddr); - - uint32_t hash = PcScriptCache::Hash(retAddr); - - // Lazily initialize the cache. The allocation may safely fail and will not - // GC. - if (MOZ_UNLIKELY(cx->ionPcScriptCache == nullptr)) { - cx->ionPcScriptCache = - MakeUnique<PcScriptCache>(cx->runtime()->gc.gcNumber()); - } - - if (cx->ionPcScriptCache.ref() && - cx->ionPcScriptCache->get(cx->runtime(), hash, retAddr, scriptRes, - pcRes)) { - return; - } - - // Lookup failed: undertake expensive process to determine script and pc. - if (it.frame().isIonJS() || it.frame().isBailoutJS()) { - InlineFrameIterator ifi(cx, &it.frame()); - *scriptRes = ifi.script(); - *pcRes = ifi.pc(); - } else { - MOZ_ASSERT(it.frame().isBaselineJS()); - it.frame().baselineScriptAndPc(scriptRes, pcRes); - } - - // Add entry to cache. - if (cx->ionPcScriptCache.ref()) { - cx->ionPcScriptCache->add(hash, retAddr, *pcRes, *scriptRes); - } -} - RInstructionResults::RInstructionResults(JitFrameLayout* fp) : results_(nullptr), fp_(fp), initialized_(false) {} diff --git a/js/src/jit/JitFrames.h b/js/src/jit/JitFrames.h index fe9b2942d3..ab882e7986 100644 --- a/js/src/jit/JitFrames.h +++ b/js/src/jit/JitFrames.h @@ -771,8 +771,6 @@ class InvalidationBailoutStack { void checkInvariants() const; }; -void GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes); - // Baseline requires one slot for this/argument type checks. static const uint32_t MinJITStackSize = 1; diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index f8cdbef8ba..e9d389cf60 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -376,6 +376,10 @@ DefaultJitOptions::DefaultJitOptions() { // ***** Irregexp shim flags ***** + // Whether the stage 3 regexp modifiers proposal is enabled. + SET_DEFAULT(js_regexp_modifiers, false); + // Whether the stage 3 duplicate named capture groups proposal is enabled. + SET_DEFAULT(js_regexp_duplicate_named_groups, false); // V8 uses this for differential fuzzing to handle stack overflows. // We address the same problem in StackLimitCheck::HasOverflowed. SET_DEFAULT(correctness_fuzzer_suppressions, false); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index fd5a9726ed..d1fcae081c 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -143,6 +143,8 @@ struct DefaultJitOptions { // Irregexp shim flags bool correctness_fuzzer_suppressions; bool enable_regexp_unaligned_accesses; + bool js_regexp_modifiers; + bool js_regexp_duplicate_named_groups; bool regexp_possessive_quantifier; bool regexp_optimization; bool regexp_peephole_optimization; diff --git a/js/src/jit/JitRuntime.h b/js/src/jit/JitRuntime.h index d0ce8422de..7d038ed0e2 100644 --- a/js/src/jit/JitRuntime.h +++ b/js/src/jit/JitRuntime.h @@ -75,15 +75,15 @@ enum class BailoutReturnKind { class BaselineICFallbackCode { JitCode* code_ = nullptr; using OffsetArray = - mozilla::EnumeratedArray<BaselineICFallbackKind, - BaselineICFallbackKind::Count, uint32_t>; + mozilla::EnumeratedArray<BaselineICFallbackKind, uint32_t, + size_t(BaselineICFallbackKind::Count)>; OffsetArray offsets_ = {}; // Keep track of offset into various baseline stubs' code at return // point from called script. using BailoutReturnArray = - mozilla::EnumeratedArray<BailoutReturnKind, BailoutReturnKind::Count, - uint32_t>; + mozilla::EnumeratedArray<BailoutReturnKind, uint32_t, + size_t(BailoutReturnKind::Count)>; BailoutReturnArray bailoutReturnOffsets_ = {}; public: @@ -175,13 +175,13 @@ class JitRuntime { WriteOnceData<uint32_t> doubleToInt32ValueStubOffset_{0}; // Thunk to do a generic call from Ion. - mozilla::EnumeratedArray<IonGenericCallKind, IonGenericCallKind::Count, - WriteOnceData<uint32_t>> + mozilla::EnumeratedArray<IonGenericCallKind, WriteOnceData<uint32_t>, + size_t(IonGenericCallKind::Count)> ionGenericCallStubOffset_; // Thunk used by the debugger for breakpoint and step mode. - mozilla::EnumeratedArray<DebugTrapHandlerKind, DebugTrapHandlerKind::Count, - WriteOnceData<JitCode*>> + mozilla::EnumeratedArray<DebugTrapHandlerKind, WriteOnceData<JitCode*>, + size_t(DebugTrapHandlerKind::Count)> debugTrapHandlers_; // BaselineInterpreter state. diff --git a/js/src/jit/JitScript.cpp b/js/src/jit/JitScript.cpp index f2f6ee2c25..62a14a70b6 100644 --- a/js/src/jit/JitScript.cpp +++ b/js/src/jit/JitScript.cpp @@ -517,7 +517,13 @@ void ICScript::purgeStubs(Zone* zone, ICStubSpace& newStubSpace) { if (fallback->trialInliningState() == TrialInliningState::Inlined && hasInlinedChild(fallback->pcOffset())) { MOZ_ASSERT(active()); - MOZ_ASSERT(findInlinedChild(fallback->pcOffset())->active()); +#ifdef DEBUG + // The callee script must be active. Also assert its bytecode size field + // is valid, because this helps catch memory safety issues (bug 1871947). + ICScript* callee = findInlinedChild(fallback->pcOffset()); + MOZ_ASSERT(callee->active()); + MOZ_ASSERT(callee->bytecodeSize() < inliningRoot()->totalBytecodeSize()); +#endif JSRuntime* rt = zone->runtimeFromMainThread(); ICCacheIRStub* prev = nullptr; @@ -718,6 +724,9 @@ static void MarkActiveICScriptsAndCopyStubs( ICCacheIRStub* newStub = stub->clone(cx->runtime(), newStubSpace); layout->setStubPtr(newStub); + // If this is a trial-inlining call site, also preserve the callee + // ICScript. Inlined constructor calls invoke CreateThisFromIC (which + // can trigger GC) before using the inlined ICScript. JSJitFrameIter parentFrame(frame); ++parentFrame; BaselineFrame* blFrame = parentFrame.baselineFrame(); diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp index 6fcd25d6e3..11e3165240 100644 --- a/js/src/jit/JitSpewer.cpp +++ b/js/src/jit/JitSpewer.cpp @@ -369,7 +369,6 @@ static void PrintHelpAndExit(int status = 0) { "compiled functions only).\n" " profiling Profiling-related information\n" " dump-mir-expr Dump the MIR expressions\n" - " scriptstats Tracelogger summary stats\n" " warp-snapshots WarpSnapshots created by WarpOracle\n" " warp-transpiler Warp CacheIR transpiler\n" " warp-trial-inlining Trial inlining for Warp\n" @@ -475,8 +474,6 @@ void jit::CheckLogging() { EnableChannel(JitSpew_Profiling); } else if (IsFlag(found, "dump-mir-expr")) { EnableChannel(JitSpew_MIRExpressions); - } else if (IsFlag(found, "scriptstats")) { - EnableChannel(JitSpew_ScriptStats); } else if (IsFlag(found, "warp-snapshots")) { EnableChannel(JitSpew_WarpSnapshots); } else if (IsFlag(found, "warp-transpiler")) { diff --git a/js/src/jit/JitSpewer.h b/js/src/jit/JitSpewer.h index 2cc56d9cf7..bfc92c74f2 100644 --- a/js/src/jit/JitSpewer.h +++ b/js/src/jit/JitSpewer.h @@ -69,8 +69,6 @@ namespace jit { _(MarkLoadsUsedAsPropertyKeys) \ /* Output a list of MIR expressions */ \ _(MIRExpressions) \ - /* Spew Tracelogger summary stats */ \ - _(ScriptStats) \ \ /* BASELINE COMPILER SPEW */ \ \ diff --git a/js/src/jit/JitZone.h b/js/src/jit/JitZone.h index a17b73c20e..d4f2350b8d 100644 --- a/js/src/jit/JitZone.h +++ b/js/src/jit/JitZone.h @@ -141,7 +141,8 @@ class JitZone { Count }; - mozilla::EnumeratedArray<StubIndex, StubIndex::Count, WeakHeapPtr<JitCode*>> + mozilla::EnumeratedArray<StubIndex, WeakHeapPtr<JitCode*>, + size_t(StubIndex::Count)> stubs_; mozilla::Maybe<IonCompilationId> currentCompilationId_; diff --git a/js/src/jit/LIROps.yaml b/js/src/jit/LIROps.yaml index 44ef48a4d8..f13c4b0745 100644 --- a/js/src/jit/LIROps.yaml +++ b/js/src/jit/LIROps.yaml @@ -1875,6 +1875,49 @@ operands: object: WordSized +# Read the length of a resizable typed array. +- name: ResizableTypedArrayLength + result_type: WordSized + operands: + object: WordSized + arguments: + synchronization: js::jit::Synchronization + num_temps: 1 + +# Read the possibly out-of-bounds byteOffset of a resizable typed array. +- name: ResizableTypedArrayByteOffsetMaybeOutOfBounds + result_type: WordSized + operands: + object: WordSized + num_temps: 1 + +# Read the byte length of a resizable data view. +- name: ResizableDataViewByteLength + result_type: WordSized + operands: + object: WordSized + arguments: + synchronization: js::jit::Synchronization + num_temps: 1 + +# Read the byte length of a growable shared array buffer. +- name: GrowableSharedArrayBufferByteLength + result_type: WordSized + operands: + object: WordSized + +# Guard a resizable array buffer view is in-bounds. +- name: GuardResizableArrayBufferViewInBounds + operands: + object: WordSized + num_temps: 1 + +# Guard a resizable array buffer view is in-bounds. +- name: GuardResizableArrayBufferViewInBoundsOrDetached + operands: + object: WordSized + num_temps: 1 + - name: GuardHasAttachedArrayBuffer operands: object: WordSized @@ -2052,9 +2095,9 @@ - name: LoadTypedArrayElementHole result_type: BoxedValue operands: - object: WordSized + elements: WordSized index: WordSized - num_temps: 1 + length: WordSized mir_op: true - name: LoadTypedArrayElementHoleBigInt @@ -2941,6 +2984,11 @@ object: WordSized num_temps: 1 +- name: GuardIsResizableTypedArray + operands: + object: WordSized + num_temps: 1 + - name: GuardHasProxyHandler operands: object: WordSized @@ -3069,6 +3117,13 @@ num_temps: 1 mir_op: true +- name: GuardToEitherClass + result_type: WordSized + operands: + lhs: WordSized + num_temps: 1 + mir_op: true + - name: GuardToFunction result_type: WordSized operands: diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 8a28ea123c..b0007a114d 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3828,6 +3828,20 @@ void LIRGenerator::visitGetNextEntryForIterator(MGetNextEntryForIterator* ins) { define(lir, ins); } +static auto SynchronizeLoad(MemoryBarrierRequirement requiresBarrier) { + if (requiresBarrier == MemoryBarrierRequirement::Required) { + return Synchronization::Load(); + } + return Synchronization::None(); +} + +static auto SynchronizeStore(MemoryBarrierRequirement requiresBarrier) { + if (requiresBarrier == MemoryBarrierRequirement::Required) { + return Synchronization::Store(); + } + return Synchronization::None(); +} + void LIRGenerator::visitArrayBufferByteLength(MArrayBufferByteLength* ins) { MOZ_ASSERT(ins->object()->type() == MIRType::Object); MOZ_ASSERT(ins->type() == MIRType::IntPtr); @@ -3870,6 +3884,70 @@ void LIRGenerator::visitTypedArrayElementSize(MTypedArrayElementSize* ins) { ins); } +void LIRGenerator::visitResizableTypedArrayLength( + MResizableTypedArrayLength* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::IntPtr); + + auto sync = SynchronizeLoad(ins->requiresMemoryBarrier()); + auto* lir = new (alloc()) + LResizableTypedArrayLength(useRegister(ins->object()), temp(), sync); + define(lir, ins); +} + +void LIRGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds( + MResizableTypedArrayByteOffsetMaybeOutOfBounds* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::IntPtr); + + auto* lir = new (alloc()) LResizableTypedArrayByteOffsetMaybeOutOfBounds( + useRegister(ins->object()), temp()); + define(lir, ins); +} + +void LIRGenerator::visitResizableDataViewByteLength( + MResizableDataViewByteLength* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::IntPtr); + + auto sync = SynchronizeLoad(ins->requiresMemoryBarrier()); + auto* lir = new (alloc()) + LResizableDataViewByteLength(useRegister(ins->object()), temp(), sync); + define(lir, ins); +} + +void LIRGenerator::visitGrowableSharedArrayBufferByteLength( + MGrowableSharedArrayBufferByteLength* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::IntPtr); + + auto* lir = new (alloc()) + LGrowableSharedArrayBufferByteLength(useRegisterAtStart(ins->object())); + define(lir, ins); +} + +void LIRGenerator::visitGuardResizableArrayBufferViewInBounds( + MGuardResizableArrayBufferViewInBounds* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + auto* lir = new (alloc()) LGuardResizableArrayBufferViewInBounds( + useRegister(ins->object()), temp()); + assignSnapshot(lir, ins->bailoutKind()); + add(lir, ins); + redefine(ins, ins->object()); +} + +void LIRGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached( + MGuardResizableArrayBufferViewInBoundsOrDetached* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + auto* lir = new (alloc()) LGuardResizableArrayBufferViewInBoundsOrDetached( + useRegister(ins->object()), temp()); + assignSnapshot(lir, ins->bailoutKind()); + add(lir, ins); + redefine(ins, ins->object()); +} + void LIRGenerator::visitGuardHasAttachedArrayBuffer( MGuardHasAttachedArrayBuffer* ins) { MOZ_ASSERT(ins->object()->type() == MIRType::Object); @@ -4298,8 +4376,9 @@ void LIRGenerator::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) { MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); MOZ_ASSERT(IsNumericType(ins->type()) || ins->type() == MIRType::Boolean); - if (Scalar::isBigIntType(ins->storageType()) && - ins->requiresMemoryBarrier()) { + auto sync = SynchronizeLoad(ins->requiresMemoryBarrier()); + + if (Scalar::isBigIntType(ins->storageType()) && !sync.isNone()) { lowerAtomicLoad64(ins); return; } @@ -4310,8 +4389,7 @@ void LIRGenerator::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) { // NOTE: the generated code must match the assembly code in gen_load in // GenerateAtomicOperations.py - Synchronization sync = Synchronization::Load(); - if (ins->requiresMemoryBarrier()) { + if (!sync.isNone()) { LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierBefore); add(fence, ins); } @@ -4338,7 +4416,7 @@ void LIRGenerator::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) { assignSafepoint(lir, ins); } - if (ins->requiresMemoryBarrier()) { + if (!sync.isNone()) { LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierAfter); add(fence, ins); } @@ -4431,29 +4509,32 @@ void LIRGenerator::visitClampToUint8(MClampToUint8* ins) { void LIRGenerator::visitLoadTypedArrayElementHole( MLoadTypedArrayElementHole* ins) { - MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); + MOZ_ASSERT(ins->length()->type() == MIRType::IntPtr); MOZ_ASSERT(ins->type() == MIRType::Value); - const LUse object = useRegister(ins->object()); + const LUse elements = useRegister(ins->elements()); const LAllocation index = useRegister(ins->index()); + const LAllocation length = useRegister(ins->length()); if (!Scalar::isBigIntType(ins->arrayType())) { - auto* lir = new (alloc()) LLoadTypedArrayElementHole(object, index, temp()); + auto* lir = + new (alloc()) LLoadTypedArrayElementHole(elements, index, length); if (ins->fallible()) { assignSnapshot(lir, ins->bailoutKind()); } defineBox(lir, ins); } else { #ifdef JS_CODEGEN_X86 - LDefinition tmp = LDefinition::BogusTemp(); + LInt64Definition temp64 = LInt64Definition::BogusTemp(); #else - LDefinition tmp = temp(); + LInt64Definition temp64 = tempInt64(); #endif - auto* lir = new (alloc()) - LLoadTypedArrayElementHoleBigInt(object, index, tmp, tempInt64()); + auto* lir = new (alloc()) LLoadTypedArrayElementHoleBigInt( + elements, index, length, temp(), temp64); defineBox(lir, ins); assignSafepoint(lir, ins); } @@ -4474,7 +4555,9 @@ void LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) { MOZ_ASSERT(ins->value()->type() == MIRType::Int32); } - if (ins->isBigIntWrite() && ins->requiresMemoryBarrier()) { + auto sync = SynchronizeStore(ins->requiresMemoryBarrier()); + + if (ins->isBigIntWrite() && !sync.isNone()) { lowerAtomicStore64(ins); return; } @@ -4500,8 +4583,7 @@ void LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) { // // NOTE: the generated code must match the assembly code in gen_store in // GenerateAtomicOperations.py - Synchronization sync = Synchronization::Store(); - if (ins->requiresMemoryBarrier()) { + if (!sync.isNone()) { LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierBefore); add(fence, ins); } @@ -4511,7 +4593,7 @@ void LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) { add(new (alloc()) LStoreUnboxedBigInt(elements, index, value, tempInt64()), ins); } - if (ins->requiresMemoryBarrier()) { + if (!sync.isNone()) { LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierAfter); add(fence, ins); } @@ -5154,6 +5236,17 @@ void LIRGenerator::visitGuardIsFixedLengthTypedArray( redefine(ins, ins->object()); } +void LIRGenerator::visitGuardIsResizableTypedArray( + MGuardIsResizableTypedArray* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + auto* lir = new (alloc()) + LGuardIsResizableTypedArray(useRegister(ins->object()), temp()); + assignSnapshot(lir, ins->bailoutKind()); + add(lir, ins); + redefine(ins, ins->object()); +} + void LIRGenerator::visitGuardHasProxyHandler(MGuardHasProxyHandler* ins) { MOZ_ASSERT(ins->object()->type() == MIRType::Object); @@ -5694,6 +5787,15 @@ void LIRGenerator::visitGuardToClass(MGuardToClass* ins) { defineReuseInput(lir, ins, 0); } +void LIRGenerator::visitGuardToEitherClass(MGuardToEitherClass* ins) { + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Object); + auto* lir = new (alloc()) + LGuardToEitherClass(useRegisterAtStart(ins->object()), temp()); + assignSnapshot(lir, ins->bailoutKind()); + defineReuseInput(lir, ins, 0); +} + void LIRGenerator::visitGuardToFunction(MGuardToFunction* ins) { MOZ_ASSERT(ins->object()->type() == MIRType::Object); MOZ_ASSERT(ins->type() == MIRType::Object); @@ -7018,6 +7120,11 @@ void LIRGenerator::visitMapObjectSize(MMapObjectSize* ins) { define(lir, ins); } +void LIRGenerator::visitPostIntPtrConversion(MPostIntPtrConversion* ins) { + // This operation is a no-op. + redefine(ins, ins->input()); +} + void LIRGenerator::visitConstant(MConstant* ins) { if (!IsFloatingPointType(ins->type()) && ins->canEmitAtUses()) { emitAtUses(ins); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index dbaa73c9dd..c6daecb166 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -6365,6 +6365,81 @@ AliasSet MGuardHasAttachedArrayBuffer::getAliasSet() const { return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot); } +AliasSet MResizableTypedArrayByteOffsetMaybeOutOfBounds::getAliasSet() const { + // Loads the byteOffset and additionally checks for detached buffers, so the + // alias set also has to include |ObjectFields| and |FixedSlot|. + return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset | + AliasSet::ObjectFields | AliasSet::FixedSlot); +} + +AliasSet MResizableTypedArrayLength::getAliasSet() const { + // Loads the length and byteOffset slots, the shared-elements flag, the + // auto-length fixed slot, and the shared raw-buffer length. + auto flags = AliasSet::ArrayBufferViewLengthOrOffset | + AliasSet::ObjectFields | AliasSet::FixedSlot | + AliasSet::SharedArrayRawBufferLength; + + // When a barrier is needed make the instruction effectful by giving it a + // "store" effect. Also prevent reordering LoadUnboxedScalar before this + // instruction by including |UnboxedElement| in the alias set. + if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { + return AliasSet::Store(flags | AliasSet::UnboxedElement); + } + return AliasSet::Load(flags); +} + +bool MResizableTypedArrayLength::congruentTo(const MDefinition* ins) const { + if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { + return false; + } + return congruentIfOperandsEqual(ins); +} + +AliasSet MResizableDataViewByteLength::getAliasSet() const { + // Loads the length and byteOffset slots, the shared-elements flag, the + // auto-length fixed slot, and the shared raw-buffer length. + auto flags = AliasSet::ArrayBufferViewLengthOrOffset | + AliasSet::ObjectFields | AliasSet::FixedSlot | + AliasSet::SharedArrayRawBufferLength; + + // When a barrier is needed make the instruction effectful by giving it a + // "store" effect. Also prevent reordering LoadUnboxedScalar before this + // instruction by including |UnboxedElement| in the alias set. + if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { + return AliasSet::Store(flags | AliasSet::UnboxedElement); + } + return AliasSet::Load(flags); +} + +bool MResizableDataViewByteLength::congruentTo(const MDefinition* ins) const { + if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { + return false; + } + return congruentIfOperandsEqual(ins); +} + +AliasSet MGrowableSharedArrayBufferByteLength::getAliasSet() const { + // Requires a barrier, so make the instruction effectful by giving it a + // "store" effect. Also prevent reordering LoadUnboxedScalar before this + // instruction by including |UnboxedElement| in the alias set. + return AliasSet::Store(AliasSet::FixedSlot | + AliasSet::SharedArrayRawBufferLength | + AliasSet::UnboxedElement); +} + +AliasSet MGuardResizableArrayBufferViewInBounds::getAliasSet() const { + // Additionally reads the |initialLength| and |initialByteOffset| slots, but + // since these can't change after construction, we don't need to track them. + return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset); +} + +AliasSet MGuardResizableArrayBufferViewInBoundsOrDetached::getAliasSet() const { + // Loads the byteOffset and additionally checks for detached buffers, so the + // alias set also has to include |ObjectFields| and |FixedSlot|. + return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset | + AliasSet::ObjectFields | AliasSet::FixedSlot); +} + AliasSet MArrayPush::getAliasSet() const { return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } @@ -6882,6 +6957,16 @@ MDefinition* MGuardToClass::foldsTo(TempAllocator& alloc) { return object(); } +MDefinition* MGuardToEitherClass::foldsTo(TempAllocator& alloc) { + const JSClass* clasp = GetObjectKnownJSClass(object()); + if (!clasp || (getClass1() != clasp && getClass2() != clasp)) { + return this; + } + + AssertKnownClass(alloc, this, object()); + return object(); +} + MDefinition* MGuardToFunction::foldsTo(TempAllocator& alloc) { if (GetObjectKnownClass(object()) != KnownClass::Function) { return this; diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 07701847eb..d882665a65 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -421,10 +421,13 @@ class AliasSet { // The generation counter associated with the global object GlobalGenerationCounter = 1 << 26, - Last = GlobalGenerationCounter, + // The SharedArrayRawBuffer::length field. + SharedArrayRawBufferLength = 1 << 27, + + Last = SharedArrayRawBufferLength, Any = Last | (Last - 1), - NumCategories = 27, + NumCategories = 28, // Indicates load or store. Store_ = 1 << 31 @@ -657,7 +660,13 @@ class MDefinition : public MNode { virtual HashNumber valueHash() const; virtual bool congruentTo(const MDefinition* ins) const { return false; } const MDefinition* skipObjectGuards() const; + + // Note that, for a call `congruentIfOperandsEqual(ins)` inside some class + // MFoo, if `true` is returned then we are ensured that `ins` is also an + // MFoo, so it is safe to do `ins->toMFoo()` without first checking whether + // `ins->isMFoo()`. bool congruentIfOperandsEqual(const MDefinition* ins) const; + virtual MDefinition* foldsTo(TempAllocator& alloc); virtual void analyzeEdgeCasesForward(); virtual void analyzeEdgeCasesBackward(); @@ -1277,6 +1286,35 @@ class MVariadicT : public T { // initializes the operands_ array and must be checked for OOM. using MVariadicInstruction = MVariadicT<MInstruction>; +// All barriered operations: +// - MCompareExchangeTypedArrayElement +// - MExchangeTypedArrayElement +// - MAtomicTypedArrayElementBinop +// - MGrowableSharedArrayBufferByteLength +// +// And operations which are optionally barriered: +// - MLoadUnboxedScalar +// - MStoreUnboxedScalar +// - MResizableTypedArrayLength +// - MResizableDataViewByteLength +// +// Must have the following attributes: +// +// - Not movable +// - Not removable +// - Not congruent with any other instruction +// - Effectful (they alias every TypedArray store) +// +// The intended effect of those constraints is to prevent all loads and stores +// preceding the barriered operation from being moved to after the barriered +// operation, and vice versa, and to prevent the barriered operation from being +// removed or hoisted. + +enum class MemoryBarrierRequirement : bool { + NotRequired, + Required, +}; + MIR_OPCODE_CLASS_GENERATED // Truncation barrier. This is intended for protecting its input against @@ -7040,44 +7078,22 @@ class MArrayPopShift : public MUnaryInstruction, ALLOW_CLONE(MArrayPopShift) }; -// All barriered operations - MCompareExchangeTypedArrayElement, -// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as -// well as MLoadUnboxedScalar and MStoreUnboxedScalar when they are -// marked as requiring a memory barrer - have the following -// attributes: -// -// - Not movable -// - Not removable -// - Not congruent with any other instruction -// - Effectful (they alias every TypedArray store) -// -// The intended effect of those constraints is to prevent all loads -// and stores preceding the barriered operation from being moved to -// after the barriered operation, and vice versa, and to prevent the -// barriered operation from being removed or hoisted. - -enum MemoryBarrierRequirement { - DoesNotRequireMemoryBarrier, - DoesRequireMemoryBarrier -}; - -// Also see comments at MMemoryBarrierRequirement, above. - // Load an unboxed scalar value from an array buffer view or other object. class MLoadUnboxedScalar : public MBinaryInstruction, public NoTypePolicy::Data { int32_t offsetAdjustment_ = 0; Scalar::Type storageType_; - bool requiresBarrier_; + MemoryBarrierRequirement requiresBarrier_; - MLoadUnboxedScalar( - MDefinition* elements, MDefinition* index, Scalar::Type storageType, - MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier) + MLoadUnboxedScalar(MDefinition* elements, MDefinition* index, + Scalar::Type storageType, + MemoryBarrierRequirement requiresBarrier = + MemoryBarrierRequirement::NotRequired) : MBinaryInstruction(classOpcode, elements, index), storageType_(storageType), - requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier) { + requiresBarrier_(requiresBarrier) { setResultType(MIRType::Value); - if (requiresBarrier_) { + if (requiresBarrier_ == MemoryBarrierRequirement::Required) { setGuard(); // Not removable or movable } else { setMovable(); @@ -7097,7 +7113,7 @@ class MLoadUnboxedScalar : public MBinaryInstruction, // Bailout if the result does not fit in an int32. return storageType_ == Scalar::Uint32 && type() == MIRType::Int32; } - bool requiresMemoryBarrier() const { return requiresBarrier_; } + auto requiresMemoryBarrier() const { return requiresBarrier_; } int32_t offsetAdjustment() const { return offsetAdjustment_; } void setOffsetAdjustment(int32_t offsetAdjustment) { offsetAdjustment_ = offsetAdjustment; @@ -7105,14 +7121,14 @@ class MLoadUnboxedScalar : public MBinaryInstruction, AliasSet getAliasSet() const override { // When a barrier is needed make the instruction effectful by // giving it a "store" effect. - if (requiresBarrier_) { + if (requiresBarrier_ == MemoryBarrierRequirement::Required) { return AliasSet::Store(AliasSet::UnboxedElement); } return AliasSet::Load(AliasSet::UnboxedElement); } bool congruentTo(const MDefinition* ins) const override { - if (requiresBarrier_) { + if (requiresBarrier_ == MemoryBarrierRequirement::Required) { return false; } if (!ins->isLoadUnboxedScalar()) { @@ -7198,26 +7214,29 @@ class MLoadDataViewElement : public MTernaryInstruction, }; // Load a value from a typed array. Out-of-bounds accesses are handled in-line. -class MLoadTypedArrayElementHole : public MBinaryInstruction, - public SingleObjectPolicy::Data { +class MLoadTypedArrayElementHole : public MTernaryInstruction, + public NoTypePolicy::Data { Scalar::Type arrayType_; bool forceDouble_; - MLoadTypedArrayElementHole(MDefinition* object, MDefinition* index, - Scalar::Type arrayType, bool forceDouble) - : MBinaryInstruction(classOpcode, object, index), + MLoadTypedArrayElementHole(MDefinition* elements, MDefinition* index, + MDefinition* length, Scalar::Type arrayType, + bool forceDouble) + : MTernaryInstruction(classOpcode, elements, index, length), arrayType_(arrayType), forceDouble_(forceDouble) { setResultType(MIRType::Value); setMovable(); + MOZ_ASSERT(elements->type() == MIRType::Elements); MOZ_ASSERT(index->type() == MIRType::IntPtr); + MOZ_ASSERT(length->type() == MIRType::IntPtr); MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType); } public: INSTRUCTION_HEADER(LoadTypedArrayElementHole) TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object), (1, index)) + NAMED_OPERANDS((0, elements), (1, index), (2, length)) Scalar::Type arrayType() const { return arrayType_; } bool forceDouble() const { return forceDouble_; } @@ -7239,8 +7258,7 @@ class MLoadTypedArrayElementHole : public MBinaryInstruction, return congruentIfOperandsEqual(other); } AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::UnboxedElement | AliasSet::ObjectFields | - AliasSet::ArrayBufferViewLengthOrOffset); + return AliasSet::Load(AliasSet::UnboxedElement); } bool canProduceFloat32() const override { return arrayType_ == Scalar::Float32; @@ -7280,16 +7298,16 @@ class StoreUnboxedScalarBase { class MStoreUnboxedScalar : public MTernaryInstruction, public StoreUnboxedScalarBase, public StoreUnboxedScalarPolicy::Data { - bool requiresBarrier_; + MemoryBarrierRequirement requiresBarrier_; - MStoreUnboxedScalar( - MDefinition* elements, MDefinition* index, MDefinition* value, - Scalar::Type storageType, - MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier) + MStoreUnboxedScalar(MDefinition* elements, MDefinition* index, + MDefinition* value, Scalar::Type storageType, + MemoryBarrierRequirement requiresBarrier = + MemoryBarrierRequirement::NotRequired) : MTernaryInstruction(classOpcode, elements, index, value), StoreUnboxedScalarBase(storageType), - requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier) { - if (requiresBarrier_) { + requiresBarrier_(requiresBarrier) { + if (requiresBarrier_ == MemoryBarrierRequirement::Required) { setGuard(); // Not removable or movable } MOZ_ASSERT(elements->type() == MIRType::Elements); @@ -7305,7 +7323,7 @@ class MStoreUnboxedScalar : public MTernaryInstruction, AliasSet getAliasSet() const override { return AliasSet::Store(AliasSet::UnboxedElement); } - bool requiresMemoryBarrier() const { return requiresBarrier_; } + auto requiresMemoryBarrier() const { return requiresBarrier_; } TruncateKind operandTruncateKind(size_t index) const override; bool canConsumeFloat32(MUse* use) const override { @@ -8997,6 +9015,55 @@ class MGuardToClass : public MUnaryInstruction, } }; +class MGuardToEitherClass : public MUnaryInstruction, + public SingleObjectPolicy::Data { + const JSClass* class1_; + const JSClass* class2_; + + MGuardToEitherClass(MDefinition* object, const JSClass* clasp1, + const JSClass* clasp2) + : MUnaryInstruction(classOpcode, object), + class1_(clasp1), + class2_(clasp2) { + MOZ_ASSERT(object->type() == MIRType::Object); + MOZ_ASSERT(clasp1 != clasp2, "Use MGuardToClass instead"); + MOZ_ASSERT(!clasp1->isJSFunction(), "Use MGuardToFunction instead"); + MOZ_ASSERT(!clasp2->isJSFunction(), "Use MGuardToFunction instead"); + setResultType(MIRType::Object); + setMovable(); + + // We will bail out if the class type is incorrect, so we need to ensure we + // don't eliminate this instruction + setGuard(); + } + + public: + INSTRUCTION_HEADER(GuardToEitherClass) + TRIVIAL_NEW_WRAPPERS + NAMED_OPERANDS((0, object)) + + const JSClass* getClass1() const { return class1_; } + const JSClass* getClass2() const { return class2_; } + + MDefinition* foldsTo(TempAllocator& alloc) override; + AliasSet getAliasSet() const override { return AliasSet::None(); } + bool congruentTo(const MDefinition* ins) const override { + if (!ins->isGuardToEitherClass()) { + return false; + } + const auto* other = ins->toGuardToEitherClass(); + if (getClass1() != other->getClass1() && + getClass1() != other->getClass2()) { + return false; + } + if (getClass2() != other->getClass1() && + getClass2() != other->getClass2()) { + return false; + } + return congruentIfOperandsEqual(ins); + } +}; + class MGuardToFunction : public MUnaryInstruction, public SingleObjectPolicy::Data { explicit MGuardToFunction(MDefinition* object) @@ -9337,6 +9404,23 @@ class MObjectToIterator : public MUnaryInstruction, void setWantsIndices(bool value) { wantsIndices_ = value; } }; +class MPostIntPtrConversion : public MUnaryInstruction, + public NoTypePolicy::Data { + explicit MPostIntPtrConversion(MDefinition* input) + : MUnaryInstruction(classOpcode, input) { + // Passes through the input. + setResultType(input->type()); + + // Note: Must be non-movable so we can attach a resume point. + } + + public: + INSTRUCTION_HEADER(PostIntPtrConversion) + TRIVIAL_NEW_WRAPPERS + + AliasSet getAliasSet() const override { return AliasSet::None(); } +}; + // Flips the input's sign bit, independently of the rest of the number's // payload. Note this is different from multiplying by minus-one, which has // side-effects for e.g. NaNs. @@ -10808,6 +10892,8 @@ class MWasmReinterpret : public MUnaryInstruction, public NoTypePolicy::Data { AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { + // No need to check type() here, because congruentIfOperandsEqual will + // check it. return congruentIfOperandsEqual(ins); } @@ -10867,7 +10953,8 @@ class MWasmTernarySimd128 : public MTernaryInstruction, AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); + return congruentIfOperandsEqual(ins) && + simdOp() == ins->toWasmTernarySimd128()->simdOp(); } #ifdef ENABLE_WASM_SIMD MDefinition* foldsTo(TempAllocator& alloc) override; @@ -10908,8 +10995,8 @@ class MWasmBinarySimd128 : public MBinaryInstruction, AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { - return ins->toWasmBinarySimd128()->simdOp() == simdOp_ && - congruentIfOperandsEqual(ins); + return congruentIfOperandsEqual(ins) && + ins->toWasmBinarySimd128()->simdOp() == simdOp_; } #ifdef ENABLE_WASM_SIMD MDefinition* foldsTo(TempAllocator& alloc) override; @@ -10945,8 +11032,8 @@ class MWasmBinarySimd128WithConstant : public MUnaryInstruction, AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { - return ins->toWasmBinarySimd128WithConstant()->simdOp() == simdOp_ && - congruentIfOperandsEqual(ins) && + return congruentIfOperandsEqual(ins) && + ins->toWasmBinarySimd128WithConstant()->simdOp() == simdOp_ && rhs_.bitwiseEqual(ins->toWasmBinarySimd128WithConstant()->rhs()); } @@ -10978,9 +11065,9 @@ class MWasmReplaceLaneSimd128 : public MBinaryInstruction, AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { - return ins->toWasmReplaceLaneSimd128()->simdOp() == simdOp_ && - ins->toWasmReplaceLaneSimd128()->laneIndex() == laneIndex_ && - congruentIfOperandsEqual(ins); + return congruentIfOperandsEqual(ins) && + ins->toWasmReplaceLaneSimd128()->simdOp() == simdOp_ && + ins->toWasmReplaceLaneSimd128()->laneIndex() == laneIndex_; } uint32_t laneIndex() const { return laneIndex_; } @@ -11006,8 +11093,8 @@ class MWasmScalarToSimd128 : public MUnaryInstruction, AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { - return ins->toWasmScalarToSimd128()->simdOp() == simdOp_ && - congruentIfOperandsEqual(ins); + return congruentIfOperandsEqual(ins) && + ins->toWasmScalarToSimd128()->simdOp() == simdOp_; } #ifdef ENABLE_WASM_SIMD MDefinition* foldsTo(TempAllocator& alloc) override; @@ -11036,9 +11123,9 @@ class MWasmReduceSimd128 : public MUnaryInstruction, public NoTypePolicy::Data { AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { - return ins->toWasmReduceSimd128()->simdOp() == simdOp_ && - ins->toWasmReduceSimd128()->imm() == imm_ && - congruentIfOperandsEqual(ins); + return congruentIfOperandsEqual(ins) && + ins->toWasmReduceSimd128()->simdOp() == simdOp_ && + ins->toWasmReduceSimd128()->imm() == imm_; } #ifdef ENABLE_WASM_SIMD MDefinition* foldsTo(TempAllocator& alloc) override; diff --git a/js/src/jit/MIROps.yaml b/js/src/jit/MIROps.yaml index 565c3c9c2b..7f0df52742 100644 --- a/js/src/jit/MIROps.yaml +++ b/js/src/jit/MIROps.yaml @@ -1331,7 +1331,6 @@ folds_to: custom congruent_to: if_operands_equal alias_set: none - movable: true can_recover: true - name: ModuleMetadata @@ -1492,6 +1491,54 @@ alias_set: custom clone: true +# Implements the TypedArrayByteOffset intrinsic for resizable typed arrays, +# which calls TypedArrayObject::byteOffsetMaybeOutOfBounds(). +- name: ResizableTypedArrayByteOffsetMaybeOutOfBounds + operands: + object: Object + result_type: IntPtr + movable: true + congruent_to: if_operands_equal + alias_set: custom + compute_range: custom + +# Read the length of a resizable typed array. +- name: ResizableTypedArrayLength + operands: + object: Object + arguments: + requiresMemoryBarrier: MemoryBarrierRequirement + result_type: IntPtr + # Not removable or movable when a barrier is needed. + guard: true + movable: false + congruent_to: custom + alias_set: custom + compute_range: custom + +# Read the byteLength of a resizable dataview. +- name: ResizableDataViewByteLength + operands: + object: Object + arguments: + requiresMemoryBarrier: MemoryBarrierRequirement + result_type: IntPtr + # Not removable or movable when a barrier is needed. + guard: true + movable: false + congruent_to: custom + alias_set: custom + compute_range: custom + +# Read the byte length of a growable shared array buffer as IntPtr. +- name: GrowableSharedArrayBufferByteLength + operands: + object: Object + result_type: IntPtr + guard: true + movable: false + alias_set: custom + # Return the element size of a typed array. - name: TypedArrayElementSize operands: @@ -1513,6 +1560,26 @@ congruent_to: if_operands_equal alias_set: custom +# Guard a resizable typed array is in-bounds. +- name: GuardResizableArrayBufferViewInBounds + operands: + object: Object + result_type: Object + guard: true + movable: true + congruent_to: if_operands_equal + alias_set: custom + +# Guard a resizable typed array is in-bounds or detached. +- name: GuardResizableArrayBufferViewInBoundsOrDetached + operands: + object: Object + result_type: Object + guard: true + movable: true + congruent_to: if_operands_equal + alias_set: custom + - name: GuardNumberToIntPtrIndex gen_boilerplate: false @@ -1951,6 +2018,15 @@ congruent_to: if_operands_equal alias_set: none +- name: GuardIsResizableTypedArray + operands: + object: Object + result_type: Object + guard: true + movable: true + congruent_to: if_operands_equal + alias_set: none + - name: GuardHasProxyHandler operands: object: Object @@ -2510,6 +2586,9 @@ - name: GuardToClass gen_boilerplate: false +- name: GuardToEitherClass + gen_boilerplate: false + - name: GuardToFunction gen_boilerplate: false @@ -3106,6 +3185,9 @@ congruent_to: if_operands_equal alias_set: custom +- name: PostIntPtrConversion + gen_boilerplate: false + - name: WasmNeg gen_boilerplate: false diff --git a/js/src/jit/MacroAssembler-inl.h b/js/src/jit/MacroAssembler-inl.h index beba576a22..e1df31eff9 100644 --- a/js/src/jit/MacroAssembler-inl.h +++ b/js/src/jit/MacroAssembler-inl.h @@ -606,9 +606,7 @@ void MacroAssembler::branchTestObjClass(Condition cond, Register obj, MOZ_ASSERT(obj != scratch); MOZ_ASSERT(scratch != spectreRegToZero); - loadPtr(Address(obj, JSObject::offsetOfShape()), scratch); - loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch); - loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch); + loadObjClassUnsafe(obj, scratch); branchPtr(cond, clasp, scratch, label); if (JitOptions.spectreObjectMitigations) { @@ -620,9 +618,7 @@ void MacroAssembler::branchTestObjClassNoSpectreMitigations( Condition cond, Register obj, const Address& clasp, Register scratch, Label* label) { MOZ_ASSERT(obj != scratch); - loadPtr(Address(obj, JSObject::offsetOfShape()), scratch); - loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch); - loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch); + loadObjClassUnsafe(obj, scratch); branchPtr(cond, clasp, scratch, label); } @@ -633,9 +629,7 @@ void MacroAssembler::branchTestObjClass(Condition cond, Register obj, MOZ_ASSERT(obj != scratch); MOZ_ASSERT(scratch != spectreRegToZero); - loadPtr(Address(obj, JSObject::offsetOfShape()), scratch); - loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch); - loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch); + loadObjClassUnsafe(obj, scratch); branchPtr(cond, clasp, scratch, label); if (JitOptions.spectreObjectMitigations) { @@ -643,20 +637,51 @@ void MacroAssembler::branchTestObjClass(Condition cond, Register obj, } } -void MacroAssembler::branchTestClassIsFunction(Condition cond, Register clasp, - Label* label) { +void MacroAssembler::branchTestClass( + Condition cond, Register clasp, + std::pair<const JSClass*, const JSClass*> classes, Label* label) { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); if (cond == Assembler::Equal) { - branchPtr(Assembler::Equal, clasp, ImmPtr(&FunctionClass), label); - branchPtr(Assembler::Equal, clasp, ImmPtr(&ExtendedFunctionClass), label); + branchPtr(Assembler::Equal, clasp, ImmPtr(classes.first), label); + branchPtr(Assembler::Equal, clasp, ImmPtr(classes.second), label); return; } - Label isFunction; - branchPtr(Assembler::Equal, clasp, ImmPtr(&FunctionClass), &isFunction); - branchPtr(Assembler::NotEqual, clasp, ImmPtr(&ExtendedFunctionClass), label); - bind(&isFunction); + Label isClass; + branchPtr(Assembler::Equal, clasp, ImmPtr(classes.first), &isClass); + branchPtr(Assembler::NotEqual, clasp, ImmPtr(classes.second), label); + bind(&isClass); +} + +void MacroAssembler::branchTestObjClass( + Condition cond, Register obj, + std::pair<const JSClass*, const JSClass*> classes, Register scratch, + Register spectreRegToZero, Label* label) { + MOZ_ASSERT(scratch != spectreRegToZero); + + branchTestObjClassNoSpectreMitigations(cond, obj, classes, scratch, label); + + if (JitOptions.spectreObjectMitigations) { + spectreZeroRegister(cond, scratch, spectreRegToZero); + } +} + +void MacroAssembler::branchTestObjClassNoSpectreMitigations( + Condition cond, Register obj, + std::pair<const JSClass*, const JSClass*> classes, Register scratch, + Label* label) { + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + MOZ_ASSERT(obj != scratch); + + loadObjClassUnsafe(obj, scratch); + branchTestClass(cond, scratch, classes, label); +} + +void MacroAssembler::branchTestClassIsFunction(Condition cond, Register clasp, + Label* label) { + return branchTestClass(cond, clasp, {&FunctionClass, &ExtendedFunctionClass}, + label); } void MacroAssembler::branchTestObjIsFunction(Condition cond, Register obj, @@ -677,9 +702,7 @@ void MacroAssembler::branchTestObjIsFunctionNoSpectreMitigations( MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); MOZ_ASSERT(obj != scratch); - loadPtr(Address(obj, JSObject::offsetOfShape()), scratch); - loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch); - loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch); + loadObjClassUnsafe(obj, scratch); branchTestClassIsFunction(cond, scratch, label); } diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 54da676014..3b094d49dc 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -2661,6 +2661,7 @@ void MacroAssembler::loadMegamorphicSetPropCache(Register dest) { void MacroAssembler::lookupStringInAtomCacheLastLookups(Register str, Register scratch, + Register output, Label* fail) { Label found; @@ -2680,7 +2681,7 @@ void MacroAssembler::lookupStringInAtomCacheLastLookups(Register str, // and jump back up to our usual atom handling code bind(&found); size_t atomOffset = StringToAtomCache::LastLookup::offsetOfAtom(); - loadPtr(Address(scratch, atomOffset), str); + loadPtr(Address(scratch, atomOffset), output); } void MacroAssembler::loadAtomHash(Register id, Register outHash, Label* done) { @@ -2740,7 +2741,7 @@ void MacroAssembler::loadAtomOrSymbolAndHash(ValueOperand value, Register outId, loadAtomHash(outId, outHash, &done); bind(&nonAtom); - lookupStringInAtomCacheLastLookups(outId, outHash, cacheMiss); + lookupStringInAtomCacheLastLookups(outId, outHash, outId, cacheMiss); jump(&atom); bind(&done); @@ -3255,6 +3256,95 @@ void MacroAssembler::loadArrayBufferViewLengthIntPtr(Register obj, loadPrivate(slotAddr, output); } +void MacroAssembler::loadGrowableSharedArrayBufferByteLengthIntPtr( + Synchronization sync, Register obj, Register output) { + // Load the SharedArrayRawBuffer. + loadPrivate(Address(obj, SharedArrayBufferObject::rawBufferOffset()), output); + + memoryBarrierBefore(sync); + + // Load the byteLength of the SharedArrayRawBuffer into |output|. + static_assert(sizeof(mozilla::Atomic<size_t>) == sizeof(size_t)); + loadPtr(Address(output, SharedArrayRawBuffer::offsetOfByteLength()), output); + + memoryBarrierAfter(sync); +} + +void MacroAssembler::loadResizableArrayBufferViewLengthIntPtr( + ResizableArrayBufferView view, Synchronization sync, Register obj, + Register output, Register scratch) { + // Inline implementation of ArrayBufferViewObject::length(), when the input is + // guaranteed to be a resizable arraybuffer view object. + + loadArrayBufferViewLengthIntPtr(obj, output); + + Label done; + branchPtr(Assembler::NotEqual, output, ImmWord(0), &done); + + // Load obj->elements in |scratch|. + loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + + // If backed by non-shared memory, detached and out-of-bounds both return + // zero, so we're done here. + branchTest32(Assembler::Zero, + Address(scratch, ObjectElements::offsetOfFlags()), + Imm32(ObjectElements::SHARED_MEMORY), &done); + + // Load the auto-length slot. + unboxBoolean(Address(obj, ArrayBufferViewObject::autoLengthOffset()), + scratch); + + // If non-auto length, there's nothing to do. + branchTest32(Assembler::Zero, scratch, scratch, &done); + + // Load bufferByteLength into |output|. + { + // Resizable TypedArrays are guaranteed to have an ArrayBuffer. + unboxObject(Address(obj, ArrayBufferViewObject::bufferOffset()), output); + + // Load the byte length from the raw-buffer of growable SharedArrayBuffers. + loadGrowableSharedArrayBufferByteLengthIntPtr(sync, output, output); + } + + // Load the byteOffset into |scratch|. + loadArrayBufferViewByteOffsetIntPtr(obj, scratch); + + // Compute the accessible byte length |bufferByteLength - byteOffset|. + subPtr(scratch, output); + + if (view == ResizableArrayBufferView::TypedArray) { + // Compute the array length from the byte length. + resizableTypedArrayElementShiftBy(obj, output, scratch); + } + + bind(&done); +} + +void MacroAssembler::loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr( + Register obj, Register output, Register scratch) { + // Inline implementation of TypedArrayObject::byteOffsetMaybeOutOfBounds(), + // when the input is guaranteed to be a resizable typed array object. + + loadArrayBufferViewByteOffsetIntPtr(obj, output); + + // TypedArray is neither detached nor out-of-bounds when byteOffset non-zero. + Label done; + branchPtr(Assembler::NotEqual, output, ImmWord(0), &done); + + // We're done when the initial byteOffset is zero. + loadPrivate(Address(obj, ArrayBufferViewObject::initialByteOffsetOffset()), + output); + branchPtr(Assembler::Equal, output, ImmWord(0), &done); + + // If the buffer is attached, return initialByteOffset. + branchIfHasAttachedArrayBuffer(obj, scratch, &done); + + // Otherwise return zero to match the result for fixed-length TypedArrays. + movePtr(ImmWord(0), output); + + bind(&done); +} + void MacroAssembler::loadDOMExpandoValueGuardGeneration( Register obj, ValueOperand output, JS::ExpandoAndGeneration* expandoAndGeneration, uint64_t generation, @@ -7433,11 +7523,11 @@ void MacroAssembler::debugAssertCanonicalInt32(Register r) { } #endif -void MacroAssembler::memoryBarrierBefore(const Synchronization& sync) { +void MacroAssembler::memoryBarrierBefore(Synchronization sync) { memoryBarrier(sync.barrierBefore); } -void MacroAssembler::memoryBarrierAfter(const Synchronization& sync) { +void MacroAssembler::memoryBarrierAfter(Synchronization sync) { memoryBarrier(sync.barrierAfter); } @@ -7834,6 +7924,74 @@ void MacroAssembler::typedArrayElementSize(Register obj, Register output) { bind(&done); } +void MacroAssembler::resizableTypedArrayElementShiftBy(Register obj, + Register output, + Register scratch) { + loadObjClassUnsafe(obj, scratch); + +#ifdef DEBUG + Label invalidClass, validClass; + branchPtr(Assembler::Below, scratch, + ImmPtr(std::begin(TypedArrayObject::resizableClasses)), + &invalidClass); + branchPtr(Assembler::Below, scratch, + ImmPtr(std::end(TypedArrayObject::resizableClasses)), &validClass); + bind(&invalidClass); + assumeUnreachable("value isn't a valid ResizableLengthTypedArray class"); + bind(&validClass); +#endif + + auto classForType = [](Scalar::Type type) { + MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType); + return &TypedArrayObject::resizableClasses[type]; + }; + + Label zero, one, two, three; + + static_assert(ValidateSizeRange(Scalar::Int8, Scalar::Int16), + "element shift is zero in [Int8, Int16)"); + branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::Int16)), + &zero); + + static_assert(ValidateSizeRange(Scalar::Int16, Scalar::Int32), + "element shift is one in [Int16, Int32)"); + branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::Int32)), + &one); + + static_assert(ValidateSizeRange(Scalar::Int32, Scalar::Float64), + "element shift is two in [Int32, Float64)"); + branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::Float64)), + &two); + + static_assert(ValidateSizeRange(Scalar::Float64, Scalar::Uint8Clamped), + "element shift is three in [Float64, Uint8Clamped)"); + branchPtr(Assembler::Below, scratch, + ImmPtr(classForType(Scalar::Uint8Clamped)), &three); + + static_assert(ValidateSizeRange(Scalar::Uint8Clamped, Scalar::BigInt64), + "element shift is zero in [Uint8Clamped, BigInt64)"); + branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::BigInt64)), + &zero); + + static_assert( + ValidateSizeRange(Scalar::BigInt64, Scalar::MaxTypedArrayViewType), + "element shift is three in [BigInt64, MaxTypedArrayViewType)"); + // Fall through for BigInt64 and BigUint64 + + bind(&three); + rshiftPtr(Imm32(3), output); + jump(&zero); + + bind(&two); + rshiftPtr(Imm32(2), output); + jump(&zero); + + bind(&one); + rshiftPtr(Imm32(1), output); + + bind(&zero); +} + void MacroAssembler::branchIfClassIsNotTypedArray(Register clasp, Label* notTypedArray) { // Inline implementation of IsTypedArrayClass(). @@ -7867,28 +8025,103 @@ void MacroAssembler::branchIfClassIsNotFixedLengthTypedArray( notTypedArray); } -void MacroAssembler::branchIfHasDetachedArrayBuffer(Register obj, Register temp, +void MacroAssembler::branchIfClassIsNotResizableTypedArray( + Register clasp, Label* notTypedArray) { + // Inline implementation of IsResizableTypedArrayClass(). + + const auto* firstTypedArrayClass = + std::begin(TypedArrayObject::resizableClasses); + const auto* lastTypedArrayClass = + std::prev(std::end(TypedArrayObject::resizableClasses)); + + branchPtr(Assembler::Below, clasp, ImmPtr(firstTypedArrayClass), + notTypedArray); + branchPtr(Assembler::Above, clasp, ImmPtr(lastTypedArrayClass), + notTypedArray); +} + +void MacroAssembler::branchIfHasDetachedArrayBuffer(BranchIfDetached branchIf, + Register obj, Register temp, Label* label) { // Inline implementation of ArrayBufferViewObject::hasDetachedBuffer(). + // TODO: The data-slot of detached views is set to undefined, which would be + // a faster way to detect detached buffers. + + // auto cond = branchIf == BranchIfDetached::Yes ? Assembler::Equal + // : Assembler::NotEqual; + // branchTestUndefined(cond, Address(obj, + // ArrayBufferViewObject::dataOffset()), label); + + Label done; + Label* ifNotDetached = branchIf == BranchIfDetached::Yes ? &done : label; + Condition detachedCond = + branchIf == BranchIfDetached::Yes ? Assembler::NonZero : Assembler::Zero; + // Load obj->elements in temp. loadPtr(Address(obj, NativeObject::offsetOfElements()), temp); // Shared buffers can't be detached. - Label done; branchTest32(Assembler::NonZero, Address(temp, ObjectElements::offsetOfFlags()), - Imm32(ObjectElements::SHARED_MEMORY), &done); + Imm32(ObjectElements::SHARED_MEMORY), ifNotDetached); // An ArrayBufferView with a null/true buffer has never had its buffer // exposed, so nothing can possibly detach it. fallibleUnboxObject(Address(obj, ArrayBufferViewObject::bufferOffset()), temp, - &done); + ifNotDetached); - // Load the ArrayBuffer flags and branch if the detached flag is set. + // Load the ArrayBuffer flags and branch if the detached flag is (not) set. unboxInt32(Address(temp, ArrayBufferObject::offsetOfFlagsSlot()), temp); - branchTest32(Assembler::NonZero, temp, Imm32(ArrayBufferObject::DETACHED), - label); + branchTest32(detachedCond, temp, Imm32(ArrayBufferObject::DETACHED), label); + + if (branchIf == BranchIfDetached::Yes) { + bind(&done); + } +} + +void MacroAssembler::branchIfResizableArrayBufferViewOutOfBounds(Register obj, + Register temp, + Label* label) { + // Implementation of ArrayBufferViewObject::isOutOfBounds(). + + Label done; + + loadArrayBufferViewLengthIntPtr(obj, temp); + branchPtr(Assembler::NotEqual, temp, ImmWord(0), &done); + + loadArrayBufferViewByteOffsetIntPtr(obj, temp); + branchPtr(Assembler::NotEqual, temp, ImmWord(0), &done); + + loadPrivate(Address(obj, ArrayBufferViewObject::initialLengthOffset()), temp); + branchPtr(Assembler::NotEqual, temp, ImmWord(0), label); + + loadPrivate(Address(obj, ArrayBufferViewObject::initialByteOffsetOffset()), + temp); + branchPtr(Assembler::NotEqual, temp, ImmWord(0), label); + + bind(&done); +} + +void MacroAssembler::branchIfResizableArrayBufferViewInBounds(Register obj, + Register temp, + Label* label) { + // Implementation of ArrayBufferViewObject::isOutOfBounds(). + + Label done; + + loadArrayBufferViewLengthIntPtr(obj, temp); + branchPtr(Assembler::NotEqual, temp, ImmWord(0), label); + + loadArrayBufferViewByteOffsetIntPtr(obj, temp); + branchPtr(Assembler::NotEqual, temp, ImmWord(0), label); + + loadPrivate(Address(obj, ArrayBufferViewObject::initialLengthOffset()), temp); + branchPtr(Assembler::NotEqual, temp, ImmWord(0), &done); + + loadPrivate(Address(obj, ArrayBufferViewObject::initialByteOffsetOffset()), + temp); + branchPtr(Assembler::Equal, temp, ImmWord(0), label); bind(&done); } diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 43974a6ccc..361de3ac5f 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -13,6 +13,8 @@ #include "mozilla/Maybe.h" #include "mozilla/Variant.h" +#include <utility> + #if defined(JS_CODEGEN_X86) # include "jit/x86/MacroAssembler-x86.h" #elif defined(JS_CODEGEN_X64) @@ -1784,6 +1786,21 @@ class MacroAssembler : public MacroAssemblerSpecific { Register scratch, Register spectreRegToZero, Label* label); + private: + inline void branchTestClass(Condition cond, Register clasp, + std::pair<const JSClass*, const JSClass*> classes, + Label* label); + + public: + inline void branchTestObjClass( + Condition cond, Register obj, + std::pair<const JSClass*, const JSClass*> classes, Register scratch, + Register spectreRegToZero, Label* label); + inline void branchTestObjClassNoSpectreMitigations( + Condition cond, Register obj, + std::pair<const JSClass*, const JSClass*> classes, Register scratch, + Label* label); + inline void branchTestObjShape(Condition cond, Register obj, const Shape* shape, Register scratch, Register spectreRegToZero, Label* label); @@ -4191,23 +4208,23 @@ class MacroAssembler : public MacroAssemblerSpecific { // MIPS: `valueTemp`, `offsetTemp` and `maskTemp` must be defined for 8-bit // and 16-bit wide operations. - void compareExchange(Scalar::Type type, const Synchronization& sync, + void compareExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register expected, Register replacement, Register output) DEFINED_ON(arm, arm64, x86_shared); - void compareExchange(Scalar::Type type, const Synchronization& sync, + void compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register expected, Register replacement, Register output) DEFINED_ON(arm, arm64, x86_shared); - void compareExchange(Scalar::Type type, const Synchronization& sync, + void compareExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register expected, Register replacement, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) DEFINED_ON(mips_shared, loong64, riscv64); - void compareExchange(Scalar::Type type, const Synchronization& sync, + void compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register expected, Register replacement, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) @@ -4218,12 +4235,12 @@ class MacroAssembler : public MacroAssemblerSpecific { // ARM: Registers must be distinct; `replacement` and `output` must be // (even,odd) pairs. - void compareExchange64(const Synchronization& sync, const Address& mem, + void compareExchange64(Synchronization sync, const Address& mem, Register64 expected, Register64 replacement, Register64 output) DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); - void compareExchange64(const Synchronization& sync, const BaseIndex& mem, + void compareExchange64(Synchronization sync, const BaseIndex& mem, Register64 expected, Register64 replacement, Register64 output) DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); @@ -4232,20 +4249,20 @@ class MacroAssembler : public MacroAssemblerSpecific { // MIPS: `valueTemp`, `offsetTemp` and `maskTemp` must be defined for 8-bit // and 16-bit wide operations. - void atomicExchange(Scalar::Type type, const Synchronization& sync, + void atomicExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register value, Register output) DEFINED_ON(arm, arm64, x86_shared); - void atomicExchange(Scalar::Type type, const Synchronization& sync, + void atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register value, Register output) DEFINED_ON(arm, arm64, x86_shared); - void atomicExchange(Scalar::Type type, const Synchronization& sync, + void atomicExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) DEFINED_ON(mips_shared, loong64, riscv64); - void atomicExchange(Scalar::Type type, const Synchronization& sync, + void atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) DEFINED_ON(mips_shared, loong64, riscv64); @@ -4254,11 +4271,11 @@ class MacroAssembler : public MacroAssemblerSpecific { // ARM: `value` and `output` must be distinct and (even,odd) pairs. // ARM64: `value` and `output` must be distinct. - void atomicExchange64(const Synchronization& sync, const Address& mem, + void atomicExchange64(Synchronization sync, const Address& mem, Register64 value, Register64 output) DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); - void atomicExchange64(const Synchronization& sync, const BaseIndex& mem, + void atomicExchange64(Synchronization sync, const BaseIndex& mem, Register64 value, Register64 output) DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); @@ -4275,33 +4292,31 @@ class MacroAssembler : public MacroAssemblerSpecific { // MIPS: `valueTemp`, `offsetTemp` and `maskTemp` must be defined for 8-bit // and 16-bit wide operations; `value` and `output` must differ. - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Register value, const Address& mem, - Register temp, Register output) - DEFINED_ON(arm, arm64, x86_shared); + void atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op, + Register value, const Address& mem, Register temp, + Register output) DEFINED_ON(arm, arm64, x86_shared); - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Imm32 value, const Address& mem, - Register temp, Register output) DEFINED_ON(x86_shared); + void atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op, + Imm32 value, const Address& mem, Register temp, + Register output) DEFINED_ON(x86_shared); - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Register value, const BaseIndex& mem, - Register temp, Register output) - DEFINED_ON(arm, arm64, x86_shared); + void atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op, + Register value, const BaseIndex& mem, Register temp, + Register output) DEFINED_ON(arm, arm64, x86_shared); - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Imm32 value, const BaseIndex& mem, - Register temp, Register output) DEFINED_ON(x86_shared); + void atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op, + Imm32 value, const BaseIndex& mem, Register temp, + Register output) DEFINED_ON(x86_shared); - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Register value, const Address& mem, - Register valueTemp, Register offsetTemp, Register maskTemp, - Register output) DEFINED_ON(mips_shared, loong64, riscv64); + void atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op, + Register value, const Address& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, Register output) + DEFINED_ON(mips_shared, loong64, riscv64); - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, Register maskTemp, - Register output) DEFINED_ON(mips_shared, loong64, riscv64); + void atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op, + Register value, const BaseIndex& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, Register output) + DEFINED_ON(mips_shared, loong64, riscv64); // x86: // `temp` must be ecx:ebx; `output` must be edx:eax. @@ -4313,23 +4328,21 @@ class MacroAssembler : public MacroAssemblerSpecific { // ARM64: // Registers `value`, `temp`, and `output` must all differ. - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const Address& mem, Register64 temp, - Register64 output) + void atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, + const Address& mem, Register64 temp, Register64 output) DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64); - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - const Address& value, const Address& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); + void atomicFetchOp64(Synchronization sync, AtomicOp op, const Address& value, + const Address& mem, Register64 temp, Register64 output) + DEFINED_ON(x86); - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const BaseIndex& mem, Register64 temp, - Register64 output) + void atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, + const BaseIndex& mem, Register64 temp, Register64 output) DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64); - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - const Address& value, const BaseIndex& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); + void atomicFetchOp64(Synchronization sync, AtomicOp op, const Address& value, + const BaseIndex& mem, Register64 temp, Register64 output) + DEFINED_ON(x86); // x64: // `value` can be any register. @@ -4338,18 +4351,18 @@ class MacroAssembler : public MacroAssemblerSpecific { // ARM64: // Registers `value` and `temp` must differ. - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const Address& mem) DEFINED_ON(x64); + void atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, + const Address& mem) DEFINED_ON(x64); - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const Address& mem, Register64 temp) + void atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, + const Address& mem, Register64 temp) DEFINED_ON(arm, arm64, mips64, loong64, riscv64); - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const BaseIndex& mem) DEFINED_ON(x64); + void atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, + const BaseIndex& mem) DEFINED_ON(x64); - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const BaseIndex& mem, Register64 temp) + void atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, + const BaseIndex& mem, Register64 temp) DEFINED_ON(arm, arm64, mips64, loong64, riscv64); // 64-bit atomic load. On 64-bit systems, use regular load with @@ -4358,16 +4371,16 @@ class MacroAssembler : public MacroAssemblerSpecific { // x86: `temp` must be ecx:ebx; `output` must be edx:eax. // ARM: `output` must be (even,odd) pair. - void atomicLoad64(const Synchronization& sync, const Address& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); + void atomicLoad64(Synchronization sync, const Address& mem, Register64 temp, + Register64 output) DEFINED_ON(x86); - void atomicLoad64(const Synchronization& sync, const BaseIndex& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); + void atomicLoad64(Synchronization sync, const BaseIndex& mem, Register64 temp, + Register64 output) DEFINED_ON(x86); - void atomicLoad64(const Synchronization& sync, const Address& mem, - Register64 output) DEFINED_ON(arm); + void atomicLoad64(Synchronization sync, const Address& mem, Register64 output) + DEFINED_ON(arm); - void atomicLoad64(const Synchronization& sync, const BaseIndex& mem, + void atomicLoad64(Synchronization sync, const BaseIndex& mem, Register64 output) DEFINED_ON(arm); // 64-bit atomic store. On 64-bit systems, use regular store with @@ -4376,10 +4389,10 @@ class MacroAssembler : public MacroAssemblerSpecific { // x86: `value` must be ecx:ebx; `temp` must be edx:eax. // ARM: `value` and `temp` must be (even,odd) pairs. - void atomicStore64(const Synchronization& sync, const Address& mem, - Register64 value, Register64 temp) DEFINED_ON(x86, arm); + void atomicStore64(Synchronization sync, const Address& mem, Register64 value, + Register64 temp) DEFINED_ON(x86, arm); - void atomicStore64(const Synchronization& sync, const BaseIndex& mem, + void atomicStore64(Synchronization sync, const BaseIndex& mem, Register64 value, Register64 temp) DEFINED_ON(x86, arm); // ======================================================================== @@ -4594,105 +4607,105 @@ class MacroAssembler : public MacroAssemblerSpecific { // For additional register constraints, see the primitive 32-bit operations // and/or wasm operations above. - void compareExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void compareExchangeJS(Scalar::Type arrayType, Synchronization sync, const Address& mem, Register expected, Register replacement, Register temp, AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - void compareExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void compareExchangeJS(Scalar::Type arrayType, Synchronization sync, const BaseIndex& mem, Register expected, Register replacement, Register temp, AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - void compareExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void compareExchangeJS(Scalar::Type arrayType, Synchronization sync, const Address& mem, Register expected, Register replacement, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) DEFINED_ON(mips_shared, loong64, riscv64); - void compareExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void compareExchangeJS(Scalar::Type arrayType, Synchronization sync, const BaseIndex& mem, Register expected, Register replacement, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) DEFINED_ON(mips_shared, loong64, riscv64); - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicExchangeJS(Scalar::Type arrayType, Synchronization sync, const Address& mem, Register value, Register temp, AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicExchangeJS(Scalar::Type arrayType, Synchronization sync, const BaseIndex& mem, Register value, Register temp, AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicExchangeJS(Scalar::Type arrayType, Synchronization sync, const Address& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) DEFINED_ON(mips_shared, loong64, riscv64); - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicExchangeJS(Scalar::Type arrayType, Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) DEFINED_ON(mips_shared, loong64, riscv64); - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const Address& mem, Register temp1, Register temp2, AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register temp1, Register temp2, AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Imm32 value, const Address& mem, Register temp1, Register temp2, AnyRegister output) DEFINED_ON(x86_shared); - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Imm32 value, const BaseIndex& mem, Register temp1, Register temp2, AnyRegister output) DEFINED_ON(x86_shared); - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) DEFINED_ON(mips_shared, loong64, riscv64); - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) DEFINED_ON(mips_shared, loong64, riscv64); - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicEffectOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const Address& mem, Register temp) DEFINED_ON(arm, arm64, x86_shared); - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicEffectOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register temp) DEFINED_ON(arm, arm64, x86_shared); - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicEffectOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Imm32 value, const Address& mem, Register temp) DEFINED_ON(x86_shared); - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicEffectOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Imm32 value, const BaseIndex& mem, Register temp) DEFINED_ON(x86_shared); - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicEffectOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp) DEFINED_ON(mips_shared, loong64, riscv64); - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + void atomicEffectOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp) @@ -5245,8 +5258,8 @@ class MacroAssembler : public MacroAssemblerSpecific { void storeToTypedBigIntArray(Scalar::Type arrayType, Register64 value, const Address& dest); - void memoryBarrierBefore(const Synchronization& sync); - void memoryBarrierAfter(const Synchronization& sync); + void memoryBarrierBefore(Synchronization sync); + void memoryBarrierAfter(Synchronization sync); void debugAssertIsObject(const ValueOperand& val); void debugAssertObjHasFixedSlots(Register obj, Register scratch); @@ -5284,12 +5297,41 @@ class MacroAssembler : public MacroAssemblerSpecific { Label* label); void typedArrayElementSize(Register obj, Register output); + + private: + // Shift |output| by the element shift of the ResizableTypedArray in |obj|. + void resizableTypedArrayElementShiftBy(Register obj, Register output, + Register scratch); + + public: void branchIfClassIsNotTypedArray(Register clasp, Label* notTypedArray); void branchIfClassIsNotFixedLengthTypedArray(Register clasp, Label* notTypedArray); + void branchIfClassIsNotResizableTypedArray(Register clasp, + Label* notTypedArray); + + private: + enum class BranchIfDetached { No, Yes }; + + void branchIfHasDetachedArrayBuffer(BranchIfDetached branchIf, Register obj, + Register temp, Label* label); + public: void branchIfHasDetachedArrayBuffer(Register obj, Register temp, - Label* label); + Label* label) { + branchIfHasDetachedArrayBuffer(BranchIfDetached::Yes, obj, temp, label); + } + + void branchIfHasAttachedArrayBuffer(Register obj, Register temp, + Label* label) { + branchIfHasDetachedArrayBuffer(BranchIfDetached::No, obj, temp, label); + } + + void branchIfResizableArrayBufferViewOutOfBounds(Register obj, Register temp, + Label* label); + + void branchIfResizableArrayBufferViewInBounds(Register obj, Register temp, + Label* label); void branchIfNativeIteratorNotReusable(Register ni, Label* notReusable); void branchNativeIteratorIndices(Condition cond, Register ni, Register temp, @@ -5560,7 +5602,7 @@ class MacroAssembler : public MacroAssemblerSpecific { void loadMegamorphicCache(Register dest); void lookupStringInAtomCacheLastLookups(Register str, Register scratch, - Label* fail); + Register output, Label* fail); void loadMegamorphicSetPropCache(Register dest); void loadAtomOrSymbolAndHash(ValueOperand value, Register outId, @@ -5627,6 +5669,35 @@ class MacroAssembler : public MacroAssemblerSpecific { void loadArrayBufferViewByteOffsetIntPtr(Register obj, Register output); void loadArrayBufferViewLengthIntPtr(Register obj, Register output); + void loadGrowableSharedArrayBufferByteLengthIntPtr(Synchronization sync, + Register obj, + Register output); + + private: + enum class ResizableArrayBufferView { TypedArray, DataView }; + + void loadResizableArrayBufferViewLengthIntPtr(ResizableArrayBufferView view, + Synchronization sync, + Register obj, Register output, + Register scratch); + + public: + void loadResizableTypedArrayLengthIntPtr(Synchronization sync, Register obj, + Register output, Register scratch) { + loadResizableArrayBufferViewLengthIntPtr( + ResizableArrayBufferView::TypedArray, sync, obj, output, scratch); + } + + void loadResizableDataViewByteLengthIntPtr(Synchronization sync, Register obj, + Register output, + Register scratch) { + loadResizableArrayBufferViewLengthIntPtr(ResizableArrayBufferView::DataView, + sync, obj, output, scratch); + } + + void loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr( + Register obj, Register output, Register scratch); + private: void isCallableOrConstructor(bool isCallable, Register obj, Register output, Label* isProxy); diff --git a/js/src/jit/PcScriptCache.h b/js/src/jit/PcScriptCache.h deleted file mode 100644 index c83c479c85..0000000000 --- a/js/src/jit/PcScriptCache.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -#ifndef jit_PcScriptCache_h -#define jit_PcScriptCache_h - -#include "mozilla/Array.h" -#include "js/TypeDecls.h" -#include "vm/Runtime.h" - -// Defines a fixed-size hash table solely for the purpose of caching -// jit::GetPcScript(). One cache is attached to each JSRuntime; it functions as -// if cleared on GC. - -namespace js { -namespace jit { - -struct PcScriptCacheEntry { - uint8_t* returnAddress; // Key into the hash table. - jsbytecode* pc; // Cached PC. - JSScript* script; // Cached script. -}; - -struct PcScriptCache { - private: - static const uint32_t Length = 73; - - // GC number at the time the cache was filled or created. - // Storing and checking against this number allows us to not bother - // clearing this cache on every GC -- only when actually necessary. - uint64_t gcNumber; - - // List of cache entries. - mozilla::Array<PcScriptCacheEntry, Length> entries; - - public: - explicit PcScriptCache(uint64_t gcNumber) { clear(gcNumber); } - - void clear(uint64_t gcNumber) { - for (uint32_t i = 0; i < Length; i++) { - entries[i].returnAddress = nullptr; - } - this->gcNumber = gcNumber; - } - - // Get a value from the cache. May perform lazy allocation. - [[nodiscard]] bool get(JSRuntime* rt, uint32_t hash, uint8_t* addr, - JSScript** scriptRes, jsbytecode** pcRes) { - // If a GC occurred, lazily clear the cache now. - if (gcNumber != rt->gc.gcNumber()) { - clear(rt->gc.gcNumber()); - return false; - } - - if (entries[hash].returnAddress != addr) { - return false; - } - - *scriptRes = entries[hash].script; - if (pcRes) { - *pcRes = entries[hash].pc; - } - - return true; - } - - void add(uint32_t hash, uint8_t* addr, jsbytecode* pc, JSScript* script) { - MOZ_ASSERT(addr); - MOZ_ASSERT(pc); - MOZ_ASSERT(script); - entries[hash].returnAddress = addr; - entries[hash].pc = pc; - entries[hash].script = script; - } - - static uint32_t Hash(uint8_t* addr) { - uint32_t key = (uint32_t)((uintptr_t)addr); - return ((key >> 3) * 2654435761u) % Length; - } -}; - -} // namespace jit -} // namespace js - -#endif /* jit_PcScriptCache_h */ diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 4ed15daabb..bd8380a690 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1802,6 +1802,25 @@ void MArrayBufferViewByteOffset::computeRange(TempAllocator& alloc) { } } +void MResizableTypedArrayByteOffsetMaybeOutOfBounds::computeRange( + TempAllocator& alloc) { + if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) { + setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX)); + } +} + +void MResizableTypedArrayLength::computeRange(TempAllocator& alloc) { + if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) { + setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX)); + } +} + +void MResizableDataViewByteLength::computeRange(TempAllocator& alloc) { + if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) { + setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX)); + } +} + void MTypedArrayElementSize::computeRange(TempAllocator& alloc) { constexpr auto MaxTypedArraySize = sizeof(double); diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index e70722bffe..220ffe7bb2 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -11,6 +11,7 @@ #include "builtin/Object.h" #include "builtin/RegExp.h" #include "builtin/String.h" +#include "jit/AtomicOperations.h" #include "jit/Bailouts.h" #include "jit/CompileInfo.h" #include "jit/Ion.h" @@ -2013,15 +2014,11 @@ bool MAtomicIsLockFree::writeRecoverData(CompactBufferWriter& writer) const { RAtomicIsLockFree::RAtomicIsLockFree(CompactBufferReader& reader) {} bool RAtomicIsLockFree::recover(JSContext* cx, SnapshotIterator& iter) const { - RootedValue operand(cx, iter.read()); + Value operand = iter.read(); MOZ_ASSERT(operand.isInt32()); - int32_t result; - if (!js::AtomicIsLockFree(cx, operand, &result)) { - return false; - } - - iter.storeInstructionResult(Int32Value(result)); + bool result = AtomicOperations::isLockfreeJS(operand.toInt32()); + iter.storeInstructionResult(BooleanValue(result)); return true; } diff --git a/js/src/jit/Registers.h b/js/src/jit/Registers.h index 1ae9c1954c..43e94e6bff 100644 --- a/js/src/jit/Registers.h +++ b/js/src/jit/Registers.h @@ -205,6 +205,7 @@ struct Register64 { return high != other.high || low != other.low; } Register scratchReg() { return high; } + Register secondScratchReg() { return low; } static Register64 Invalid() { return Register64(Register::Invalid(), Register::Invalid()); } diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 29777d08c7..ed3f63c88c 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -2557,13 +2557,14 @@ BigInt* BigIntAsUintN(JSContext* cx, HandleBigInt x, int32_t bits) { } template <typename T> -static int32_t AtomicsCompareExchange(FixedLengthTypedArrayObject* typedArray, +static int32_t AtomicsCompareExchange(TypedArrayObject* typedArray, size_t index, int32_t expected, int32_t replacement) { AutoUnsafeCallWithABI unsafe; MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>(); return jit::AtomicOperations::compareExchangeSeqCst(addr + index, T(expected), @@ -2590,12 +2591,13 @@ AtomicsCompareExchangeFn AtomicsCompareExchange(Scalar::Type elementType) { } template <typename T> -static int32_t AtomicsExchange(FixedLengthTypedArrayObject* typedArray, - size_t index, int32_t value) { +static int32_t AtomicsExchange(TypedArrayObject* typedArray, size_t index, + int32_t value) { AutoUnsafeCallWithABI unsafe; MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>(); return jit::AtomicOperations::exchangeSeqCst(addr + index, T(value)); @@ -2621,12 +2623,13 @@ AtomicsReadWriteModifyFn AtomicsExchange(Scalar::Type elementType) { } template <typename T> -static int32_t AtomicsAdd(FixedLengthTypedArrayObject* typedArray, size_t index, +static int32_t AtomicsAdd(TypedArrayObject* typedArray, size_t index, int32_t value) { AutoUnsafeCallWithABI unsafe; MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>(); return jit::AtomicOperations::fetchAddSeqCst(addr + index, T(value)); @@ -2652,12 +2655,13 @@ AtomicsReadWriteModifyFn AtomicsAdd(Scalar::Type elementType) { } template <typename T> -static int32_t AtomicsSub(FixedLengthTypedArrayObject* typedArray, size_t index, +static int32_t AtomicsSub(TypedArrayObject* typedArray, size_t index, int32_t value) { AutoUnsafeCallWithABI unsafe; MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>(); return jit::AtomicOperations::fetchSubSeqCst(addr + index, T(value)); @@ -2683,12 +2687,13 @@ AtomicsReadWriteModifyFn AtomicsSub(Scalar::Type elementType) { } template <typename T> -static int32_t AtomicsAnd(FixedLengthTypedArrayObject* typedArray, size_t index, +static int32_t AtomicsAnd(TypedArrayObject* typedArray, size_t index, int32_t value) { AutoUnsafeCallWithABI unsafe; MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>(); return jit::AtomicOperations::fetchAndSeqCst(addr + index, T(value)); @@ -2714,12 +2719,13 @@ AtomicsReadWriteModifyFn AtomicsAnd(Scalar::Type elementType) { } template <typename T> -static int32_t AtomicsOr(FixedLengthTypedArrayObject* typedArray, size_t index, +static int32_t AtomicsOr(TypedArrayObject* typedArray, size_t index, int32_t value) { AutoUnsafeCallWithABI unsafe; MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>(); return jit::AtomicOperations::fetchOrSeqCst(addr + index, T(value)); @@ -2745,12 +2751,13 @@ AtomicsReadWriteModifyFn AtomicsOr(Scalar::Type elementType) { } template <typename T> -static int32_t AtomicsXor(FixedLengthTypedArrayObject* typedArray, size_t index, +static int32_t AtomicsXor(TypedArrayObject* typedArray, size_t index, int32_t value) { AutoUnsafeCallWithABI unsafe; MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>(); return jit::AtomicOperations::fetchXorSeqCst(addr + index, T(value)); @@ -2776,12 +2783,12 @@ AtomicsReadWriteModifyFn AtomicsXor(Scalar::Type elementType) { } template <typename AtomicOp, typename... Args> -static BigInt* AtomicAccess64(JSContext* cx, - FixedLengthTypedArrayObject* typedArray, +static BigInt* AtomicAccess64(JSContext* cx, TypedArrayObject* typedArray, size_t index, AtomicOp op, Args... args) { MOZ_ASSERT(Scalar::isBigIntType(typedArray->type())); MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); if (typedArray->type() == Scalar::BigInt64) { SharedMem<int64_t*> addr = typedArray->dataPointerEither().cast<int64_t*>(); @@ -2795,11 +2802,12 @@ static BigInt* AtomicAccess64(JSContext* cx, } template <typename AtomicOp, typename... Args> -static auto AtomicAccess64(FixedLengthTypedArrayObject* typedArray, - size_t index, AtomicOp op, Args... args) { +static auto AtomicAccess64(TypedArrayObject* typedArray, size_t index, + AtomicOp op, Args... args) { MOZ_ASSERT(Scalar::isBigIntType(typedArray->type())); MOZ_ASSERT(!typedArray->hasDetachedBuffer()); - MOZ_ASSERT(index < typedArray->length()); + MOZ_ASSERT_IF(typedArray->hasResizableBuffer(), !typedArray->isOutOfBounds()); + MOZ_ASSERT(index < typedArray->length().valueOr(0)); if (typedArray->type() == Scalar::BigInt64) { SharedMem<int64_t*> addr = typedArray->dataPointerEither().cast<int64_t*>(); @@ -2810,14 +2818,14 @@ static auto AtomicAccess64(FixedLengthTypedArrayObject* typedArray, return op(addr + index, BigInt::toUint64(args)...); } -BigInt* AtomicsLoad64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, +BigInt* AtomicsLoad64(JSContext* cx, TypedArrayObject* typedArray, size_t index) { return AtomicAccess64(cx, typedArray, index, [](auto addr) { return jit::AtomicOperations::loadSeqCst(addr); }); } -void AtomicsStore64(FixedLengthTypedArrayObject* typedArray, size_t index, +void AtomicsStore64(TypedArrayObject* typedArray, size_t index, const BigInt* value) { AutoUnsafeCallWithABI unsafe; @@ -2829,8 +2837,7 @@ void AtomicsStore64(FixedLengthTypedArrayObject* typedArray, size_t index, value); } -BigInt* AtomicsCompareExchange64(JSContext* cx, - FixedLengthTypedArrayObject* typedArray, +BigInt* AtomicsCompareExchange64(JSContext* cx, TypedArrayObject* typedArray, size_t index, const BigInt* expected, const BigInt* replacement) { return AtomicAccess64( @@ -2842,9 +2849,8 @@ BigInt* AtomicsCompareExchange64(JSContext* cx, expected, replacement); } -BigInt* AtomicsExchange64(JSContext* cx, - FixedLengthTypedArrayObject* typedArray, size_t index, - const BigInt* value) { +BigInt* AtomicsExchange64(JSContext* cx, TypedArrayObject* typedArray, + size_t index, const BigInt* value) { return AtomicAccess64( cx, typedArray, index, [](auto addr, auto val) { @@ -2853,8 +2859,8 @@ BigInt* AtomicsExchange64(JSContext* cx, value); } -BigInt* AtomicsAdd64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value) { +BigInt* AtomicsAdd64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value) { return AtomicAccess64( cx, typedArray, index, [](auto addr, auto val) { @@ -2863,8 +2869,8 @@ BigInt* AtomicsAdd64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, value); } -BigInt* AtomicsAnd64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value) { +BigInt* AtomicsAnd64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value) { return AtomicAccess64( cx, typedArray, index, [](auto addr, auto val) { @@ -2873,8 +2879,8 @@ BigInt* AtomicsAnd64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, value); } -BigInt* AtomicsOr64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value) { +BigInt* AtomicsOr64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value) { return AtomicAccess64( cx, typedArray, index, [](auto addr, auto val) { @@ -2883,8 +2889,8 @@ BigInt* AtomicsOr64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, value); } -BigInt* AtomicsSub64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value) { +BigInt* AtomicsSub64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value) { return AtomicAccess64( cx, typedArray, index, [](auto addr, auto val) { @@ -2893,8 +2899,8 @@ BigInt* AtomicsSub64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, value); } -BigInt* AtomicsXor64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value) { +BigInt* AtomicsXor64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value) { return AtomicAccess64( cx, typedArray, index, [](auto addr, auto val) { diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index cfce36caaa..a68dd8279f 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -27,7 +27,6 @@ namespace js { class AbstractGeneratorObject; class ArrayObject; -class FixedLengthTypedArrayObject; class GlobalObject; class InterpreterFrame; class LexicalScope; @@ -640,11 +639,11 @@ bool StringBigIntCompare(JSContext* cx, HandleString x, HandleBigInt y, BigInt* BigIntAsIntN(JSContext* cx, HandleBigInt x, int32_t bits); BigInt* BigIntAsUintN(JSContext* cx, HandleBigInt x, int32_t bits); -using AtomicsCompareExchangeFn = int32_t (*)(FixedLengthTypedArrayObject*, - size_t, int32_t, int32_t); +using AtomicsCompareExchangeFn = int32_t (*)(TypedArrayObject*, size_t, int32_t, + int32_t); -using AtomicsReadWriteModifyFn = int32_t (*)(FixedLengthTypedArrayObject*, - size_t, int32_t); +using AtomicsReadWriteModifyFn = int32_t (*)(TypedArrayObject*, size_t, + int32_t); AtomicsCompareExchangeFn AtomicsCompareExchange(Scalar::Type elementType); AtomicsReadWriteModifyFn AtomicsExchange(Scalar::Type elementType); @@ -654,31 +653,29 @@ AtomicsReadWriteModifyFn AtomicsAnd(Scalar::Type elementType); AtomicsReadWriteModifyFn AtomicsOr(Scalar::Type elementType); AtomicsReadWriteModifyFn AtomicsXor(Scalar::Type elementType); -BigInt* AtomicsLoad64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, +BigInt* AtomicsLoad64(JSContext* cx, TypedArrayObject* typedArray, size_t index); -void AtomicsStore64(FixedLengthTypedArrayObject* typedArray, size_t index, +void AtomicsStore64(TypedArrayObject* typedArray, size_t index, const BigInt* value); -BigInt* AtomicsCompareExchange64(JSContext* cx, - FixedLengthTypedArrayObject* typedArray, +BigInt* AtomicsCompareExchange64(JSContext* cx, TypedArrayObject* typedArray, size_t index, const BigInt* expected, const BigInt* replacement); -BigInt* AtomicsExchange64(JSContext* cx, - FixedLengthTypedArrayObject* typedArray, size_t index, - const BigInt* value); - -BigInt* AtomicsAdd64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value); -BigInt* AtomicsAnd64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value); -BigInt* AtomicsOr64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value); -BigInt* AtomicsSub64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value); -BigInt* AtomicsXor64(JSContext* cx, FixedLengthTypedArrayObject* typedArray, - size_t index, const BigInt* value); +BigInt* AtomicsExchange64(JSContext* cx, TypedArrayObject* typedArray, + size_t index, const BigInt* value); + +BigInt* AtomicsAdd64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value); +BigInt* AtomicsAnd64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value); +BigInt* AtomicsOr64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value); +BigInt* AtomicsSub64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value); +BigInt* AtomicsXor64(JSContext* cx, TypedArrayObject* typedArray, size_t index, + const BigInt* value); JSAtom* AtomizeStringNoGC(JSContext* cx, JSString* str); diff --git a/js/src/jit/WarpBuilderShared.cpp b/js/src/jit/WarpBuilderShared.cpp index 89e93d6150..e04984690c 100644 --- a/js/src/jit/WarpBuilderShared.cpp +++ b/js/src/jit/WarpBuilderShared.cpp @@ -22,9 +22,12 @@ WarpBuilderShared::WarpBuilderShared(WarpSnapshot& snapshot, bool WarpBuilderShared::resumeAfter(MInstruction* ins, BytecodeLocation loc) { // resumeAfter should only be used with effectful instructions. The only - // exception is MInt64ToBigInt, it's used to convert the result of a call into - // Wasm code so we attach the resume point to that instead of to the call. - MOZ_ASSERT(ins->isEffectful() || ins->isInt64ToBigInt()); + // exceptions are: + // 1. MInt64ToBigInt, which is used to convert the result of a call into Wasm + // code so we attach the resume point to that instead of to the call. + // 2. MPostIntPtrConversion which is used after conversion from IntPtr. + MOZ_ASSERT(ins->isEffectful() || ins->isInt64ToBigInt() || + ins->isPostIntPtrConversion()); MOZ_ASSERT(!ins->isMovable()); MResumePoint* resumePoint = MResumePoint::New( diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp index 7654232ecd..9a99e0f5c3 100644 --- a/js/src/jit/WarpCacheIRTranspiler.cpp +++ b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -11,8 +11,6 @@ #include "jsmath.h" -#include "builtin/DataViewObject.h" -#include "builtin/MapObject.h" #include "jit/AtomicOp.h" #include "jit/CacheIR.h" #include "jit/CacheIRCompiler.h" @@ -26,7 +24,6 @@ #include "jit/WarpBuilderShared.h" #include "jit/WarpSnapshot.h" #include "js/ScalarType.h" // js::Scalar::Type -#include "vm/ArgumentsObject.h" #include "vm/BytecodeLocation.h" #include "wasm/WasmCode.h" @@ -52,7 +49,8 @@ class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared { // Array mapping call arguments to OperandId. using ArgumentKindArray = - mozilla::EnumeratedArray<ArgumentKind, ArgumentKind::NumKinds, OperandId>; + mozilla::EnumeratedArray<ArgumentKind, OperandId, + size_t(ArgumentKind::NumKinds)>; ArgumentKindArray argumentOperandIds_; void setArgumentId(ArgumentKind kind, OperandId id) { @@ -255,14 +253,20 @@ class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared { ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId, uint32_t newShapeOffset); - void addDataViewData(MDefinition* obj, Scalar::Type type, - MDefinition** offset, MInstruction** elements); + MInstruction* emitTypedArrayLength(ArrayBufferViewKind viewKind, + MDefinition* obj); - [[nodiscard]] bool emitAtomicsBinaryOp(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect, AtomicOp op); + MInstruction* emitDataViewLength(ArrayBufferViewKind viewKind, + MDefinition* obj); + + void addDataViewData(ArrayBufferViewKind viewKind, MDefinition* obj, + Scalar::Type type, MDefinition** offset, + MInstruction** elements); + + [[nodiscard]] bool emitAtomicsBinaryOp( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind, + AtomicOp op); [[nodiscard]] bool emitLoadArgumentSlot(ValOperandId resultId, uint32_t slotIndex); @@ -347,9 +351,14 @@ bool WarpCacheIRTranspiler::transpile( // Effectful instructions should have a resume point. MIonToWasmCall is an // exception: we can attach the resume point to the MInt64ToBigInt instruction - // instead. + // instead. Other exceptions are MResizableTypedArrayLength and + // MResizableDataViewByteLength, and MGrowableSharedArrayBufferByteLength, + // which add the resume point to MPostIntPtrConversion. MOZ_ASSERT_IF(effectful_, - effectful_->resumePoint() || effectful_->isIonToWasmCall()); + effectful_->resumePoint() || effectful_->isIonToWasmCall() || + effectful_->isResizableTypedArrayLength() || + effectful_->isResizableDataViewByteLength() || + effectful_->isGrowableSharedArrayBufferByteLength()); return true; } @@ -385,31 +394,44 @@ bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId, return true; } +bool WarpCacheIRTranspiler::emitGuardEitherClass(ObjOperandId objId, + GuardClassKind kind1, + GuardClassKind kind2) { + MDefinition* def = getOperand(objId); + + // We don't yet need this case, so it's unsupported for now. + MOZ_ASSERT(kind1 != GuardClassKind::JSFunction && + kind2 != GuardClassKind::JSFunction); + + const JSClass* classp1 = classForGuardClassKind(kind1); + const JSClass* classp2 = classForGuardClassKind(kind2); + auto* ins = MGuardToEitherClass::New(alloc(), def, classp1, classp2); + + add(ins); + + setOperand(objId, ins); + return true; +} + const JSClass* WarpCacheIRTranspiler::classForGuardClassKind( GuardClassKind kind) { switch (kind) { case GuardClassKind::Array: - return &ArrayObject::class_; case GuardClassKind::PlainObject: - return &PlainObject::class_; case GuardClassKind::FixedLengthArrayBuffer: - return &FixedLengthArrayBufferObject::class_; + case GuardClassKind::ResizableArrayBuffer: case GuardClassKind::FixedLengthSharedArrayBuffer: - return &FixedLengthSharedArrayBufferObject::class_; + case GuardClassKind::GrowableSharedArrayBuffer: case GuardClassKind::FixedLengthDataView: - return &FixedLengthDataViewObject::class_; + case GuardClassKind::ResizableDataView: case GuardClassKind::MappedArguments: - return &MappedArgumentsObject::class_; case GuardClassKind::UnmappedArguments: - return &UnmappedArgumentsObject::class_; - case GuardClassKind::WindowProxy: - return mirGen().runtime->maybeWindowProxyClass(); case GuardClassKind::Set: - return &SetObject::class_; case GuardClassKind::Map: - return &MapObject::class_; case GuardClassKind::BoundFunction: - return &BoundFunctionObject::class_; + return ClassFor(kind); + case GuardClassKind::WindowProxy: + return mirGen().runtime->maybeWindowProxyClass(); case GuardClassKind::JSFunction: break; } @@ -830,6 +852,16 @@ bool WarpCacheIRTranspiler::emitGuardIsFixedLengthTypedArray( return true; } +bool WarpCacheIRTranspiler::emitGuardIsResizableTypedArray(ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + auto* ins = MGuardIsResizableTypedArray::New(alloc(), obj); + add(ins); + + setOperand(objId, ins); + return true; +} + bool WarpCacheIRTranspiler::emitGuardHasProxyHandler(ObjOperandId objId, uint32_t handlerOffset) { MDefinition* obj = getOperand(objId); @@ -2121,13 +2153,35 @@ bool WarpCacheIRTranspiler::emitCallObjectHasSparseElementResult( return true; } +MInstruction* WarpCacheIRTranspiler::emitTypedArrayLength( + ArrayBufferViewKind viewKind, MDefinition* obj) { + if (viewKind == ArrayBufferViewKind::FixedLength) { + auto* length = MArrayBufferViewLength::New(alloc(), obj); + add(length); + + return length; + } + + // Bounds check doesn't require a memory barrier. See IsValidIntegerIndex + // abstract operation which reads the underlying buffer byte length using + // "unordered" memory order. + auto barrier = MemoryBarrierRequirement::NotRequired; + + // Movable and removable because no memory barrier is needed. + auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier); + length->setMovable(); + length->setNotGuard(); + add(length); + + return length; +} + bool WarpCacheIRTranspiler::emitLoadTypedArrayElementExistsResult( - ObjOperandId objId, IntPtrOperandId indexId) { + ObjOperandId objId, IntPtrOperandId indexId, ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitTypedArrayLength(viewKind, obj); // Unsigned comparison to catch negative indices. auto* ins = MCompare::New(alloc(), index, length, JSOp::Lt, @@ -2165,27 +2219,29 @@ static MIRType MIRTypeForArrayBufferViewRead(Scalar::Type arrayType, bool WarpCacheIRTranspiler::emitLoadTypedArrayElementResult( ObjOperandId objId, IntPtrOperandId indexId, Scalar::Type elementType, - bool handleOOB, bool forceDoubleForUint32) { + bool handleOOB, bool forceDoubleForUint32, ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); + auto* length = emitTypedArrayLength(viewKind, obj); + + if (!handleOOB) { + // MLoadTypedArrayElementHole does the bounds checking. + index = addBoundsCheck(index, length); + } + + auto* elements = MArrayBufferViewElements::New(alloc(), obj); + add(elements); + if (handleOOB) { auto* load = MLoadTypedArrayElementHole::New( - alloc(), obj, index, elementType, forceDoubleForUint32); + alloc(), elements, index, length, elementType, forceDoubleForUint32); add(load); pushResult(load); return true; } - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); - - index = addBoundsCheck(index, length); - - auto* elements = MArrayBufferViewElements::New(alloc(), obj); - add(elements); - auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType); load->setResultType( MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32)); @@ -2717,17 +2773,14 @@ bool WarpCacheIRTranspiler::emitStoreDenseElementHole(ObjOperandId objId, return resumeAfter(store); } -bool WarpCacheIRTranspiler::emitStoreTypedArrayElement(ObjOperandId objId, - Scalar::Type elementType, - IntPtrOperandId indexId, - uint32_t rhsId, - bool handleOOB) { +bool WarpCacheIRTranspiler::emitStoreTypedArrayElement( + ObjOperandId objId, Scalar::Type elementType, IntPtrOperandId indexId, + uint32_t rhsId, bool handleOOB, ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); MDefinition* rhs = getOperand(ValOperandId(rhsId)); - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitTypedArrayLength(viewKind, obj); if (!handleOOB) { // MStoreTypedArrayElementHole does the bounds checking. @@ -2749,11 +2802,34 @@ bool WarpCacheIRTranspiler::emitStoreTypedArrayElement(ObjOperandId objId, return resumeAfter(store); } -void WarpCacheIRTranspiler::addDataViewData(MDefinition* obj, Scalar::Type type, +MInstruction* WarpCacheIRTranspiler::emitDataViewLength( + ArrayBufferViewKind viewKind, MDefinition* obj) { + if (viewKind == ArrayBufferViewKind::FixedLength) { + auto* length = MArrayBufferViewLength::New(alloc(), obj); + add(length); + + return length; + } + + // Bounds check doesn't require a memory barrier. See GetViewValue and + // SetViewValue abstract operations which read the underlying buffer byte + // length using "unordered" memory order. + auto barrier = MemoryBarrierRequirement::NotRequired; + + // Movable and removable because no memory barrier is needed. + auto* length = MResizableDataViewByteLength::New(alloc(), obj, barrier); + length->setMovable(); + length->setNotGuard(); + add(length); + + return length; +} + +void WarpCacheIRTranspiler::addDataViewData(ArrayBufferViewKind viewKind, + MDefinition* obj, Scalar::Type type, MDefinition** offset, MInstruction** elements) { - MInstruction* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitDataViewLength(viewKind, obj); // Adjust the length to account for accesses near the end of the dataview. if (size_t byteSize = Scalar::byteSize(type); byteSize > 1) { @@ -2773,14 +2849,14 @@ void WarpCacheIRTranspiler::addDataViewData(MDefinition* obj, Scalar::Type type, bool WarpCacheIRTranspiler::emitLoadDataViewValueResult( ObjOperandId objId, IntPtrOperandId offsetId, BooleanOperandId littleEndianId, Scalar::Type elementType, - bool forceDoubleForUint32) { + bool forceDoubleForUint32, ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* offset = getOperand(offsetId); MDefinition* littleEndian = getOperand(littleEndianId); // Add bounds check and get the DataViewObject's elements. MInstruction* elements; - addDataViewData(obj, elementType, &offset, &elements); + addDataViewData(viewKind, obj, elementType, &offset, &elements); // Load the element. MInstruction* load; @@ -2802,7 +2878,8 @@ bool WarpCacheIRTranspiler::emitLoadDataViewValueResult( bool WarpCacheIRTranspiler::emitStoreDataViewValueResult( ObjOperandId objId, IntPtrOperandId offsetId, uint32_t valueId, - BooleanOperandId littleEndianId, Scalar::Type elementType) { + BooleanOperandId littleEndianId, Scalar::Type elementType, + ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* offset = getOperand(offsetId); MDefinition* value = getOperand(ValOperandId(valueId)); @@ -2810,7 +2887,7 @@ bool WarpCacheIRTranspiler::emitStoreDataViewValueResult( // Add bounds check and get the DataViewObject's elements. MInstruction* elements; - addDataViewData(obj, elementType, &offset, &elements); + addDataViewData(viewKind, obj, elementType, &offset, &elements); // Store the element. MInstruction* store; @@ -4067,6 +4144,78 @@ bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetDoubleResult( return true; } +bool WarpCacheIRTranspiler:: + emitResizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + auto* byteOffset = + MResizableTypedArrayByteOffsetMaybeOutOfBounds::New(alloc(), obj); + add(byteOffset); + + auto* byteOffsetInt32 = MNonNegativeIntPtrToInt32::New(alloc(), byteOffset); + add(byteOffsetInt32); + + pushResult(byteOffsetInt32); + return true; +} + +bool WarpCacheIRTranspiler:: + emitResizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + auto* byteOffset = + MResizableTypedArrayByteOffsetMaybeOutOfBounds::New(alloc(), obj); + add(byteOffset); + + auto* byteOffsetDouble = MIntPtrToDouble::New(alloc(), byteOffset); + add(byteOffsetDouble); + + pushResult(byteOffsetDouble); + return true; +} + +bool WarpCacheIRTranspiler::emitResizableTypedArrayLengthInt32Result( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + // Explicit |length| accesses are seq-consistent atomic loads. + auto barrier = MemoryBarrierRequirement::Required; + + auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier); + addEffectful(length); + + auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length); + add(lengthInt32); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthInt32); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + +bool WarpCacheIRTranspiler::emitResizableTypedArrayLengthDoubleResult( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + // Explicit |length| accesses are seq-consistent atomic loads. + auto barrier = MemoryBarrierRequirement::Required; + + auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier); + addEffectful(length); + + auto* lengthDouble = MIntPtrToDouble::New(alloc(), length); + add(lengthDouble); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthDouble); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + bool WarpCacheIRTranspiler::emitTypedArrayByteLengthInt32Result( ObjOperandId objId) { MDefinition* obj = getOperand(objId); @@ -4112,6 +4261,63 @@ bool WarpCacheIRTranspiler::emitTypedArrayByteLengthDoubleResult( return true; } +bool WarpCacheIRTranspiler::emitResizableTypedArrayByteLengthInt32Result( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto barrier = MemoryBarrierRequirement::Required; + + auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier); + addEffectful(length); + + auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length); + add(lengthInt32); + + auto* size = MTypedArrayElementSize::New(alloc(), obj); + add(size); + + auto* mul = MMul::New(alloc(), lengthInt32, size, MIRType::Int32); + mul->setCanBeNegativeZero(false); + add(mul); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), mul); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + +bool WarpCacheIRTranspiler::emitResizableTypedArrayByteLengthDoubleResult( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto barrier = MemoryBarrierRequirement::Required; + + auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier); + addEffectful(length); + + auto* lengthDouble = MIntPtrToDouble::New(alloc(), length); + add(lengthDouble); + + auto* size = MTypedArrayElementSize::New(alloc(), obj); + add(size); + + auto* sizeDouble = MToDouble::New(alloc(), size); + add(sizeDouble); + + auto* mul = MMul::New(alloc(), lengthDouble, sizeDouble, MIRType::Double); + mul->setCanBeNegativeZero(false); + add(mul); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), mul); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + bool WarpCacheIRTranspiler::emitTypedArrayElementSizeResult( ObjOperandId objId) { MDefinition* obj = getOperand(objId); @@ -4123,6 +4329,80 @@ bool WarpCacheIRTranspiler::emitTypedArrayElementSizeResult( return true; } +bool WarpCacheIRTranspiler::emitResizableDataViewByteLengthInt32Result( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto barrier = MemoryBarrierRequirement::Required; + + auto* length = MResizableDataViewByteLength::New(alloc(), obj, barrier); + addEffectful(length); + + auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length); + add(lengthInt32); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthInt32); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + +bool WarpCacheIRTranspiler::emitResizableDataViewByteLengthDoubleResult( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + // Explicit |byteLength| accesses are seq-consistent atomic loads. + auto barrier = MemoryBarrierRequirement::Required; + + auto* length = MResizableDataViewByteLength::New(alloc(), obj, barrier); + addEffectful(length); + + auto* lengthDouble = MIntPtrToDouble::New(alloc(), length); + add(lengthDouble); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthDouble); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + +bool WarpCacheIRTranspiler::emitGrowableSharedArrayBufferByteLengthInt32Result( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + auto* length = MGrowableSharedArrayBufferByteLength::New(alloc(), obj); + addEffectful(length); + + auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length); + add(lengthInt32); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthInt32); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + +bool WarpCacheIRTranspiler::emitGrowableSharedArrayBufferByteLengthDoubleResult( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + auto* length = MGrowableSharedArrayBufferByteLength::New(alloc(), obj); + addEffectful(length); + + auto* lengthDouble = MIntPtrToDouble::New(alloc(), length); + add(lengthDouble); + + auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthDouble); + add(postConversion); + + pushResult(postConversion); + return resumeAfterUnchecked(postConversion); +} + bool WarpCacheIRTranspiler::emitGuardHasAttachedArrayBuffer( ObjOperandId objId) { MDefinition* obj = getOperand(objId); @@ -4134,6 +4414,29 @@ bool WarpCacheIRTranspiler::emitGuardHasAttachedArrayBuffer( return true; } +bool WarpCacheIRTranspiler::emitGuardResizableArrayBufferViewInBounds( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + auto* ins = MGuardResizableArrayBufferViewInBounds::New(alloc(), obj); + add(ins); + + setOperand(objId, ins); + return true; +} + +bool WarpCacheIRTranspiler::emitGuardResizableArrayBufferViewInBoundsOrDetached( + ObjOperandId objId) { + MDefinition* obj = getOperand(objId); + + auto* ins = + MGuardResizableArrayBufferViewInBoundsOrDetached::New(alloc(), obj); + add(ins); + + setOperand(objId, ins); + return true; +} + bool WarpCacheIRTranspiler::emitIsTypedArrayConstructorResult( ObjOperandId objId) { MDefinition* obj = getOperand(objId); @@ -4318,14 +4621,14 @@ bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayResult( bool WarpCacheIRTranspiler::emitAtomicsCompareExchangeResult( ObjOperandId objId, IntPtrOperandId indexId, uint32_t expectedId, - uint32_t replacementId, Scalar::Type elementType) { + uint32_t replacementId, Scalar::Type elementType, + ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); MDefinition* expected = getOperand(ValOperandId(expectedId)); MDefinition* replacement = getOperand(ValOperandId(replacementId)); - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitTypedArrayLength(viewKind, obj); index = addBoundsCheck(index, length); @@ -4347,13 +4650,12 @@ bool WarpCacheIRTranspiler::emitAtomicsCompareExchangeResult( bool WarpCacheIRTranspiler::emitAtomicsExchangeResult( ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, - Scalar::Type elementType) { + Scalar::Type elementType, ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); MDefinition* value = getOperand(ValOperandId(valueId)); - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitTypedArrayLength(viewKind, obj); index = addBoundsCheck(index, length); @@ -4373,17 +4675,15 @@ bool WarpCacheIRTranspiler::emitAtomicsExchangeResult( return resumeAfter(exchange); } -bool WarpCacheIRTranspiler::emitAtomicsBinaryOp(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect, AtomicOp op) { +bool WarpCacheIRTranspiler::emitAtomicsBinaryOp( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind, + AtomicOp op) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); MDefinition* value = getOperand(ValOperandId(valueId)); - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitTypedArrayLength(viewKind, obj); index = addBoundsCheck(index, length); @@ -4409,59 +4709,48 @@ bool WarpCacheIRTranspiler::emitAtomicsBinaryOp(ObjOperandId objId, return resumeAfter(binop); } -bool WarpCacheIRTranspiler::emitAtomicsAddResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool WarpCacheIRTranspiler::emitAtomicsAddResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect, - AtomicFetchAddOp); + viewKind, AtomicOp::Add); } -bool WarpCacheIRTranspiler::emitAtomicsSubResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool WarpCacheIRTranspiler::emitAtomicsSubResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect, - AtomicFetchSubOp); + viewKind, AtomicOp::Sub); } -bool WarpCacheIRTranspiler::emitAtomicsAndResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool WarpCacheIRTranspiler::emitAtomicsAndResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect, - AtomicFetchAndOp); + viewKind, AtomicOp::And); } -bool WarpCacheIRTranspiler::emitAtomicsOrResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool WarpCacheIRTranspiler::emitAtomicsOrResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect, - AtomicFetchOrOp); + viewKind, AtomicOp::Or); } -bool WarpCacheIRTranspiler::emitAtomicsXorResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType, - bool forEffect) { +bool WarpCacheIRTranspiler::emitAtomicsXorResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) { return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect, - AtomicFetchXorOp); + viewKind, AtomicOp::Xor); } -bool WarpCacheIRTranspiler::emitAtomicsLoadResult(ObjOperandId objId, - IntPtrOperandId indexId, - Scalar::Type elementType) { +bool WarpCacheIRTranspiler::emitAtomicsLoadResult( + ObjOperandId objId, IntPtrOperandId indexId, Scalar::Type elementType, + ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitTypedArrayLength(viewKind, obj); index = addBoundsCheck(index, length); @@ -4473,7 +4762,7 @@ bool WarpCacheIRTranspiler::emitAtomicsLoadResult(ObjOperandId objId, MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32); auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType, - DoesRequireMemoryBarrier); + MemoryBarrierRequirement::Required); load->setResultType(knownType); addEffectful(load); @@ -4481,24 +4770,23 @@ bool WarpCacheIRTranspiler::emitAtomicsLoadResult(ObjOperandId objId, return resumeAfter(load); } -bool WarpCacheIRTranspiler::emitAtomicsStoreResult(ObjOperandId objId, - IntPtrOperandId indexId, - uint32_t valueId, - Scalar::Type elementType) { +bool WarpCacheIRTranspiler::emitAtomicsStoreResult( + ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId, + Scalar::Type elementType, ArrayBufferViewKind viewKind) { MDefinition* obj = getOperand(objId); MDefinition* index = getOperand(indexId); MDefinition* value = getOperand(ValOperandId(valueId)); - auto* length = MArrayBufferViewLength::New(alloc(), obj); - add(length); + auto* length = emitTypedArrayLength(viewKind, obj); index = addBoundsCheck(index, length); auto* elements = MArrayBufferViewElements::New(alloc(), obj); add(elements); - auto* store = MStoreUnboxedScalar::New(alloc(), elements, index, value, - elementType, DoesRequireMemoryBarrier); + auto* store = + MStoreUnboxedScalar::New(alloc(), elements, index, value, elementType, + MemoryBarrierRequirement::Required); addEffectful(store); pushResult(value); diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index 0c35309c7e..98675164a9 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -2404,10 +2404,6 @@ void CodeGenerator::visitNegF(LNegF* ins) { masm.ma_vneg_f32(input, ToFloatRegister(ins->output())); } -void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { - masm.memoryBarrier(ins->type()); -} - void CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) { auto input = ToFloatRegister(lir->input()); auto output = ToRegister(lir->output()); diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 50d5d6645c..be8348a1fc 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -5008,7 +5008,7 @@ static Register ComputePointerForAtomic(MacroAssembler& masm, template <typename T> static void CompareExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register oldval, Register newval, Register output) { bool signExtend = Scalar::isSignedIntType(type); @@ -5087,15 +5087,13 @@ static void CompareExchange(MacroAssembler& masm, masm.memoryBarrierAfter(sync); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const Address& address, Register oldval, Register newval, Register output) { CompareExchange(*this, nullptr, type, sync, address, oldval, newval, output); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& address, Register oldval, Register newval, Register output) { CompareExchange(*this, nullptr, type, sync, address, oldval, newval, output); @@ -5118,7 +5116,7 @@ void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register value, Register output) { bool signExtend = Scalar::isSignedIntType(type); unsigned nbytes = Scalar::byteSize(type); @@ -5175,15 +5173,13 @@ static void AtomicExchange(MacroAssembler& masm, masm.memoryBarrierAfter(sync); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const Address& address, Register value, Register output) { AtomicExchange(*this, nullptr, type, sync, address, value, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& address, Register value, Register output) { AtomicExchange(*this, nullptr, type, sync, address, value, output); @@ -5225,8 +5221,8 @@ void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicFetchOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const Register& value, const T& mem, + Scalar::Type type, Synchronization sync, AtomicOp op, + const Register& value, const T& mem, Register flagTemp, Register output) { bool signExtend = Scalar::isSignedIntType(type); unsigned nbytes = Scalar::byteSize(type); @@ -5274,19 +5270,19 @@ static void AtomicFetchOp(MacroAssembler& masm, } switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add(scratch, output, O2Reg(value)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub(scratch, output, O2Reg(value)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(scratch, output, O2Reg(value)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_orr(scratch, output, O2Reg(value)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_eor(scratch, output, O2Reg(value)); break; default: @@ -5312,17 +5308,17 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.memoryBarrierAfter(sync); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const Address& mem, - Register temp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const Address& mem, Register temp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const BaseIndex& mem, - Register temp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const BaseIndex& mem, Register temp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, value, mem, temp, output); } @@ -5357,8 +5353,8 @@ void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicEffectOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const Register& value, const T& mem, + Scalar::Type type, Synchronization sync, AtomicOp op, + const Register& value, const T& mem, Register flagTemp) { unsigned nbytes = Scalar::byteSize(type); @@ -5396,19 +5392,19 @@ static void AtomicEffectOp(MacroAssembler& masm, } switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add(scratch, scratch, O2Reg(value)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub(scratch, scratch, O2Reg(value)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(scratch, scratch, O2Reg(value)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_orr(scratch, scratch, O2Reg(value)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_eor(scratch, scratch, O2Reg(value)); break; default: @@ -5451,7 +5447,7 @@ void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicLoad64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 output) { MOZ_ASSERT((output.low.code() & 1) == 0); MOZ_ASSERT(output.low.code() + 1 == output.high.code()); @@ -5495,7 +5491,7 @@ void MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access, template <typename T> static void CompareExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 expect, Register64 replace, Register64 output) { MOZ_ASSERT(expect != replace && replace != output && output != expect); @@ -5556,13 +5552,13 @@ void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, - const Address& mem, Register64 expect, - Register64 replace, Register64 output) { +void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem, + Register64 expect, Register64 replace, + Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, +void MacroAssembler::compareExchange64(Synchronization sync, const BaseIndex& mem, Register64 expect, Register64 replace, Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); @@ -5571,7 +5567,7 @@ void MacroAssembler::compareExchange64(const Synchronization& sync, template <typename T> static void AtomicExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 value, Register64 output) { MOZ_ASSERT(output != value); @@ -5624,13 +5620,12 @@ void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, WasmAtomicExchange64(*this, access, mem, value, output); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, - const Address& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, + Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, +void MacroAssembler::atomicExchange64(Synchronization sync, const BaseIndex& mem, Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); @@ -5639,9 +5634,8 @@ void MacroAssembler::atomicExchange64(const Synchronization& sync, template <typename T> static void AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, AtomicOp op, - Register64 value, const T& mem, Register64 temp, - Register64 output) { + Synchronization sync, AtomicOp op, Register64 value, + const T& mem, Register64 temp, Register64 output) { MOZ_ASSERT(temp.low != InvalidReg && temp.high != InvalidReg); MOZ_ASSERT(output != value); MOZ_ASSERT(temp != value); @@ -5671,23 +5665,23 @@ static void AtomicFetchOp64(MacroAssembler& masm, FaultingCodeOffset(load.getOffset())); } switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add(temp.low, output.low, O2Reg(value.low), SetCC); masm.as_adc(temp.high, output.high, O2Reg(value.high)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub(temp.low, output.low, O2Reg(value.low), SetCC); masm.as_sbc(temp.high, output.high, O2Reg(value.high)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(temp.low, output.low, O2Reg(value.low)); masm.as_and(temp.high, output.high, O2Reg(value.high)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_orr(temp.low, output.low, O2Reg(value.low)); masm.as_orr(temp.high, output.high, O2Reg(value.high)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_eor(temp.low, output.low, O2Reg(value.low)); masm.as_eor(temp.high, output.high, O2Reg(value.high)); break; @@ -5725,25 +5719,25 @@ void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, WasmAtomicFetchOp64(*this, access, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); @@ -5754,7 +5748,7 @@ void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, template <typename T> static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register oldval, Register newval, Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { @@ -5766,15 +5760,14 @@ static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register oldval, - Register newval, Register temp, - AnyRegister output) { + Synchronization sync, const Address& mem, + Register oldval, Register newval, + Register temp, AnyRegister output) { CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, temp, output); } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register temp, AnyRegister output) { @@ -5783,9 +5776,8 @@ void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, - Register value, Register temp, - AnyRegister output) { + Synchronization sync, const T& mem, Register value, + Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicExchange(arrayType, sync, mem, value, temp); masm.convertUInt32ToDouble(temp, output.fpu()); @@ -5795,14 +5787,14 @@ static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register value, - Register temp, AnyRegister output) { + Synchronization sync, const Address& mem, + Register value, Register temp, + AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register value, Register temp, AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); @@ -5810,9 +5802,9 @@ void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, - Register value, const T& mem, Register temp1, - Register temp2, AnyRegister output) { + Synchronization sync, AtomicOp op, Register value, + const T& mem, Register temp1, Register temp2, + AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicFetchOp(arrayType, sync, op, value, mem, temp2, temp1); masm.convertUInt32ToDouble(temp1, output.fpu()); @@ -5822,7 +5814,7 @@ static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register temp1, Register temp2, AnyRegister output) { @@ -5830,7 +5822,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register temp1, Register temp2, AnyRegister output) { @@ -5838,14 +5830,14 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register temp) { AtomicEffectOp(*this, nullptr, arrayType, sync, op, value, mem, temp); } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register temp) { AtomicEffectOp(*this, nullptr, arrayType, sync, op, value, mem, temp); @@ -5854,25 +5846,23 @@ void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, // ======================================================================== // Primitive atomic operations. -void MacroAssembler::atomicLoad64(const Synchronization& sync, - const Address& mem, Register64 output) { +void MacroAssembler::atomicLoad64(Synchronization sync, const Address& mem, + Register64 output) { AtomicLoad64(*this, nullptr, sync, mem, output); } -void MacroAssembler::atomicLoad64(const Synchronization& sync, - const BaseIndex& mem, Register64 output) { +void MacroAssembler::atomicLoad64(Synchronization sync, const BaseIndex& mem, + Register64 output) { AtomicLoad64(*this, nullptr, sync, mem, output); } -void MacroAssembler::atomicStore64(const Synchronization& sync, - const Address& mem, Register64 value, - Register64 temp) { +void MacroAssembler::atomicStore64(Synchronization sync, const Address& mem, + Register64 value, Register64 temp) { AtomicExchange64(*this, nullptr, sync, mem, value, temp); } -void MacroAssembler::atomicStore64(const Synchronization& sync, - const BaseIndex& mem, Register64 value, - Register64 temp) { +void MacroAssembler::atomicStore64(Synchronization sync, const BaseIndex& mem, + Register64 value, Register64 temp) { AtomicExchange64(*this, nullptr, sync, mem, value, temp); } diff --git a/js/src/jit/arm64/CodeGenerator-arm64.cpp b/js/src/jit/arm64/CodeGenerator-arm64.cpp index ff3ea96a7d..a232135419 100644 --- a/js/src/jit/arm64/CodeGenerator-arm64.cpp +++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp @@ -2563,10 +2563,6 @@ void CodeGenerator::visitWasmStoreI64(LWasmStoreI64* lir) { ToRegister(lir->memoryBase()), ToRegister(lir->ptr())); } -void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { - masm.memoryBarrier(ins->type()); -} - void CodeGenerator::visitWasmAddOffset(LWasmAddOffset* lir) { MWasmAddOffset* mir = lir->mir(); Register base = ToRegister(lir->base()); diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index 682f69df59..e3ec2494ff 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -2324,8 +2324,8 @@ template <typename T> static void CompareExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Scalar::Type type, Width targetWidth, - const Synchronization& sync, const T& mem, - Register oldval, Register newval, Register output) { + Synchronization sync, const T& mem, Register oldval, + Register newval, Register output) { MOZ_ASSERT(oldval != output && newval != output); vixl::UseScratchRegisterScope temps(&masm); @@ -2395,8 +2395,8 @@ template <typename T> static void AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Scalar::Type type, Width targetWidth, - const Synchronization& sync, const T& mem, - Register value, Register output) { + Synchronization sync, const T& mem, Register value, + Register output) { MOZ_ASSERT(value != output); vixl::UseScratchRegisterScope temps(&masm); @@ -2458,9 +2458,8 @@ template <bool wantResult, typename T> static void AtomicFetchOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Scalar::Type type, Width targetWidth, - const Synchronization& sync, AtomicOp op, - const T& mem, Register value, Register temp, - Register output) { + Synchronization sync, AtomicOp op, const T& mem, + Register value, Register temp, Register output) { MOZ_ASSERT(value != output); MOZ_ASSERT(value != temp); MOZ_ASSERT_IF(wantResult, output != temp); @@ -2514,25 +2513,25 @@ static void AtomicFetchOp(MacroAssembler& masm, } switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: FETCH_OP_CASE(add, value); break; - case AtomicFetchSubOp: { + case AtomicOp::Sub: { Register scratch = temps.AcquireX().asUnsized(); masm.Neg(X(scratch), X(value)); FETCH_OP_CASE(add, scratch); break; } - case AtomicFetchAndOp: { + case AtomicOp::And: { Register scratch = temps.AcquireX().asUnsized(); masm.Eor(X(scratch), X(value), Operand(~0)); FETCH_OP_CASE(clr, scratch); break; } - case AtomicFetchOrOp: + case AtomicOp::Or: FETCH_OP_CASE(set, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: FETCH_OP_CASE(eor, value); break; } @@ -2558,19 +2557,19 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.bind(&again); LoadExclusive(masm, access, type, targetWidth, ptr, output); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.Add(X(temp), X(output), X(value)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.Sub(X(temp), X(output), X(value)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.And(X(temp), X(output), X(value)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.Orr(X(temp), X(output), X(value)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.Eor(X(temp), X(output), X(value)); break; } @@ -2583,72 +2582,69 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.memoryBarrierAfter(sync); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register oldval, Register newval, Register output) { CompareExchange(*this, nullptr, type, Width::_32, sync, mem, oldval, newval, output); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register output) { CompareExchange(*this, nullptr, type, Width::_32, sync, mem, oldval, newval, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, - const Address& mem, Register64 expect, - Register64 replace, Register64 output) { +void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem, + Register64 expect, Register64 replace, + Register64 output) { CompareExchange(*this, nullptr, Scalar::Int64, Width::_64, sync, mem, expect.reg, replace.reg, output.reg); } -void MacroAssembler::compareExchange64(const Synchronization& sync, +void MacroAssembler::compareExchange64(Synchronization sync, const BaseIndex& mem, Register64 expect, Register64 replace, Register64 output) { CompareExchange(*this, nullptr, Scalar::Int64, Width::_64, sync, mem, expect.reg, replace.reg, output.reg); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, - const Address& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, + Register64 value, Register64 output) { AtomicExchange(*this, nullptr, Scalar::Int64, Width::_64, sync, mem, value.reg, output.reg); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, +void MacroAssembler::atomicExchange64(Synchronization sync, const BaseIndex& mem, Register64 value, Register64 output) { AtomicExchange(*this, nullptr, Scalar::Int64, Width::_64, sync, mem, value.reg, output.reg); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp, Register64 output) { AtomicFetchOp<true>(*this, nullptr, Scalar::Int64, Width::_64, sync, op, mem, value.reg, temp.reg, output.reg); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicFetchOp<true>(*this, nullptr, Scalar::Int64, Width::_64, sync, op, mem, value.reg, temp.reg, output.reg); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp) { AtomicFetchOp<false>(*this, nullptr, Scalar::Int64, Width::_64, sync, op, mem, value.reg, temp.reg, temp.reg); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp) { AtomicFetchOp<false>(*this, nullptr, Scalar::Int64, Width::_64, sync, op, mem, @@ -2669,15 +2665,13 @@ void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, oldval, newval, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register value, Register output) { AtomicExchange(*this, nullptr, type, Width::_32, sync, mem, value, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register value, Register output) { AtomicExchange(*this, nullptr, type, Width::_32, sync, mem, value, output); @@ -2697,18 +2691,18 @@ void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, value, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const Address& mem, - Register temp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const Address& mem, Register temp, + Register output) { AtomicFetchOp<true>(*this, nullptr, type, Width::_32, sync, op, mem, value, temp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const BaseIndex& mem, - Register temp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const BaseIndex& mem, Register temp, + Register output) { AtomicFetchOp<true>(*this, nullptr, type, Width::_32, sync, op, mem, value, temp, output); } @@ -2804,7 +2798,7 @@ void MacroAssembler::wasmAtomicEffectOp64(const wasm::MemoryAccessDesc& access, template <typename T> static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register oldval, Register newval, Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { @@ -2816,15 +2810,14 @@ static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register oldval, - Register newval, Register temp, - AnyRegister output) { + Synchronization sync, const Address& mem, + Register oldval, Register newval, + Register temp, AnyRegister output) { CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, temp, output); } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register temp, AnyRegister output) { @@ -2833,9 +2826,8 @@ void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, - Register value, Register temp, - AnyRegister output) { + Synchronization sync, const T& mem, Register value, + Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicExchange(arrayType, sync, mem, value, temp); masm.convertUInt32ToDouble(temp, output.fpu()); @@ -2845,14 +2837,14 @@ static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register value, - Register temp, AnyRegister output) { + Synchronization sync, const Address& mem, + Register value, Register temp, + AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register value, Register temp, AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); @@ -2860,9 +2852,9 @@ void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, - Register value, const T& mem, Register temp1, - Register temp2, AnyRegister output) { + Synchronization sync, AtomicOp op, Register value, + const T& mem, Register temp1, Register temp2, + AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicFetchOp(arrayType, sync, op, value, mem, temp2, temp1); masm.convertUInt32ToDouble(temp1, output.fpu()); @@ -2872,7 +2864,7 @@ static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register temp1, Register temp2, AnyRegister output) { @@ -2880,7 +2872,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register temp1, Register temp2, AnyRegister output) { @@ -2888,7 +2880,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register temp) { AtomicFetchOp<false>(*this, nullptr, arrayType, Width::_32, sync, op, mem, @@ -2896,7 +2888,7 @@ void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register temp) { AtomicFetchOp<false>(*this, nullptr, arrayType, Width::_32, sync, op, mem, diff --git a/js/src/jit/loong64/Assembler-loong64.cpp b/js/src/jit/loong64/Assembler-loong64.cpp index 6c7a5f53da..07dac546c1 100644 --- a/js/src/jit/loong64/Assembler-loong64.cpp +++ b/js/src/jit/loong64/Assembler-loong64.cpp @@ -103,15 +103,15 @@ uint32_t js::jit::SA3(uint32_t value) { } Register js::jit::toRK(Instruction& i) { - return Register::FromCode((i.encode() & RKMask) >> RKShift); + return Register::FromCode(((i.encode() >> RKShift) & RKMask)); } Register js::jit::toRJ(Instruction& i) { - return Register::FromCode((i.encode() & RJMask) >> RJShift); + return Register::FromCode(((i.encode() >> RJShift) & RJMask)); } Register js::jit::toRD(Instruction& i) { - return Register::FromCode((i.encode() & RDMask) >> RDShift); + return Register::FromCode(((i.encode() >> RDShift) & RDMask)); } Register js::jit::toR(Instruction& i) { diff --git a/js/src/jit/loong64/Assembler-loong64.h b/js/src/jit/loong64/Assembler-loong64.h index 4e0b8d6b66..a385d71f5f 100644 --- a/js/src/jit/loong64/Assembler-loong64.h +++ b/js/src/jit/loong64/Assembler-loong64.h @@ -309,6 +309,7 @@ static const uint32_t Imm26Shift = 0; static const uint32_t Imm26Bits = 26; static const uint32_t CODEShift = 0; static const uint32_t CODEBits = 15; +static const uint32_t HINTBits = 5; // LoongArch instruction field bit masks. static const uint32_t RJMask = (1 << RJBits) - 1; @@ -316,7 +317,9 @@ static const uint32_t RKMask = (1 << RKBits) - 1; static const uint32_t RDMask = (1 << RDBits) - 1; static const uint32_t SA2Mask = (1 << SA2Bits) - 1; static const uint32_t SA3Mask = (1 << SA3Bits) - 1; +static const uint32_t CDMask = (1 << CDBits) - 1; static const uint32_t CONDMask = (1 << CONDBits) - 1; +static const uint32_t HINTMask = (1 << HINTBits) - 1; static const uint32_t LSBWMask = (1 << LSBWBits) - 1; static const uint32_t LSBDMask = (1 << LSBDBits) - 1; static const uint32_t MSBWMask = (1 << MSBWBits) - 1; @@ -1611,7 +1614,7 @@ class InstReg : public Instruction { InstReg(OpcodeField op, int32_t cond, FloatRegister fk, FloatRegister fj, AssemblerLOONG64::FPConditionBit cd) : Instruction(op | (cond & CONDMask) << CONDShift | FK(fk) | FJ(fj) | - (cd & RDMask)) { + (cd & CDMask)) { MOZ_ASSERT(is_uintN(cond, 5)); } @@ -1700,7 +1703,7 @@ class InstImm : public Instruction { } InstImm(OpcodeField op, int32_t si12, Register rj, int32_t hint) : Instruction(op | (si12 & Imm12Mask) << Imm12Shift | RJ(rj) | - (hint & RDMask)) { + (hint & HINTMask)) { MOZ_ASSERT(op == op_preld); } InstImm(OpcodeField op, int32_t msb, int32_t lsb, Register rj, Register rd, @@ -1738,7 +1741,9 @@ class InstImm : public Instruction { uint32_t extractRJ() { return extractBitField(RJShift + RJBits - 1, RJShift); } - void setRJ(uint32_t rj) { data = (data & ~RJMask) | (rj << RJShift); } + void setRJ(uint32_t rj) { + data = (data & ~(RJMask << RJShift)) | (rj << RJShift); + } uint32_t extractRD() { return extractBitField(RDShift + RDBits - 1, RDShift); } diff --git a/js/src/jit/loong64/CodeGenerator-loong64.cpp b/js/src/jit/loong64/CodeGenerator-loong64.cpp index 4c4dfd18ff..76d3047680 100644 --- a/js/src/jit/loong64/CodeGenerator-loong64.cpp +++ b/js/src/jit/loong64/CodeGenerator-loong64.cpp @@ -1988,10 +1988,6 @@ void CodeGenerator::visitNotF(LNotF* ins) { Assembler::DoubleEqualOrUnordered); } -void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { - masm.memoryBarrier(ins->type()); -} - void CodeGenerator::visitWasmLoad(LWasmLoad* lir) { emitWasmLoad(lir); } void CodeGenerator::visitWasmStore(LWasmStore* lir) { emitWasmStore(lir); } diff --git a/js/src/jit/loong64/MacroAssembler-loong64.cpp b/js/src/jit/loong64/MacroAssembler-loong64.cpp index 528c120058..1c07f7f91a 100644 --- a/js/src/jit/loong64/MacroAssembler-loong64.cpp +++ b/js/src/jit/loong64/MacroAssembler-loong64.cpp @@ -3357,7 +3357,7 @@ void MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest) { template <typename T> static void CompareExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -3463,7 +3463,7 @@ static void CompareExchange(MacroAssembler& masm, template <typename T> static void CompareExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 expect, Register64 replace, Register64 output) { MOZ_ASSERT(expect != output && replace != output); @@ -3499,7 +3499,7 @@ static void CompareExchange64(MacroAssembler& masm, template <typename T> static void AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -3602,7 +3602,7 @@ static void AtomicExchange(MacroAssembler& masm, template <typename T> static void AtomicExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 value, Register64 output) { MOZ_ASSERT(value != output); ScratchRegisterScope scratch(masm); @@ -3633,10 +3633,10 @@ static void AtomicExchange64(MacroAssembler& masm, template <typename T> static void AtomicFetchOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const T& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { + Scalar::Type type, Synchronization sync, AtomicOp op, + const T& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { ScratchRegisterScope scratch(masm); SecondScratchRegisterScope scratch2(masm); bool signExtend = Scalar::isSignedIntType(type); @@ -3671,19 +3671,19 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.as_ll_w(output, scratch, 0); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add_w(scratch2, output, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub_w(scratch2, output, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(scratch2, output, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(scratch2, output, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(scratch2, output, value); break; default: @@ -3718,19 +3718,19 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.as_srl_w(output, scratch2, offsetTemp); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add_w(valueTemp, output, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub_w(valueTemp, output, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(valueTemp, output, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(valueTemp, output, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(valueTemp, output, value); break; default: @@ -3778,9 +3778,8 @@ static void AtomicFetchOp(MacroAssembler& masm, template <typename T> static void AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, AtomicOp op, - Register64 value, const T& mem, Register64 temp, - Register64 output) { + Synchronization sync, AtomicOp op, Register64 value, + const T& mem, Register64 temp, Register64 output) { MOZ_ASSERT(value != output); MOZ_ASSERT(value != temp); ScratchRegisterScope scratch(masm); @@ -3801,19 +3800,19 @@ static void AtomicFetchOp64(MacroAssembler& masm, masm.as_ll_d(output.reg, scratch, 0); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add_d(temp.reg, output.reg, value.reg); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub_d(temp.reg, output.reg, value.reg); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(temp.reg, output.reg, value.reg); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(temp.reg, output.reg, value.reg); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(temp.reg, output.reg, value.reg); break; default: @@ -3826,8 +3825,7 @@ static void AtomicFetchOp64(MacroAssembler& masm, masm.memoryBarrierAfter(sync); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -3836,8 +3834,7 @@ void MacroAssembler::compareExchange(Scalar::Type type, offsetTemp, maskTemp, output); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -3846,13 +3843,13 @@ void MacroAssembler::compareExchange(Scalar::Type type, offsetTemp, maskTemp, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, - const Address& mem, Register64 expect, - Register64 replace, Register64 output) { +void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem, + Register64 expect, Register64 replace, + Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, +void MacroAssembler::compareExchange64(Synchronization sync, const BaseIndex& mem, Register64 expect, Register64 replace, Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); @@ -3894,8 +3891,7 @@ void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -3903,8 +3899,7 @@ void MacroAssembler::atomicExchange(Scalar::Type type, maskTemp, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -3912,13 +3907,12 @@ void MacroAssembler::atomicExchange(Scalar::Type type, maskTemp, output); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, - const Address& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, + Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, +void MacroAssembler::atomicExchange64(Synchronization sync, const BaseIndex& mem, Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); @@ -3940,43 +3934,43 @@ void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, valueTemp, offsetTemp, maskTemp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const Address& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const Address& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const BaseIndex& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); @@ -4003,10 +3997,9 @@ void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicEffectOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const T& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp) { + Scalar::Type type, Synchronization sync, AtomicOp op, + const T& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp) { ScratchRegisterScope scratch(masm); SecondScratchRegisterScope scratch2(masm); unsigned nbytes = Scalar::byteSize(type); @@ -4040,19 +4033,19 @@ static void AtomicEffectOp(MacroAssembler& masm, masm.as_ll_w(scratch2, scratch, 0); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add_w(scratch2, scratch2, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub_w(scratch2, scratch2, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(scratch2, scratch2, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(scratch2, scratch2, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(scratch2, scratch2, value); break; default: @@ -4087,19 +4080,19 @@ static void AtomicEffectOp(MacroAssembler& masm, masm.as_srl_w(valueTemp, scratch2, offsetTemp); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_add_w(valueTemp, valueTemp, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sub_w(valueTemp, valueTemp, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(valueTemp, valueTemp, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(valueTemp, valueTemp, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(valueTemp, valueTemp, value); break; default: @@ -4184,7 +4177,7 @@ void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, template <typename T> static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -4201,10 +4194,10 @@ static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, template <typename T> static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, - Register value, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register temp, AnyRegister output) { + Synchronization sync, const T& mem, Register value, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, + AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp); @@ -4217,8 +4210,8 @@ static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, template <typename T> static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, - Register value, const T& mem, Register valueTemp, + Synchronization sync, AtomicOp op, Register value, + const T& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { @@ -4232,17 +4225,17 @@ static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register oldval, - Register newval, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register temp, AnyRegister output) { + Synchronization sync, const Address& mem, + Register oldval, Register newval, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, + AnyRegister output) { CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp, temp, output); } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -4252,17 +4245,16 @@ void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register temp, - AnyRegister output) { + Synchronization sync, const Address& mem, + Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register temp, AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp, output); } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -4272,7 +4264,7 @@ void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -4282,7 +4274,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -4292,7 +4284,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp) { @@ -4301,7 +4293,7 @@ void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp) { diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp index 424ddab061..f7f1d7a16d 100644 --- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp @@ -1401,10 +1401,6 @@ void CodeGenerator::visitNotF(LNotF* ins) { Assembler::DoubleEqualOrUnordered); } -void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { - masm.memoryBarrier(ins->type()); -} - void CodeGeneratorMIPSShared::generateInvalidateEpilogue() { // Ensure that there is enough space in the buffer for the OsiPoint // patching to occur. Otherwise, we could overwrite the invalidation diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp index 052c76ba0f..284bbe0a12 100644 --- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp @@ -2262,7 +2262,7 @@ void MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch, template <typename T> static void CompareExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -2366,8 +2366,7 @@ static void CompareExchange(MacroAssembler& masm, masm.bind(&end); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -2376,8 +2375,7 @@ void MacroAssembler::compareExchange(Scalar::Type type, offsetTemp, maskTemp, output); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -2407,7 +2405,7 @@ void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -2508,8 +2506,7 @@ static void AtomicExchange(MacroAssembler& masm, masm.memoryBarrierAfter(sync); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -2517,8 +2514,7 @@ void MacroAssembler::atomicExchange(Scalar::Type type, maskTemp, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -2545,10 +2541,10 @@ void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicFetchOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const T& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { + Scalar::Type type, Synchronization sync, AtomicOp op, + const T& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { bool signExtend = Scalar::isSignedIntType(type); unsigned nbytes = Scalar::byteSize(type); @@ -2580,19 +2576,19 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.as_ll(output, SecondScratchReg, 0); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_addu(ScratchRegister, output, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_subu(ScratchRegister, output, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(ScratchRegister, output, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(ScratchRegister, output, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(ScratchRegister, output, value); break; default: @@ -2630,19 +2626,19 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.as_srlv(output, ScratchRegister, offsetTemp); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_addu(valueTemp, output, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_subu(valueTemp, output, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(valueTemp, output, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(valueTemp, output, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(valueTemp, output, value); break; default: @@ -2688,20 +2684,20 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.memoryBarrierAfter(sync); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const Address& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const Address& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const BaseIndex& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output); } @@ -2727,10 +2723,9 @@ void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, template <typename T> static void AtomicEffectOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const T& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp) { + Scalar::Type type, Synchronization sync, AtomicOp op, + const T& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp) { unsigned nbytes = Scalar::byteSize(type); switch (nbytes) { @@ -2761,19 +2756,19 @@ static void AtomicEffectOp(MacroAssembler& masm, masm.as_ll(ScratchRegister, SecondScratchReg, 0); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_addu(ScratchRegister, ScratchRegister, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_subu(ScratchRegister, ScratchRegister, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(ScratchRegister, ScratchRegister, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(ScratchRegister, ScratchRegister, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(ScratchRegister, ScratchRegister, value); break; default: @@ -2811,19 +2806,19 @@ static void AtomicEffectOp(MacroAssembler& masm, masm.as_srlv(valueTemp, ScratchRegister, offsetTemp); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_addu(valueTemp, valueTemp, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_subu(valueTemp, valueTemp, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(valueTemp, valueTemp, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(valueTemp, valueTemp, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(valueTemp, valueTemp, value); break; default: @@ -2875,7 +2870,7 @@ void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, template <typename T> static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2891,17 +2886,17 @@ static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register oldval, - Register newval, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register temp, AnyRegister output) { + Synchronization sync, const Address& mem, + Register oldval, Register newval, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, + AnyRegister output) { CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp, temp, output); } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -2912,10 +2907,10 @@ void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, - Register value, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register temp, AnyRegister output) { + Synchronization sync, const T& mem, Register value, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, + AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp); @@ -2927,17 +2922,16 @@ static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register temp, - AnyRegister output) { + Synchronization sync, const Address& mem, + Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register temp, AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp, output); } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2948,8 +2942,8 @@ void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, - Register value, const T& mem, Register valueTemp, + Synchronization sync, AtomicOp op, Register value, + const T& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { @@ -2963,7 +2957,7 @@ static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2973,7 +2967,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2983,7 +2977,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp) { @@ -2992,7 +2986,7 @@ void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp) { diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index f4b3d557a5..747db53799 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -2745,27 +2745,27 @@ static void AtomicFetchOp64(MacroAssembler& masm, masm.load64(Address(SecondScratchReg, 0), output); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_addu(temp.low, output.low, value.low); masm.as_sltu(temp.high, temp.low, output.low); masm.as_addu(temp.high, temp.high, output.high); masm.as_addu(temp.high, temp.high, value.high); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_sltu(temp.high, output.low, value.low); masm.as_subu(temp.high, output.high, temp.high); masm.as_subu(temp.low, output.low, value.low); masm.as_subu(temp.high, temp.high, value.high); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(temp.low, output.low, value.low); masm.as_and(temp.high, output.high, value.high); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(temp.low, output.low, value.low); masm.as_or(temp.high, output.high, value.high); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(temp.low, output.low, value.low); masm.as_xor(temp.high, output.high, value.high); break; diff --git a/js/src/jit/mips64/MacroAssembler-mips64.cpp b/js/src/jit/mips64/MacroAssembler-mips64.cpp index cbf66ccac4..1530bfcbc8 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.cpp +++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp @@ -2611,7 +2611,7 @@ void MacroAssemblerMIPS64Compat::wasmStoreI64Impl( template <typename T> static void CompareExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 expect, Register64 replace, Register64 output) { MOZ_ASSERT(expect != output && replace != output); @@ -2658,13 +2658,13 @@ void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, - const Address& mem, Register64 expect, - Register64 replace, Register64 output) { +void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem, + Register64 expect, Register64 replace, + Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, +void MacroAssembler::compareExchange64(Synchronization sync, const BaseIndex& mem, Register64 expect, Register64 replace, Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); @@ -2673,7 +2673,7 @@ void MacroAssembler::compareExchange64(const Synchronization& sync, template <typename T> static void AtomicExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 value, Register64 output) { MOZ_ASSERT(value != output); masm.computeEffectiveAddress(mem, SecondScratchReg); @@ -2717,13 +2717,12 @@ void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, WasmAtomicExchange64(*this, access, mem, src, output); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, - const Address& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, + Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, +void MacroAssembler::atomicExchange64(Synchronization sync, const BaseIndex& mem, Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); @@ -2732,9 +2731,8 @@ void MacroAssembler::atomicExchange64(const Synchronization& sync, template <typename T> static void AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, AtomicOp op, - Register64 value, const T& mem, Register64 temp, - Register64 output) { + Synchronization sync, AtomicOp op, Register64 value, + const T& mem, Register64 temp, Register64 output) { MOZ_ASSERT(value != output); MOZ_ASSERT(value != temp); masm.computeEffectiveAddress(mem, SecondScratchReg); @@ -2751,19 +2749,19 @@ static void AtomicFetchOp64(MacroAssembler& masm, masm.as_lld(output.reg, SecondScratchReg, 0); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.as_daddu(temp.reg, output.reg, value.reg); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.as_dsubu(temp.reg, output.reg, value.reg); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.as_and(temp.reg, output.reg, value.reg); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.as_or(temp.reg, output.reg, value.reg); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.as_xor(temp.reg, output.reg, value.reg); break; default: @@ -2790,25 +2788,25 @@ void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); diff --git a/js/src/jit/riscv64/CodeGenerator-riscv64.cpp b/js/src/jit/riscv64/CodeGenerator-riscv64.cpp index 1c890799ed..3cfb91a036 100644 --- a/js/src/jit/riscv64/CodeGenerator-riscv64.cpp +++ b/js/src/jit/riscv64/CodeGenerator-riscv64.cpp @@ -1986,10 +1986,6 @@ void CodeGenerator::visitNotF(LNotF* ins) { masm.ma_compareF32(dest, Assembler::DoubleEqualOrUnordered, in, fpscratch); } -void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { - masm.memoryBarrier(ins->type()); -} - void CodeGenerator::visitWasmLoad(LWasmLoad* lir) { emitWasmLoad(lir); } void CodeGenerator::visitWasmStore(LWasmStore* lir) { emitWasmStore(lir); } diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64.cpp b/js/src/jit/riscv64/MacroAssembler-riscv64.cpp index 93ccf1cc27..dc5721ea9f 100644 --- a/js/src/jit/riscv64/MacroAssembler-riscv64.cpp +++ b/js/src/jit/riscv64/MacroAssembler-riscv64.cpp @@ -2243,7 +2243,7 @@ uint32_t MacroAssembler::pushFakeReturnAddress(Register scratch) { template <typename T> static void AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -2352,7 +2352,7 @@ static void AtomicExchange(MacroAssembler& masm, template <typename T> static void AtomicExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 value, Register64 output) { MOZ_ASSERT(value != output); UseScratchRegisterScope temps(&masm); @@ -2382,9 +2382,8 @@ static void AtomicExchange64(MacroAssembler& masm, template <typename T> static void AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, AtomicOp op, - Register64 value, const T& mem, Register64 temp, - Register64 output) { + Synchronization sync, AtomicOp op, Register64 value, + const T& mem, Register64 temp, Register64 output) { MOZ_ASSERT(value != output); MOZ_ASSERT(value != temp); UseScratchRegisterScope temps(&masm); @@ -2405,19 +2404,19 @@ static void AtomicFetchOp64(MacroAssembler& masm, masm.lr_d(true, true, output.reg, SecondScratchReg); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.add(temp.reg, output.reg, value.reg); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.sub(temp.reg, output.reg, value.reg); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.and_(temp.reg, output.reg, value.reg); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.or_(temp.reg, output.reg, value.reg); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.xor_(temp.reg, output.reg, value.reg); break; default: @@ -2433,10 +2432,9 @@ static void AtomicFetchOp64(MacroAssembler& masm, template <typename T> static void AtomicEffectOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const T& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp) { + Scalar::Type type, Synchronization sync, AtomicOp op, + const T& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp) { ScratchRegisterScope scratch(masm); UseScratchRegisterScope temps(&masm); Register scratch2 = temps.Acquire(); @@ -2471,19 +2469,19 @@ static void AtomicEffectOp(MacroAssembler& masm, masm.lr_w(true, true, scratch2, scratch); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.addw(scratch2, scratch2, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.subw(scratch2, scratch2, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.and_(scratch2, scratch2, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.or_(scratch2, scratch2, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.xor_(scratch2, scratch2, value); break; default: @@ -2519,19 +2517,19 @@ static void AtomicEffectOp(MacroAssembler& masm, masm.srlw(valueTemp, scratch2, offsetTemp); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.addw(valueTemp, valueTemp, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.subw(valueTemp, valueTemp, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.and_(valueTemp, valueTemp, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.or_(valueTemp, valueTemp, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.xor_(valueTemp, valueTemp, value); break; default: @@ -2563,10 +2561,10 @@ static void AtomicEffectOp(MacroAssembler& masm, template <typename T> static void AtomicFetchOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, - AtomicOp op, const T& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { + Scalar::Type type, Synchronization sync, AtomicOp op, + const T& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { ScratchRegisterScope scratch(masm); UseScratchRegisterScope temps(&masm); Register scratch2 = temps.Acquire(); @@ -2602,19 +2600,19 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.lr_w(true, true, output, scratch); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.addw(scratch2, output, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.subw(scratch2, output, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.and_(scratch2, output, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.or_(scratch2, output, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.xor_(scratch2, output, value); break; default: @@ -2650,19 +2648,19 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.srlw(output, scratch2, offsetTemp); switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.addw(valueTemp, output, value); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.subw(valueTemp, output, value); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.and_(valueTemp, output, value); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.or_(valueTemp, output, value); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.xor_(valueTemp, output, value); break; default: @@ -2715,7 +2713,7 @@ static void AtomicFetchOp(MacroAssembler& masm, template <typename T> static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2732,10 +2730,10 @@ static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, template <typename T> static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, - Register value, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register temp, AnyRegister output) { + Synchronization sync, const T& mem, Register value, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, + AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp); @@ -2748,8 +2746,8 @@ static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, template <typename T> static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, - Register value, const T& mem, Register valueTemp, + Synchronization sync, AtomicOp op, Register value, + const T& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { @@ -2763,7 +2761,7 @@ static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp) { @@ -2772,37 +2770,35 @@ void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp) { AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp, offsetTemp, maskTemp); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, - const Address& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, + Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); } -void MacroAssembler::atomicExchange64(const Synchronization& sync, +void MacroAssembler::atomicExchange64(Synchronization sync, const BaseIndex& mem, Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, sync, mem, value, output); } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register temp, - AnyRegister output) { + Synchronization sync, const Address& mem, + Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register temp, AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp, output); } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2811,8 +2807,7 @@ void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, maskTemp, temp, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -2820,8 +2815,7 @@ void MacroAssembler::atomicExchange(Scalar::Type type, maskTemp, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -2830,7 +2824,7 @@ void MacroAssembler::atomicExchange(Scalar::Type type, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2840,7 +2834,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register valueTemp, Register offsetTemp, Register maskTemp, Register temp, @@ -2849,20 +2843,20 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, maskTemp, temp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const Address& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const Address& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type type, - const Synchronization& sync, AtomicOp op, - Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, + AtomicOp op, Register value, + const BaseIndex& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, + Register output) { AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output); } @@ -3058,7 +3052,7 @@ void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); } template <typename T> static void CompareExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register64 expect, Register64 replace, Register64 output) { MOZ_ASSERT(expect != output && replace != output); @@ -3092,30 +3086,30 @@ static void CompareExchange64(MacroAssembler& masm, masm.bind(&exit); } -void MacroAssembler::compareExchange64(const Synchronization& sync, - const Address& mem, Register64 expect, - Register64 replace, Register64 output) { +void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem, + Register64 expect, Register64 replace, + Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); } -void MacroAssembler::compareExchange64(const Synchronization& sync, +void MacroAssembler::compareExchange64(Synchronization sync, const BaseIndex& mem, Register64 expect, Register64 replace, Register64 output) { CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register oldval, - Register newval, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register temp, AnyRegister output) { + Synchronization sync, const Address& mem, + Register oldval, Register newval, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, + AnyRegister output) { CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp, temp, output); } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -3947,25 +3941,25 @@ void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp) { AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); @@ -4034,7 +4028,7 @@ void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, template <typename T> static void CompareExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, - Scalar::Type type, const Synchronization& sync, + Scalar::Type type, Synchronization sync, const T& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, Register output) { @@ -4140,8 +4134,7 @@ static void CompareExchange(MacroAssembler& masm, masm.bind(&end); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const Address& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, @@ -4150,8 +4143,7 @@ void MacroAssembler::compareExchange(Scalar::Type type, offsetTemp, maskTemp, output); } -void MacroAssembler::compareExchange(Scalar::Type type, - const Synchronization& sync, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register valueTemp, Register offsetTemp, Register maskTemp, diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 8abf68504b..6cdf76981b 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -552,7 +552,7 @@ class MemoryAccessDesc { explicit MemoryAccessDesc( uint32_t memoryIndex, Scalar::Type type, uint32_t align, uint64_t offset, BytecodeOffset trapOffset, mozilla::DebugOnly<bool> hugeMemory, - const jit::Synchronization& sync = jit::Synchronization::None()) + jit::Synchronization sync = jit::Synchronization::None()) : memoryIndex_(memoryIndex), offset64_(offset), align_(align), @@ -592,7 +592,7 @@ class MemoryAccessDesc { uint32_t align() const { return align_; } Scalar::Type type() const { return type_; } unsigned byteSize() const { return Scalar::byteSize(type()); } - const jit::Synchronization& sync() const { return sync_; } + jit::Synchronization sync() const { return sync_; } BytecodeOffset trapOffset() const { return trapOffset_; } wasm::SimdOp widenSimdOp() const { MOZ_ASSERT(isWidenSimd128Load()); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 1a838f78c3..d8b5693d85 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -2185,25 +2185,28 @@ class LLoadDataViewElement : public LInstructionHelper<1, 3, 1 + INT64_PIECES> { }; class LLoadTypedArrayElementHoleBigInt - : public LInstructionHelper<BOX_PIECES, 2, 1 + INT64_PIECES> { + : public LInstructionHelper<BOX_PIECES, 3, 1 + INT64_PIECES> { public: LIR_HEADER(LoadTypedArrayElementHoleBigInt) - LLoadTypedArrayElementHoleBigInt(const LAllocation& object, + LLoadTypedArrayElementHoleBigInt(const LAllocation& elements, const LAllocation& index, + const LAllocation& length, const LDefinition& temp, const LInt64Definition& temp64) : LInstructionHelper(classOpcode) { - setOperand(0, object); + setOperand(0, elements); setOperand(1, index); + setOperand(2, length); setTemp(0, temp); setInt64Temp(1, temp64); } const MLoadTypedArrayElementHole* mir() const { return mir_->toLoadTypedArrayElementHole(); } - const LAllocation* object() { return getOperand(0); } + const LAllocation* elements() { return getOperand(0); } const LAllocation* index() { return getOperand(1); } + const LAllocation* length() { return getOperand(2); } const LDefinition* temp() { return getTemp(0); } const LInt64Definition temp64() { return getInt64Temp(1); } }; diff --git a/js/src/jit/wasm32/CodeGenerator-wasm32.cpp b/js/src/jit/wasm32/CodeGenerator-wasm32.cpp index 923297a0c1..4c27637203 100644 --- a/js/src/jit/wasm32/CodeGenerator-wasm32.cpp +++ b/js/src/jit/wasm32/CodeGenerator-wasm32.cpp @@ -175,7 +175,6 @@ void CodeGenerator::visitWasmAtomicBinopHeapForEffect( } void CodeGenerator::visitWasmStackArg(LWasmStackArg* ins) { MOZ_CRASH(); } void CodeGenerator::visitWasmStackArgI64(LWasmStackArgI64* ins) { MOZ_CRASH(); } -void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { MOZ_CRASH(); } void CodeGenerator::visitSimd128(LSimd128* ins) { MOZ_CRASH(); } void CodeGenerator::visitWasmTernarySimd128(LWasmTernarySimd128* ins) { MOZ_CRASH(); diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 9e5319842b..86d4bca0e0 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -432,7 +432,7 @@ void CodeGenerator::visitAtomicTypedArrayElementBinop64( // Add and Sub don't need |fetchTemp| and can save a `mov` when the value and // output register are equal to each other. - if (atomicOp == AtomicFetchAddOp || atomicOp == AtomicFetchSubOp) { + if (atomicOp == AtomicOp::Add || atomicOp == AtomicOp::Sub) { fetchTemp = Register64::Invalid(); fetchOut = temp1; createTemp = temp2.reg; diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index 55d83e3f05..9f9b1713c2 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -208,8 +208,8 @@ void LIRGenerator::visitAtomicTypedArrayElementBinop( // // For AND/OR/XOR we need to use a CMPXCHG loop with rax as a temp register. - bool bitOp = !(ins->operation() == AtomicFetchAddOp || - ins->operation() == AtomicFetchSubOp); + bool bitOp = !(ins->operation() == AtomicOp::Add || + ins->operation() == AtomicOp::Sub); LInt64Definition temp1 = tempInt64(); LInt64Definition temp2; @@ -427,8 +427,8 @@ void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { // *mem does not have the expected value, so reloading it at the // top of the loop would be redundant. - bool bitOp = !(ins->operation() == AtomicFetchAddOp || - ins->operation() == AtomicFetchSubOp); + bool bitOp = + !(ins->operation() == AtomicOp::Add || ins->operation() == AtomicOp::Sub); bool reuseInput = false; LAllocation value; diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 5106e7e382..ebc8c91eaa 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -1459,7 +1459,7 @@ static void AtomicFetchOp64(MacroAssembler& masm, Register output) { // NOTE: the generated code must match the assembly code in gen_fetchop in // GenerateAtomicOperations.py - if (op == AtomicFetchAddOp) { + if (op == AtomicOp::Add) { if (value != output) { masm.movq(value, output); } @@ -1468,7 +1468,7 @@ static void AtomicFetchOp64(MacroAssembler& masm, FaultingCodeOffset(masm.currentOffset())); } masm.lock_xaddq(output, Operand(mem)); - } else if (op == AtomicFetchSubOp) { + } else if (op == AtomicOp::Sub) { if (value != output) { masm.movq(value, output); } @@ -1492,13 +1492,13 @@ static void AtomicFetchOp64(MacroAssembler& masm, masm.bind(&again); masm.movq(rax, temp); switch (op) { - case AtomicFetchAndOp: + case AtomicOp::And: masm.andq(value, temp); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.orq(value, temp); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.xorq(value, temp); break; default: @@ -1532,19 +1532,19 @@ static void AtomicEffectOp64(MacroAssembler& masm, FaultingCodeOffset(masm.currentOffset())); } switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.lock_addq(value, Operand(mem)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.lock_subq(value, Operand(mem)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.lock_andq(value, Operand(mem)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.lock_orq(value, Operand(mem)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.lock_xorq(value, Operand(mem)); break; default: @@ -1558,8 +1558,8 @@ void MacroAssembler::wasmAtomicEffectOp64(const wasm::MemoryAccessDesc& access, AtomicEffectOp64(*this, &access, op, value.reg, mem); } -void MacroAssembler::compareExchange64(const Synchronization&, - const Address& mem, Register64 expected, +void MacroAssembler::compareExchange64(Synchronization, const Address& mem, + Register64 expected, Register64 replacement, Register64 output) { // NOTE: the generated code must match the assembly code in gen_cmpxchg in @@ -1571,8 +1571,7 @@ void MacroAssembler::compareExchange64(const Synchronization&, lock_cmpxchgq(replacement.reg, Operand(mem)); } -void MacroAssembler::compareExchange64(const Synchronization&, - const BaseIndex& mem, +void MacroAssembler::compareExchange64(Synchronization, const BaseIndex& mem, Register64 expected, Register64 replacement, Register64 output) { @@ -1583,9 +1582,8 @@ void MacroAssembler::compareExchange64(const Synchronization&, lock_cmpxchgq(replacement.reg, Operand(mem)); } -void MacroAssembler::atomicExchange64(const Synchronization&, - const Address& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization, const Address& mem, + Register64 value, Register64 output) { // NOTE: the generated code must match the assembly code in gen_exchange in // GenerateAtomicOperations.py if (value != output) { @@ -1594,33 +1592,32 @@ void MacroAssembler::atomicExchange64(const Synchronization&, xchgq(output.reg, Operand(mem)); } -void MacroAssembler::atomicExchange64(const Synchronization&, - const BaseIndex& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization, const BaseIndex& mem, + Register64 value, Register64 output) { if (value != output) { movq(value.reg, output.reg); } xchgq(output.reg, Operand(mem)); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, op, value.reg, mem, temp.reg, output.reg); } -void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, op, value.reg, mem, temp.reg, output.reg); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const Address& mem) { AtomicEffectOp64(*this, nullptr, op, value.reg, mem); } -void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, +void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op, Register64 value, const BaseIndex& mem) { AtomicEffectOp64(*this, nullptr, op, value.reg, mem); } diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 434a54669b..692e884f06 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2078,12 +2078,6 @@ void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect( } } -void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { - if (ins->type() & MembarStoreLoad) { - masm.storeLoadFence(); - } -} - void CodeGeneratorX86Shared::visitOutOfLineWasmTruncateCheck( OutOfLineWasmTruncateCheck* ool) { FloatRegister input = ool->input(); diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index bd5986298d..6d90f2f96b 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -732,8 +732,8 @@ void LIRGeneratorX86Shared::lowerAtomicTypedArrayElementBinop( // There are optimization opportunities: // - better register allocation in the x86 8-bit case, Bug #1077036. - bool bitOp = !(ins->operation() == AtomicFetchAddOp || - ins->operation() == AtomicFetchSubOp); + bool bitOp = + !(ins->operation() == AtomicOp::Add || ins->operation() == AtomicOp::Sub); bool fixedOutput = true; bool reuseInput = false; LDefinition tempDef1 = LDefinition::BogusTemp(); diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h index 9848086e7f..8ce3f68224 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -1246,7 +1246,8 @@ template FaultingCodeOffset MacroAssembler::storeFloat32(FloatRegister src, void MacroAssembler::memoryBarrier(MemoryBarrierBits barrier) { if (barrier & MembarStoreLoad) { - storeLoadFence(); + // This implementation follows Linux. + masm.mfence(); } } diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp index e474f83530..1520321260 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp @@ -1143,13 +1143,13 @@ static void CompareExchange(MacroAssembler& masm, ExtendTo32(masm, type, output); } -void MacroAssembler::compareExchange(Scalar::Type type, const Synchronization&, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization, const Address& mem, Register oldval, Register newval, Register output) { CompareExchange(*this, nullptr, type, mem, oldval, newval, output); } -void MacroAssembler::compareExchange(Scalar::Type type, const Synchronization&, +void MacroAssembler::compareExchange(Scalar::Type type, Synchronization, const BaseIndex& mem, Register oldval, Register newval, Register output) { CompareExchange(*this, nullptr, type, mem, oldval, newval, output); @@ -1201,13 +1201,13 @@ static void AtomicExchange(MacroAssembler& masm, ExtendTo32(masm, type, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, const Synchronization&, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization, const Address& mem, Register value, Register output) { AtomicExchange(*this, nullptr, type, mem, value, output); } -void MacroAssembler::atomicExchange(Scalar::Type type, const Synchronization&, +void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization, const BaseIndex& mem, Register value, Register output) { AtomicExchange(*this, nullptr, type, mem, value, output); @@ -1227,7 +1227,7 @@ void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, static void SetupValue(MacroAssembler& masm, AtomicOp op, Imm32 src, Register output) { - if (op == AtomicFetchSubOp) { + if (op == AtomicOp::Sub) { masm.movl(Imm32(-src.value), output); } else { masm.movl(src, output); @@ -1239,7 +1239,7 @@ static void SetupValue(MacroAssembler& masm, AtomicOp op, Register src, if (src != output) { masm.movl(src, output); } - if (op == AtomicFetchSubOp) { + if (op == AtomicOp::Sub) { masm.negl(output); } } @@ -1269,15 +1269,14 @@ static void AtomicFetchOp(MacroAssembler& masm, masm.j(MacroAssembler::NonZero, &again); \ } while (0) - MOZ_ASSERT_IF(op == AtomicFetchAddOp || op == AtomicFetchSubOp, - temp == InvalidReg); + MOZ_ASSERT_IF(op == AtomicOp::Add || op == AtomicOp::Sub, temp == InvalidReg); switch (Scalar::byteSize(arrayType)) { case 1: CheckBytereg(output); switch (op) { - case AtomicFetchAddOp: - case AtomicFetchSubOp: + case AtomicOp::Add: + case AtomicOp::Sub: CheckBytereg(value); // But not for the bitwise ops SetupValue(masm, op, value, output); if (access) { @@ -1286,17 +1285,17 @@ static void AtomicFetchOp(MacroAssembler& masm, } masm.lock_xaddb(output, Operand(mem)); break; - case AtomicFetchAndOp: + case AtomicOp::And: CheckBytereg(temp); ATOMIC_BITOP_BODY(movb, wasm::TrapMachineInsn::Load8, andl, lock_cmpxchgb); break; - case AtomicFetchOrOp: + case AtomicOp::Or: CheckBytereg(temp); ATOMIC_BITOP_BODY(movb, wasm::TrapMachineInsn::Load8, orl, lock_cmpxchgb); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: CheckBytereg(temp); ATOMIC_BITOP_BODY(movb, wasm::TrapMachineInsn::Load8, xorl, lock_cmpxchgb); @@ -1307,8 +1306,8 @@ static void AtomicFetchOp(MacroAssembler& masm, break; case 2: switch (op) { - case AtomicFetchAddOp: - case AtomicFetchSubOp: + case AtomicOp::Add: + case AtomicOp::Sub: SetupValue(masm, op, value, output); if (access) { masm.append(*access, wasm::TrapMachineInsn::Atomic, @@ -1316,15 +1315,15 @@ static void AtomicFetchOp(MacroAssembler& masm, } masm.lock_xaddw(output, Operand(mem)); break; - case AtomicFetchAndOp: + case AtomicOp::And: ATOMIC_BITOP_BODY(movw, wasm::TrapMachineInsn::Load16, andl, lock_cmpxchgw); break; - case AtomicFetchOrOp: + case AtomicOp::Or: ATOMIC_BITOP_BODY(movw, wasm::TrapMachineInsn::Load16, orl, lock_cmpxchgw); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: ATOMIC_BITOP_BODY(movw, wasm::TrapMachineInsn::Load16, xorl, lock_cmpxchgw); break; @@ -1334,8 +1333,8 @@ static void AtomicFetchOp(MacroAssembler& masm, break; case 4: switch (op) { - case AtomicFetchAddOp: - case AtomicFetchSubOp: + case AtomicOp::Add: + case AtomicOp::Sub: SetupValue(masm, op, value, output); if (access) { masm.append(*access, wasm::TrapMachineInsn::Atomic, @@ -1343,15 +1342,15 @@ static void AtomicFetchOp(MacroAssembler& masm, } masm.lock_xaddl(output, Operand(mem)); break; - case AtomicFetchAndOp: + case AtomicOp::And: ATOMIC_BITOP_BODY(movl, wasm::TrapMachineInsn::Load32, andl, lock_cmpxchgl); break; - case AtomicFetchOrOp: + case AtomicOp::Or: ATOMIC_BITOP_BODY(movl, wasm::TrapMachineInsn::Load32, orl, lock_cmpxchgl); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: ATOMIC_BITOP_BODY(movl, wasm::TrapMachineInsn::Load32, xorl, lock_cmpxchgl); break; @@ -1367,30 +1366,29 @@ static void AtomicFetchOp(MacroAssembler& masm, #undef ATOMIC_BITOP_BODY } -void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, - const Synchronization&, AtomicOp op, - Register value, const BaseIndex& mem, - Register temp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, Synchronization, + AtomicOp op, Register value, + const BaseIndex& mem, Register temp, + Register output) { AtomicFetchOp(*this, nullptr, arrayType, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, - const Synchronization&, AtomicOp op, - Register value, const Address& mem, - Register temp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, Synchronization, + AtomicOp op, Register value, + const Address& mem, Register temp, + Register output) { AtomicFetchOp(*this, nullptr, arrayType, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, - const Synchronization&, AtomicOp op, - Imm32 value, const BaseIndex& mem, - Register temp, Register output) { +void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, Synchronization, + AtomicOp op, Imm32 value, + const BaseIndex& mem, Register temp, + Register output) { AtomicFetchOp(*this, nullptr, arrayType, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, - const Synchronization&, AtomicOp op, - Imm32 value, const Address& mem, +void MacroAssembler::atomicFetchOp(Scalar::Type arrayType, Synchronization, + AtomicOp op, Imm32 value, const Address& mem, Register temp, Register output) { AtomicFetchOp(*this, nullptr, arrayType, op, value, mem, temp, output); } @@ -1436,19 +1434,19 @@ static void AtomicEffectOp(MacroAssembler& masm, switch (Scalar::byteSize(arrayType)) { case 1: switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.lock_addb(value, Operand(mem)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.lock_subb(value, Operand(mem)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.lock_andb(value, Operand(mem)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.lock_orb(value, Operand(mem)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.lock_xorb(value, Operand(mem)); break; default: @@ -1457,19 +1455,19 @@ static void AtomicEffectOp(MacroAssembler& masm, break; case 2: switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.lock_addw(value, Operand(mem)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.lock_subw(value, Operand(mem)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.lock_andw(value, Operand(mem)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.lock_orw(value, Operand(mem)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.lock_xorw(value, Operand(mem)); break; default: @@ -1478,19 +1476,19 @@ static void AtomicEffectOp(MacroAssembler& masm, break; case 4: switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: masm.lock_addl(value, Operand(mem)); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: masm.lock_subl(value, Operand(mem)); break; - case AtomicFetchAndOp: + case AtomicOp::And: masm.lock_andl(value, Operand(mem)); break; - case AtomicFetchOrOp: + case AtomicOp::Or: masm.lock_orl(value, Operand(mem)); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: masm.lock_xorl(value, Operand(mem)); break; default: @@ -1535,7 +1533,7 @@ void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, template <typename T> static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, + Synchronization sync, const T& mem, Register oldval, Register newval, Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { @@ -1547,15 +1545,14 @@ static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register oldval, - Register newval, Register temp, - AnyRegister output) { + Synchronization sync, const Address& mem, + Register oldval, Register newval, + Register temp, AnyRegister output) { CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, temp, output); } void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register oldval, Register newval, Register temp, AnyRegister output) { @@ -1564,9 +1561,8 @@ void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, const T& mem, - Register value, Register temp, - AnyRegister output) { + Synchronization sync, const T& mem, Register value, + Register temp, AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicExchange(arrayType, sync, mem, value, temp); masm.convertUInt32ToDouble(temp, output.fpu()); @@ -1576,14 +1572,14 @@ static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, - const Address& mem, Register value, - Register temp, AnyRegister output) { + Synchronization sync, const Address& mem, + Register value, Register temp, + AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); } void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, - const Synchronization& sync, + Synchronization sync, const BaseIndex& mem, Register value, Register temp, AnyRegister output) { AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output); @@ -1591,9 +1587,9 @@ void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, template <typename T> static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, - Register value, const T& mem, Register temp1, - Register temp2, AnyRegister output) { + Synchronization sync, AtomicOp op, Register value, + const T& mem, Register temp1, Register temp2, + AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicFetchOp(arrayType, sync, op, value, mem, temp2, temp1); masm.convertUInt32ToDouble(temp1, output.fpu()); @@ -1603,7 +1599,7 @@ static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const Address& mem, Register temp1, Register temp2, AnyRegister output) { @@ -1611,39 +1607,36 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Register value, const BaseIndex& mem, Register temp1, Register temp2, AnyRegister output) { AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, temp1, temp2, output); } -void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization&, AtomicOp op, - Register value, const BaseIndex& mem, - Register temp) { +void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, Synchronization, + AtomicOp op, Register value, + const BaseIndex& mem, Register temp) { MOZ_ASSERT(temp == InvalidReg); AtomicEffectOp(*this, nullptr, arrayType, op, value, mem); } -void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization&, AtomicOp op, - Register value, const Address& mem, - Register temp) { +void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, Synchronization, + AtomicOp op, Register value, + const Address& mem, Register temp) { MOZ_ASSERT(temp == InvalidReg); AtomicEffectOp(*this, nullptr, arrayType, op, value, mem); } -void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization&, AtomicOp op, - Imm32 value, const Address& mem, - Register temp) { +void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, Synchronization, + AtomicOp op, Imm32 value, + const Address& mem, Register temp) { MOZ_ASSERT(temp == InvalidReg); AtomicEffectOp(*this, nullptr, arrayType, op, value, mem); } void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Imm32 value, const BaseIndex& mem, Register temp) { MOZ_ASSERT(temp == InvalidReg); @@ -1652,9 +1645,9 @@ void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, template <typename T> static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, - Imm32 value, const T& mem, Register temp1, - Register temp2, AnyRegister output) { + Synchronization sync, AtomicOp op, Imm32 value, + const T& mem, Register temp1, Register temp2, + AnyRegister output) { if (arrayType == Scalar::Uint32) { masm.atomicFetchOp(arrayType, sync, op, value, mem, temp2, temp1); masm.convertUInt32ToDouble(temp1, output.fpu()); @@ -1664,7 +1657,7 @@ static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Imm32 value, const Address& mem, Register temp1, Register temp2, AnyRegister output) { @@ -1672,7 +1665,7 @@ void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, } void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, - const Synchronization& sync, AtomicOp op, + Synchronization sync, AtomicOp op, Imm32 value, const BaseIndex& mem, Register temp1, Register temp2, AnyRegister output) { diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index dd1ae53537..21af90e90d 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -161,15 +161,6 @@ class MacroAssemblerX86Shared : public Assembler { void atomic_inc32(const Operand& addr) { lock_incl(addr); } void atomic_dec32(const Operand& addr) { lock_decl(addr); } - void storeLoadFence() { - // This implementation follows Linux. - if (HasSSE2()) { - masm.mfence(); - } else { - lock_addl(Imm32(0), Operand(Address(esp, 0))); - } - } - void branch16(Condition cond, Register lhs, Register rhs, Label* label) { cmpw(rhs, lhs); j(cond, label); diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index 0577a0976e..e958e998c2 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -635,8 +635,8 @@ void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { // - better 8-bit register allocation and instruction selection, Bug // #1077036. - bool bitOp = !(ins->operation() == AtomicFetchAddOp || - ins->operation() == AtomicFetchSubOp); + bool bitOp = + !(ins->operation() == AtomicOp::Add || ins->operation() == AtomicOp::Sub); LDefinition tempDef = LDefinition::BogusTemp(); LAllocation value; diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index a68d7b03b7..232303b429 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -1423,19 +1423,19 @@ static void AtomicFetchOp64(MacroAssembler& masm, } while (0) switch (op) { - case AtomicFetchAddOp: + case AtomicOp::Add: ATOMIC_OP_BODY(add64FromMemory); break; - case AtomicFetchSubOp: + case AtomicOp::Sub: ATOMIC_OP_BODY(sub64FromMemory); break; - case AtomicFetchAndOp: + case AtomicOp::And: ATOMIC_OP_BODY(and64FromMemory); break; - case AtomicFetchOrOp: + case AtomicOp::Or: ATOMIC_OP_BODY(or64FromMemory); break; - case AtomicFetchXorOp: + case AtomicOp::Xor: ATOMIC_OP_BODY(xor64FromMemory); break; default: @@ -1626,60 +1626,57 @@ void MacroAssembler::wasmTruncateFloat32ToUInt64( // ======================================================================== // Primitive atomic operations. -void MacroAssembler::atomicLoad64(const Synchronization&, const Address& mem, +void MacroAssembler::atomicLoad64(Synchronization, const Address& mem, Register64 temp, Register64 output) { AtomicLoad64(*this, nullptr, mem, temp, output); } -void MacroAssembler::atomicLoad64(const Synchronization&, const BaseIndex& mem, +void MacroAssembler::atomicLoad64(Synchronization, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicLoad64(*this, nullptr, mem, temp, output); } -void MacroAssembler::atomicStore64(const Synchronization&, const Address& mem, +void MacroAssembler::atomicStore64(Synchronization, const Address& mem, Register64 value, Register64 temp) { AtomicExchange64(*this, nullptr, mem, value, temp); } -void MacroAssembler::atomicStore64(const Synchronization&, const BaseIndex& mem, +void MacroAssembler::atomicStore64(Synchronization, const BaseIndex& mem, Register64 value, Register64 temp) { AtomicExchange64(*this, nullptr, mem, value, temp); } -void MacroAssembler::compareExchange64(const Synchronization&, - const Address& mem, Register64 expected, +void MacroAssembler::compareExchange64(Synchronization, const Address& mem, + Register64 expected, Register64 replacement, Register64 output) { CompareExchange64(*this, nullptr, mem, expected, replacement, output); } -void MacroAssembler::compareExchange64(const Synchronization&, - const BaseIndex& mem, +void MacroAssembler::compareExchange64(Synchronization, const BaseIndex& mem, Register64 expected, Register64 replacement, Register64 output) { CompareExchange64(*this, nullptr, mem, expected, replacement, output); } -void MacroAssembler::atomicExchange64(const Synchronization&, - const Address& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization, const Address& mem, + Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, mem, value, output); } -void MacroAssembler::atomicExchange64(const Synchronization&, - const BaseIndex& mem, Register64 value, - Register64 output) { +void MacroAssembler::atomicExchange64(Synchronization, const BaseIndex& mem, + Register64 value, Register64 output) { AtomicExchange64(*this, nullptr, mem, value, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization&, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization, AtomicOp op, const Address& value, const Address& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, op, value, mem, temp, output); } -void MacroAssembler::atomicFetchOp64(const Synchronization&, AtomicOp op, +void MacroAssembler::atomicFetchOp64(Synchronization, AtomicOp op, const Address& value, const BaseIndex& mem, Register64 temp, Register64 output) { AtomicFetchOp64(*this, nullptr, op, value, mem, temp, output); diff --git a/js/src/jsapi-tests/testCompileScript.cpp b/js/src/jsapi-tests/testCompileScript.cpp index 45f5e8301a..ff7432b14b 100644 --- a/js/src/jsapi-tests/testCompileScript.cpp +++ b/js/src/jsapi-tests/testCompileScript.cpp @@ -53,9 +53,8 @@ bool testCompile() { CHECK(buf16.init(cx, src_16.data(), src_16.length(), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileGlobalScriptToStencil(fc, options, buf16, compileStorage); + CompileGlobalScriptToStencil(fc, options, buf16); CHECK(stencil); CHECK(stencil->scriptExtra.size() == 1); CHECK(stencil->scriptExtra[0].extent.sourceStart == 0); @@ -63,7 +62,6 @@ bool testCompile() { CHECK(stencil->scriptData.size() == 1); CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode CHECK(stencil->scriptData[0].gcThingsLength == 1); - CHECK(compileStorage.hasInput()); } { // 8-bit characters @@ -71,9 +69,8 @@ bool testCompile() { CHECK( buf8.init(cx, src.data(), src.length(), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileGlobalScriptToStencil(fc, options, buf8, compileStorage); + CompileGlobalScriptToStencil(fc, options, buf8); CHECK(stencil); CHECK(stencil->scriptExtra.size() == 1); CHECK(stencil->scriptExtra[0].extent.sourceStart == 0); @@ -81,7 +78,6 @@ bool testCompile() { CHECK(stencil->scriptData.size() == 1); CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode CHECK(stencil->scriptData[0].gcThingsLength == 1); - CHECK(compileStorage.hasInput()); } { // propagates failures @@ -90,9 +86,8 @@ bool testCompile() { CHECK(srcBuf.init(cx, badSrc.data(), badSrc.length(), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage); + CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(!stencil); CHECK(fc->maybeError().isSome()); const js::CompileError& error = fc->maybeError().ref(); @@ -120,9 +115,8 @@ bool testNonsyntacticCompile() { auto destroyFc = mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); }); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage); + CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); @@ -153,9 +147,8 @@ bool testCompileModule() { CHECK(buf16.init(cx, src_16.data(), src_16.length(), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileModuleScriptToStencil(fc, options, buf16, compileStorage); + CompileModuleScriptToStencil(fc, options, buf16); CHECK(stencil); CHECK(stencil->isModule()); CHECK(stencil->scriptExtra.size() == 1); @@ -164,7 +157,6 @@ bool testCompileModule() { CHECK(stencil->scriptData.size() == 1); CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode CHECK(stencil->scriptData[0].gcThingsLength == 1); - CHECK(compileStorage.hasInput()); } { // 8-bit characters @@ -172,9 +164,8 @@ bool testCompileModule() { CHECK( buf8.init(cx, src.data(), src.length(), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileModuleScriptToStencil(fc, options, buf8, compileStorage); + CompileModuleScriptToStencil(fc, options, buf8); CHECK(stencil); CHECK(stencil->scriptExtra.size() == 1); CHECK(stencil->scriptExtra[0].extent.sourceStart == 0); @@ -182,7 +173,6 @@ bool testCompileModule() { CHECK(stencil->scriptData.size() == 1); CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode CHECK(stencil->scriptData[0].gcThingsLength == 1); - CHECK(compileStorage.hasInput()); } { // propagates failures @@ -191,9 +181,8 @@ bool testCompileModule() { CHECK(srcBuf.init(cx, badSrc.data(), badSrc.length(), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileModuleScriptToStencil(fc, options, srcBuf, compileStorage); + CompileModuleScriptToStencil(fc, options, srcBuf); CHECK(!stencil); CHECK(fc->maybeError().isSome()); const js::CompileError& error = fc->maybeError().ref(); @@ -220,15 +209,12 @@ bool testPrepareForInstantiate() { auto destroyFc = mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); }); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - CompileGlobalScriptToStencil(fc, options, buf16, compileStorage); + CompileGlobalScriptToStencil(fc, options, buf16); CHECK(stencil); CHECK(stencil->scriptData.size() == 2); CHECK(stencil->scopeData.size() == 1); // function f CHECK(stencil->parserAtomData.size() == 1); // 'field' - CHECK(compileStorage.hasInput()); - CHECK(compileStorage.getInput().atomCache.empty()); JS::InstantiationStorage storage; CHECK(JS::PrepareForInstantiate(fc, *stencil, storage)); diff --git a/js/src/jsapi-tests/testFrontendCompileStencil.cpp b/js/src/jsapi-tests/testFrontendCompileStencil.cpp index ba29b5aea2..39e46ba0b1 100644 --- a/js/src/jsapi-tests/testFrontendCompileStencil.cpp +++ b/js/src/jsapi-tests/testFrontendCompileStencil.cpp @@ -41,11 +41,9 @@ BEGIN_FRONTEND_TEST(testFrontendContextCompileGlobalScriptToStencil) { JS::SourceText<mozilla::Utf8Unit> srcBuf; CHECK( srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - JS::CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage); + JS::CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(stencil); - CHECK(compileStorage.hasInput()); } { @@ -54,11 +52,9 @@ BEGIN_FRONTEND_TEST(testFrontendContextCompileGlobalScriptToStencil) { JS::SourceText<char16_t> srcBuf; CHECK(srcBuf.init(fc, source, std::char_traits<char16_t>::length(source), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - JS::CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage); + JS::CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(stencil); - CHECK(compileStorage.hasInput()); } JS::DestroyFrontendContext(fc); diff --git a/js/src/jsapi-tests/testFrontendErrors.cpp b/js/src/jsapi-tests/testFrontendErrors.cpp index c9d3ee1eb5..47acf559d7 100644 --- a/js/src/jsapi-tests/testFrontendErrors.cpp +++ b/js/src/jsapi-tests/testFrontendErrors.cpp @@ -51,9 +51,8 @@ BEGIN_TEST(testFrontendErrors_error) { JS::SourceText<mozilla::Utf8Unit> srcBuf; CHECK( srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - JS::CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage); + JS::CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(!stencil); } @@ -133,9 +132,8 @@ BEGIN_TEST(testFrontendErrors_warning) { JS::SourceText<mozilla::Utf8Unit> srcBuf; CHECK( srcBuf.init(fc, source, strlen(source), JS::SourceOwnership::Borrowed)); - JS::CompilationStorage compileStorage; RefPtr<JS::Stencil> stencil = - JS::CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage); + JS::CompileGlobalScriptToStencil(fc, options, srcBuf); CHECK(stencil); } diff --git a/js/src/jsapi-tests/testStencil.cpp b/js/src/jsapi-tests/testStencil.cpp index ab89222ebd..7d6b78d7d8 100644 --- a/js/src/jsapi-tests/testStencil.cpp +++ b/js/src/jsapi-tests/testStencil.cpp @@ -85,6 +85,8 @@ bool basic_test(const CharT* chars) { CHECK(srcBuf.init(cx, chars, length, JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); + options.setFile("testStencil_Module"); + RefPtr<JS::Stencil> stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf); CHECK(stencil); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 356a5687a2..77c3ae5f09 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1289,7 +1289,7 @@ JS_PUBLIC_API void JS_RemoveExtraGCRootsTracer(JSContext* cx, } JS_PUBLIC_API JS::GCReason JS::WantEagerMinorGC(JSRuntime* rt) { - if (rt->gc.nursery().shouldCollect()) { + if (rt->gc.nursery().wantEagerCollection()) { return JS::GCReason::EAGER_NURSERY_COLLECTION; } return JS::GCReason::NO_REASON; @@ -1302,7 +1302,7 @@ JS_PUBLIC_API JS::GCReason JS::WantEagerMajorGC(JSRuntime* rt) { JS_PUBLIC_API void JS::MaybeRunNurseryCollection(JSRuntime* rt, JS::GCReason reason) { gc::GCRuntime& gc = rt->gc; - if (gc.nursery().shouldCollect()) { + if (gc.nursery().wantEagerCollection()) { gc.minorGC(reason); } } @@ -4966,7 +4966,25 @@ JS_PUBLIC_API bool JS::CopyAsyncStack(JSContext* cx, return true; } -JS_PUBLIC_API Zone* JS::GetObjectZone(JSObject* obj) { return obj->zone(); } +JS_PUBLIC_API Zone* JS::GetObjectZone(JSObject* obj) { + Zone* zone = obj->zone(); + + // Check zone pointer is valid and not a poison value. See bug 1878421. + MOZ_RELEASE_ASSERT(zone->runtimeFromMainThread()); + + return zone; +} + +JS_PUBLIC_API Zone* JS::GetTenuredGCThingZone(GCCellPtr thing) { + js::gc::Cell* cell = thing.asCell(); + MOZ_ASSERT(!js::gc::IsInsideNursery(cell)); + Zone* zone = js::gc::detail::GetTenuredGCThingZone(cell); + + // Check zone pointer is valid and not a poison value. See bug 1878421. + MOZ_RELEASE_ASSERT(zone->runtimeFromMainThread()); + + return zone; +} JS_PUBLIC_API Zone* JS::GetNurseryCellZone(gc::Cell* cell) { return cell->nurseryZone(); diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 84921c4d20..7040213f56 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -56,7 +56,6 @@ #include "vm/JSObject.h" #include "vm/StringType.h" #include "vm/Time.h" -#include "vm/Warnings.h" #include "vm/Compartment-inl.h" // For js::UnwrapAndTypeCheckThis #include "vm/GeckoProfiler-inl.h" @@ -1071,23 +1070,6 @@ int FixupNonFullYear(int year) { } template <typename CharT> -bool IsPrefixOfKeyword(const CharT* s, size_t len, const char* keyword) { - while (len > 0 && *keyword) { - MOZ_ASSERT(IsAsciiAlpha(*s)); - MOZ_ASSERT(IsAsciiLowercaseAlpha(*keyword)); - - if (unicode::ToLowerCase(static_cast<Latin1Char>(*s)) != *keyword) { - break; - } - - s++, keyword++; - len--; - } - - return len == 0; -} - -template <typename CharT> bool MatchesKeyword(const CharT* s, size_t len, const char* keyword) { while (len > 0) { MOZ_ASSERT(IsAsciiAlpha(*s)); @@ -1329,10 +1311,6 @@ struct CharsAndAction { int action; }; -static constexpr const char* const days_of_week[] = { - "monday", "tuesday", "wednesday", "thursday", - "friday", "saturday", "sunday"}; - static constexpr CharsAndAction keywords[] = { // clang-format off // AM/PM @@ -1365,8 +1343,7 @@ constexpr size_t MinKeywordLength(const CharsAndAction (&keywords)[N]) { template <typename CharT> static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s, - size_t length, ClippedTime* result, - bool* countLateWeekday) { + size_t length, ClippedTime* result) { if (length == 0) { return false; } @@ -1434,8 +1411,6 @@ static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s, bool negativeYear = false; // Includes "GMT", "UTC", "UT", and "Z" timezone keywords bool seenGmtAbbr = false; - // For telemetry purposes - bool seenLateWeekday = false; // Try parsing the leading dashed-date. // @@ -1667,21 +1642,6 @@ static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s, return false; } - // Completely ignore days of the week, and don't derive any semantics - // from them. - bool isLateWeekday = false; - for (const char* weekday : days_of_week) { - if (IsPrefixOfKeyword(s + start, index - start, weekday)) { - isLateWeekday = true; - seenLateWeekday = true; - break; - } - } - if (isLateWeekday) { - prevc = 0; - continue; - } - // Record a month if it is a month name. Note that some numbers are // initially treated as months; if a numeric field has already been // interpreted as a month, store that value to the actually appropriate @@ -1882,48 +1842,16 @@ static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s, date += tzOffset * msPerMinute; } - // Setting this down here so that it only counts the telemetry in - // the case of a successful parse. - if (seenLateWeekday) { - *countLateWeekday = true; - } - *result = TimeClip(date); return true; } static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, JSLinearString* s, - ClippedTime* result, JSContext* cx) { - bool countLateWeekday = false; - bool success; - - { - AutoCheckCannotGC nogc; - success = s->hasLatin1Chars() - ? ParseDate(forceUTC, s->latin1Chars(nogc), s->length(), - result, &countLateWeekday) - : ParseDate(forceUTC, s->twoByteChars(nogc), s->length(), - result, &countLateWeekday); - } - - // We are running telemetry to see if support for day of week after - // mday can be dropped. It is being done here to keep - // JSRuntime::setUseCounter out of AutoCheckCannotGC's scope. - if (countLateWeekday) { - cx->runtime()->setUseCounter(cx->global(), JSUseCounter::LATE_WEEKDAY); - - if (!cx->realm()->warnedAboutDateLateWeekday) { - if (!WarnNumberASCII(cx, JSMSG_DEPRECATED_LATE_WEEKDAY)) { - // Proceed as if nothing happened if warning fails - if (cx->isExceptionPending()) { - cx->clearPendingException(); - } - } - cx->realm()->warnedAboutDateLateWeekday = true; - } - } - - return success; + ClippedTime* result) { + AutoCheckCannotGC nogc; + return s->hasLatin1Chars() + ? ParseDate(forceUTC, s->latin1Chars(nogc), s->length(), result) + : ParseDate(forceUTC, s->twoByteChars(nogc), s->length(), result); } static bool date_parse(JSContext* cx, unsigned argc, Value* vp) { @@ -1945,7 +1873,7 @@ static bool date_parse(JSContext* cx, unsigned argc, Value* vp) { } ClippedTime result; - if (!ParseDate(ForceUTC(cx->realm()), linearStr, &result, cx)) { + if (!ParseDate(ForceUTC(cx->realm()), linearStr, &result)) { args.rval().setNaN(); return true; } @@ -3789,7 +3717,7 @@ static bool DateOneArgument(JSContext* cx, const CallArgs& args) { return false; } - if (!ParseDate(ForceUTC(cx->realm()), linearStr, &t, cx)) { + if (!ParseDate(ForceUTC(cx->realm()), linearStr, &t)) { t = ClippedTime::invalid(); } } else { diff --git a/js/src/moz.build b/js/src/moz.build index 422bb9d40f..3ec9c6f489 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -641,9 +641,10 @@ if CONFIG["JS_HAS_CTYPES"]: CXXFLAGS += CONFIG["MOZ_FFI_CFLAGS"] if CONFIG["JS_HAS_INTL_API"]: - DIRS += [ - "../../intl/bidi", - "../../intl/components", - ] + if CONFIG["JS_STANDALONE"]: + DIRS += [ + "../../intl/bidi", + "../../intl/components", + ] USE_LIBS += ["intlcomponents"] diff --git a/js/src/old-configure.in b/js/src/old-configure.in index c7aa44b3f8..6861d2bbac 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -30,10 +30,6 @@ dnl Set the minimum version of toolkit libs used by mozilla dnl ======================================================== W32API_VERSION=3.14 -dnl Set various checks -dnl ======================================================== -MISSING_X= - dnl Initialize the Pthread test variables early so they can be dnl overridden by each platform. dnl ======================================================== @@ -111,7 +107,7 @@ case "$target" in AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) MSVC_C_RUNTIME_DLL=vcruntime140.dll - if test -n "$IS_VS2019_OR_MORE" -a "$TARGET_CPU" != "x86"; then + if test "$TARGET_CPU" != "x86"; then MSVC_C_RUNTIME_1_DLL=vcruntime140_1.dll fi MSVC_CXX_RUNTIME_DLL=msvcp140.dll @@ -250,30 +246,12 @@ dnl ======================================================== dnl System overrides of the defaults for target dnl ======================================================== +MOZ_OPTIMIZE_LDFLAGS="${_COMPILATION_OPTIMIZE_LDFLAGS} ${MOZ_OPTIMIZE_FLAGS}" + case "$target" in *-darwin*) MOZ_OPTIMIZE_FLAGS="-O3" DSO_LDOPTS='' - - dnl DTrace and -dead_strip don't interact well. See bug 403132. - dnl =================================================================== - if test "x$enable_dtrace" = "xyes"; then - echo "Skipping -dead_strip because DTrace is enabled. See bug 403132." - else - dnl check for the presence of the -dead_strip linker flag - AC_MSG_CHECKING([for -dead_strip option to ld]) - _SAVE_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS -Wl,-dead_strip" - AC_TRY_LINK(,[return 0;],_HAVE_DEAD_STRIP=1,_HAVE_DEAD_STRIP=) - if test -n "$_HAVE_DEAD_STRIP" ; then - AC_MSG_RESULT([yes]) - MOZ_OPTIMIZE_LDFLAGS="-Wl,-dead_strip" - else - AC_MSG_RESULT([no]) - fi - - LDFLAGS=$_SAVE_LDFLAGS - fi MOZ_FIX_LINK_PATHS= ;; @@ -458,12 +436,10 @@ fi if test -z "$SKIP_COMPILER_CHECKS"; then dnl Checks for typedefs, structures, and compiler characteristics. dnl ======================================================== -AC_C_CONST AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_PID_T -AC_TYPE_SIZE_T -AC_LANG_CPLUSPLUS + AC_LANG_C AC_MSG_CHECKING(for ssize_t) AC_CACHE_VAL(ac_cv_type_ssize_t, @@ -619,75 +595,6 @@ then fi -dnl Checks for library functions. -dnl ======================================================== - -dnl check for clock_gettime(), the CLOCK_MONOTONIC clock -AC_CACHE_CHECK(for clock_gettime(CLOCK_MONOTONIC), - ac_cv_clock_monotonic, - [for libs in "" -lrt; do - _SAVE_LIBS="$LIBS" - LIBS="$LIBS $libs" -dnl clock_gettime is available on OSX since 10.12, so depending on MACOSX_DEPLOYMENT_TARGET, -dnl we should or not be able to use it. To detect if we can, we need to make the -dnl availability attribute strict, so that compilation fails when the target is < 10.12. - AC_TRY_LINK([#define availability(os, ...) availability(os, strict, __VA_ARGS) - #include <time.h>], - [ struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); ], - ac_cv_clock_monotonic=$libs - LIBS="$_SAVE_LIBS" - break, - ac_cv_clock_monotonic=no) - LIBS="$_SAVE_LIBS" - done]) -if test "$ac_cv_clock_monotonic" != "no"; then - HAVE_CLOCK_MONOTONIC=1 - REALTIME_LIBS=$ac_cv_clock_monotonic - AC_DEFINE(HAVE_CLOCK_MONOTONIC) - AC_SUBST(HAVE_CLOCK_MONOTONIC) - AC_SUBST_LIST(REALTIME_LIBS) -fi - -dnl Checks for math functions. -dnl ======================================================== -AC_CHECK_LIB(m, sin) - -AC_CACHE_CHECK( - [for res_ninit()], - ac_cv_func_res_ninit, - [if test "$OS_TARGET" = NetBSD -o "$OS_TARGET" = OpenBSD; then - dnl no need for res_ninit() on NetBSD and OpenBSD - ac_cv_func_res_ninit=no - else - AC_TRY_LINK([ - #ifdef linux - #define _BSD_SOURCE 1 - #endif - #include <sys/types.h> - #include <netinet/in.h> - #include <arpa/nameser.h> - #include <resolv.h> - ], - [int foo = res_ninit(&_res);], - [ac_cv_func_res_ninit=yes], - [ac_cv_func_res_ninit=no]) - fi - ]) - -if test "$ac_cv_func_res_ninit" = "yes"; then - AC_DEFINE(HAVE_RES_NINIT) -dnl must add the link line we do something as foolish as this... dougt -dnl else -dnl AC_CHECK_LIB(bind, res_ninit, AC_DEFINE(HAVE_RES_NINIT), -dnl AC_CHECK_LIB(resolv, res_ninit, AC_DEFINE(HAVE_RES_NINIT))) -fi - -AM_LANGINFO_CODESET - -AC_LANG_C - -dnl =================================================================== dnl ======================================================== dnl Put your C++ language/feature checks below dnl ======================================================== @@ -719,35 +626,6 @@ if test "$GNU_CC"; then TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-${ARM_ABI_PREFIX}gcc3}" fi -# try harder, when checking for __thread support, see bug 521750 comment #33 and below -# We pass MOZ_OPTIMIZE_LDFLAGS to the linker because if dead_strip is -# enabled, the linker in xcode 4.1 will crash. Without this it would crash when -# linking XUL. -_SAVE_LDFLAGS=$LDFLAGS -LDFLAGS="$LDFLAGS $DSO_LDOPTS $MOZ_OPTIMIZE_LDFLAGS" -AC_CACHE_CHECK(for __thread keyword for TLS variables, - ac_cv_thread_keyword, - [AC_TRY_LINK([__thread bool tlsIsMainThread = false;], - [return tlsIsMainThread;], - ac_cv_thread_keyword=yes, - ac_cv_thread_keyword=no)]) -LDFLAGS=$_SAVE_LDFLAGS -if test "$ac_cv_thread_keyword" = yes; then - # mips builds fail with TLS variables because of a binutils bug. - # See bug 528687 - case "${target}" in - mips*-*) - : - ;; - *-android*|*-linuxandroid*) - : - ;; - *) - AC_DEFINE(HAVE_THREAD_TLS_KEYWORD) - ;; - esac -fi - dnl End of C++ language/feature checks AC_LANG_C @@ -834,9 +712,6 @@ else fi if test "$MOZ_MEMORY"; then - dnl The generic feature tests that determine how to compute ncpus are long and - dnl complicated. Therefore, simply define special cpp variables for the - dnl platforms we have special knowledge of. case "${target}" in *-mingw*) export MOZ_NO_DEBUG_RTL=1 diff --git a/js/src/shell/ModuleLoader.cpp b/js/src/shell/ModuleLoader.cpp index aca109cbcd..9bf1c015ac 100644 --- a/js/src/shell/ModuleLoader.cpp +++ b/js/src/shell/ModuleLoader.cpp @@ -122,7 +122,7 @@ bool ModuleLoader::ImportModuleDynamically(JSContext* cx, bool ModuleLoader::loadRootModule(JSContext* cx, HandleString path) { RootedValue rval(cx); - if (!loadAndExecute(cx, path, &rval)) { + if (!loadAndExecute(cx, path, nullptr, &rval)) { return false; } @@ -156,8 +156,9 @@ void ModuleLoader::clearModules(JSContext* cx) { } bool ModuleLoader::loadAndExecute(JSContext* cx, HandleString path, + HandleObject moduleRequestArg, MutableHandleValue rval) { - RootedObject module(cx, loadAndParse(cx, path)); + RootedObject module(cx, loadAndParse(cx, path, moduleRequestArg)); if (!module) { return false; } @@ -178,7 +179,7 @@ JSObject* ModuleLoader::resolveImportedModule( return nullptr; } - return loadAndParse(cx, path); + return loadAndParse(cx, path, moduleRequest); } bool ModuleLoader::populateImportMeta(JSContext* cx, @@ -328,7 +329,7 @@ bool ModuleLoader::tryDynamicImport(JSContext* cx, return false; } - return loadAndExecute(cx, path, rval); + return loadAndExecute(cx, path, moduleRequest, rval); } JSLinearString* ModuleLoader::resolve(JSContext* cx, @@ -418,7 +419,8 @@ JSLinearString* ModuleLoader::resolve(JSContext* cx, HandleString specifier, return normalizePath(cx, linear); } -JSObject* ModuleLoader::loadAndParse(JSContext* cx, HandleString pathArg) { +JSObject* ModuleLoader::loadAndParse(JSContext* cx, HandleString pathArg, + JS::HandleObject moduleRequestArg) { Rooted<JSLinearString*> path(cx, JS_EnsureLinearString(cx, pathArg)); if (!path) { return nullptr; @@ -461,17 +463,40 @@ JSObject* ModuleLoader::loadAndParse(JSContext* cx, HandleString pathArg) { return nullptr; } - module = JS::CompileModule(cx, options, srcBuf); - if (!module) { - return nullptr; + JS::ModuleType moduleType = JS::ModuleType::JavaScript; + if (moduleRequestArg) { + Rooted<ModuleRequestObject*> moduleRequest( + cx, &moduleRequestArg->as<ModuleRequestObject>()); + if (!ModuleRequestObject::getModuleType(cx, moduleRequest, moduleType)) { + return nullptr; + } } - RootedObject info(cx, js::CreateScriptPrivate(cx, path)); - if (!info) { - return nullptr; - } + switch (moduleType) { + case JS::ModuleType::Unknown: + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_BAD_MODULE_TYPE); + return nullptr; + case JS::ModuleType::JavaScript: { + module = JS::CompileModule(cx, options, srcBuf); + if (!module) { + return nullptr; + } + + RootedObject info(cx, js::CreateScriptPrivate(cx, path)); + if (!info) { + return nullptr; + } - JS::SetModulePrivate(module, ObjectValue(*info)); + JS::SetModulePrivate(module, ObjectValue(*info)); + } break; + case JS::ModuleType::JSON: + module = JS::CompileJsonModule(cx, options, srcBuf); + if (!module) { + return nullptr; + } + break; + } if (!addModuleToRegistry(cx, path, module)) { return nullptr; diff --git a/js/src/shell/ModuleLoader.h b/js/src/shell/ModuleLoader.h index 276e199661..c61ca755bc 100644 --- a/js/src/shell/ModuleLoader.h +++ b/js/src/shell/ModuleLoader.h @@ -42,7 +42,8 @@ class ModuleLoader { static bool DynamicImportDelayRejected(JSContext* cx, unsigned argc, Value* vp); - bool loadAndExecute(JSContext* cx, HandleString path, MutableHandleValue); + bool loadAndExecute(JSContext* cx, HandleString path, + HandleObject moduleRequestArg, MutableHandleValue); JSObject* resolveImportedModule(JSContext* cx, HandleValue referencingPrivate, HandleObject moduleRequest); bool populateImportMeta(JSContext* cx, HandleValue privateValue, @@ -58,7 +59,8 @@ class ModuleLoader { bool tryDynamicImport(JSContext* cx, HandleValue referencingPrivate, HandleObject moduleRequest, HandleObject promise, MutableHandleValue rval); - JSObject* loadAndParse(JSContext* cx, HandleString path); + JSObject* loadAndParse(JSContext* cx, HandleString path, + HandleObject moduleRequestArg); bool lookupModuleInRegistry(JSContext* cx, HandleString path, MutableHandleObject moduleOut); bool addModuleToRegistry(JSContext* cx, HandleString path, diff --git a/js/src/shell/ShellModuleObjectWrapper.cpp b/js/src/shell/ShellModuleObjectWrapper.cpp index 9f1b9fd59c..4e8c234e61 100644 --- a/js/src/shell/ShellModuleObjectWrapper.cpp +++ b/js/src/shell/ShellModuleObjectWrapper.cpp @@ -315,12 +315,12 @@ bool ShellModuleNativeWrapperGetter(JSContext* cx, const JS::CallArgs& args, DEFINE_GETTER_FUNCTIONS(ModuleRequestObject, specifier, StringOrNullValue, IdentFilter) -DEFINE_GETTER_FUNCTIONS(ModuleRequestObject, assertions, ObjectOrNullValue, +DEFINE_GETTER_FUNCTIONS(ModuleRequestObject, attributes, ObjectOrNullValue, IdentFilter) static const JSPropertySpec ShellModuleRequestObjectWrapper_accessors[] = { JS_PSG("specifier", ShellModuleRequestObjectWrapper_specifierGetter, 0), - JS_PSG("assertions", ShellModuleRequestObjectWrapper_assertionsGetter, 0), + JS_PSG("assertions", ShellModuleRequestObjectWrapper_attributesGetter, 0), JS_PS_END}; DEFINE_GETTER_FUNCTIONS(ImportEntry, moduleRequest, ObjectOrNullValue, diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 03e9e0c109..624b8217c3 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -11,6 +11,7 @@ #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_RELEASE_ASSERT, MOZ_CRASH #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" +#include "mozilla/Compression.h" #include "mozilla/DebugOnly.h" #include "mozilla/EnumSet.h" #include "mozilla/IntegerPrintfMacros.h" @@ -127,7 +128,7 @@ #include "js/ErrorReport.h" // JS::PrintError #include "js/Exception.h" // JS::StealPendingExceptionStack #include "js/experimental/CodeCoverage.h" // js::EnableCodeCoverage -#include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::CompilationStorage +#include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil #include "js/experimental/CTypes.h" // JS::InitCTypesClass #include "js/experimental/Intl.h" // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo @@ -213,6 +214,8 @@ #include "vm/Realm-inl.h" #include "vm/Stack-inl.h" +#undef compress + using namespace js; using namespace js::cli; using namespace js::shell; @@ -637,15 +640,11 @@ void OffThreadJob::run() { switch (kind_) { case Kind::CompileScript: { - JS::CompilationStorage compileStorage; - stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_, - compileStorage); + stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_); break; } case Kind::CompileModule: { - JS::CompilationStorage compileStorage; - stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_, - compileStorage); + stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_); break; } case Kind::Decode: { @@ -724,12 +723,6 @@ bool shell::enableWasm = false; bool shell::enableSharedMemory = SHARED_MEMORY_DEFAULT; bool shell::enableWasmBaseline = false; bool shell::enableWasmOptimizing = false; - -#define WASM_FEATURE(NAME, _, STAGE, ...) \ - bool shell::enableWasm##NAME = STAGE != WasmFeatureStage::Experimental; -JS_FOR_WASM_FEATURES(WASM_FEATURE); -#undef WASM_FEATURE - bool shell::enableWasmVerbose = false; bool shell::enableTestWasmAwaitTier2 = false; bool shell::enableSourcePragmas = true; @@ -1538,7 +1531,6 @@ static bool BoundToAsyncStack(JSContext* cx, unsigned argc, Value* vp) { } RootedString causeString(cx, ToString(cx, v)); if (!causeString) { - MOZ_ASSERT(cx->isExceptionPending()); return false; } @@ -9004,6 +8996,121 @@ static bool IsValidJSON(JSContext* cx, unsigned argc, Value* vp) { return true; } +// Quick file format for a LZ4 compressed file +static constexpr uint32_t LZ4MagicHeader = -1; +// A magic word and a length field +static constexpr size_t LZ4HeaderSize = sizeof(uint32_t) * 2; +static constexpr size_t LZ4MaxSize = UINT32_MAX; + +static bool CompressLZ4(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (!args.get(0).isObject() || + !args.get(0).toObject().is<ArrayBufferObject>()) { + ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer"); + return false; + } + + JS::Rooted<ArrayBufferObject*> bytes( + cx, &args.get(0).toObject().as<ArrayBufferObject>()); + size_t byteLength = bytes->byteLength(); + if (byteLength > LZ4MaxSize) { + ReportOutOfMemory(cx); + return false; + } + + // Create a buffer big enough for the header and the max amount of compressed + // bytes. + size_t outputCapacity = + LZ4HeaderSize + mozilla::Compression::LZ4::maxCompressedSize(byteLength); + + mozilla::UniquePtr<void, JS::FreePolicy> output(js_malloc(outputCapacity)); + if (!output) { + ReportOutOfMemory(cx); + return false; + } + + // Write the magic header word and decompressed size in bytes. + ((uint32_t*)(output.get()))[0] = LZ4MagicHeader; + ((uint32_t*)(output.get()))[1] = byteLength; + + // Compress the bytes into the output + char* compressedBytesStart = ((char*)output.get()) + LZ4HeaderSize; + size_t compressedBytesLength = mozilla::Compression::LZ4::compress( + (const char*)bytes->dataPointer(), byteLength, compressedBytesStart); + size_t outputLength = compressedBytesLength + LZ4HeaderSize; + + // Create an ArrayBuffer wrapping the compressed bytes + JSObject* outputArrayBuffer = + NewArrayBufferWithContents(cx, outputLength, std::move(output)); + if (!outputArrayBuffer) { + return false; + } + + args.rval().setObject(*outputArrayBuffer); + return true; +} + +static bool DecompressLZ4(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (!args.get(0).isObject() || + !args.get(0).toObject().is<ArrayBufferObject>()) { + ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer"); + return false; + } + + JS::Rooted<ArrayBufferObject*> bytes( + cx, &args.get(0).toObject().as<ArrayBufferObject>()); + size_t byteLength = bytes->byteLength(); + if (byteLength < LZ4HeaderSize) { + JS_ReportErrorASCII(cx, "Invalid LZ4 buffer"); + return false; + } + + // Check the magic header and get the decompressed byte length. + uint32_t magicHeader = ((uint32_t*)(bytes->dataPointer()))[0]; + uint32_t decompressedBytesLength = ((uint32_t*)(bytes->dataPointer()))[1]; + if (magicHeader != LZ4MagicHeader) { + JS_ReportErrorASCII(cx, "Invalid magic header"); + return false; + } + + // Allocate a buffer to store the decompressed bytes. + mozilla::UniquePtr<void, JS::FreePolicy> decompressedBytes( + js_malloc(decompressedBytesLength)); + if (!decompressedBytes) { + ReportOutOfMemory(cx); + return false; + } + + // Decompress the bytes into the output + const char* compressedBytesStart = + ((const char*)bytes->dataPointer()) + LZ4HeaderSize; + size_t compressedBytesLength = byteLength - LZ4HeaderSize; + size_t actualDecompressedBytesLength = 0; + if (!mozilla::Compression::LZ4::decompress( + compressedBytesStart, compressedBytesLength, + (char*)decompressedBytes.get(), decompressedBytesLength, + &actualDecompressedBytesLength) || + actualDecompressedBytesLength != decompressedBytesLength) { + JS_ReportErrorASCII(cx, "Invalid LZ4 buffer"); + return false; + } + + // Create an ArrayBuffer wrapping the decompressed bytes + JSObject* outputArrayBuffer = NewArrayBufferWithContents( + cx, decompressedBytesLength, std::move(decompressedBytes)); + if (!outputArrayBuffer) { + return false; + } + + args.rval().setObject(*outputArrayBuffer); + return true; +} + // clang-format off static const JSFunctionSpecWithHelp shell_functions[] = { JS_FN_HELP("options", Options, 0, 0, @@ -9675,6 +9782,14 @@ JS_FN_HELP("createUserArrayBuffer", CreateUserArrayBuffer, 1, 0, "isValidJSON(source)", " Returns true if the given source is valid JSON."), + JS_FN_HELP("compressLZ4", CompressLZ4, 1, 0, +"compressLZ4(bytes)", +" Return a compressed copy of bytes using LZ4."), + + JS_FN_HELP("decompressLZ4", DecompressLZ4, 1, 0, +"decompressLZ4(bytes)", +" Return a decompressed copy of bytes using LZ4."), + JS_FS_HELP_END }; // clang-format on @@ -11015,9 +11130,6 @@ static void SetWorkerContextOptions(JSContext* cx) { .setWasm(enableWasm) .setWasmBaseline(enableWasmBaseline) .setWasmIon(enableWasmOptimizing) -#define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME) - JS_FOR_WASM_FEATURES(WASM_FEATURE) -#undef WASM_FEATURE .setWasmVerbose(enableWasmVerbose) .setTestWasmAwaitTier2(enableTestWasmAwaitTier2) @@ -11453,21 +11565,34 @@ static bool ParsePrefValue(const char* name, const char* val, T* result) { } } -static bool SetJSPref(const char* pref) { - const char* assign = strchr(pref, '='); - if (!assign) { - fprintf(stderr, "Missing '=' for --setpref\n"); - return false; +static bool SetJSPrefToTrueForBool(const char* name) { + // Search for a matching pref and try to set it to a default value for the + // type. +#define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ + if (strcmp(name, NAME) == 0) { \ + if constexpr (std::is_same_v<TYPE, bool>) { \ + JS::Prefs::SETTER(true); \ + return true; \ + } else { \ + fprintf(stderr, "Pref %s must have a value specified.\n", name); \ + return false; \ + } \ } + FOR_EACH_JS_PREF(CHECK_PREF) +#undef CHECK_PREF - size_t nameLen = assign - pref; - const char* valStart = assign + 1; // Skip '='. + // Nothing matched, return false + fprintf(stderr, "Invalid pref name: %s\n", name); + return false; +} - // Search for a matching pref and try to set it. +static bool SetJSPrefToValue(const char* name, size_t nameLen, + const char* value) { + // Search for a matching pref and try to set it to the provided value. #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ - if (nameLen == strlen(NAME) && memcmp(pref, NAME, strlen(NAME)) == 0) { \ + if (nameLen == strlen(NAME) && memcmp(name, NAME, strlen(NAME)) == 0) { \ TYPE v; \ - if (!ParsePrefValue<TYPE>(NAME, valStart, &v)) { \ + if (!ParsePrefValue<TYPE>(NAME, value, &v)) { \ return false; \ } \ JS::Prefs::SETTER(v); \ @@ -11476,10 +11601,29 @@ static bool SetJSPref(const char* pref) { FOR_EACH_JS_PREF(CHECK_PREF) #undef CHECK_PREF - fprintf(stderr, "Invalid pref name: %s\n", pref); + // Nothing matched, return false + fprintf(stderr, "Invalid pref name: %s\n", name); return false; } +static bool SetJSPref(const char* pref) { + const char* assign = strchr(pref, '='); + if (!assign) { + if (!SetJSPrefToTrueForBool(pref)) { + return false; + } + return true; + } + + size_t nameLen = assign - pref; + const char* valStart = assign + 1; // Skip '='. + + if (!SetJSPrefToValue(pref, nameLen, valStart)) { + return false; + } + return true; +} + static void ListJSPrefs() { auto printPref = [](const char* name, auto defaultVal) { using T = decltype(defaultVal); @@ -11832,20 +11976,8 @@ bool InitOptionParser(OptionParser& op) { !op.addBoolOption('\0', "test-wasm-await-tier2", "Forcibly activate tiering and block " "instantiation on completion of tier2") || -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \ - !op.addBoolOption('\0', "no-wasm-" SHELL, \ - STAGE == WasmFeatureStage::Experimental \ - ? "No-op." \ - : "Disable wasm " SHELL " feature.") || \ - !op.addBoolOption('\0', "wasm-" SHELL, \ - STAGE == WasmFeatureStage::Experimental \ - ? "Enable wasm " SHELL " feature." \ - : "No-op.") || - JS_FOR_WASM_FEATURES(WASM_FEATURE) -#undef WASM_FEATURE - !op.addBoolOption('\0', "no-native-regexp", - "Disable native regexp compilation") || + !op.addBoolOption('\0', "no-native-regexp", + "Disable native regexp compilation") || !op.addIntOption( '\0', "regexp-warmup-threshold", "COUNT", "Wait for COUNT invocations before compiling regexps to native code " @@ -12206,14 +12338,29 @@ bool InitOptionParser(OptionParser& op) { #endif !op.addStringOption('\0', "telemetry-dir", "[directory]", "Output telemetry results in a directory") || - !op.addMultiStringOption('\0', "setpref", "name=val", - "Set the value of a JS pref. Use --list-prefs " + !op.addMultiStringOption('P', "setpref", "name[=val]", + "Set the value of a JS pref. The value may " + "be omitted for boolean prefs, in which case " + "they default to true. Use --list-prefs " "to print all pref names.") || !op.addBoolOption( '\0', "list-prefs", "Print list of prefs that can be set with --setpref.") || !op.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan", - "Use fdlibm for Math.sin, Math.cos, and Math.tan")) { + "Use fdlibm for Math.sin, Math.cos, and Math.tan") || + !op.addBoolOption('\0', "wasm-gc", "Enable WebAssembly gc proposal.") || + !op.addBoolOption('\0', "wasm-relaxed-simd", + "Enable WebAssembly relaxed-simd proposal.") || + !op.addBoolOption('\0', "wasm-multi-memory", + "Enable WebAssembly multi-memory proposal.") || + !op.addBoolOption('\0', "wasm-memory-control", + "Enable WebAssembly memory-control proposal.") || + !op.addBoolOption('\0', "wasm-memory64", + "Enable WebAssembly memory64 proposal.") || + !op.addBoolOption('\0', "wasm-tail-calls", + "Enable WebAssembly tail-calls proposal.") || + !op.addBoolOption('\0', "wasm-js-string-builtins", + "Enable WebAssembly js-string-builtins proposal.")) { return false; } @@ -12234,36 +12381,60 @@ bool SetGlobalOptionsPreJSInit(const OptionParser& op) { // Override pref values for prefs that have a custom shell flag. // If you're adding a new feature, consider using --setpref instead. - JS::Prefs::setAtStartup_array_grouping( - !op.getBoolOption("disable-array-grouping")); - JS::Prefs::setAtStartup_arraybuffer_transfer( - !op.getBoolOption("disable-arraybuffer-transfer")); - JS::Prefs::set_experimental_shadow_realms( - op.getBoolOption("enable-shadow-realms")); - JS::Prefs::setAtStartup_well_formed_unicode_strings( - !op.getBoolOption("disable-well-formed-unicode-strings")); + if (op.getBoolOption("disable-array-grouping")) { + JS::Prefs::setAtStartup_array_grouping(false); + } + if (op.getBoolOption("disable-arraybuffer-transfer")) { + JS::Prefs::setAtStartup_arraybuffer_transfer(false); + } + if (op.getBoolOption("enable-shadow-realms")) { + JS::Prefs::set_experimental_shadow_realms(true); + } + if (op.getBoolOption("disable-well-formed-unicode-strings")) { + JS::Prefs::setAtStartup_well_formed_unicode_strings(false); + } #ifdef NIGHTLY_BUILD - JS::Prefs::setAtStartup_experimental_arraybuffer_resizable( - op.getBoolOption("enable-arraybuffer-resizable")); - JS::Prefs::setAtStartup_experimental_sharedarraybuffer_growable( - op.getBoolOption("enable-arraybuffer-resizable")); - JS::Prefs::setAtStartup_experimental_iterator_helpers( - op.getBoolOption("enable-iterator-helpers")); - JS::Prefs::setAtStartup_experimental_new_set_methods( - op.getBoolOption("enable-new-set-methods")); - JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys( - op.getBoolOption("enable-symbols-as-weakmap-keys")); + if (op.getBoolOption("enable-arraybuffer-resizable")) { + JS::Prefs::setAtStartup_experimental_arraybuffer_resizable(true); + JS::Prefs::setAtStartup_experimental_sharedarraybuffer_growable(true); + } + if (op.getBoolOption("enable-iterator-helpers")) { + JS::Prefs::setAtStartup_experimental_iterator_helpers(true); + } + if (op.getBoolOption("enable-new-set-methods")) { + JS::Prefs::setAtStartup_experimental_new_set_methods(true); + } + if (op.getBoolOption("enable-symbols-as-weakmap-keys")) { + JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys(true); + } #endif - JS::Prefs::setAtStartup_weakrefs(!op.getBoolOption("disable-weak-refs")); + if (op.getBoolOption("disable-weak-refs")) { + JS::Prefs::setAtStartup_weakrefs(false); + } JS::Prefs::setAtStartup_experimental_weakrefs_expose_cleanupSome(true); - JS::Prefs::setAtStartup_destructuring_fuse( - !op.getBoolOption("disable-destructuring-fuse")); + if (op.getBoolOption("disable-destructuring-fuse")) { + JS::Prefs::setAtStartup_destructuring_fuse(false); + } + if (op.getBoolOption("disable-property-error-message-fix")) { + JS::Prefs::setAtStartup_property_error_message_fix(false); + } + JS::Prefs::set_use_fdlibm_for_sin_cos_tan( op.getBoolOption("use-fdlibm-for-sin-cos-tan")); - JS::Prefs::setAtStartup_property_error_message_fix( - !op.getBoolOption("disable-property-error-message-fix")); + + if (op.getBoolOption("wasm-gc") || op.getBoolOption("wasm-relaxed-simd") || + op.getBoolOption("wasm-multi-memory") || + op.getBoolOption("wasm-memory-control") || + op.getBoolOption("wasm-memory64") || + op.getBoolOption("wasm-tail-calls") || + op.getBoolOption("wasm-js-string-builtins")) { + fprintf( + stderr, + "Wasm shell flags are now using prefs, use -P wasm_feature instead.\n"); + return false; + } if (op.getBoolOption("list-prefs")) { ListJSPrefs(); @@ -12556,17 +12727,6 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { } } -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \ - if (STAGE == WasmFeatureStage::Experimental) { \ - enableWasm##NAME = op.getBoolOption("wasm-" SHELL); \ - } else { \ - enableWasm##NAME = !op.getBoolOption("no-wasm-" SHELL); \ - } - - JS_FOR_WASM_FEATURES(WASM_FEATURE); -#undef WASM_FEATURE - enableWasmVerbose = op.getBoolOption("wasm-verbose"); enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2"); @@ -12575,11 +12735,7 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { .setWasm(enableWasm) .setWasmForTrustedPrinciples(enableWasm) .setWasmBaseline(enableWasmBaseline) - .setWasmIon(enableWasmOptimizing) -#define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME) - JS_FOR_WASM_FEATURES(WASM_FEATURE) -#undef WASM_FEATURE - ; + .setWasmIon(enableWasmOptimizing); #ifndef __wasi__ // This must be set before self-hosted code is initialized, as self-hosted @@ -12598,18 +12754,12 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { // Also the following are to be propagated. const char* to_propagate[] = { -# define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \ - STAGE == WasmFeatureStage::Experimental ? "--wasm-" SHELL \ - : "--no-wasm-" SHELL, - JS_FOR_WASM_FEATURES(WASM_FEATURE) -# undef WASM_FEATURE // Compiler selection options "--test-wasm-await-tier2", - NULL}; - for (const char** p = &to_propagate[0]; *p; p++) { - if (op.getBoolOption(&(*p)[2] /* 2 => skip the leading '--' */)) { - if (!sCompilerProcessFlags.append(*p)) { + }; + for (const char* p : to_propagate) { + if (op.getBoolOption(p + 2 /* 2 => skip the leading '--' */)) { + if (!sCompilerProcessFlags.append(p)) { return false; } } diff --git a/js/src/shell/jsshell.h b/js/src/shell/jsshell.h index 6ffe8ff236..9cbf4505f9 100644 --- a/js/src/shell/jsshell.h +++ b/js/src/shell/jsshell.h @@ -112,11 +112,6 @@ extern bool enableWasm; extern bool enableSharedMemory; extern bool enableWasmBaseline; extern bool enableWasmOptimizing; - -#define WASM_FEATURE(NAME, ...) extern bool enableWasm##NAME; -JS_FOR_WASM_FEATURES(WASM_FEATURE); -#undef WASM_FEATURE - extern bool enableWasmVerbose; extern bool enableTestWasmAwaitTier2; extern bool enableSourcePragmas; diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 72d078dc40..55ffc65f6f 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -10,12 +10,6 @@ skip script non262/String/normalize-generateddata-input.js # input data for othe slow script test262/built-ins/decodeURI/S15.1.3.1_A2.5_T1.js slow script test262/built-ins/decodeURIComponent/S15.1.3.2_A2.5_T1.js -# Windows10-aarch64 fails certain tests. -# https://bugzilla.mozilla.org/show_bug.cgi?id=1526003 -# https://bugzilla.mozilla.org/show_bug.cgi?id=1526012 -skip-if((xulRuntime.XPCOMABI.match(/aarch64/))&&(xulRuntime.OS=="WINNT")) script non262/Math/fround.js -skip-if((xulRuntime.XPCOMABI.match(/aarch64/))&&(xulRuntime.OS=="WINNT")) script non262/Math/log2-approx.js - ########################################################################### # Generated jstests.list for test262 when inline |reftest| isn't possible # @@ -626,13 +620,6 @@ skip script test262/built-ins/RegExp/unicodeSets/generated/rgi-emoji-15.1.js skip script test262/intl402/DateTimeFormat/timezone-not-canonicalized.js skip script test262/intl402/DateTimeFormat/timezone-case-insensitive.js -# Resolved options reordered. -# https://github.com/tc39/ecma402/pull/811 -skip script test262/intl402/NumberFormat/constructor-option-read-order.js -skip script test262/intl402/NumberFormat/prototype/resolvedOptions/return-keys-order-default.js -skip script test262/intl402/PluralRules/constructor-option-read-order.js -skip script test262/intl402/PluralRules/prototype/resolvedOptions/return-keys-order-default.js - # Requires Unicode 15.1 # https://github.com/tc39/test262/pull/3947 # https://bugzilla.mozilla.org/show_bug.cgi?id=1859752 diff --git a/js/src/tests/lib/tasks_adb_remote.py b/js/src/tests/lib/tasks_adb_remote.py index 2d2739a281..1ddc6baa08 100644 --- a/js/src/tests/lib/tasks_adb_remote.py +++ b/js/src/tests/lib/tasks_adb_remote.py @@ -160,7 +160,7 @@ do_test() # # The timeout command send a SIGTERM signal, which should return 143 # (=128+15). However, due to a bug in tinybox, it returns 142. - if test \( $rc -eq 143 -o $rc -eq 142 \) -a $attempt -lt {retry}; then + if test \\( $rc -eq 143 -o $rc -eq 142 \\) -a $attempt -lt {retry}; then echo '\\n{tag}RETRY='$rc,$time attempt=$((attempt + 1)) do_test $idx $attempt "$@" diff --git a/js/src/tests/non262/Date/dashed-date.js b/js/src/tests/non262/Date/dashed-date.js index e479904098..d7b4692e7f 100644 --- a/js/src/tests/non262/Date/dashed-date.js +++ b/js/src/tests/non262/Date/dashed-date.js @@ -46,12 +46,10 @@ const tests = [ // ==== Date followed by hour and TZ ==== ["24-Apr-2023 12:34:56", "2023-04-24T12:34:56"], - ["24-Apr-2023 Mon 12:34:56", "2023-04-24T12:34:56"], ["24-Apr-2023 (Mon) 12:34:56", "2023-04-24T12:34:56"], ["24-Apr-2023(Mon)12:34:56", "2023-04-24T12:34:56"], ["24-Apr-2023,12:34:56", "2023-04-24T12:34:56"], - ["24-Apr-2023,Mon 12:34:56", "2023-04-24T12:34:56"], ["24-Apr-2023 12:34:56 GMT", "2023-04-24T12:34:56Z"], ["24-Apr-2023 12:34:56 +04", "2023-04-24T12:34:56+04:00"], @@ -145,6 +143,10 @@ const invalidTests = [ "24-Apr-2312+10:13:14", "24-Apr-2312=10:13:14", "24-Apr-2312?10:13:14", + + // Late weekday + "24-Apr-2023 Mon 12:34:56", + "24-Apr-2023,Mon 12:34:56", ]; for (const testString of invalidTests) { diff --git a/js/src/tests/non262/Date/parse-keywords.js b/js/src/tests/non262/Date/parse-keywords.js index c834bbfe0b..ba3b50b71b 100644 --- a/js/src/tests/non262/Date/parse-keywords.js +++ b/js/src/tests/non262/Date/parse-keywords.js @@ -12,7 +12,6 @@ const accepted = { "Sep 26 1995 10:00 am": "1995-09-26T10:00:00", "Sep 26 1995 10:00 AM": "1995-09-26T10:00:00", "Sep 26 1995 10:00 pm": "1995-09-26T22:00:00", - "Sep 26 Thurs 1995 Mon 10:thursday:00": "1995-09-26T10:00:00", }; const rejected = [ "Sep 26 1995 G", @@ -22,6 +21,12 @@ const rejected = [ "Sep 26 1995 10:00 a", "Sep 26 1995 10:00 p", "0/zx", + + // Late weekday + "Sep 26 Thurs 1995 10:00", + "Sep 26 1995 Thurs 10:00", + "Sep 26 1995 10:Thurs:00", + "Sep 26 1995 10:00 Thurs", ]; for (const [test, expected] of Object.entries(accepted)) { diff --git a/js/src/tests/non262/Intl/ListFormat/unit-type.js b/js/src/tests/non262/Intl/ListFormat/unit-type.js index 8c76677865..4f1c7321cf 100644 --- a/js/src/tests/non262/Intl/ListFormat/unit-type.js +++ b/js/src/tests/non262/Intl/ListFormat/unit-type.js @@ -1,4 +1,4 @@ -// |reftest| skip -- "unit" type currently not supported +// |reftest| skip-if(!this.hasOwnProperty('Intl')) const {Element, Literal} = ListFormatParts; const styles = ["long", "short", "narrow"]; @@ -38,7 +38,6 @@ const styles = ["long", "short", "narrow"]; const testData = { "ar": { long: [Element("A"), Literal(" Ùˆ"), Element("B")], - narrow: [Element("A"), Literal("ØŒ "), Element("B")], }, "de": { long: [Element("A"), Literal(", "), Element("B")], @@ -90,7 +89,7 @@ const styles = ["long", "short", "narrow"]; // non-ASCII case "ar": { long: [Element("A"), Literal("ØŒ Ùˆ"), Element("B"), Literal("ØŒ Ùˆ"), Element("C"), Literal("ØŒ Ùˆ"), Element("D")], - narrow: [Element("A"), Literal("ØŒ "), Element("B"), Literal("ØŒ "), Element("C"), Literal("ØŒ "), Element("D")], + narrow: [Element("A"), Literal(" Ùˆ"), Element("B"), Literal(" Ùˆ"), Element("C"), Literal(" Ùˆ"), Element("D")], }, // all values are equal diff --git a/js/src/tests/non262/String/make-normalize-generateddata-input.py b/js/src/tests/non262/String/make-normalize-generateddata-input.py index 5c3d2d3e44..086275e67d 100644 --- a/js/src/tests/non262/String/make-normalize-generateddata-input.py +++ b/js/src/tests/non262/String/make-normalize-generateddata-input.py @@ -18,7 +18,7 @@ def to_code_list(codes): def convert(dir): - ver_pat = re.compile("NormalizationTest-([0-9\.]+)\.txt") + ver_pat = re.compile(r"NormalizationTest-([0-9\.]+)\.txt") part_pat = re.compile("^@(Part([0-9]+) .+)$") test_pat = re.compile( "^([0-9A-Fa-f ]+);([0-9A-Fa-f ]+);([0-9A-Fa-f ]+);([0-9A-Fa-f ]+);([0-9A-Fa-f ]+);$" diff --git a/js/src/tests/non262/argumentsLengthOpt.js b/js/src/tests/non262/argumentsLengthOpt.js new file mode 100644 index 0000000000..16385fa5a4 --- /dev/null +++ b/js/src/tests/non262/argumentsLengthOpt.js @@ -0,0 +1,87 @@ +// Test cases for arguments.length optimization. + +function f1() { + return arguments.length; +} + +function f2(a, b, c) { + return arguments.length; +} + +// arrow functions don't have their own arguments, and so capture the enclosing +// scope. +function f3(a, b, c, d) { + return (() => arguments.length)(); +} + +// Test a function which mutates arguments.length +function f4(a, b, c, d) { + arguments.length = 42; + return arguments.length; +} + +// Manually read out arguments; should disable the length opt +function f5() { + for (var i = 0; i < arguments.length; i++) { + if (arguments[i] == 10) { return true } + } + return false; +} + +function f6() { + function inner() { + return arguments.length; + } + return inner(1, 2, 3); +} + +// edge cases of the arguments bindings: +function f7() { + var arguments = 42; + return arguments; +} + +function f8() { + var arguments = [1, 2]; + return arguments.length; +} + +function f9() { + eval("arguments.length = 42"); + return arguments.length; +} + +function test() { + assertEq(f1(), 0); + assertEq(f1(1), 1); + assertEq(f1(1, 2), 2); + assertEq(f1(1, 2, 3), 3); + + assertEq(f2(), 0); + assertEq(f2(1, 2, 3), 3); + + assertEq(f3(), 0); + assertEq(f3(1, 2, 3), 3); + + assertEq(f4(), 42); + assertEq(f4(1, 2, 3), 42); + + assertEq(f5(), false); + assertEq(f5(1, 2, 3, 10), true); + assertEq(f5(1, 2, 3, 10, 20), true); + assertEq(f5(1, 2, 3, 9, 20, 30), false); + + assertEq(f6(), 3) + assertEq(f6(1, 2, 3, 4), 3) + + assertEq(f7(), 42); + + assertEq(f8(), 2); + + assertEq(f9(), 42); +} + +test(); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/non262/extensions/typedarray-set-neutering.js b/js/src/tests/non262/extensions/typedarray-set-detach.js index 23df8158c5..23df8158c5 100644 --- a/js/src/tests/non262/extensions/typedarray-set-neutering.js +++ b/js/src/tests/non262/extensions/typedarray-set-detach.js diff --git a/js/src/tests/non262/reflect-parse/argumentsReflect.js b/js/src/tests/non262/reflect-parse/argumentsReflect.js new file mode 100644 index 0000000000..69a8624acc --- /dev/null +++ b/js/src/tests/non262/reflect-parse/argumentsReflect.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Test reflect.parse on a function with arguments.length +let ast = Reflect.parse(`function f10() { + return arguments.length; +}`); + +assertEq(ast.body[0].body.body[0].argument.object.type, "Identifier"); +assertEq(ast.body[0].body.body[0].argument.object.name, "arguments"); +assertEq(ast.body[0].body.body[0].argument.property.type, "Identifier"); +assertEq(ast.body[0].body.body[0].argument.property.name, "length"); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/shell/compression.js b/js/src/tests/shell/compression.js new file mode 100644 index 0000000000..4836d832fb --- /dev/null +++ b/js/src/tests/shell/compression.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Compressed buffers must have magic header and length +assertThrows(() => decompressLZ4(new ArrayBuffer())); + +// Compress and decompress take an array buffer, not arrays +assertThrows(() => compressLZ4([])); +assertThrows(() => decompressLZ4([])); + +// Round trip several buffers +let tests = [ + new Uint8Array([]), + new Uint8Array([0]), + new Uint8Array([0, 1, 2, 3]), + new Uint8Array(1000), +]; + +for (let test of tests) { + let original = test.buffer; + + let compressed = compressLZ4(original); + assertEq(compressed instanceof ArrayBuffer, true); + + let decompressed = decompressLZ4(compressed); + assertEq(decompressed instanceof ArrayBuffer, true); + + assertEqArray(new Uint8Array(original), new Uint8Array(decompressed)); +} + +reportCompare(true,true); diff --git a/js/src/tests/test262-export.py b/js/src/tests/test262-export.py index 477db883ea..dc30776973 100755 --- a/js/src/tests/test262-export.py +++ b/js/src/tests/test262-export.py @@ -237,7 +237,7 @@ def mergeMeta(reftest, frontmatter, includes): if info: # Open some space in an existing info text if "info" in frontmatter: - frontmatter["info"] += "\n\n \%s" % info + frontmatter["info"] += "\n\n \\%s" % info else: frontmatter["info"] = info diff --git a/js/src/tests/test262-update.py b/js/src/tests/test262-update.py index 8578f57d68..fc6fa62c45 100755 --- a/js/src/tests/test262-update.py +++ b/js/src/tests/test262-update.py @@ -23,7 +23,6 @@ UNSUPPORTED_FEATURES = set( "Intl.DurationFormat", # Bug 1648139 "Atomics.waitAsync", # Bug 1467846 "legacy-regexp", # Bug 1306461 - "json-modules", # Bug 1670176 "regexp-duplicate-named-groups", # Bug 1773135 "json-parse-with-source", # Bug 1658310 "set-methods", # Bug 1805038 diff --git a/js/src/tests/test262/language/expressions/dynamic-import/import-assertions/2nd-param-assert-enumeration-enumerable.js b/js/src/tests/test262/language/expressions/dynamic-import/import-assertions/2nd-param-assert-enumeration-enumerable.js index a4bc49e2de..1c80a751e4 100644 --- a/js/src/tests/test262/language/expressions/dynamic-import/import-assertions/2nd-param-assert-enumeration-enumerable.js +++ b/js/src/tests/test262/language/expressions/dynamic-import/import-assertions/2nd-param-assert-enumeration-enumerable.js @@ -1,4 +1,4 @@ -// |reftest| skip async -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) async -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/expressions/dynamic-import/import-attributes/2nd-param-with-enumeration-enumerable.js b/js/src/tests/test262/language/expressions/dynamic-import/import-attributes/2nd-param-with-enumeration-enumerable.js index 68f5d91899..61967b8b7f 100644 --- a/js/src/tests/test262/language/expressions/dynamic-import/import-attributes/2nd-param-with-enumeration-enumerable.js +++ b/js/src/tests/test262/language/expressions/dynamic-import/import-attributes/2nd-param-with-enumeration-enumerable.js @@ -1,4 +1,4 @@ -// |reftest| skip async -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) async -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-extensibility-array.js b/js/src/tests/test262/language/import/import-assertions/json-extensibility-array.js index bcd7f27f0f..8a20170d9b 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-extensibility-array.js +++ b/js/src/tests/test262/language/import/import-assertions/json-extensibility-array.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-extensibility-object.js b/js/src/tests/test262/language/import/import-assertions/json-extensibility-object.js index bf68fb37f2..99ea5cca86 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-extensibility-object.js +++ b/js/src/tests/test262/language/import/import-assertions/json-extensibility-object.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-idempotency.js b/js/src/tests/test262/language/import/import-assertions/json-idempotency.js index 8b7e7fc85d..9509e199ef 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-idempotency.js +++ b/js/src/tests/test262/language/import/import-assertions/json-idempotency.js @@ -1,4 +1,4 @@ -// |reftest| skip module async -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module async -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-invalid.js b/js/src/tests/test262/language/import/import-assertions/json-invalid.js index a4aab457d3..bfe57e645a 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-invalid.js +++ b/js/src/tests/test262/language/import/import-assertions/json-invalid.js @@ -1,4 +1,4 @@ -// |reftest| skip error:SyntaxError module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) error:SyntaxError module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-named-bindings.js b/js/src/tests/test262/language/import/import-assertions/json-named-bindings.js index 5d2f5faabb..bf506c576d 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-named-bindings.js +++ b/js/src/tests/test262/language/import/import-assertions/json-named-bindings.js @@ -1,4 +1,4 @@ -// |reftest| skip error:SyntaxError module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) error:SyntaxError module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-value-array.js b/js/src/tests/test262/language/import/import-assertions/json-value-array.js index ffc2061793..5a67cb51ef 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-value-array.js +++ b/js/src/tests/test262/language/import/import-assertions/json-value-array.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-value-boolean.js b/js/src/tests/test262/language/import/import-assertions/json-value-boolean.js index cd3425681d..55c334e89b 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-value-boolean.js +++ b/js/src/tests/test262/language/import/import-assertions/json-value-boolean.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-value-null.js b/js/src/tests/test262/language/import/import-assertions/json-value-null.js index 14a7b89ef8..e8fb11bbb2 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-value-null.js +++ b/js/src/tests/test262/language/import/import-assertions/json-value-null.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-value-number.js b/js/src/tests/test262/language/import/import-assertions/json-value-number.js index 9fc19d060a..8e66e646c4 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-value-number.js +++ b/js/src/tests/test262/language/import/import-assertions/json-value-number.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-value-object.js b/js/src/tests/test262/language/import/import-assertions/json-value-object.js index 0a042d45c3..7436ae242f 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-value-object.js +++ b/js/src/tests/test262/language/import/import-assertions/json-value-object.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-value-string.js b/js/src/tests/test262/language/import/import-assertions/json-value-string.js index a02e3381a4..6fc2e1a64f 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-value-string.js +++ b/js/src/tests/test262/language/import/import-assertions/json-value-string.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-assertions/json-via-namespace.js b/js/src/tests/test262/language/import/import-assertions/json-via-namespace.js index c4142df74b..bbf4acf71e 100644 --- a/js/src/tests/test262/language/import/import-assertions/json-via-namespace.js +++ b/js/src/tests/test262/language/import/import-assertions/json-via-namespace.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-assertions) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-extensibility-array.js b/js/src/tests/test262/language/import/import-attributes/json-extensibility-array.js index c654861c19..1f59154309 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-extensibility-array.js +++ b/js/src/tests/test262/language/import/import-attributes/json-extensibility-array.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-extensibility-object.js b/js/src/tests/test262/language/import/import-attributes/json-extensibility-object.js index 12883345a9..cf1ca9a810 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-extensibility-object.js +++ b/js/src/tests/test262/language/import/import-attributes/json-extensibility-object.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-idempotency.js b/js/src/tests/test262/language/import/import-attributes/json-idempotency.js index 1cadd19aae..fab2e9d1eb 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-idempotency.js +++ b/js/src/tests/test262/language/import/import-attributes/json-idempotency.js @@ -1,4 +1,4 @@ -// |reftest| skip module async -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module async -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-invalid.js b/js/src/tests/test262/language/import/import-attributes/json-invalid.js index 4e121f368a..fe2f2cd4bd 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-invalid.js +++ b/js/src/tests/test262/language/import/import-attributes/json-invalid.js @@ -1,4 +1,4 @@ -// |reftest| skip error:SyntaxError module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) error:SyntaxError module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-named-bindings.js b/js/src/tests/test262/language/import/import-attributes/json-named-bindings.js index a45e97ce54..2654cd6d7a 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-named-bindings.js +++ b/js/src/tests/test262/language/import/import-attributes/json-named-bindings.js @@ -1,4 +1,4 @@ -// |reftest| skip error:SyntaxError module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) error:SyntaxError module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-value-array.js b/js/src/tests/test262/language/import/import-attributes/json-value-array.js index 41fa87ca79..c7a7e28ff0 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-value-array.js +++ b/js/src/tests/test262/language/import/import-attributes/json-value-array.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-value-boolean.js b/js/src/tests/test262/language/import/import-attributes/json-value-boolean.js index 91d62565f2..5a9b7a686e 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-value-boolean.js +++ b/js/src/tests/test262/language/import/import-attributes/json-value-boolean.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-value-null.js b/js/src/tests/test262/language/import/import-attributes/json-value-null.js index a1c1ff35f5..ea1e95c10a 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-value-null.js +++ b/js/src/tests/test262/language/import/import-attributes/json-value-null.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-value-number.js b/js/src/tests/test262/language/import/import-attributes/json-value-number.js index 2bd1c60270..bb14011524 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-value-number.js +++ b/js/src/tests/test262/language/import/import-attributes/json-value-number.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-value-object.js b/js/src/tests/test262/language/import/import-attributes/json-value-object.js index 6c1b6d36f1..b9105f0818 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-value-object.js +++ b/js/src/tests/test262/language/import/import-attributes/json-value-object.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-value-string.js b/js/src/tests/test262/language/import/import-attributes/json-value-string.js index 4b938405b2..fff0ed3e3a 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-value-string.js +++ b/js/src/tests/test262/language/import/import-attributes/json-value-string.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/tests/test262/language/import/import-attributes/json-via-namespace.js b/js/src/tests/test262/language/import/import-attributes/json-via-namespace.js index a115b30412..d7c5ce332c 100644 --- a/js/src/tests/test262/language/import/import-attributes/json-via-namespace.js +++ b/js/src/tests/test262/language/import/import-attributes/json-via-namespace.js @@ -1,4 +1,4 @@ -// |reftest| skip module -- json-modules is not supported +// |reftest| shell-option(--enable-import-attributes) skip-if(!xulRuntime.shell) module -- requires shell-options // Copyright (C) 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- diff --git a/js/src/util/StructuredSpewer.cpp b/js/src/util/StructuredSpewer.cpp index f74570ab0c..72f483c6b5 100644 --- a/js/src/util/StructuredSpewer.cpp +++ b/js/src/util/StructuredSpewer.cpp @@ -183,54 +183,46 @@ void StructuredSpewer::parseSpewFlags(const char* flags) { } if (ContainsFlag(flags, "help")) { + // clang-format off printf( - "\n" - "usage: SPEW=option,option,... where options can be:\n" - "\n" - " help Dump this help message\n" - " channel Enable the selected channel from below, " - "if\n" - " more than one channel is specified, then " - "the\n" - " channel will be set whichever specified " - "filter\n" - " comes first in STRUCTURED_CHANNEL_LIST." - " AtStartup Enable spewing at browser startup instead\n" - " of when gecko profiling starts." - "\n" - " Channels: \n" - "\n" - // List Channels - " BaselineICStats Dump the IC Entry counters during Ion " - "analysis\n" - " ScriptStats Dump statistics collected by tracelogger " - "that\n" - " is aggregated by script. Requires\n" - " JS_TRACE_LOGGING=1\n" - " CacheIRHealthReport Dump the CacheIR information and " - "associated " - "rating\n" - // End Channel list - "\n\n" - "By default output goes to a file called spew_output.$PID.$THREAD\n" - "\n" - "Further control of the spewer can be accomplished with the below\n" - "environment variables:\n" - "\n" - " SPEW_FILE: Selects the file to write to. An absolute path.\n" - "\n" - " SPEW_FILTER: A string which is matched against 'signature'\n" - " constructed from a JSScript, currently connsisting of \n" - " filename:line:col.\n" - "\n" - " A JSScript matches the filter string is found in the\n" - " signature\n" - "\n" - " SPEW_UPLOAD: If this variable is set as well as MOZ_UPLOAD_DIR,\n" - " output goes to $MOZ_UPLOAD_DIR/spew_output* to ease usage\n" - " with Treeherder.\n" + "\n" + "usage: SPEW=option,option,... where options can be:\n" + "\n" + " help Dump this help message\n" + " channel Enable the selected channel from below, if\n" + " more than one channel is specified, then the\n" + " channel will be set whichever specified filter\n" + " comes first in STRUCTURED_CHANNEL_LIST.\n" + " AtStartup Enable spewing at browser startup instead\n" + " of when gecko profiling starts." + "\n" + " Channels: \n" + "\n" + // List Channels + " BaselineICStats Dump the IC Entry counters during Ion analysis\n" + " CacheIRHealthReport Dump the CacheIR information and associated rating\n" + // End Channel list + "\n\n" + "By default output goes to a file called spew_output.$PID.$THREAD\n" + "\n" + "Further control of the spewer can be accomplished with the below\n" + "environment variables:\n" + "\n" + " SPEW_FILE: Selects the file to write to. An absolute path.\n" + "\n" + " SPEW_FILTER: A string which is matched against 'signature'\n" + " constructed from a JSScript, currently connsisting of \n" + " filename:line:col.\n" + "\n" + " A JSScript matches the filter string is found in the\n" + " signature\n" + "\n" + " SPEW_UPLOAD: If this variable is set as well as MOZ_UPLOAD_DIR,\n" + " output goes to $MOZ_UPLOAD_DIR/spew_output* to ease usage\n" + " with Treeherder.\n" ); + // clang-format on exit(0); } } diff --git a/js/src/util/StructuredSpewer.h b/js/src/util/StructuredSpewer.h index 407dc34a25..613ef65b59 100644 --- a/js/src/util/StructuredSpewer.h +++ b/js/src/util/StructuredSpewer.h @@ -73,7 +73,6 @@ namespace js { # define STRUCTURED_CHANNEL_LIST(_) \ _(BaselineICStats) \ - _(ScriptStats) \ _(CacheIRHealthReport) // Structured spew channels @@ -187,8 +186,8 @@ class StructuredSpewer { // Globally selected channel. StructuredSpewFilter selectedChannel_; - using NameArray = - mozilla::EnumeratedArray<SpewChannel, SpewChannel::Count, const char*>; + using NameArray = mozilla::EnumeratedArray<SpewChannel, const char*, + size_t(SpewChannel::Count)>; // Channel Names static NameArray const names_; diff --git a/js/src/util/make_unicode.py b/js/src/util/make_unicode.py index 6ddecd6cbb..21ad718f3e 100755 --- a/js/src/util/make_unicode.py +++ b/js/src/util/make_unicode.py @@ -1111,7 +1111,7 @@ def make_regexp_space_test(version, test_space_table, codepoint_table): test_space.write(",\n".join(map(hex_and_name, test_space_table))) test_space.write("\n);\n") test_space.write( - """ + r""" assertEq(/^\s+$/.exec(onlySpace) !== null, true); assertEq(/^[\s]+$/.exec(onlySpace) !== null, true); assertEq(/^[^\s]+$/.exec(onlySpace) === null, true); diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index eeaca41a97..9ac3989885 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -275,12 +275,14 @@ class ArgumentsObject : public NativeObject { return argc; } - // True iff arguments.length has been assigned or deleted. - bool hasOverriddenLength() const { + bool hasFlags(uint32_t flags) const { const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & LENGTH_OVERRIDDEN_BIT; + return v.toInt32() & flags; } + // True iff arguments.length has been assigned or deleted. + bool hasOverriddenLength() const { return hasFlags(LENGTH_OVERRIDDEN_BIT); } + void markLengthOverridden() { uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | LENGTH_OVERRIDDEN_BIT; @@ -292,8 +294,7 @@ class ArgumentsObject : public NativeObject { // True iff arguments[@@iterator] has been assigned or deleted. bool hasOverriddenIterator() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & ITERATOR_OVERRIDDEN_BIT; + return hasFlags(ITERATOR_OVERRIDDEN_BIT); } void markIteratorOverridden() { @@ -311,10 +312,7 @@ class ArgumentsObject : public NativeObject { static bool getArgumentsIterator(JSContext* cx, MutableHandleValue val); // True iff any element has been assigned or deleted. - bool hasOverriddenElement() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & ELEMENT_OVERRIDDEN_BIT; - } + bool hasOverriddenElement() const { return hasFlags(ELEMENT_OVERRIDDEN_BIT); } void markElementOverridden() { uint32_t v = @@ -409,10 +407,7 @@ class ArgumentsObject : public NativeObject { return IsMagicScopeSlotValue(v); } - bool anyArgIsForwarded() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & FORWARDED_ARGUMENTS_BIT; - } + bool anyArgIsForwarded() const { return hasFlags(FORWARDED_ARGUMENTS_BIT); } void markArgumentForwarded() { uint32_t v = @@ -504,10 +499,7 @@ class MappedArgumentsObject : public ArgumentsObject { return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>(); } - bool hasOverriddenCallee() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & CALLEE_OVERRIDDEN_BIT; - } + bool hasOverriddenCallee() const { return hasFlags(CALLEE_OVERRIDDEN_BIT); } void markCalleeOverridden() { uint32_t v = diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 72c9ebeb18..2fe4f01f8d 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -536,12 +536,7 @@ bool ArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>(); // Steps 4-6. - size_t maxByteLength; - if (buffer->isResizable()) { - maxByteLength = buffer->as<ResizableArrayBufferObject>().maxByteLength(); - } else { - maxByteLength = buffer->byteLength(); - } + size_t maxByteLength = buffer->maxByteLength(); MOZ_ASSERT_IF(buffer->isDetached(), maxByteLength == 0); // Step 7. @@ -914,8 +909,6 @@ void ArrayBufferObject::detach(JSContext* cx, // Update all views of the buffer to account for the buffer having been // detached, and clear the buffer's data and list of views. - // - // Typed object buffers are not exposed and cannot be detached. auto& innerViews = ObjectRealm::get(buffer).innerViews.get(); if (InnerViewTable::ViewVector* views = @@ -962,6 +955,20 @@ void ResizableArrayBufferObject::resize(size_t newByteLength) { } setByteLength(newByteLength); + + // Update all views of the buffer to account for the buffer having been + // resized. + + auto& innerViews = ObjectRealm::get(this).innerViews.get(); + if (InnerViewTable::ViewVector* views = + innerViews.maybeViewsUnbarriered(this)) { + for (auto& view : *views) { + view->notifyBufferResized(); + } + } + if (auto* view = firstView()) { + view->as<ArrayBufferViewObject>().notifyBufferResized(); + } } /* clang-format off */ @@ -1490,10 +1497,7 @@ size_t ArrayBufferObject::byteLength() const { inline size_t ArrayBufferObject::associatedBytes() const { if (isMalloced()) { - if (isResizable()) { - return as<ResizableArrayBufferObject>().maxByteLength(); - } - return byteLength(); + return maxByteLength(); } if (isMapped()) { return RoundUp(byteLength(), js::gc::SystemPageSize()); @@ -2472,7 +2476,7 @@ bool ArrayBufferObject::ensureNonInline(JSContext* cx, return true; } - size_t nbytes = buffer->byteLength(); + size_t nbytes = buffer->maxByteLength(); ArrayBufferContents copy = NewCopiedBufferContents(cx, buffer); if (!copy) { return false; diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 17faf7682e..5aa96bf887 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -566,6 +566,12 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared { void setDataPointer(BufferContents contents); void setByteLength(size_t length); + /** + * Return the byte length for fixed-length buffers or the maximum byte length + * for resizable buffers. + */ + inline size_t maxByteLength() const; + size_t associatedBytes() const; uint32_t flags() const; @@ -703,6 +709,13 @@ class ResizableArrayBufferObject : public ArrayBufferObject { JS::Handle<ResizableArrayBufferObject*> source); }; +size_t ArrayBufferObject::maxByteLength() const { + if (isResizable()) { + return as<ResizableArrayBufferObject>().maxByteLength(); + } + return byteLength(); +} + // Create a buffer for a wasm memory, whose type is determined by // memory.indexType(). ArrayBufferObjectMaybeShared* CreateWasmBuffer(JSContext* cx, @@ -775,6 +788,7 @@ class InnerViewTable { private: friend class ArrayBufferObject; + friend class ResizableArrayBufferObject; bool addView(JSContext* cx, ArrayBufferObject* buffer, ArrayBufferViewObject* view); ViewVector* maybeViewsUnbarriered(ArrayBufferObject* buffer); diff --git a/js/src/vm/ArrayBufferViewObject.cpp b/js/src/vm/ArrayBufferViewObject.cpp index 4bcd7890c1..27004f3e2a 100644 --- a/js/src/vm/ArrayBufferViewObject.cpp +++ b/js/src/vm/ArrayBufferViewObject.cpp @@ -41,7 +41,7 @@ void ArrayBufferViewObject::trace(JSTracer* trc, JSObject* obj) { &gc::MaybeForwardedObjectAs<ResizableArrayBufferObject>(bufferObj); } if (buffer) { - size_t offset = view->byteOffset(); + size_t offset = view->dataPointerOffset(); MOZ_ASSERT_IF(!buffer->dataPointer(), offset == 0); // The data may or may not be inline with the buffer. The buffer can only @@ -69,13 +69,22 @@ void ArrayBufferViewObject::notifyBufferDetached() { setFixedSlot(DATA_SLOT, UndefinedValue()); } +void ArrayBufferViewObject::notifyBufferResized() { + MOZ_ASSERT(!isSharedMemory()); + MOZ_ASSERT(hasBuffer()); + MOZ_ASSERT(!bufferUnshared()->isLengthPinned()); + MOZ_ASSERT(bufferUnshared()->isResizable()); + + computeResizableLengthAndByteOffset(bytesPerElement()); +} + void ArrayBufferViewObject::notifyBufferMoved(uint8_t* srcBufStart, uint8_t* dstBufStart) { MOZ_ASSERT(!isSharedMemory()); MOZ_ASSERT(hasBuffer()); if (srcBufStart != dstBufStart) { - void* data = dstBufStart + byteOffset(); + void* data = dstBufStart + dataPointerOffset(); getFixedSlotRef(DATA_SLOT).unbarrieredSet(PrivateValue(data)); } } @@ -183,6 +192,76 @@ bool ArrayBufferViewObject::init(JSContext* cx, return true; } +bool ArrayBufferViewObject::initResizable(JSContext* cx, + ArrayBufferObjectMaybeShared* buffer, + size_t byteOffset, size_t length, + uint32_t bytesPerElement, + AutoLength autoLength) { + MOZ_ASSERT(buffer->isResizable()); + + if (!init(cx, buffer, byteOffset, length, bytesPerElement)) { + return false; + } + + initFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(static_cast<bool>(autoLength))); + initFixedSlot(INITIAL_LENGTH_SLOT, PrivateValue(length)); + initFixedSlot(INITIAL_BYTE_OFFSET_SLOT, PrivateValue(byteOffset)); + + // Compute the actual byteLength and byteOffset for non-shared buffers. + if (!isSharedMemory()) { + computeResizableLengthAndByteOffset(bytesPerElement); + } + + MOZ_ASSERT(!isOutOfBounds(), "can't create out-of-bounds views"); + + return true; +} + +void ArrayBufferViewObject::computeResizableLengthAndByteOffset( + size_t bytesPerElement) { + MOZ_ASSERT(!isSharedMemory()); + MOZ_ASSERT(hasBuffer()); + MOZ_ASSERT(!bufferUnshared()->isLengthPinned()); + MOZ_ASSERT(bufferUnshared()->isResizable()); + + size_t byteOffsetStart = initialByteOffset(); + size_t bufferByteLength = bufferUnshared()->byteLength(); + + // Out-of-bounds if the byteOffset exceeds the buffer length. + if (byteOffsetStart > bufferByteLength) { + setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0))); + setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0))); + return; + } + + size_t length; + if (isAutoLength()) { + length = (bufferByteLength - byteOffsetStart) / bytesPerElement; + } else { + length = initialLength(); + + // Out-of-bounds if the byteOffset end index exceeds the buffer length. + size_t byteOffsetEnd = byteOffsetStart + length * bytesPerElement; + if (byteOffsetEnd > bufferByteLength) { + setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0))); + setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0))); + return; + } + } + + setFixedSlot(LENGTH_SLOT, PrivateValue(length)); + setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(byteOffsetStart)); +} + +size_t ArrayBufferViewObject::bytesPerElement() const { + if (is<TypedArrayObject>()) { + return as<TypedArrayObject>().bytesPerElement(); + } + + MOZ_ASSERT(is<DataViewObject>()); + return 1; +} + bool ArrayBufferViewObject::hasResizableBuffer() const { if (auto* buffer = bufferEither()) { return buffer->isResizable(); @@ -190,6 +269,98 @@ bool ArrayBufferViewObject::hasResizableBuffer() const { return false; } +size_t ArrayBufferViewObject::dataPointerOffset() const { + // Views without a buffer have a zero offset. + if (!hasBuffer()) { + MOZ_ASSERT(byteOffsetSlotValue() == 0); + return 0; + } + + // Views on shared buffers store the offset in |byteOffset|. + if (isSharedMemory()) { + return byteOffsetSlotValue(); + } + + // Can be called during tracing, so the buffer is possibly forwarded. + const auto* bufferObj = gc::MaybeForwarded(&bufferValue().toObject()); + + // Two distinct classes are used for non-shared buffers. + MOZ_ASSERT( + gc::MaybeForwardedObjectIs<FixedLengthArrayBufferObject>(bufferObj) || + gc::MaybeForwardedObjectIs<ResizableArrayBufferObject>(bufferObj)); + + // Ensure these two classes can be casted to ArrayBufferObject. + static_assert( + std::is_base_of_v<ArrayBufferObject, FixedLengthArrayBufferObject>); + static_assert( + std::is_base_of_v<ArrayBufferObject, ResizableArrayBufferObject>); + + // Manual cast necessary because the buffer is possibly forwarded. + const auto* buffer = static_cast<const ArrayBufferObject*>(bufferObj); + + // Views on resizable buffers store the offset in |initialByteOffset|. + if (buffer->isResizable() && !buffer->isDetached()) { + return initialByteOffsetValue(); + } + + // Callers expect that this method returns zero for detached buffers. + MOZ_ASSERT_IF(buffer->isDetached(), byteOffsetSlotValue() == 0); + + // Views on fixed-length buffers store the offset in |byteOffset|. + return byteOffsetSlotValue(); +} + +mozilla::Maybe<size_t> ArrayBufferViewObject::byteOffset() const { + // |byteOffset| is set to zero for detached or out-of-bounds views, so a + // non-zero value indicates the view is in-bounds. + size_t byteOffset = byteOffsetSlotValue(); + if (byteOffset > 0) { + MOZ_ASSERT(!hasDetachedBuffer()); + MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds()); + return mozilla::Some(byteOffset); + } + if (hasDetachedBufferOrIsOutOfBounds()) { + return mozilla::Nothing{}; + } + return mozilla::Some(0); +} + +mozilla::Maybe<size_t> ArrayBufferViewObject::length() const { + // |length| is set to zero for detached or out-of-bounds views, so a non-zero + // value indicates the view is in-bounds. + size_t length = lengthSlotValue(); + if (MOZ_LIKELY(length > 0)) { + MOZ_ASSERT(!hasDetachedBuffer()); + MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds()); + MOZ_ASSERT(!isSharedMemory() || !hasResizableBuffer() || !isAutoLength(), + "length is zero for auto-length growable shared buffers"); + return mozilla::Some(length); + } + + if (hasDetachedBufferOrIsOutOfBounds()) { + return mozilla::Nothing{}; + } + + if (isSharedMemory()) { + auto* buffer = bufferShared(); + MOZ_ASSERT(buffer, "shared memory doesn't use inline data"); + + // Views backed by a growable SharedArrayBuffer can never get out-of-bounds, + // but we have to dynamically compute the length when the auto-length flag + // is set. + if (buffer->isGrowable() && isAutoLength()) { + size_t bufferByteLength = buffer->byteLength(); + size_t byteOffset = byteOffsetSlotValue(); + MOZ_ASSERT(byteOffset <= bufferByteLength); + MOZ_ASSERT(byteOffset == initialByteOffset(), + "views on growable shared buffers can't get out-of-bounds"); + + return mozilla::Some((bufferByteLength - byteOffset) / bytesPerElement()); + } + } + return mozilla::Some(0); +} + #if defined(DEBUG) || defined(JS_JITSPEW) void ArrayBufferViewObject::dumpOwnFields(js::JSONPrinter& json) const { json.formatProperty("length", "%zu", diff --git a/js/src/vm/ArrayBufferViewObject.h b/js/src/vm/ArrayBufferViewObject.h index c1c1ccac88..babba93f9e 100644 --- a/js/src/vm/ArrayBufferViewObject.h +++ b/js/src/vm/ArrayBufferViewObject.h @@ -7,6 +7,8 @@ #ifndef vm_ArrayBufferViewObject_h #define vm_ArrayBufferViewObject_h +#include "mozilla/Maybe.h" + #include "builtin/TypedArrayConstants.h" #include "vm/ArrayBufferObject.h" #include "vm/NativeObject.h" @@ -47,6 +49,14 @@ class ArrayBufferViewObject : public NativeObject { static constexpr size_t RESERVED_SLOTS = 4; + // Additional slots for views on resizable/growable (Shared)ArrayBufferObject. + + static const uint8_t AUTO_LENGTH_SLOT = 4; + static const uint8_t INITIAL_LENGTH_SLOT = 5; + static const uint8_t INITIAL_BYTE_OFFSET_SLOT = 6; + + static constexpr size_t RESIZABLE_RESERVED_SLOTS = 7; + #ifdef DEBUG static const uint8_t ZeroLengthArrayData = 0x4A; #endif @@ -63,6 +73,15 @@ class ArrayBufferViewObject : public NativeObject { static constexpr int dataOffset() { return NativeObject::getFixedSlotOffset(DATA_SLOT); } + static constexpr int autoLengthOffset() { + return NativeObject::getFixedSlotOffset(AUTO_LENGTH_SLOT); + } + static constexpr int initialLengthOffset() { + return NativeObject::getFixedSlotOffset(INITIAL_LENGTH_SLOT); + } + static constexpr int initialByteOffsetOffset() { + return NativeObject::getFixedSlotOffset(INITIAL_BYTE_OFFSET_SLOT); + } private: void* dataPointerEither_() const { @@ -76,10 +95,19 @@ class ArrayBufferViewObject : public NativeObject { size_t byteOffset, size_t length, uint32_t bytesPerElement); + enum class AutoLength : bool { No, Yes }; + + [[nodiscard]] bool initResizable(JSContext* cx, + ArrayBufferObjectMaybeShared* buffer, + size_t byteOffset, size_t length, + uint32_t bytesPerElement, + AutoLength autoLength); + static ArrayBufferObjectMaybeShared* ensureBufferObject( JSContext* cx, Handle<ArrayBufferViewObject*> obj); void notifyBufferDetached(); + void notifyBufferResized(); void notifyBufferMoved(uint8_t* srcBufStart, uint8_t* dstBufStart); void initDataPointer(SharedMem<uint8_t*> viewData) { @@ -156,6 +184,24 @@ class ArrayBufferViewObject : public NativeObject { bool hasResizableBuffer() const; + private: + bool hasDetachedBufferOrIsOutOfBounds() const { + // Shared buffers can't be detached or get out-of-bounds. + if (isSharedMemory()) { + return false; + } + + // A view with a null buffer has never had its buffer exposed to become + // detached or get out-of-bounds. + auto* buffer = bufferUnshared(); + if (!buffer) { + return false; + } + + return buffer->isDetached() || (buffer->isResizable() && isOutOfBounds()); + } + + public: bool isLengthPinned() const { Value buffer = bufferValue(); if (buffer.isBoolean()) { @@ -193,11 +239,75 @@ class ArrayBufferViewObject : public NativeObject { static bool ensureNonInline(JSContext* cx, JS::Handle<ArrayBufferViewObject*> view); + private: + void computeResizableLengthAndByteOffset(size_t bytesPerElement); + + size_t bytesPerElement() const; + protected: - size_t byteOffset() const { + size_t lengthSlotValue() const { + return size_t(getFixedSlot(LENGTH_SLOT).toPrivate()); + } + + size_t byteOffsetSlotValue() const { return size_t(getFixedSlot(BYTEOFFSET_SLOT).toPrivate()); } + /** + * Offset into the buffer's data-pointer. Different from |byteOffset| for + * views on non-detached resizable buffers which are currently out-of-bounds. + */ + size_t dataPointerOffset() const; + + /** + * Return the current length, or |Nothing| if the view is detached or + * out-of-bounds. + */ + mozilla::Maybe<size_t> length() const; + + public: + /** + * Return the current byteOffset, or |Nothing| if the view is detached or + * out-of-bounds. + */ + mozilla::Maybe<size_t> byteOffset() const; + + private: + size_t initialByteOffsetValue() const { + // No assertion for resizable buffers here, because this method is called + // from dataPointerOffset(), which can be called during tracing. + return size_t(getFixedSlot(INITIAL_BYTE_OFFSET_SLOT).toPrivate()); + } + + public: + // The following methods can only be called on views for resizable buffers. + + bool isAutoLength() const { + MOZ_ASSERT(hasResizableBuffer()); + return getFixedSlot(AUTO_LENGTH_SLOT).toBoolean(); + } + + size_t initialLength() const { + MOZ_ASSERT(hasResizableBuffer()); + return size_t(getFixedSlot(INITIAL_LENGTH_SLOT).toPrivate()); + } + + size_t initialByteOffset() const { + MOZ_ASSERT(hasResizableBuffer()); + return initialByteOffsetValue(); + } + + bool isOutOfBounds() const { + MOZ_ASSERT(hasResizableBuffer()); + + // The view is out-of-bounds if the length and byteOffset slots are both set + // to zero and the initial length or initial byteOffset are non-zero. If the + // initial length and initial byteOffset are both zero, the view can never + // get out-of-bounds. + return lengthSlotValue() == 0 && byteOffsetSlotValue() == 0 && + (initialLength() > 0 || initialByteOffset() > 0); + } + public: static void trace(JSTracer* trc, JSObject* obj); diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h index 24f544cd81..fb9f4085e6 100644 --- a/js/src/vm/BigIntType.h +++ b/js/src/vm/BigIntType.h @@ -419,6 +419,7 @@ class BigInt final : public js::gc::CellWithLengthAndFlags { static JSLinearString* toStringGeneric(JSContext* cx, Handle<BigInt*>, unsigned radix); + friend struct ::JSStructuredCloneReader; // So it can call the following: static BigInt* destructivelyTrimHighZeroDigits(JSContext* cx, BigInt* x); bool absFitsInUint64() const { return digitLength() <= 64 / DigitBits; } diff --git a/js/src/vm/CharacterEncoding.cpp b/js/src/vm/CharacterEncoding.cpp index 79d28ab719..3d05275e2d 100644 --- a/js/src/vm/CharacterEncoding.cpp +++ b/js/src/vm/CharacterEncoding.cpp @@ -286,11 +286,6 @@ static bool InflateUTF8ToUTF16(JSContext* cx, const UTF8Chars& src, break; } } else { - // Non-ASCII code unit. Determine its length in bytes (n). - uint32_t n = 1; - while (v & (0x80 >> n)) { - n++; - } #define INVALID(report, arg, n2) \ do { \ @@ -315,6 +310,14 @@ static bool InflateUTF8ToUTF16(JSContext* cx, const UTF8Chars& src, } \ } while (0) + // Non-ASCII code unit. Determine its length in bytes (n). + // + // Avoid undefined behavior from passing in 0 + // (https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fclz) + // by turning on the low bit so that 0xff will set n=31-24=7, which will + // be detected as an invalid character. + uint32_t n = mozilla::CountLeadingZeroes32(~int8_t(src[i]) | 0x1) - 24; + // Check the leading byte. if (n < 2 || n > 4) { INVALID(ReportInvalidCharacter, i, 1); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 7e81f4cb8a..d4376ec6a4 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -332,6 +332,7 @@ MACRO_(join, "join") \ MACRO2(js, "js") \ MACRO_(jsStringModule, "js-string") \ + MACRO_(json, "json") \ MACRO_(keys, "keys") \ IF_DECORATORS(MACRO_(kind, "kind")) \ MACRO_(label, "label") \ diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index cbb14f93f2..008cfca260 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -416,6 +416,40 @@ ModuleEnvironmentObject* ModuleEnvironmentObject::create( return env; } +/* static */ +ModuleEnvironmentObject* ModuleEnvironmentObject::createSynthetic( + JSContext* cx, Handle<ModuleObject*> module) { + Rooted<SharedShape*> shape(cx, + CreateEnvironmentShapeForSyntheticModule( + cx, &class_, JSSLOT_FREE(&class_), module)); + MOZ_ASSERT(shape->getObjectClass() == &class_); + + Rooted<ModuleEnvironmentObject*> env( + cx, CreateEnvironmentObject<ModuleEnvironmentObject>(cx, shape, + TenuredObject)); + if (!env) { + return nullptr; + } + + env->initReservedSlot(MODULE_SLOT, ObjectValue(*module)); + + // Initialize this early so that we can manipulate the env object without + // causing assertions. + env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); + + // It is not be possible to add or remove bindings from a module environment + // after this point as module code is always strict. +#ifdef DEBUG + for (ShapePropertyIter<NoGC> iter(env->shape()); !iter.done(); iter++) { + MOZ_ASSERT(!iter->configurable()); + } + MOZ_ASSERT(env->hasFlag(ObjectFlag::NotExtensible)); + MOZ_ASSERT(!env->inDictionaryMode()); +#endif + + return env; +} + ModuleObject& ModuleEnvironmentObject::module() const { return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>(); } diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h index fd60128e1f..192d8d2ce7 100644 --- a/js/src/vm/EnvironmentObject.h +++ b/js/src/vm/EnvironmentObject.h @@ -622,6 +622,8 @@ class ModuleEnvironmentObject : public EnvironmentObject { static const JSClassOps classOps_; public: + using EnvironmentObject::setAliasedBinding; + static const JSClass class_; static constexpr uint32_t RESERVED_SLOTS = 2; @@ -630,6 +632,9 @@ class ModuleEnvironmentObject : public EnvironmentObject { static ModuleEnvironmentObject* create(JSContext* cx, Handle<ModuleObject*> module); + static ModuleEnvironmentObject* createSynthetic(JSContext* cx, + Handle<ModuleObject*> module); + ModuleObject& module() const; IndirectBindingMap& importBindings() const; @@ -648,6 +653,8 @@ class ModuleEnvironmentObject : public EnvironmentObject { // `env` may be a DebugEnvironmentProxy, but not a hollow environment. static ModuleEnvironmentObject* find(JSObject* env); + uint32_t firstSyntheticValueSlot() { return RESERVED_SLOTS; } + private: static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject objp, PropertyResult* propp); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 9c9003a2ba..6782433fd3 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -202,7 +202,7 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { return false; case JSProto_Segmenter: -# if defined(MOZ_ICU4X) && defined(NIGHTLY_BUILD) +# if defined(MOZ_ICU4X) return false; # else return true; diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 92dec6698f..a296336385 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -129,8 +129,8 @@ class GlobalObjectData { HeapPtr<JSObject*> constructor; HeapPtr<JSObject*> prototype; }; - using CtorArray = - mozilla::EnumeratedArray<JSProtoKey, JSProto_LIMIT, ConstructorWithProto>; + using CtorArray = mozilla::EnumeratedArray<JSProtoKey, ConstructorWithProto, + size_t(JSProto_LIMIT)>; CtorArray builtinConstructors; // Built-in prototypes for this global. Note that this is different from the @@ -154,8 +154,8 @@ class GlobalObjectData { Limit }; - using ProtoArray = - mozilla::EnumeratedArray<ProtoKind, ProtoKind::Limit, HeapPtr<JSObject*>>; + using ProtoArray = mozilla::EnumeratedArray<ProtoKind, HeapPtr<JSObject*>, + size_t(ProtoKind::Limit)>; ProtoArray builtinProtos; HeapPtr<GlobalScope*> emptyGlobalScope; @@ -195,8 +195,9 @@ class GlobalObjectData { // Shape for PlainObject with %Object.prototype% as proto, for each object // AllocKind. - using PlainObjectShapeArray = mozilla::EnumeratedArray< - PlainObjectSlotsKind, PlainObjectSlotsKind::Limit, HeapPtr<SharedShape*>>; + using PlainObjectShapeArray = + mozilla::EnumeratedArray<PlainObjectSlotsKind, HeapPtr<SharedShape*>, + size_t(PlainObjectSlotsKind::Limit)>; PlainObjectShapeArray plainObjectShapesWithDefaultProto; // Shape for JSFunction with %Function.prototype% as proto, for both diff --git a/js/src/vm/HelperThreadState.h b/js/src/vm/HelperThreadState.h index 8e601a385c..a43efef7af 100644 --- a/js/src/vm/HelperThreadState.h +++ b/js/src/vm/HelperThreadState.h @@ -26,13 +26,12 @@ #include <stdint.h> // uint32_t, uint64_t #include <utility> // std::move -#include "ds/Fifo.h" // Fifo -#include "frontend/CompilationStencil.h" // frontend::CompilationStencil -#include "gc/GCRuntime.h" // gc::GCRuntime -#include "js/AllocPolicy.h" // SystemAllocPolicy -#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions -#include "js/experimental/CompileScript.h" // JS::CompilationStorage -#include "js/experimental/JSStencil.h" // JS::InstantiationStorage +#include "ds/Fifo.h" // Fifo +#include "frontend/CompilationStencil.h" // frontend::CompilationStencil +#include "gc/GCRuntime.h" // gc::GCRuntime +#include "js/AllocPolicy.h" // SystemAllocPolicy +#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions +#include "js/experimental/JSStencil.h" // JS::InstantiationStorage #include "js/HelperThreadAPI.h" // JS::HelperThreadTaskCallback, JS::DispatchReason #include "js/MemoryMetrics.h" // JS::GlobalStats #include "js/ProfilingStack.h" // JS::RegisterThreadCallback, JS::UnregisterThreadCallback @@ -115,13 +114,19 @@ class GlobalHelperThreadState { PromiseHelperTaskVector; // Count of running task by each threadType. - mozilla::EnumeratedArray<ThreadType, ThreadType::THREAD_TYPE_MAX, size_t> + mozilla::EnumeratedArray<ThreadType, size_t, + size_t(ThreadType::THREAD_TYPE_MAX)> runningTaskCount; size_t totalCountRunningTasks; WriteOnceData<JS::RegisterThreadCallback> registerThread; WriteOnceData<JS::UnregisterThreadCallback> unregisterThread; + // Count of helper threads 'reserved' for parallel marking. This is used to + // prevent too many runtimes trying to mark in parallel at once. Does not stop + // threads from being used for other kinds of task, including GC tasks. + HelperThreadLockData<size_t> gcParallelMarkingThreads; + private: // The lists below are all protected by |lock|. diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 36792b1d09..da8231c1dc 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -859,6 +859,8 @@ void GlobalHelperThreadState::finish(AutoLockHelperThreadState& lock) { return; } + MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), gcParallelMarkingThreads == 0); + finishThreads(lock); // Make sure there are no Ion free tasks left. We check this here because, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 255ab9aa94..9eec4b81dd 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -31,7 +31,6 @@ #include "builtin/Object.h" #include "builtin/Promise.h" #include "gc/GC.h" -#include "jit/AtomicOperations.h" #include "jit/BaselineJIT.h" #include "jit/Jit.h" #include "jit/JitRuntime.h" @@ -4738,15 +4737,6 @@ bool js::GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs, return GreaterThanOrEqualOperation(cx, lhs, rhs, res); } -bool js::AtomicIsLockFree(JSContext* cx, HandleValue in, int* out) { - int i; - if (!ToInt32(cx, in, &i)) { - return false; - } - *out = js::jit::AtomicOperations::isLockfreeJS(i); - return true; -} - bool js::DeleteNameOperation(JSContext* cx, Handle<PropertyName*> name, HandleObject scopeObj, MutableHandleValue res) { RootedObject scope(cx), pobj(cx); diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index d962e7f344..8ac7db7004 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -589,8 +589,6 @@ bool GreaterThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res); -bool AtomicIsLockFree(JSContext* cx, HandleValue in, int* out); - template <bool strict> bool DelPropOperation(JSContext* cx, HandleValue val, Handle<PropertyName*> name, bool* res); diff --git a/js/src/vm/Iteration.cpp b/js/src/vm/Iteration.cpp index 304054e0ec..d02f9de8cf 100644 --- a/js/src/vm/Iteration.cpp +++ b/js/src/vm/Iteration.cpp @@ -829,7 +829,7 @@ static PropertyIteratorObject* CreatePropertyIterator( bool supportsIndices, PropertyIndexVector* indices, uint32_t cacheableProtoChainLength) { MOZ_ASSERT_IF(indices, supportsIndices); - if (props.length() > NativeIterator::PropCountLimit) { + if (props.length() >= NativeIterator::PropCountLimit) { ReportAllocationOverflow(cx); return nullptr; } diff --git a/js/src/vm/JSContext-inl.h b/js/src/vm/JSContext-inl.h index f20c4b3c7d..252fefd88d 100644 --- a/js/src/vm/JSContext-inl.h +++ b/js/src/vm/JSContext-inl.h @@ -352,52 +352,6 @@ inline void JSContext::setRealmForJitExceptionHandler(JS::Realm* realm) { realm_ = realm; } -inline JSScript* JSContext::currentScript( - jsbytecode** ppc, AllowCrossRealm allowCrossRealm) const { - if (ppc) { - *ppc = nullptr; - } - - js::Activation* act = activation(); - if (!act) { - return nullptr; - } - - MOZ_ASSERT(act->cx() == this); - - // Cross-compartment implies cross-realm. - if (allowCrossRealm == AllowCrossRealm::DontAllow && - act->compartment() != compartment()) { - return nullptr; - } - - JSScript* script = nullptr; - jsbytecode* pc = nullptr; - if (act->isJit()) { - if (act->hasWasmExitFP()) { - return nullptr; - } - js::jit::GetPcScript(const_cast<JSContext*>(this), &script, &pc); - } else { - js::InterpreterFrame* fp = act->asInterpreter()->current(); - MOZ_ASSERT(!fp->runningInJit()); - script = fp->script(); - pc = act->asInterpreter()->regs().pc; - } - - MOZ_ASSERT(script->containsPC(pc)); - - if (allowCrossRealm == AllowCrossRealm::DontAllow && - script->realm() != realm()) { - return nullptr; - } - - if (ppc) { - *ppc = pc; - } - return script; -} - inline js::RuntimeCaches& JSContext::caches() { return runtime()->caches(); } template <typename T, js::AllowGC allowGC, typename... Args> diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp index 4d355dc828..5a4bfa86cd 100644 --- a/js/src/vm/JSContext.cpp +++ b/js/src/vm/JSContext.cpp @@ -55,6 +55,7 @@ #include "vm/BytecodeUtil.h" // JSDVG_IGNORE_STACK #include "vm/ErrorObject.h" #include "vm/ErrorReporting.h" +#include "vm/FrameIter.h" #include "vm/JSFunction.h" #include "vm/JSObject.h" #include "vm/PlainObject.h" // js::PlainObject @@ -997,7 +998,6 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options) suppressProfilerSampling(false), tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), debuggerMutations(this, 0), - ionPcScriptCache(this, nullptr), status(this, JS::ExceptionStatus::None), unwrappedException_(this), unwrappedExceptionStack_(this), @@ -1263,6 +1263,34 @@ void JSContext::resetJitStackLimit() { void JSContext::initJitStackLimit() { resetJitStackLimit(); } +JSScript* JSContext::currentScript(jsbytecode** ppc, + AllowCrossRealm allowCrossRealm) { + if (ppc) { + *ppc = nullptr; + } + + // Fast path: there are no JS frames on the stack if there's no activation. + if (!activation()) { + return nullptr; + } + + FrameIter iter(this); + if (iter.done() || !iter.hasScript()) { + return nullptr; + } + + JSScript* script = iter.script(); + if (allowCrossRealm == AllowCrossRealm::DontAllow && + script->realm() != realm()) { + return nullptr; + } + + if (ppc) { + *ppc = iter.pc(); + } + return script; +} + #ifdef JS_CRASH_DIAGNOSTICS void ContextChecks::check(AbstractFramePtr frame, int argIndex) { if (frame) { diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h index 1801816fa0..57aa236801 100644 --- a/js/src/vm/JSContext.h +++ b/js/src/vm/JSContext.h @@ -20,7 +20,6 @@ #include "gc/GCEnum.h" #include "gc/Memory.h" #include "irregexp/RegExpTypes.h" -#include "jit/PcScriptCache.h" #include "js/ContextOptions.h" // JS::ContextOptions #include "js/Exception.h" #include "js/GCVector.h" @@ -563,9 +562,6 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, js::ContextData<uint32_t> debuggerMutations; - // Cache for jit::GetPcScript(). - js::ContextData<js::UniquePtr<js::jit::PcScriptCache>> ionPcScriptCache; - private: // Indicates if an exception is pending and the reason for it. js::ContextData<JS::ExceptionStatus> status; @@ -693,9 +689,9 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, * overridden by passing AllowCrossRealm::Allow. */ enum class AllowCrossRealm { DontAllow = false, Allow = true }; - inline JSScript* currentScript( - jsbytecode** pc = nullptr, - AllowCrossRealm allowCrossRealm = AllowCrossRealm::DontAllow) const; + JSScript* currentScript( + jsbytecode** ppc = nullptr, + AllowCrossRealm allowCrossRealm = AllowCrossRealm::DontAllow); inline void minorGC(JS::GCReason reason); diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp index addd9aa263..f151f261b5 100644 --- a/js/src/vm/JSONParser.cpp +++ b/js/src/vm/JSONParser.cpp @@ -692,8 +692,9 @@ inline bool JSONFullParseHandlerAnyChar::finishObject( if (gcHeap == gc::Heap::Tenured) { newKind = TenuredObject; } + // properties is traced in the parser; see JSONParser<CharT>::trace() JSObject* obj = NewPlainObjectWithMaybeDuplicateKeys( - cx, properties->begin(), properties->length(), newKind); + cx, Handle<IdValueVector>::fromMarkedLocation(properties), newKind); if (!obj) { return false; } diff --git a/js/src/vm/JSONParser.h b/js/src/vm/JSONParser.h index 3a3da721f6..91e33c02b3 100644 --- a/js/src/vm/JSONParser.h +++ b/js/src/vm/JSONParser.h @@ -167,7 +167,7 @@ class MOZ_STACK_CLASS JSONFullParseHandlerAnyChar { // State for an object that is currently being parsed. This includes all // the key/value pairs that have been seen so far. - using PropertyVector = JS::GCVector<IdValuePair, 10>; + using PropertyVector = IdValueVector; enum class ParseType { // Parsing a string as if by JSON.parse. diff --git a/js/src/vm/JSObject.cpp b/js/src/vm/JSObject.cpp index bafc7a4437..ea4dfeb6f7 100644 --- a/js/src/vm/JSObject.cpp +++ b/js/src/vm/JSObject.cpp @@ -3147,38 +3147,44 @@ js::gc::AllocKind JSObject::allocKindForTenure( MOZ_ASSERT(IsInsideNursery(this)); - if (canHaveFixedElements()) { - const NativeObject& nobj = as<NativeObject>(); - MOZ_ASSERT(nobj.numFixedSlots() == 0); + if (is<NativeObject>()) { + if (canHaveFixedElements()) { + const NativeObject& nobj = as<NativeObject>(); + MOZ_ASSERT(nobj.numFixedSlots() == 0); - /* Use minimal size object if we are just going to copy the pointer. */ - if (!nursery.isInside(nobj.getUnshiftedElementsHeader())) { - return gc::AllocKind::OBJECT0_BACKGROUND; - } + /* Use minimal size object if we are just going to copy the pointer. */ + if (!nursery.isInside(nobj.getUnshiftedElementsHeader())) { + return gc::AllocKind::OBJECT0_BACKGROUND; + } - size_t nelements = nobj.getDenseCapacity(); - return ForegroundToBackgroundAllocKind(GetGCArrayKind(nelements)); - } + size_t nelements = nobj.getDenseCapacity(); + return ForegroundToBackgroundAllocKind(GetGCArrayKind(nelements)); + } - if (is<JSFunction>()) { - return as<JSFunction>().getAllocKind(); - } + if (is<JSFunction>()) { + return as<JSFunction>().getAllocKind(); + } - // Fixed length typed arrays in the nursery may have a lazily allocated - // buffer, make sure there is room for the array's fixed data when moving the - // array. - if (is<FixedLengthTypedArrayObject>() && - !as<FixedLengthTypedArrayObject>().hasBuffer()) { - gc::AllocKind allocKind; - if (as<FixedLengthTypedArrayObject>().hasInlineElements()) { - size_t nbytes = as<FixedLengthTypedArrayObject>().byteLength(); - allocKind = FixedLengthTypedArrayObject::AllocKindForLazyBuffer(nbytes); - } else { - allocKind = GetGCObjectKind(getClass()); + // Fixed length typed arrays in the nursery may have a lazily allocated + // buffer, make sure there is room for the array's fixed data when moving + // the array. + if (is<FixedLengthTypedArrayObject>() && + !as<FixedLengthTypedArrayObject>().hasBuffer()) { + gc::AllocKind allocKind; + if (as<FixedLengthTypedArrayObject>().hasInlineElements()) { + size_t nbytes = as<FixedLengthTypedArrayObject>().byteLength(); + allocKind = FixedLengthTypedArrayObject::AllocKindForLazyBuffer(nbytes); + } else { + allocKind = GetGCObjectKind(getClass()); + } + return ForegroundToBackgroundAllocKind(allocKind); } - return ForegroundToBackgroundAllocKind(allocKind); + + return as<NativeObject>().allocKindForTenure(); } + // Handle all non-native objects. + // Proxies that are CrossCompartmentWrappers may be nursery allocated. if (is<ProxyObject>()) { return as<ProxyObject>().allocKindForTenure(); @@ -3194,13 +3200,9 @@ js::gc::AllocKind JSObject::allocKindForTenure( // WasmArrayObjects sometimes have a variable-length tail which contains the // data for small arrays. Make sure we copy it all over to the new object. - if (is<WasmArrayObject>()) { - gc::AllocKind allocKind = as<WasmArrayObject>().allocKind(); - return allocKind; - } - - // All nursery allocatable non-native objects are handled above. - return as<NativeObject>().allocKindForTenure(); + MOZ_ASSERT(is<WasmArrayObject>()); + gc::AllocKind allocKind = as<WasmArrayObject>().allocKind(); + return allocKind; } void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index 6ea871ad42..7c3ed975a0 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -3245,8 +3245,10 @@ void JSScript::resetWarmUpCounterToDelayIonCompilation() { #if defined(DEBUG) || defined(JS_JITSPEW) void BaseScript::dumpStringContent(js::GenericPrinter& out) const { - out.printf("%s:%u:%u @ 0x%p", filename() ? filename() : "<null>", lineno(), - column().oneOriginValue(), this); + StringEscape esc('"'); + EscapePrinter ep(out, esc); + ep.printf("%s:%u:%u @ 0x%p", filename() ? filename() : "<null>", lineno(), + column().oneOriginValue(), this); } void JSScript::dump(JSContext* cx) { diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index 3a852cf9ea..c9c43fceed 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -211,11 +211,12 @@ static void StatsZoneCallback(JSRuntime* rt, void* data, Zone* zone, rtStats->currZoneStats = &zStats; zone->addSizeOfIncludingThis( - rtStats->mallocSizeOf_, &zStats.code, &zStats.regexpZone, &zStats.jitZone, - &zStats.cacheIRStubs, &zStats.uniqueIdMap, &zStats.initialPropMapTable, - &zStats.shapeTables, &rtStats->runtime.atomsMarkBitmaps, - &zStats.compartmentObjects, &zStats.crossCompartmentWrappersTables, - &zStats.compartmentsPrivateData, &zStats.scriptCountsMap); + rtStats->mallocSizeOf_, &zStats.zoneObject, &zStats.code, + &zStats.regexpZone, &zStats.jitZone, &zStats.cacheIRStubs, + &zStats.uniqueIdMap, &zStats.initialPropMapTable, &zStats.shapeTables, + &rtStats->runtime.atomsMarkBitmaps, &zStats.compartmentObjects, + &zStats.crossCompartmentWrappersTables, &zStats.compartmentsPrivateData, + &zStats.scriptCountsMap); } static void StatsRealmCallback(JSContext* cx, void* data, Realm* realm, diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp index 87546cf280..f461e7bec1 100644 --- a/js/src/vm/Modules.cpp +++ b/js/src/vm/Modules.cpp @@ -15,7 +15,9 @@ #include "jstypes.h" // JS_PUBLIC_API +#include "builtin/JSON.h" // js::ParseJSONWithReviver #include "builtin/ModuleObject.h" // js::FinishDynamicModuleImport, js::{,Requested}ModuleObject +#include "builtin/Promise.h" // js::CreatePromiseObjectForAsync, js::AsyncFunctionReturned #include "ds/Sort.h" #include "frontend/BytecodeCompiler.h" // js::frontend::CompileModule #include "frontend/FrontendContext.h" // js::AutoReportFrontendContext @@ -33,6 +35,8 @@ #include "vm/JSAtomUtils-inl.h" // AtomToId #include "vm/JSContext-inl.h" // JSContext::{c,releaseC}heck +#include "vm/JSObject-inl.h" +#include "vm/NativeObject-inl.h" using namespace js; @@ -120,6 +124,46 @@ JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx, return CompileModuleHelper(cx, options, srcBuf); } +JS_PUBLIC_API JSObject* JS::CompileJsonModule( + JSContext* cx, const ReadOnlyCompileOptions& options, + SourceText<char16_t>& srcBuf) { + MOZ_ASSERT(!cx->zone()->isAtomsZone()); + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + JS::RootedValue jsonValue(cx); + auto charRange = + mozilla::Range<const char16_t>(srcBuf.get(), srcBuf.length()); + if (!js::ParseJSONWithReviver(cx, charRange, NullHandleValue, &jsonValue)) { + return nullptr; + } + + Rooted<ExportNameVector> exportNames(cx); + if (!exportNames.reserve(1)) { + return nullptr; + } + exportNames.infallibleAppend(cx->names().default_); + + Rooted<ModuleObject*> moduleObject( + cx, ModuleObject::createSynthetic(cx, &exportNames)); + if (!moduleObject) { + return nullptr; + } + + Rooted<GCVector<Value>> exportValues(cx, GCVector<Value>(cx)); + if (!exportValues.reserve(1)) { + return nullptr; + } + exportValues.infallibleAppend(jsonValue); + + if (!ModuleObject::createSyntheticEnvironment(cx, moduleObject, + exportValues)) { + return nullptr; + } + + return moduleObject; +} + JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module, const Value& value) { JSRuntime* rt = module->zone()->runtimeFromMainThread(); module->as<ModuleObject>().scriptSourceObject()->setPrivate(rt, value); @@ -150,6 +194,10 @@ JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx, CHECK_THREAD(cx); cx->releaseCheck(moduleRecord); + if (moduleRecord.as<ModuleObject>()->hasSyntheticModuleFields()) { + return SyntheticModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval); + } + return js::ModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval); } @@ -312,6 +360,10 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module, Handle<JSAtom*> exportName, MutableHandle<ResolveSet> resolveSet, MutableHandle<Value> result); +static bool SyntheticModuleResolveExport(JSContext* cx, + Handle<ModuleObject*> module, + Handle<JSAtom*> exportName, + MutableHandle<Value> result); static ModuleNamespaceObject* ModuleNamespaceCreate( JSContext* cx, Handle<ModuleObject*> module, MutableHandle<UniquePtr<ExportNameVector>> exports); @@ -345,7 +397,7 @@ static const char* ModuleStatusName(ModuleStatus status) { } } -static bool ContainsElement(Handle<ExportNameVector> list, JSAtom* atom) { +static bool ContainsElement(const ExportNameVector& list, JSAtom* atom) { for (JSAtom* a : list) { if (a == atom) { return true; @@ -378,6 +430,20 @@ static size_t CountElements(Handle<ModuleVector> stack, ModuleObject* module) { } #endif +// https://tc39.es/proposal-json-modules/#sec-smr-getexportednames +static bool SyntheticModuleGetExportedNames( + JSContext* cx, Handle<ModuleObject*> module, + MutableHandle<ExportNameVector> exportedNames) { + MOZ_ASSERT(exportedNames.empty()); + + if (!exportedNames.appendAll(module->syntheticExportNames())) { + ReportOutOfMemory(cx); + return false; + } + + return true; +} + // https://tc39.es/ecma262/#sec-getexportednames // ES2023 16.2.1.6.2 GetExportedNames static bool ModuleGetExportedNames( @@ -387,6 +453,10 @@ static bool ModuleGetExportedNames( // Step 4. Let exportedNames be a new empty List. MOZ_ASSERT(exportedNames.empty()); + if (module->hasSyntheticModuleFields()) { + return SyntheticModuleGetExportedNames(cx, module, exportedNames); + } + // Step 2. If exportStarSet contains module, then: if (exportStarSet.has(module)) { // Step 2.a. We've reached the starting point of an export * circularity. @@ -482,12 +552,10 @@ static ModuleObject* HostResolveImportedModule( if (!requestedModule) { return nullptr; } - if (requestedModule->status() < expectedMinimumStatus) { ThrowUnexpectedModuleStatus(cx, requestedModule->status()); return nullptr; } - return requestedModule; } @@ -510,6 +578,10 @@ static ModuleObject* HostResolveImportedModule( bool js::ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module, Handle<JSAtom*> exportName, MutableHandle<Value> result) { + if (module->hasSyntheticModuleFields()) { + return ::SyntheticModuleResolveExport(cx, module, exportName, result); + } + // Step 1. If resolveSet is not present, set resolveSet to a new empty List. Rooted<ResolveSet> resolveSet(cx); @@ -683,6 +755,22 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module, return true; } +// https://tc39.es/proposal-json-modules/#sec-smr-resolveexport +static bool SyntheticModuleResolveExport(JSContext* cx, + Handle<ModuleObject*> module, + Handle<JSAtom*> exportName, + MutableHandle<Value> result) { + // Step 2. If module.[[ExportNames]] does not contain exportName, return null. + if (!ContainsElement(module->syntheticExportNames(), exportName)) { + result.setNull(); + return true; + } + + // Step 3. Return ResolvedBinding Record { [[Module]]: module, + // [[BindingName]]: exportName }. + return CreateResolvedBindingObject(cx, module, exportName, result); +} + // https://tc39.es/ecma262/#sec-getmodulenamespace // ES2023 16.2.1.10 GetModuleNamespace ModuleNamespaceObject* js::GetOrCreateModuleNamespace( @@ -1097,6 +1185,14 @@ bool js::ModuleLink(JSContext* cx, Handle<ModuleObject*> module) { static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, MutableHandle<ModuleVector> stack, size_t index, size_t* indexOut) { + // Step 1. If module is not a Cyclic Module Record, then + if (!module->hasCyclicModuleFields()) { + // Step 1.a. Perform ? module.Link(). (Skipped) + // Step 2.b. Return index. + *indexOut = index; + return true; + } + // Step 2. If module.[[Status]] is linking, linked, evaluating-async, or // evaluated, then: if (module->status() == ModuleStatus::Linking || @@ -1155,25 +1251,29 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, } // Step 9.c. If requiredModule is a Cyclic Module Record, then: - // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking, linked, - // evaluating-async, or evaluated. - MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking || - requiredModule->status() == ModuleStatus::Linked || - requiredModule->status() == ModuleStatus::EvaluatingAsync || - requiredModule->status() == ModuleStatus::Evaluated); - - // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only if - // requiredModule is in stack. - MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) == - ContainsElement(stack, requiredModule)); - - // Step 9.c.iii. If requiredModule.[[Status]] is linking, then: - if (requiredModule->status() == ModuleStatus::Linking) { - // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to - // min(module.[[DFSAncestorIndex]], - // requiredModule.[[DFSAncestorIndex]]). - module->setDfsAncestorIndex(std::min(module->dfsAncestorIndex(), - requiredModule->dfsAncestorIndex())); + if (requiredModule->hasCyclicModuleFields()) { + // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking, + // linked, + // evaluating-async, or evaluated. + MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking || + requiredModule->status() == ModuleStatus::Linked || + requiredModule->status() == ModuleStatus::EvaluatingAsync || + requiredModule->status() == ModuleStatus::Evaluated); + + // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only + // if + // requiredModule is in stack. + MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) == + ContainsElement(stack, requiredModule)); + + // Step 9.c.iii. If requiredModule.[[Status]] is linking, then: + if (requiredModule->status() == ModuleStatus::Linking) { + // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to + // min(module.[[DFSAncestorIndex]], + // requiredModule.[[DFSAncestorIndex]]). + module->setDfsAncestorIndex(std::min( + module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex())); + } } } @@ -1213,6 +1313,28 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, return true; } +bool js::SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg, + MutableHandle<Value> result) { + // Steps 1-12 happens elsewhere in the engine. + + // Step 13. Let pc be ! NewPromiseCapability(%Promise%). + Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx)); + if (!resultPromise) { + return false; + } + + // Step 14. IfAbruptRejectPromise(result, pc) (Skipped) + + // 15. Perform ! pc.[[Resolve]](result). + if (!AsyncFunctionReturned(cx, resultPromise, result)) { + return false; + } + + // 16. Return pc.[[Promise]]. + result.set(ObjectValue(*resultPromise)); + return true; +} + // https://tc39.es/ecma262/#sec-moduleevaluation // ES2023 16.2.1.5.2 Evaluate bool js::ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg, @@ -1349,6 +1471,17 @@ bool js::ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg, static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module, MutableHandle<ModuleVector> stack, size_t index, size_t* indexOut) { + // Step 1: If module is not a Cyclic Module Record, then + if (!module->hasCyclicModuleFields()) { + // Step 1.a. Let promise be ! module.Evaluate(). (Skipped) + // Step 1.b. Assert: promise.[[PromiseState]] is not pending. (Skipped) + // Step 1.c. If promise.[[PromiseState]] is rejected, then (Skipped) + // Step 1.c.i Return ThrowCompletion(promise.[[PromiseResult]]). (Skipped) + // Step 1.d. Return index. + *indexOut = index; + return true; + } + // Step 2. If module.[[Status]] is evaluating-async or evaluated, then: if (module->status() == ModuleStatus::EvaluatingAsync || module->status() == ModuleStatus::Evaluated) { @@ -1419,55 +1552,59 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module, } // Step 11.d. If requiredModule is a Cyclic Module Record, then: - // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating, - // evaluating-async, or evaluated. - MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating || - requiredModule->status() == ModuleStatus::EvaluatingAsync || - requiredModule->status() == ModuleStatus::Evaluated); - - // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and only - // if requiredModule is in stack. - MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) == - ContainsElement(stack, requiredModule)); - - // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then: - if (requiredModule->status() == ModuleStatus::Evaluating) { - // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to - // min(module.[[DFSAncestorIndex]], - // requiredModule.[[DFSAncestorIndex]]). - module->setDfsAncestorIndex(std::min(module->dfsAncestorIndex(), - requiredModule->dfsAncestorIndex())); - } else { - // Step 11.d.iv. Else: - // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]]. - requiredModule = requiredModule->getCycleRoot(); - - // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async - // or evaluated. - MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync || + if (requiredModule->hasCyclicModuleFields()) { + // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating, + // evaluating-async, or evaluated. + MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating || + requiredModule->status() == ModuleStatus::EvaluatingAsync || requiredModule->status() == ModuleStatus::Evaluated); - // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty, - // return ? requiredModule.[[EvaluationError]]. - if (requiredModule->hadEvaluationError()) { - Rooted<Value> error(cx, requiredModule->evaluationError()); - cx->setPendingException(error, ShouldCaptureStack::Maybe); - return false; + // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and + // only if requiredModule is in stack. + MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) == + ContainsElement(stack, requiredModule)); + + // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then: + if (requiredModule->status() == ModuleStatus::Evaluating) { + // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to + // min(module.[[DFSAncestorIndex]], + // requiredModule.[[DFSAncestorIndex]]). + module->setDfsAncestorIndex(std::min( + module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex())); + } else { + // Step 11.d.iv. Else: + // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]]. + requiredModule = requiredModule->getCycleRoot(); + + // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async + // or evaluated. + MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync || + requiredModule->status() == ModuleStatus::Evaluated); + + // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty, + // return ? requiredModule.[[EvaluationError]]. + if (requiredModule->hadEvaluationError()) { + Rooted<Value> error(cx, requiredModule->evaluationError()); + cx->setPendingException(error, ShouldCaptureStack::Maybe); + return false; + } } - } - // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then: - if (requiredModule->isAsyncEvaluating() && - requiredModule->status() != ModuleStatus::Evaluated) { - // Step 11.d.v.2. Append module to requiredModule.[[AsyncParentModules]]. - if (!ModuleObject::appendAsyncParentModule(cx, requiredModule, module)) { - return false; - } + // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then: + if (requiredModule->isAsyncEvaluating() && + requiredModule->status() != ModuleStatus::Evaluated) { + // Step 11.d.v.2. Append module to + // requiredModule.[[AsyncParentModules]]. + if (!ModuleObject::appendAsyncParentModule(cx, requiredModule, + module)) { + return false; + } - // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to - // module.[[PendingAsyncDependencies]] + 1. - module->setPendingAsyncDependencies(module->pendingAsyncDependencies() + - 1); + // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to + // module.[[PendingAsyncDependencies]] + 1. + module->setPendingAsyncDependencies(module->pendingAsyncDependencies() + + 1); + } } } diff --git a/js/src/vm/Modules.h b/js/src/vm/Modules.h index 18b97ac3d7..21aff46b90 100644 --- a/js/src/vm/Modules.h +++ b/js/src/vm/Modules.h @@ -34,6 +34,8 @@ bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module); // Start evaluating the module. If TLA is enabled, result will be a promise. bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module, MutableHandle<Value> result); +bool SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module, + MutableHandle<Value> result); void AsyncModuleExecutionFulfilled(JSContext* cx, Handle<ModuleObject*> module); diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index c952e1b40a..640a185981 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -943,8 +943,8 @@ bool NativeObject::growElements(JSContext* cx, uint32_t reqCapacity) { // For arrays with writable length, and all non-Array objects, call // `NativeObject::goodElementsAllocationAmount()` to determine the // amount to allocate from the the requested capacity and existing length. - if (!goodElementsAllocationAmount(cx, reqCapacity + numShifted, - getElementsHeader()->length, + uint32_t length = is<ArrayObject>() ? as<ArrayObject>().length() : 0; + if (!goodElementsAllocationAmount(cx, reqCapacity + numShifted, length, &newAllocated)) { return false; } diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index d71ca98fd0..438f361bcf 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2937,7 +2937,8 @@ /* * Push the number of actual arguments as Int32Value. * - * This is emitted for the ArgumentsLength() intrinsic in self-hosted code. + * This is emitted for the ArgumentsLength() intrinsic in self-hosted code, + * and if the script uses only arguments.length. * * Category: Variables and scopes * Type: Getting binding values diff --git a/js/src/vm/PlainObject.cpp b/js/src/vm/PlainObject.cpp index 8341636796..0eef3acbce 100644 --- a/js/src/vm/PlainObject.cpp +++ b/js/src/vm/PlainObject.cpp @@ -207,16 +207,15 @@ void js::NewPlainObjectWithPropsCache::add(SharedShape* shape) { entries_[0] = shape; } -static bool ShapeMatches(IdValuePair* properties, size_t nproperties, - SharedShape* shape) { - if (shape->slotSpan() != nproperties) { +static bool ShapeMatches(Handle<IdValueVector> properties, SharedShape* shape) { + if (shape->slotSpan() != properties.length()) { return false; } SharedShapePropertyIter<NoGC> iter(shape); - for (size_t i = nproperties; i > 0; i--) { + for (size_t i = properties.length(); i > 0; i--) { MOZ_ASSERT(iter->isDataProperty()); MOZ_ASSERT(iter->flags() == PropertyFlags::defaultDataPropFlags); - if (properties[i - 1].id != iter->key()) { + if (properties[i - 1].get().id != iter->key()) { return false; } iter++; @@ -226,10 +225,10 @@ static bool ShapeMatches(IdValuePair* properties, size_t nproperties, } SharedShape* js::NewPlainObjectWithPropsCache::lookup( - IdValuePair* properties, size_t nproperties) const { + Handle<IdValueVector> properties) const { for (size_t i = 0; i < NumEntries; i++) { SharedShape* shape = entries_[i]; - if (shape && ShapeMatches(properties, nproperties, shape)) { + if (shape && ShapeMatches(properties, shape)) { return shape; } } @@ -239,35 +238,33 @@ SharedShape* js::NewPlainObjectWithPropsCache::lookup( enum class KeysKind { UniqueNames, Unknown }; template <KeysKind Kind> -static PlainObject* NewPlainObjectWithProperties(JSContext* cx, - IdValuePair* properties, - size_t nproperties, - NewObjectKind newKind) { +static PlainObject* NewPlainObjectWithProperties( + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind) { auto& cache = cx->realm()->newPlainObjectWithPropsCache; // If we recently created an object with these properties, we can use that // Shape directly. - if (SharedShape* shape = cache.lookup(properties, nproperties)) { + if (SharedShape* shape = cache.lookup(properties)) { Rooted<SharedShape*> shapeRoot(cx, shape); PlainObject* obj = PlainObject::createWithShape(cx, shapeRoot, newKind); if (!obj) { return nullptr; } - MOZ_ASSERT(obj->slotSpan() == nproperties); - for (size_t i = 0; i < nproperties; i++) { - obj->initSlot(i, properties[i].value); + MOZ_ASSERT(obj->slotSpan() == properties.length()); + for (size_t i = 0; i < properties.length(); i++) { + obj->initSlot(i, properties[i].get().value); } return obj; } - gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); + gc::AllocKind allocKind = gc::GetGCObjectKind(properties.length()); Rooted<PlainObject*> obj(cx, NewPlainObjectWithAllocKind(cx, allocKind, newKind)); if (!obj) { return nullptr; } - if (nproperties == 0) { + if (properties.empty()) { return obj; } @@ -275,9 +272,9 @@ static PlainObject* NewPlainObjectWithProperties(JSContext* cx, Rooted<Value> value(cx); bool canCache = true; - for (size_t i = 0; i < nproperties; i++) { - key = properties[i].id; - value = properties[i].value; + for (const auto& prop : properties) { + key = prop.id; + value = prop.value; // Integer keys may need to be stored in dense elements. This is uncommon so // just fall back to NativeDefineDataProperty. @@ -314,7 +311,7 @@ static PlainObject* NewPlainObjectWithProperties(JSContext* cx, if (canCache && !obj->inDictionaryMode()) { MOZ_ASSERT(obj->getDenseInitializedLength() == 0); - MOZ_ASSERT(obj->slotSpan() == nproperties); + MOZ_ASSERT(obj->slotSpan() == properties.length()); cache.add(obj->sharedShape()); } @@ -322,17 +319,14 @@ static PlainObject* NewPlainObjectWithProperties(JSContext* cx, } PlainObject* js::NewPlainObjectWithUniqueNames(JSContext* cx, - IdValuePair* properties, - size_t nproperties, + Handle<IdValueVector> properties, NewObjectKind newKind) { - return NewPlainObjectWithProperties<KeysKind::UniqueNames>( - cx, properties, nproperties, newKind); + return NewPlainObjectWithProperties<KeysKind::UniqueNames>(cx, properties, + newKind); } -PlainObject* js::NewPlainObjectWithMaybeDuplicateKeys(JSContext* cx, - IdValuePair* properties, - size_t nproperties, - NewObjectKind newKind) { +PlainObject* js::NewPlainObjectWithMaybeDuplicateKeys( + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind) { return NewPlainObjectWithProperties<KeysKind::Unknown>(cx, properties, - nproperties, newKind); + newKind); } diff --git a/js/src/vm/PlainObject.h b/js/src/vm/PlainObject.h index 5dcbe29a46..aa2a202fa0 100644 --- a/js/src/vm/PlainObject.h +++ b/js/src/vm/PlainObject.h @@ -7,6 +7,7 @@ #ifndef vm_PlainObject_h #define vm_PlainObject_h +#include "ds/IdValuePair.h" #include "gc/AllocKind.h" // js::gc::AllocKind #include "js/Class.h" // JSClass #include "js/RootingAPI.h" // JS::Handle @@ -98,13 +99,13 @@ extern PlainObject* NewPlainObjectWithProtoAndAllocKind( // Create a plain object with the given properties. The list must not contain // duplicate keys or integer keys. extern PlainObject* NewPlainObjectWithUniqueNames( - JSContext* cx, IdValuePair* properties, size_t nproperties, + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind = GenericObject); // Create a plain object with the given properties. The list may contain integer // keys or duplicate keys. extern PlainObject* NewPlainObjectWithMaybeDuplicateKeys( - JSContext* cx, IdValuePair* properties, size_t nproperties, + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind = GenericObject); } // namespace js diff --git a/js/src/vm/PortableBaselineInterpret.cpp b/js/src/vm/PortableBaselineInterpret.cpp index e2acaf2d7b..2990942dc6 100644 --- a/js/src/vm/PortableBaselineInterpret.cpp +++ b/js/src/vm/PortableBaselineInterpret.cpp @@ -33,6 +33,8 @@ #include "jit/JitScript.h" #include "jit/JSJitFrameIter.h" #include "jit/VMFunctions.h" +#include "proxy/DeadObjectProxy.h" +#include "proxy/DOMProxy.h" #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" #include "vm/EnvironmentObject.h" @@ -448,6 +450,12 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, goto cacheop_##name; \ } +#define PREDICT_RETURN() \ + if (icregs.cacheIRReader.peekOp() == CacheOp::ReturnFromIC) { \ + TRACE_PRINTF("stub successful, predicted return\n"); \ + return ICInterpretOpResult::Return; \ + } + CacheOp cacheop; DISPATCH_CACHEOP(); @@ -498,6 +506,15 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardIsNotUninitializedLexical) { + ValOperandId valId = icregs.cacheIRReader.valOperandId(); + Value val = Value::fromRawBits(icregs.icVals[valId.id()]); + if (val == MagicValue(JS_UNINITIALIZED_LEXICAL)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardToBoolean) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); Value v = Value::fromRawBits(icregs.icVals[inputId.id()]); @@ -565,6 +582,15 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardToNonGCThing) { + ValOperandId inputId = icregs.cacheIRReader.valOperandId(); + Value input = Value::fromRawBits(icregs.icVals[inputId.id()]); + if (input.isGCThing()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardBooleanToInt32) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); @@ -595,6 +621,36 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } + CACHEOP_CASE(Int32ToIntPtr) { + Int32OperandId inputId = icregs.cacheIRReader.int32OperandId(); + IntPtrOperandId resultId = icregs.cacheIRReader.intPtrOperandId(); + BOUNDSCHECK(resultId); + int32_t input = int32_t(icregs.icVals[inputId.id()]); + // Note that this must sign-extend to pointer width: + icregs.icVals[resultId.id()] = intptr_t(input); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardToInt32ModUint32) { + ValOperandId inputId = icregs.cacheIRReader.valOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + Value input = Value::fromRawBits(icregs.icVals[inputId.id()]); + if (input.isInt32()) { + icregs.icVals[resultId.id()] = Int32Value(input.toInt32()).asRawBits(); + DISPATCH_CACHEOP(); + } else if (input.isDouble()) { + double doubleVal = input.toDouble(); + // Accept any double that fits in an int64_t but truncate the top 32 bits. + if (doubleVal >= double(INT64_MIN) && doubleVal <= double(INT64_MAX)) { + icregs.icVals[resultId.id()] = + Int32Value(int64_t(doubleVal)).asRawBits(); + DISPATCH_CACHEOP(); + } + } + return ICInterpretOpResult::NextIC; + } + CACHEOP_CASE(GuardNonDoubleType) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); ValueType type = icregs.cacheIRReader.valueType(); @@ -668,15 +724,24 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ObjOperandId objId = icregs.cacheIRReader.objOperandId(); uint32_t protoOffset = icregs.cacheIRReader.stubOffset(); JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); - uintptr_t expectedProto = - cstub->stubInfo()->getStubRawWord(cstub, protoOffset); - if (reinterpret_cast<uintptr_t>(obj->staticPrototype()) != expectedProto) { + JSObject* proto = reinterpret_cast<JSObject*>( + cstub->stubInfo()->getStubRawWord(cstub, protoOffset)); + if (obj->staticPrototype() != proto) { return ICInterpretOpResult::NextIC; } PREDICT_NEXT(LoadProto); DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardNullProto) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->taggedProto().raw()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardClass) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); GuardClassKind kind = icregs.cacheIRReader.guardClassKind(); @@ -697,16 +762,31 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } break; + case GuardClassKind::ResizableArrayBuffer: + if (object->getClass() != &ResizableArrayBufferObject::class_) { + return ICInterpretOpResult::NextIC; + } + break; case GuardClassKind::FixedLengthSharedArrayBuffer: if (object->getClass() != &FixedLengthSharedArrayBufferObject::class_) { return ICInterpretOpResult::NextIC; } break; + case GuardClassKind::GrowableSharedArrayBuffer: + if (object->getClass() != &GrowableSharedArrayBufferObject::class_) { + return ICInterpretOpResult::NextIC; + } + break; case GuardClassKind::FixedLengthDataView: if (object->getClass() != &FixedLengthDataViewObject::class_) { return ICInterpretOpResult::NextIC; } break; + case GuardClassKind::ResizableDataView: + if (object->getClass() != &ResizableDataViewObject::class_) { + return ICInterpretOpResult::NextIC; + } + break; case GuardClassKind::MappedArguments: if (object->getClass() != &MappedArgumentsObject::class_) { return ICInterpretOpResult::NextIC; @@ -747,6 +827,18 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardAnyClass) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t claspOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSClass* clasp = reinterpret_cast<JSClass*>( + cstub->stubInfo()->getStubRawWord(cstub, claspOffset)); + if (obj->getClass() != clasp) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardGlobalGeneration) { uint32_t expectedOffset = icregs.cacheIRReader.stubOffset(); uint32_t generationAddrOffset = icregs.cacheIRReader.stubOffset(); @@ -760,12 +852,131 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(HasClassResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t claspOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSClass* clasp = reinterpret_cast<JSClass*>( + cstub->stubInfo()->getStubRawWord(cstub, claspOffset)); + icregs.icResult = BooleanValue(obj->getClass() == clasp).asRawBits(); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardCompartment) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t globalOffset = icregs.cacheIRReader.stubOffset(); + uint32_t compartmentOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* global = reinterpret_cast<JSObject*>( + cstub->stubInfo()->getStubRawWord(cstub, globalOffset)); + JS::Compartment* compartment = reinterpret_cast<JS::Compartment*>( + cstub->stubInfo()->getStubRawWord(cstub, compartmentOffset)); + if (IsDeadProxyObject(global)) { + return ICInterpretOpResult::NextIC; + } + if (obj->compartment() != compartment) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsExtensible) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->nonProxyIsExtensible()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNativeObject) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (!obj->is<NativeObject>()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsProxy) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (!obj->is<ProxyObject>()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNotProxy) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->is<ProxyObject>()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNotArrayBufferMaybeShared) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + const JSClass* clasp = obj->getClass(); + if (clasp == &ArrayBufferObject::protoClass_ || + clasp == &SharedArrayBufferObject::protoClass_) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsTypedArray) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (!IsTypedArrayClass(obj->getClass())) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardHasProxyHandler) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t handlerOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + BaseProxyHandler* handler = reinterpret_cast<BaseProxyHandler*>( + cstub->stubInfo()->getStubRawWord(cstub, handlerOffset)); + if (obj->as<ProxyObject>().handler() != handler) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNotDOMProxy) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->as<ProxyObject>().handler()->family() == + GetDOMProxyHandlerFamily()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardSpecificObject) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); uint32_t expectedOffset = icregs.cacheIRReader.stubOffset(); - uintptr_t expected = - cstub->stubInfo()->getStubRawWord(cstub, expectedOffset); - if (expected != icregs.icVals[objId.id()]) { + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* expected = reinterpret_cast<JSObject*>( + cstub->stubInfo()->getStubRawWord(cstub, expectedOffset)); + if (obj != expected) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardObjectIdentity) { + ObjOperandId obj1Id = icregs.cacheIRReader.objOperandId(); + ObjOperandId obj2Id = icregs.cacheIRReader.objOperandId(); + JSObject* obj1 = reinterpret_cast<JSObject*>(icregs.icVals[obj1Id.id()]); + JSObject* obj2 = reinterpret_cast<JSObject*>(icregs.icVals[obj2Id.id()]); + if (obj1 != obj2) { return ICInterpretOpResult::NextIC; } DISPATCH_CACHEOP(); @@ -808,6 +1019,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, uintptr_t expected = cstub->stubInfo()->getStubRawWord(cstub, expectedOffset); if (expected != icregs.icVals[strId.id()]) { + // TODO: BaselineCacheIRCompiler also checks for equal strings return ICInterpretOpResult::NextIC; } DISPATCH_CACHEOP(); @@ -833,23 +1045,211 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardNoDenseElements) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->as<NativeObject>().getDenseInitializedLength() != 0) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardStringToIndex) { + StringOperandId strId = icregs.cacheIRReader.stringOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + JSString* str = reinterpret_cast<JSString*>(icregs.icVals[strId.id()]); + int32_t result; + if (str->hasIndexValue()) { + uint32_t index = str->getIndexValue(); + MOZ_ASSERT(index <= INT32_MAX); + result = index; + } else { + result = GetIndexFromString(str); + if (result < 0) { + return ICInterpretOpResult::NextIC; + } + } + icregs.icVals[resultId.id()] = result; + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardStringToInt32) { + StringOperandId strId = icregs.cacheIRReader.stringOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + JSString* str = reinterpret_cast<JSString*>(icregs.icVals[strId.id()]); + int32_t result; + // Use indexed value as fast path if possible. + if (str->hasIndexValue()) { + uint32_t index = str->getIndexValue(); + MOZ_ASSERT(index <= INT32_MAX); + result = index; + } else { + if (!GetInt32FromStringPure(frameMgr.cxForLocalUseOnly(), str, &result)) { + return ICInterpretOpResult::NextIC; + } + } + icregs.icVals[resultId.id()] = result; + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardStringToNumber) { + StringOperandId strId = icregs.cacheIRReader.stringOperandId(); + NumberOperandId resultId = icregs.cacheIRReader.numberOperandId(); + BOUNDSCHECK(resultId); + JSString* str = reinterpret_cast<JSString*>(icregs.icVals[strId.id()]); + Value result; + // Use indexed value as fast path if possible. + if (str->hasIndexValue()) { + uint32_t index = str->getIndexValue(); + MOZ_ASSERT(index <= INT32_MAX); + result = Int32Value(index); + } else { + double value; + if (!StringToNumberPure(frameMgr.cxForLocalUseOnly(), str, &value)) { + return ICInterpretOpResult::NextIC; + } + result = DoubleValue(value); + } + icregs.icVals[resultId.id()] = result.asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(BooleanToNumber) { + BooleanOperandId booleanId = icregs.cacheIRReader.booleanOperandId(); + NumberOperandId resultId = icregs.cacheIRReader.numberOperandId(); + BOUNDSCHECK(resultId); + uint64_t boolean = icregs.icVals[booleanId.id()]; + MOZ_ASSERT((boolean & ~1) == 0); + icregs.icVals[resultId.id()] = Int32Value(boolean).asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardHasGetterSetter) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t idOffset = icregs.cacheIRReader.stubOffset(); + uint32_t getterSetterOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + jsid id = + jsid::fromRawBits(cstub->stubInfo()->getStubRawWord(cstub, idOffset)); + GetterSetter* getterSetter = reinterpret_cast<GetterSetter*>( + cstub->stubInfo()->getStubRawWord(cstub, getterSetterOffset)); + if (!ObjectHasGetterSetterPure(frameMgr.cxForLocalUseOnly(), obj, id, + getterSetter)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardInt32IsNonNegative) { + Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); + int32_t index = int32_t(icregs.icVals[indexId.id()]); + if (index < 0) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardDynamicSlotIsSpecificObject) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); ObjOperandId expectedId = icregs.cacheIRReader.objOperandId(); uint32_t slotOffset = icregs.cacheIRReader.stubOffset(); JSObject* expected = reinterpret_cast<JSObject*>(icregs.icVals[expectedId.id()]); - uintptr_t offset = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); + uintptr_t slot = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); NativeObject* nobj = reinterpret_cast<NativeObject*>(icregs.icVals[objId.id()]); HeapSlot* slots = nobj->getSlotsUnchecked(); - Value actual = slots[offset / sizeof(Value)]; + // Note that unlike similar opcodes, GuardDynamicSlotIsSpecificObject takes + // a slot index rather than a byte offset. + Value actual = slots[slot]; if (actual != ObjectValue(*expected)) { return ICInterpretOpResult::NextIC; } DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardDynamicSlotIsNotObject) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t slotOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t slot = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); + NativeObject* nobj = &obj->as<NativeObject>(); + HeapSlot* slots = nobj->getSlotsUnchecked(); + // Note that unlike similar opcodes, GuardDynamicSlotIsNotObject takes a + // slot index rather than a byte offset. + Value actual = slots[slot]; + if (actual.isObject()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFixedSlotValue) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + uint32_t valOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value val = Value::fromRawBits( + cstub->stubInfo()->getStubRawInt64(cstub, valOffset)); + GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>( + reinterpret_cast<uintptr_t>(obj) + offset); + Value actual = slot->get(); + if (actual != val) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardDynamicSlotValue) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + uint32_t valOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value val = Value::fromRawBits( + cstub->stubInfo()->getStubRawInt64(cstub, valOffset)); + NativeObject* nobj = &obj->as<NativeObject>(); + HeapSlot* slots = nobj->getSlotsUnchecked(); + Value actual = slots[offset / sizeof(Value)]; + if (actual != val) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadFixedSlot) { + ValOperandId resultId = icregs.cacheIRReader.valOperandId(); + BOUNDSCHECK(resultId); + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>( + reinterpret_cast<uintptr_t>(obj) + offset); + Value actual = slot->get(); + icregs.icVals[resultId.id()] = actual.asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadDynamicSlot) { + ValOperandId resultId = icregs.cacheIRReader.valOperandId(); + BOUNDSCHECK(resultId); + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t slotOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t slot = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); + NativeObject* nobj = &obj->as<NativeObject>(); + HeapSlot* slots = nobj->getSlotsUnchecked(); + // Note that unlike similar opcodes, LoadDynamicSlot takes a slot index + // rather than a byte offset. + Value actual = slots[slot]; + icregs.icVals[resultId.id()] = actual.asRawBits(); + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardNoAllocationMetadataBuilder) { uint32_t builderAddrOffset = icregs.cacheIRReader.stubOffset(); uintptr_t builderAddr = @@ -860,6 +1260,73 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardFunctionHasJitEntry) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + bool constructing = icregs.cacheIRReader.readBool(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + uint16_t flags = FunctionFlags::HasJitEntryFlags(constructing); + if (!fun->as<JSFunction>().flags().hasFlags(flags)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFunctionHasNoJitEntry) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + uint16_t flags = FunctionFlags::HasJitEntryFlags(/*constructing =*/false); + if (fun->as<JSFunction>().flags().hasFlags(flags)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFunctionIsNonBuiltinCtor) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + if (!fun->as<JSFunction>().isNonBuiltinConstructor()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFunctionIsConstructor) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + if (!fun->as<JSFunction>().isConstructor()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardNotClassConstructor) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + if (fun->as<JSFunction>().isClassConstructor()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardArrayIsPacked) { + ObjOperandId arrayId = icregs.cacheIRReader.objOperandId(); + JSObject* array = reinterpret_cast<JSObject*>(icregs.icVals[arrayId.id()]); + if (!IsPackedArray(array)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardArgumentsObjectFlags) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint8_t flags = icregs.cacheIRReader.readByte(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->as<ArgumentsObject>().hasFlags(flags)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(LoadObject) { ObjOperandId resultId = icregs.cacheIRReader.objOperandId(); BOUNDSCHECK(resultId); @@ -893,6 +1360,35 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(LoadEnclosingEnvironment) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ObjOperandId resultId = icregs.cacheIRReader.objOperandId(); + BOUNDSCHECK(resultId); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* env = &obj->as<EnvironmentObject>().enclosingEnvironment(); + icregs.icVals[resultId.id()] = reinterpret_cast<uintptr_t>(env); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadWrapperTarget) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ObjOperandId resultId = icregs.cacheIRReader.objOperandId(); + BOUNDSCHECK(resultId); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* target = &obj->as<ProxyObject>().private_().toObject(); + icregs.icVals[resultId.id()] = reinterpret_cast<uintptr_t>(target); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadValueTag) { + ValOperandId valId = icregs.cacheIRReader.valOperandId(); + ValueTagOperandId resultId = icregs.cacheIRReader.valueTagOperandId(); + BOUNDSCHECK(resultId); + Value val = Value::fromRawBits(icregs.icVals[valId.id()]); + icregs.icVals[resultId.id()] = val.extractNonDoubleType(); + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(LoadArgumentFixedSlot) { ValOperandId resultId = icregs.cacheIRReader.valOperandId(); BOUNDSCHECK(resultId); @@ -915,6 +1411,70 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(TruncateDoubleToUInt32) { + NumberOperandId inputId = icregs.cacheIRReader.numberOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + Value input = Value::fromRawBits(icregs.icVals[inputId.id()]); + icregs.icVals[resultId.id()] = JS::ToInt32(input.toNumber()); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MegamorphicLoadSlotResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t nameOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + jsid name = + jsid::fromRawBits(cstub->stubInfo()->getStubRawWord(cstub, nameOffset)); + if (!obj->shape()->isNative()) { + return ICInterpretOpResult::NextIC; + } + Value result; + if (!GetNativeDataPropertyPureWithCacheLookup( + frameMgr.cxForLocalUseOnly(), obj, name, nullptr, &result)) { + return ICInterpretOpResult::NextIC; + } + icregs.icResult = result.asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MegamorphicLoadSlotByValueResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ValOperandId idId = icregs.cacheIRReader.valOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + Value id = Value::fromRawBits(icregs.icVals[idId.id()]); + if (!obj->shape()->isNative()) { + return ICInterpretOpResult::NextIC; + } + Value values[2] = {id}; + if (!GetNativeDataPropertyByValuePure(frameMgr.cxForLocalUseOnly(), obj, + nullptr, values)) { + return ICInterpretOpResult::NextIC; + } + icregs.icResult = values[1].asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MegamorphicSetElement) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ValOperandId idId = icregs.cacheIRReader.valOperandId(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + bool strict = icregs.cacheIRReader.readBool(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + Value id = Value::fromRawBits(icregs.icVals[idId.id()]); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + { + PUSH_IC_FRAME(); + ReservedRooted<JSObject*> obj0(&state.obj0, obj); + ReservedRooted<Value> value0(&state.value0, id); + ReservedRooted<Value> value1(&state.value1, rhs); + if (!SetElementMegamorphic<false>(cx, obj0, value0, value1, strict)) { + return ICInterpretOpResult::Error; + } + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(StoreFixedSlot) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); @@ -926,7 +1486,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, reinterpret_cast<uintptr_t>(nobj) + offset); Value val = Value::fromRawBits(icregs.icVals[rhsId.id()]); slot->set(val); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -942,7 +1502,75 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, size_t dynSlot = offset / sizeof(Value); size_t slot = dynSlot + nobj->numFixedSlots(); slots[dynSlot].set(nobj, HeapSlot::Slot, slot, val); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(AddAndStoreFixedSlot) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + uint32_t newShapeOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + int32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + Shape* newShape = reinterpret_cast<Shape*>( + cstub->stubInfo()->getStubRawWord(cstub, newShapeOffset)); + obj->setShape(newShape); + GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>( + reinterpret_cast<uintptr_t>(obj) + offset); + slot->init(rhs); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(AddAndStoreDynamicSlot) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + uint32_t newShapeOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + int32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + Shape* newShape = reinterpret_cast<Shape*>( + cstub->stubInfo()->getStubRawWord(cstub, newShapeOffset)); + NativeObject* nobj = &obj->as<NativeObject>(); + obj->setShape(newShape); + HeapSlot* slots = nobj->getSlotsUnchecked(); + size_t dynSlot = offset / sizeof(Value); + size_t slot = dynSlot + nobj->numFixedSlots(); + slots[dynSlot].init(nobj, HeapSlot::Slot, slot, rhs); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(AllocateAndStoreDynamicSlot) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + uint32_t newShapeOffset = icregs.cacheIRReader.stubOffset(); + uint32_t numNewSlotsOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + int32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + Shape* newShape = reinterpret_cast<Shape*>( + cstub->stubInfo()->getStubRawWord(cstub, newShapeOffset)); + int32_t numNewSlots = + cstub->stubInfo()->getStubRawInt32(cstub, numNewSlotsOffset); + NativeObject* nobj = &obj->as<NativeObject>(); + // We have to (re)allocate dynamic slots. Do this first, as it's the + // only fallible operation here. Note that growSlotsPure is fallible but + // does not GC. Otherwise this is the same as AddAndStoreDynamicSlot above. + if (!NativeObject::growSlotsPure(frameMgr.cxForLocalUseOnly(), nobj, + numNewSlots)) { + return ICInterpretOpResult::NextIC; + } + obj->setShape(newShape); + HeapSlot* slots = nobj->getSlotsUnchecked(); + size_t dynSlot = offset / sizeof(Value); + size_t slot = dynSlot + nobj->numFixedSlots(); + slots[dynSlot].init(nobj, HeapSlot::Slot, slot, rhs); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -964,7 +1592,71 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, Value val = Value::fromRawBits(icregs.icVals[rhsId.id()]); slot->set(nobj, HeapSlot::Element, index + elems->numShiftedElements(), val); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(StoreDenseElementHole) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + bool handleAdd = icregs.cacheIRReader.readBool(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t index = uint32_t(icregs.icVals[indexId.id()]); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + NativeObject* nobj = &obj->as<NativeObject>(); + uint32_t initLength = nobj->getDenseInitializedLength(); + if (index < initLength) { + nobj->setDenseElement(index, rhs); + } else if (!handleAdd || index > initLength) { + return ICInterpretOpResult::NextIC; + } else { + if (index >= nobj->getDenseCapacity()) { + if (!NativeObject::addDenseElementPure(frameMgr.cxForLocalUseOnly(), + nobj)) { + return ICInterpretOpResult::NextIC; + } + } + nobj->setDenseInitializedLength(initLength + 1); + + // Baseline always updates the length field by directly accessing its + // offset in ObjectElements. If the object is not an ArrayObject then this + // field is never read, so it's okay to skip the update here in that case. + if (nobj->is<ArrayObject>()) { + ArrayObject* aobj = &nobj->as<ArrayObject>(); + uint32_t len = aobj->length(); + if (len <= index) { + aobj->setLength(len + 1); + } + } + + nobj->initDenseElement(index, rhs); + } + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(ArrayPush) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + ArrayObject* aobj = &obj->as<ArrayObject>(); + uint32_t initLength = aobj->getDenseInitializedLength(); + if (aobj->length() != initLength) { + return ICInterpretOpResult::NextIC; + } + if (initLength >= aobj->getDenseCapacity()) { + if (!NativeObject::addDenseElementPure(frameMgr.cxForLocalUseOnly(), + aobj)) { + return ICInterpretOpResult::NextIC; + } + } + aobj->setDenseInitializedLength(initLength + 1); + aobj->setLength(initLength + 1); + aobj->initDenseElement(initLength, rhs); + icregs.icResult = Int32Value(initLength + 1).asRawBits(); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -972,7 +1664,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ValOperandId inputId = icregs.cacheIRReader.valOperandId(); Value val = Value::fromRawBits(icregs.icVals[inputId.id()]); icregs.icResult = BooleanValue(val.isObject()).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -989,6 +1681,66 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(StoreTypedArrayElement) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + Scalar::Type elementType = icregs.cacheIRReader.scalarType(); + IntPtrOperandId indexId = icregs.cacheIRReader.intPtrOperandId(); + uint32_t rhsId = icregs.cacheIRReader.rawOperandId(); + bool handleOOB = icregs.cacheIRReader.readBool(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uintptr_t index = uintptr_t(icregs.icVals[indexId.id()]); + uint64_t rhs = icregs.icVals[rhsId]; + if (obj->as<TypedArrayObject>().length().isNothing()) { + return ICInterpretOpResult::NextIC; + } + if (index >= obj->as<TypedArrayObject>().length().value()) { + if (!handleOOB) { + return ICInterpretOpResult::NextIC; + } + } else { + Value v; + switch (elementType) { + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Uint8Clamped: + v = Int32Value(rhs); + break; + + case Scalar::Float32: + case Scalar::Float64: + v = Value::fromRawBits(rhs); + MOZ_ASSERT(v.isNumber()); + break; + + case Scalar::BigInt64: + case Scalar::BigUint64: + v = BigIntValue(reinterpret_cast<JS::BigInt*>(rhs)); + break; + + case Scalar::MaxTypedArrayViewType: + case Scalar::Int64: + case Scalar::Simd128: + MOZ_CRASH("Unsupported TypedArray type"); + } + + // SetTypedArrayElement doesn't do anything that can actually GC or need a + // new context when the value can only be Int32, Double, or BigInt, as the + // above switch statement enforces. + FakeRooted<TypedArrayObject*> obj0(nullptr, &obj->as<TypedArrayObject>()); + FakeRooted<Value> value0(nullptr, v); + ObjectOpResult result; + MOZ_ASSERT(elementType == obj0->type()); + MOZ_ALWAYS_TRUE(SetTypedArrayElement(frameMgr.cxForLocalUseOnly(), obj0, + index, value0, result)); + MOZ_ALWAYS_TRUE(result.ok()); + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(CallInt32ToString) { Int32OperandId inputId = icregs.cacheIRReader.int32OperandId(); StringOperandId resultId = icregs.cacheIRReader.stringOperandId(); @@ -1031,8 +1783,12 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } // For now, fail any constructing or different-realm cases. - if (flags.isConstructing() || !flags.isSameRealm()) { - TRACE_PRINTF("failing: constructing or not same realm\n"); + if (flags.isConstructing()) { + TRACE_PRINTF("failing: constructing\n"); + return ICInterpretOpResult::NextIC; + } + if (!flags.isSameRealm()) { + TRACE_PRINTF("failing: not same realm\n"); return ICInterpretOpResult::NextIC; } // And support only "standard" arg formats. @@ -1123,6 +1879,15 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } } + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MetaScriptedThisShape) { + uint32_t thisShapeOffset = icregs.cacheIRReader.stubOffset(); + // This op is only metadata for the Warp Transpiler and should be ignored. + (void)thisShapeOffset; + PREDICT_NEXT(CallScriptedFunction); DISPATCH_CACHEOP(); } @@ -1139,7 +1904,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, "slot %" PRIx64 "\n", nobj, int(offsetOffset), int(offset), slot, slot->asRawBits()); icregs.icResult = slot->asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1151,7 +1916,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, reinterpret_cast<NativeObject*>(icregs.icVals[objId.id()]); HeapSlot* slots = nobj->getSlotsUnchecked(); icregs.icResult = slots[offset / sizeof(Value)].get().asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1171,7 +1936,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = val.asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1184,7 +1949,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = Int32Value(length).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1202,6 +1967,22 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(LoadArgumentsObjectArgResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t index = uint32_t(icregs.icVals[indexId.id()]); + ArgumentsObject* args = &obj->as<ArgumentsObject>(); + if (index >= args->initialLength() || args->hasOverriddenElement()) { + return ICInterpretOpResult::NextIC; + } + if (args->argIsForwarded(index)) { + return ICInterpretOpResult::NextIC; + } + icregs.icResult = args->arg(index).asRawBits(); + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(LinearizeForCharAccess) { StringOperandId strId = icregs.cacheIRReader.stringOperandId(); Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); @@ -1258,7 +2039,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } } icregs.icResult = StringValue(result).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1286,7 +2067,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, result = Int32Value(c); } icregs.icResult = result.asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1298,7 +2079,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = Int32Value(length).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1307,7 +2088,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = ObjectValue(*reinterpret_cast<JSObject*>(icregs.icVals[objId.id()])) .asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1316,6 +2097,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = StringValue(reinterpret_cast<JSString*>(icregs.icVals[strId.id()])) .asRawBits(); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1324,14 +2106,14 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = SymbolValue(reinterpret_cast<JS::Symbol*>(icregs.icVals[symId.id()])) .asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadInt32Result) { Int32OperandId valId = icregs.cacheIRReader.int32OperandId(); icregs.icResult = Int32Value(icregs.icVals[valId.id()]).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1342,7 +2124,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, val = DoubleValue(val.toInt32()); } icregs.icResult = val.asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1351,14 +2133,14 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = BigIntValue(reinterpret_cast<JS::BigInt*>(icregs.icVals[valId.id()])) .asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadBooleanResult) { bool val = icregs.cacheIRReader.readBool(); icregs.icResult = BooleanValue(val).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1376,7 +2158,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, JSString* str = reinterpret_cast<JSString*>( cstub->stubInfo()->getStubRawWord(cstub, strOffset)); icregs.icResult = StringValue(str).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1392,12 +2174,14 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; \ } \ icregs.icResult = Int32Value(int32_t(result)).asRawBits(); \ - PREDICT_NEXT(ReturnFromIC); \ + PREDICT_RETURN(); \ DISPATCH_CACHEOP(); \ } + // clang-format off INT32_OP(Add, +, {}); INT32_OP(Sub, -, {}); + // clang-format on INT32_OP(Mul, *, { if (rhs * lhs == 0 && ((rhs < 0) ^ (lhs < 0))) { return ICInterpretOpResult::NextIC; @@ -1422,8 +2206,11 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } }); + // clang-format off INT32_OP(BitOr, |, {}); + INT32_OP(BitXor, ^, {}); INT32_OP(BitAnd, &, {}); + // clang-format on CACHEOP_CASE(Int32PowResult) { Int32OperandId lhsId = icregs.cacheIRReader.int32OperandId(); @@ -1458,7 +2245,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } icregs.icResult = Int32Value(int32_t(result)).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1470,7 +2257,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = Int32Value(int32_t(value)).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1478,7 +2265,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ValOperandId inputId = icregs.cacheIRReader.valOperandId(); int32_t val = int32_t(icregs.icVals[inputId.id()]); icregs.icResult = BooleanValue(val != 0).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1487,7 +2274,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, JSString* str = reinterpret_cast<JSLinearString*>(icregs.icVals[strId.id()]); icregs.icResult = BooleanValue(str->length() > 0).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1499,21 +2286,21 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = BooleanValue(!cls->emulatesUndefined()).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadValueResult) { uint32_t valOffset = icregs.cacheIRReader.stubOffset(); icregs.icResult = cstub->stubInfo()->getStubRawInt64(cstub, valOffset); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadOperandResult) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); icregs.icResult = icregs.icVals[inputId.id()]; - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1533,7 +2320,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } else { return ICInterpretOpResult::NextIC; } - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1548,57 +2335,72 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ReservedRooted<JSString*> rhs( &state.str1, reinterpret_cast<JSString*>(icregs.icVals[rhsId.id()])); bool result; - switch (op) { - case JSOp::Eq: - case JSOp::StrictEq: - if (lhs->length() != rhs->length()) { - result = false; + if (lhs == rhs) { + // If operands point to the same instance, the strings are trivially + // equal. + result = op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le || + op == JSOp::Ge; + } else { + switch (op) { + case JSOp::Eq: + case JSOp::StrictEq: + if (lhs->isAtom() && rhs->isAtom()) { + result = false; + break; + } + if (lhs->length() != rhs->length()) { + result = false; + break; + } + if (!StringsEqual<EqualityKind::Equal>(cx, lhs, rhs, &result)) { + return ICInterpretOpResult::Error; + } break; - } - if (!StringsEqual<EqualityKind::Equal>(cx, lhs, rhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Ne: - case JSOp::StrictNe: - if (lhs->length() != rhs->length()) { - result = true; + case JSOp::Ne: + case JSOp::StrictNe: + if (lhs->isAtom() && rhs->isAtom()) { + result = true; + break; + } + if (lhs->length() != rhs->length()) { + result = true; + break; + } + if (!StringsEqual<EqualityKind::NotEqual>(cx, lhs, rhs, &result)) { + return ICInterpretOpResult::Error; + } break; - } - if (!StringsEqual<EqualityKind::NotEqual>(cx, lhs, rhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Lt: - if (!StringsCompare<ComparisonKind::LessThan>(cx, lhs, rhs, - &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Ge: - if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>(cx, lhs, rhs, - &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Le: - if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>( - cx, /* N.B. swapped order */ rhs, lhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Gt: - if (!StringsCompare<ComparisonKind::LessThan>( - cx, /* N.B. swapped order */ rhs, lhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - default: - MOZ_CRASH("bad opcode"); + case JSOp::Lt: + if (!StringsCompare<ComparisonKind::LessThan>(cx, lhs, rhs, + &result)) { + return ICInterpretOpResult::Error; + } + break; + case JSOp::Ge: + if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>( + cx, lhs, rhs, &result)) { + return ICInterpretOpResult::Error; + } + break; + case JSOp::Le: + if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>( + cx, /* N.B. swapped order */ rhs, lhs, &result)) { + return ICInterpretOpResult::Error; + } + break; + case JSOp::Gt: + if (!StringsCompare<ComparisonKind::LessThan>( + cx, /* N.B. swapped order */ rhs, lhs, &result)) { + return ICInterpretOpResult::Error; + } + break; + default: + MOZ_CRASH("bad opcode"); + } } icregs.icResult = BooleanValue(result).asRawBits(); } - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1636,7 +2438,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, MOZ_CRASH("Unexpected opcode"); } icregs.icResult = BooleanValue(result).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1671,7 +2473,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, MOZ_CRASH("bad opcode"); } icregs.icResult = BooleanValue(result).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1686,15 +2488,9 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } - CACHEOP_CASE_UNIMPL(GuardToNonGCThing) - CACHEOP_CASE_UNIMPL(Int32ToIntPtr) CACHEOP_CASE_UNIMPL(GuardNumberToIntPtrIndex) - CACHEOP_CASE_UNIMPL(GuardToInt32ModUint32) CACHEOP_CASE_UNIMPL(GuardToUint8Clamped) CACHEOP_CASE_UNIMPL(GuardMultipleShapes) - CACHEOP_CASE_UNIMPL(GuardNullProto) - CACHEOP_CASE_UNIMPL(GuardAnyClass) - CACHEOP_CASE_UNIMPL(HasClassResult) CACHEOP_CASE_UNIMPL(CallRegExpMatcherResult) CACHEOP_CASE_UNIMPL(CallRegExpSearcherResult) CACHEOP_CASE_UNIMPL(RegExpSearcherLastLimitResult) @@ -1708,53 +2504,19 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(RegExpPrototypeOptimizableResult) CACHEOP_CASE_UNIMPL(RegExpInstanceOptimizableResult) CACHEOP_CASE_UNIMPL(GetFirstDollarIndexResult) - CACHEOP_CASE_UNIMPL(GuardCompartment) - CACHEOP_CASE_UNIMPL(GuardIsExtensible) - CACHEOP_CASE_UNIMPL(GuardIsNativeObject) - CACHEOP_CASE_UNIMPL(GuardIsProxy) - CACHEOP_CASE_UNIMPL(GuardIsNotProxy) - CACHEOP_CASE_UNIMPL(GuardIsNotArrayBufferMaybeShared) - CACHEOP_CASE_UNIMPL(GuardIsTypedArray) CACHEOP_CASE_UNIMPL(GuardIsFixedLengthTypedArray) - CACHEOP_CASE_UNIMPL(GuardHasProxyHandler) - CACHEOP_CASE_UNIMPL(GuardIsNotDOMProxy) - CACHEOP_CASE_UNIMPL(GuardObjectIdentity) - CACHEOP_CASE_UNIMPL(GuardNoDenseElements) - CACHEOP_CASE_UNIMPL(GuardStringToIndex) - CACHEOP_CASE_UNIMPL(GuardStringToInt32) - CACHEOP_CASE_UNIMPL(GuardStringToNumber) + CACHEOP_CASE_UNIMPL(GuardIsResizableTypedArray) CACHEOP_CASE_UNIMPL(StringToAtom) - CACHEOP_CASE_UNIMPL(BooleanToNumber) - CACHEOP_CASE_UNIMPL(GuardHasGetterSetter) - CACHEOP_CASE_UNIMPL(GuardInt32IsNonNegative) CACHEOP_CASE_UNIMPL(GuardIndexIsValidUpdateOrAdd) CACHEOP_CASE_UNIMPL(GuardIndexIsNotDenseElement) CACHEOP_CASE_UNIMPL(GuardTagNotEqual) CACHEOP_CASE_UNIMPL(GuardXrayExpandoShapeAndDefaultProto) CACHEOP_CASE_UNIMPL(GuardXrayNoExpando) - CACHEOP_CASE_UNIMPL(GuardDynamicSlotIsNotObject) - CACHEOP_CASE_UNIMPL(GuardFixedSlotValue) - CACHEOP_CASE_UNIMPL(GuardDynamicSlotValue) + CACHEOP_CASE_UNIMPL(GuardEitherClass) CACHEOP_CASE_UNIMPL(LoadScriptedProxyHandler) CACHEOP_CASE_UNIMPL(IdToStringOrSymbol) - CACHEOP_CASE_UNIMPL(LoadFixedSlot) - CACHEOP_CASE_UNIMPL(LoadDynamicSlot) - CACHEOP_CASE_UNIMPL(GuardFunctionHasJitEntry) - CACHEOP_CASE_UNIMPL(GuardFunctionHasNoJitEntry) - CACHEOP_CASE_UNIMPL(GuardFunctionIsNonBuiltinCtor) - CACHEOP_CASE_UNIMPL(GuardFunctionIsConstructor) - CACHEOP_CASE_UNIMPL(GuardNotClassConstructor) - CACHEOP_CASE_UNIMPL(GuardArrayIsPacked) - CACHEOP_CASE_UNIMPL(GuardArgumentsObjectFlags) - CACHEOP_CASE_UNIMPL(LoadEnclosingEnvironment) - CACHEOP_CASE_UNIMPL(LoadWrapperTarget) - CACHEOP_CASE_UNIMPL(LoadValueTag) - CACHEOP_CASE_UNIMPL(TruncateDoubleToUInt32) CACHEOP_CASE_UNIMPL(DoubleToUint8Clamped) - CACHEOP_CASE_UNIMPL(MegamorphicLoadSlotResult) - CACHEOP_CASE_UNIMPL(MegamorphicLoadSlotByValueResult) CACHEOP_CASE_UNIMPL(MegamorphicStoreSlot) - CACHEOP_CASE_UNIMPL(MegamorphicSetElement) CACHEOP_CASE_UNIMPL(MegamorphicHasPropResult) CACHEOP_CASE_UNIMPL(SmallObjectVariableKeyHasOwnResult) CACHEOP_CASE_UNIMPL(ObjectToIteratorResult) @@ -1763,12 +2525,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(LoadDOMExpandoValueGuardGeneration) CACHEOP_CASE_UNIMPL(LoadDOMExpandoValueIgnoreGeneration) CACHEOP_CASE_UNIMPL(GuardDOMExpandoMissingOrGuardShape) - CACHEOP_CASE_UNIMPL(AddAndStoreFixedSlot) - CACHEOP_CASE_UNIMPL(AddAndStoreDynamicSlot) - CACHEOP_CASE_UNIMPL(AllocateAndStoreDynamicSlot) CACHEOP_CASE_UNIMPL(AddSlotAndCallAddPropHook) - CACHEOP_CASE_UNIMPL(StoreDenseElementHole) - CACHEOP_CASE_UNIMPL(ArrayPush) CACHEOP_CASE_UNIMPL(ArrayJoinResult) CACHEOP_CASE_UNIMPL(ObjectKeysResult) CACHEOP_CASE_UNIMPL(PackedArrayPopResult) @@ -1785,10 +2542,22 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(IsTypedArrayConstructorResult) CACHEOP_CASE_UNIMPL(ArrayBufferViewByteOffsetInt32Result) CACHEOP_CASE_UNIMPL(ArrayBufferViewByteOffsetDoubleResult) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult) CACHEOP_CASE_UNIMPL(TypedArrayByteLengthInt32Result) CACHEOP_CASE_UNIMPL(TypedArrayByteLengthDoubleResult) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteLengthInt32Result) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteLengthDoubleResult) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayLengthInt32Result) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayLengthDoubleResult) CACHEOP_CASE_UNIMPL(TypedArrayElementSizeResult) + CACHEOP_CASE_UNIMPL(ResizableDataViewByteLengthInt32Result) + CACHEOP_CASE_UNIMPL(ResizableDataViewByteLengthDoubleResult) + CACHEOP_CASE_UNIMPL(GrowableSharedArrayBufferByteLengthInt32Result) + CACHEOP_CASE_UNIMPL(GrowableSharedArrayBufferByteLengthDoubleResult) CACHEOP_CASE_UNIMPL(GuardHasAttachedArrayBuffer) + CACHEOP_CASE_UNIMPL(GuardResizableArrayBufferViewInBounds) + CACHEOP_CASE_UNIMPL(GuardResizableArrayBufferViewInBoundsOrDetached) CACHEOP_CASE_UNIMPL(NewArrayIteratorResult) CACHEOP_CASE_UNIMPL(NewStringIteratorResult) CACHEOP_CASE_UNIMPL(NewRegExpStringIteratorResult) @@ -1843,7 +2612,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(DoubleParseIntResult) CACHEOP_CASE_UNIMPL(ObjectToStringResult) CACHEOP_CASE_UNIMPL(ReflectGetPrototypeOfResult) - CACHEOP_CASE_UNIMPL(StoreTypedArrayElement) CACHEOP_CASE_UNIMPL(AtomicsCompareExchangeResult) CACHEOP_CASE_UNIMPL(AtomicsExchangeResult) CACHEOP_CASE_UNIMPL(AtomicsAddResult) @@ -1875,7 +2643,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(CallScriptedProxyGetResult) CACHEOP_CASE_UNIMPL(CallScriptedProxyGetByValueResult) #endif - CACHEOP_CASE_UNIMPL(MetaScriptedThisShape) CACHEOP_CASE_UNIMPL(BindFunctionResult) CACHEOP_CASE_UNIMPL(SpecializedBindFunctionResult) CACHEOP_CASE_UNIMPL(LoadFixedSlotTypedResult) @@ -1887,7 +2654,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(LoadTypedArrayElementResult) CACHEOP_CASE_UNIMPL(LoadDataViewValueResult) CACHEOP_CASE_UNIMPL(StoreDataViewValueResult) - CACHEOP_CASE_UNIMPL(LoadArgumentsObjectArgResult) CACHEOP_CASE_UNIMPL(LoadArgumentsObjectArgHoleResult) CACHEOP_CASE_UNIMPL(LoadArgumentsObjectArgExistsResult) CACHEOP_CASE_UNIMPL(LoadArgumentsObjectLengthResult) @@ -1932,7 +2698,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(BigIntDivResult) CACHEOP_CASE_UNIMPL(BigIntModResult) CACHEOP_CASE_UNIMPL(BigIntPowResult) - CACHEOP_CASE_UNIMPL(Int32BitXorResult) CACHEOP_CASE_UNIMPL(Int32LeftShiftResult) CACHEOP_CASE_UNIMPL(Int32RightShiftResult) CACHEOP_CASE_UNIMPL(Int32URightShiftResult) @@ -1996,8 +2761,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(Breakpoint) CACHEOP_CASE_UNIMPL(WrapResult) CACHEOP_CASE_UNIMPL(Bailout) - CACHEOP_CASE_UNIMPL(AssertRecoveredOnBailoutResult) - CACHEOP_CASE_UNIMPL(GuardIsNotUninitializedLexical) { + CACHEOP_CASE_UNIMPL(AssertRecoveredOnBailoutResult) { TRACE_PRINTF("unknown CacheOp: %s\n", CacheIROpNames[int(cacheop)]); return ICInterpretOpResult::NextIC; } @@ -2498,6 +3262,13 @@ PBIResult PortableBaselineInterpret(JSContext* cx_, State& state, Stack& stack, } ret->setUndefined(); + // Check if we are being debugged, and set a flag in the frame if so. This + // flag must be set before calling InitFunctionEnvironmentObjects. + if (script->isDebuggee()) { + TRACE_PRINTF("Script is debuggee\n"); + frame->setIsDebuggee(); + } + if (CalleeTokenIsFunction(frame->calleeToken())) { JSFunction* func = CalleeTokenToFunction(frame->calleeToken()); frame->setEnvironmentChain(func->environment()); @@ -2511,12 +3282,8 @@ PBIResult PortableBaselineInterpret(JSContext* cx_, State& state, Stack& stack, } } - // Check if we are being debugged, and set a flag in the frame if - // so. + // The debug prologue can't run until the function environment is set up. if (script->isDebuggee()) { - TRACE_PRINTF("Script is debuggee\n"); - frame->setIsDebuggee(); - PUSH_EXIT_FRAME(); if (!DebugPrologue(cx, frame)) { goto error; diff --git a/js/src/vm/Realm.cpp b/js/src/vm/Realm.cpp index d2ad39f3db..4e3eba5677 100644 --- a/js/src/vm/Realm.cpp +++ b/js/src/vm/Realm.cpp @@ -237,11 +237,17 @@ void Realm::traceRoots(JSTracer* trc, // The global is never nursery allocated, so we don't need to // trace it when doing a minor collection. // - // If a realm is on-stack, we mark its global so that - // JSContext::global() remains valid. + // If a realm is on-stack, we mark its global so that JSContext::global() + // remains valid. if (shouldTraceGlobal() && global_) { TraceRoot(trc, global_.unbarrieredAddress(), "on-stack realm global"); } + + // If the realm is still being initialized we set a flag so that it doesn't + // get deleted, since there may be GC things that contain pointers to it. + if (shouldTraceGlobal() && initializingGlobal_) { + allocatedDuringIncrementalGC_ = true; + } } // Nothing below here needs to be treated as a root if we aren't marking diff --git a/js/src/vm/Realm.h b/js/src/vm/Realm.h index 2e6d56aa5e..4518b4ced4 100644 --- a/js/src/vm/Realm.h +++ b/js/src/vm/Realm.h @@ -17,6 +17,7 @@ #include <stddef.h> #include "builtin/Array.h" +#include "ds/IdValuePair.h" #include "gc/Barrier.h" #include "js/GCVariant.h" #include "js/RealmOptions.h" @@ -129,7 +130,7 @@ class NewPlainObjectWithPropsCache { public: NewPlainObjectWithPropsCache() { purge(); } - SharedShape* lookup(IdValuePair* properties, size_t nproperties) const; + SharedShape* lookup(Handle<IdValueVector> properties) const; void add(SharedShape* shape); void purge() { @@ -436,9 +437,6 @@ class JS::Realm : public JS::shadow::Realm { // features are required. bool isUnlimitedStacksCapturingEnabled = false; - // Whether or not the deprecation warning for bug 1873186 has been shown. - bool warnedAboutDateLateWeekday = false; - private: void updateDebuggerObservesFlag(unsigned flag); diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 256aade5f8..29806c21d4 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -298,7 +298,6 @@ void RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlags flags, zeroLastIndex(cx); } -#if defined(DEBUG) || defined(JS_JITSPEW) template <typename KnownF, typename UnknownF> void ForEachRegExpFlag(JS::RegExpFlags flags, KnownF known, UnknownF unknown) { uint8_t raw = flags.value(); @@ -336,6 +335,14 @@ void ForEachRegExpFlag(JS::RegExpFlags flags, KnownF known, UnknownF unknown) { } } +std::ostream& JS::operator<<(std::ostream& os, RegExpFlags flags) { + ForEachRegExpFlag( + flags, [&](const char* name, const char* c) { os << c; }, + [&](uint8_t value) { os << '?'; }); + return os; +} + +#if defined(DEBUG) || defined(JS_JITSPEW) void RegExpObject::dumpOwnFields(js::JSONPrinter& json) const { { js::GenericPrinter& out = json.beginStringProperty("source"); @@ -1120,36 +1127,7 @@ static bool ParseRegExpFlags(const CharT* chars, size_t length, for (size_t i = 0; i < length; i++) { uint8_t flag; - switch (chars[i]) { - case 'd': - flag = RegExpFlag::HasIndices; - break; - case 'g': - flag = RegExpFlag::Global; - break; - case 'i': - flag = RegExpFlag::IgnoreCase; - break; - case 'm': - flag = RegExpFlag::Multiline; - break; - case 's': - flag = RegExpFlag::DotAll; - break; - case 'u': - flag = RegExpFlag::Unicode; - break; - case 'v': - flag = RegExpFlag::UnicodeSets; - break; - case 'y': - flag = RegExpFlag::Sticky; - break; - default: - *invalidFlag = chars[i]; - return false; - } - if (*flagsOut & flag) { + if (!JS::MaybeParseRegExpFlag(chars[i], &flag) || *flagsOut & flag) { *invalidFlag = chars[i]; return false; } diff --git a/js/src/vm/RegExpShared.h b/js/src/vm/RegExpShared.h index 4ff68e9ee1..07f57d1e7a 100644 --- a/js/src/vm/RegExpShared.h +++ b/js/src/vm/RegExpShared.h @@ -103,7 +103,7 @@ class RegExpShared size_t byteCodeLength() const { MOZ_ASSERT(byteCode); - return byteCode->length; + return byteCode->length(); } }; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 934a534185..57d4fb1411 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -526,9 +526,9 @@ struct JSRuntime { js::GeckoProfilerRuntime& geckoProfiler() { return geckoProfiler_.ref(); } // Heap GC roots for PersistentRooted pointers. - js::MainThreadData< - mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, - mozilla::LinkedList<js::PersistentRootedBase>>> + js::MainThreadData<mozilla::EnumeratedArray< + JS::RootKind, mozilla::LinkedList<js::PersistentRootedBase>, + size_t(JS::RootKind::Limit)>> heapRoots; void tracePersistentRoots(JSTracer* trc); diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp index c48d0976d8..45cbeb6419 100644 --- a/js/src/vm/Scope.cpp +++ b/js/src/vm/Scope.cpp @@ -148,6 +148,33 @@ SharedShape* js::CreateEnvironmentShape(JSContext* cx, BindingIter& bi, map, mapLength, objectFlags); } +SharedShape* js::CreateEnvironmentShapeForSyntheticModule( + JSContext* cx, const JSClass* cls, uint32_t numSlots, + Handle<ModuleObject*> module) { + Rooted<SharedPropMap*> map(cx); + uint32_t mapLength = 0; + + PropertyFlags propFlags = {PropertyFlag::Enumerable}; + ObjectFlags objectFlags = ModuleEnvironmentObject::OBJECT_FLAGS; + + RootedId id(cx); + uint32_t slotIndex = numSlots; + for (JSAtom* exportName : module->syntheticExportNames()) { + id = NameToId(exportName->asPropertyName()); + if (!SharedPropMap::addPropertyWithKnownSlot(cx, cls, &map, &mapLength, id, + propFlags, slotIndex, + &objectFlags)) { + return nullptr; + } + slotIndex++; + } + + uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots)); + return SharedShape::getInitialOrPropMapShape(cx, cls, cx->realm(), + TaggedProto(nullptr), numFixed, + map, mapLength, objectFlags); +} + template <class DataT> inline size_t SizeOfAllocatedData(DataT* data) { return SizeOfScopeData<DataT>(data->length); diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h index a914a14f28..22777f100a 100644 --- a/js/src/vm/Scope.h +++ b/js/src/vm/Scope.h @@ -1765,6 +1765,10 @@ SharedShape* CreateEnvironmentShape(JSContext* cx, BindingIter& bi, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags); +SharedShape* CreateEnvironmentShapeForSyntheticModule( + JSContext* cx, const JSClass* cls, uint32_t numSlots, + Handle<ModuleObject*> module); + SharedShape* EmptyEnvironmentShape(JSContext* cx, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags); diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h index 572fe2e6fb..525ee78451 100644 --- a/js/src/vm/SharedArrayObject.h +++ b/js/src/vm/SharedArrayObject.h @@ -116,7 +116,9 @@ class SharedArrayRawBuffer { // this method merely sets the number of user accessible bytes of this buffer. bool grow(size_t newByteLength); - static int32_t liveBuffers(); + static size_t offsetOfByteLength() { + return offsetof(SharedArrayRawBuffer, length_); + } }; class WasmSharedArrayRawBuffer : public SharedArrayRawBuffer { @@ -364,6 +366,10 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared { return rawBufferObject()->dataPointerShared(); } + static constexpr int rawBufferOffset() { + return NativeObject::getFixedSlotOffset(RAWBUF_SLOT); + } + // WebAssembly support: // Create a SharedArrayBufferObject using the provided buffer and size. diff --git a/js/src/vm/SharedStencil.h b/js/src/vm/SharedStencil.h index 58666919dc..a402895bd6 100644 --- a/js/src/vm/SharedStencil.h +++ b/js/src/vm/SharedStencil.h @@ -789,7 +789,11 @@ using SharedImmutableScriptDataTable = SharedImmutableScriptData::Hasher, SystemAllocPolicy>; struct MemberInitializers { +#ifdef ENABLE_DECORATORS + static constexpr size_t NumBits = 30; +#else static constexpr size_t NumBits = 31; +#endif static constexpr uint32_t MaxInitializers = BitMask(NumBits); #ifdef DEBUG @@ -798,20 +802,37 @@ struct MemberInitializers { bool hasPrivateBrand : 1; +#ifdef ENABLE_DECORATORS + bool hasDecorators : 1; +#endif + // This struct will eventually have a vector of constant values for optimizing // field initializers. uint32_t numMemberInitializers : NumBits; - MemberInitializers(bool hasPrivateBrand, uint32_t numMemberInitializers) + MemberInitializers(bool hasPrivateBrand, +#ifdef ENABLE_DECORATORS + bool hasDecorators, +#endif + uint32_t numMemberInitializers) : #ifdef DEBUG valid(true), #endif hasPrivateBrand(hasPrivateBrand), +#ifdef ENABLE_DECORATORS + hasDecorators(hasDecorators), +#endif numMemberInitializers(numMemberInitializers) { +#ifdef ENABLE_DECORATORS + MOZ_ASSERT( + this->numMemberInitializers == numMemberInitializers, + "numMemberInitializers should easily fit in the 30-bit bitfield"); +#else MOZ_ASSERT( this->numMemberInitializers == numMemberInitializers, "numMemberInitializers should easily fit in the 31-bit bitfield"); +#endif } static MemberInitializers Invalid() { return MemberInitializers(); } @@ -820,17 +841,33 @@ struct MemberInitializers { // fields. This is used when we elide the trivial data but still need a valid // set to stop scope walking. static const MemberInitializers& Empty() { - static const MemberInitializers zeroInitializers(false, 0); + static const MemberInitializers zeroInitializers(false, +#ifdef ENABLE_DECORATORS + false, +#endif + 0); return zeroInitializers; } uint32_t serialize() const { +#ifdef ENABLE_DECORATORS + auto serialised = (hasPrivateBrand << (NumBits + 1)) | + hasDecorators << NumBits | numMemberInitializers; + return serialised; +#else return (hasPrivateBrand << NumBits) | numMemberInitializers; +#endif } static MemberInitializers deserialize(uint32_t bits) { +#ifdef ENABLE_DECORATORS + return MemberInitializers((bits & Bit(NumBits + 1)) != 0, + (bits & Bit(NumBits)) != 0, + bits & BitMask(NumBits)); +#else return MemberInitializers((bits & Bit(NumBits)) != 0, bits & BitMask(NumBits)); +#endif } private: @@ -840,6 +877,9 @@ struct MemberInitializers { valid(false), #endif hasPrivateBrand(false), +#ifdef ENABLE_DECORATORS + hasDecorators(false), +#endif numMemberInitializers(0) { } }; diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index ffbe88147d..d222ddc51c 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -642,7 +642,20 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry( if (isWasm()) { Frame frame; - frame.kind = Frame_Wasm; + switch (wasmIter().category()) { + case wasm::ProfilingFrameIterator::Baseline: { + frame.kind = FrameKind::Frame_WasmBaseline; + break; + } + case wasm::ProfilingFrameIterator::Ion: { + frame.kind = FrameKind::Frame_WasmIon; + break; + } + default: { + frame.kind = FrameKind::Frame_WasmOther; + break; + } + } frame.stackAddress = stackAddr; frame.returnAddress_ = nullptr; frame.activation = activation_; diff --git a/js/src/vm/StringType-inl.h b/js/src/vm/StringType-inl.h index b0424b868c..8954a46aac 100644 --- a/js/src/vm/StringType-inl.h +++ b/js/src/vm/StringType-inl.h @@ -321,6 +321,10 @@ inline JSRope::JSRope(JSString* left, JSString* right, size_t length) { // |length| must be the sum of the length of both child nodes. MOZ_ASSERT(left->length() + right->length() == length); + // |isLatin1| is set when both children are guaranteed to contain only Latin-1 + // characters. Note that flattening either rope child can clear the Latin-1 + // flag of that child, so it's possible that a Latin-1 rope can end up with + // both children being two-byte (dependent) strings. bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars(); // Do not try to make a rope that could fit inline. diff --git a/js/src/vm/StringType.cpp b/js/src/vm/StringType.cpp index 03f6a7e1ac..63afd8864b 100644 --- a/js/src/vm/StringType.cpp +++ b/js/src/vm/StringType.cpp @@ -2224,10 +2224,12 @@ void JSInlineString::dumpOwnRepresentationFields(js::JSONPrinter& json) const {} void JSLinearString::dumpOwnRepresentationFields(js::JSONPrinter& json) const { if (!isInline()) { - js::gc::StoreBuffer* sb = storeBuffer(); - bool inNursery = sb && sb->nursery().isInside(nonInlineCharsRaw()); - - json.boolProperty("inNursery", inNursery); + // Include whether the chars are in the nursery even for tenured + // strings, which should always be false. For investigating bugs, it's + // better to not assume that. + js::Nursery& nursery = runtimeFromMainThread()->gc.nursery(); + bool inNursery = nursery.isInside(nonInlineCharsRaw()); + json.boolProperty("charsInNursery", inNursery); } } #endif diff --git a/js/src/vm/StringType.h b/js/src/vm/StringType.h index ea2174be42..f2850c33a4 100644 --- a/js/src/vm/StringType.h +++ b/js/src/vm/StringType.h @@ -297,7 +297,10 @@ class JSString : public js::gc::CellWithLengthAndFlags { * If LATIN1_CHARS_BIT is set, the string's characters are stored as Latin1 * instead of TwoByte. This flag can also be set for ropes, if both the * left and right nodes are Latin1. Flattening will result in a Latin1 - * string in this case. + * string in this case. When we flatten a TwoByte rope, we turn child ropes + * (including Latin1 ropes) into TwoByte dependent strings. If one of these + * strings is also part of another Latin1 rope tree, we can have a Latin1 rope + * with a TwoByte descendent. * * The other flags store the string's type. Instead of using a dense index * to represent the most-derived type, string types are encoded to allow @@ -385,6 +388,15 @@ class JSString : public js::gc::CellWithLengthAndFlags { static_assert((TYPE_FLAGS_MASK & js::gc::HeaderWord::RESERVED_MASK) == 0, "GC reserved bits must not be used for Strings"); + // Linear strings: + // - Content and representation are Latin-1 characters. + // - Unmodifiable after construction. + // + // Ropes: + // - Content are Latin-1 characters. + // - Flag may be cleared when the rope is changed into a dependent string. + // + // Also see LATIN1_CHARS_BIT description under "Flag Encoding". static const uint32_t LATIN1_CHARS_BIT = js::Bit(9); // Whether this atom's characters store an uint32 index value less than or diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 8f1e131021..e2e67a2ee3 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -2568,7 +2568,7 @@ BigInt* JSStructuredCloneReader::readBigInt(uint32_t data) { if (!in.readArray(result->digits().data(), length)) { return nullptr; } - return result; + return JS::BigInt::destructivelyTrimHighZeroDigits(context(), result); } static uint32_t TagToV1ArrayType(uint32_t tag) { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 0264b481b3..35a2237cd5 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -382,6 +382,7 @@ class TypedArrayObjectTemplate { using FixedLengthTypedArray = FixedLengthTypedArrayObjectTemplate<NativeType>; using ResizableTypedArray = ResizableTypedArrayObjectTemplate<NativeType>; + using AutoLength = ArrayBufferViewObject::AutoLength; static constexpr auto ByteLengthLimit = TypedArrayObject::ByteLengthLimit; static constexpr auto INLINE_BUFFER_LIMIT = @@ -574,7 +575,7 @@ class TypedArrayObjectTemplate { static bool computeAndCheckLength( JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> bufferMaybeUnwrapped, uint64_t byteOffset, uint64_t lengthIndex, size_t* length, - bool* autoLength) { + AutoLength* autoLength) { MOZ_ASSERT(byteOffset % BYTES_PER_ELEMENT == 0); MOZ_ASSERT(byteOffset < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)); MOZ_ASSERT_IF(lengthIndex != UINT64_MAX, @@ -605,7 +606,7 @@ class TypedArrayObjectTemplate { // Resizable buffers without an explicit length are auto-length. if (bufferMaybeUnwrapped->isResizable()) { *length = 0; - *autoLength = true; + *autoLength = AutoLength::Yes; return true; } @@ -642,7 +643,7 @@ class TypedArrayObjectTemplate { MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT); *length = len; - *autoLength = false; + *autoLength = AutoLength::No; return true; } @@ -654,7 +655,7 @@ class TypedArrayObjectTemplate { uint64_t byteOffset, uint64_t lengthIndex, HandleObject proto) { // Steps 5-8. size_t length = 0; - bool autoLength = false; + auto autoLength = AutoLength::No; if (!computeAndCheckLength(cx, buffer, byteOffset, lengthIndex, &length, &autoLength)) { return nullptr; @@ -703,7 +704,7 @@ class TypedArrayObjectTemplate { unwrappedBuffer = &unwrapped->as<ArrayBufferObjectMaybeShared>(); size_t length = 0; - bool autoLength = false; + auto autoLength = AutoLength::No; if (!computeAndCheckLength(cx, unwrappedBuffer, byteOffset, lengthIndex, &length, &autoLength)) { return nullptr; @@ -1025,13 +1026,13 @@ class ResizableTypedArrayObjectTemplate } static ResizableTypedArrayObject* newBuiltinClassInstance( - JSContext* cx, gc::AllocKind allocKind) { + JSContext* cx, gc::AllocKind allocKind, gc::Heap heap) { RootedObject proto(cx, GlobalObject::getOrCreatePrototype(cx, protoKey())); if (!proto) { return nullptr; } return NewTypedArrayObject<ResizableTypedArrayObject>( - cx, instanceClass(), proto, allocKind, gc::Heap::Default); + cx, instanceClass(), proto, allocKind, heap); } static ResizableTypedArrayObject* makeProtoInstance(JSContext* cx, @@ -1044,11 +1045,12 @@ class ResizableTypedArrayObjectTemplate static ResizableTypedArrayObject* makeInstance( JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, - size_t byteOffset, size_t len, bool autoLength, HandleObject proto) { + size_t byteOffset, size_t len, AutoLength autoLength, + HandleObject proto) { MOZ_ASSERT(buffer); MOZ_ASSERT(buffer->isResizable()); MOZ_ASSERT(!buffer->isDetached()); - MOZ_ASSERT(!autoLength || len == 0, + MOZ_ASSERT(autoLength == AutoLength::No || len == 0, "length is zero for 'auto' length views"); MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT); @@ -1059,16 +1061,43 @@ class ResizableTypedArrayObjectTemplate if (proto) { obj = makeProtoInstance(cx, proto, allocKind); } else { - obj = newBuiltinClassInstance(cx, allocKind); + obj = newBuiltinClassInstance(cx, allocKind, gc::Heap::Default); } - if (!obj || !obj->init(cx, buffer, byteOffset, len, BYTES_PER_ELEMENT)) { + if (!obj || !obj->initResizable(cx, buffer, byteOffset, len, + BYTES_PER_ELEMENT, autoLength)) { return nullptr; } - obj->setFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(autoLength)); - return obj; } + + static ResizableTypedArrayObject* makeTemplateObject(JSContext* cx) { + gc::AllocKind allocKind = gc::GetGCObjectKind(instanceClass()); + + AutoSetNewObjectMetadata metadata(cx); + + auto* tarray = newBuiltinClassInstance(cx, allocKind, gc::Heap::Tenured); + if (!tarray) { + return nullptr; + } + + tarray->initFixedSlot(TypedArrayObject::BUFFER_SLOT, JS::FalseValue()); + tarray->initFixedSlot(TypedArrayObject::LENGTH_SLOT, + PrivateValue(size_t(0))); + tarray->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, + PrivateValue(size_t(0))); + tarray->initFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(false)); + tarray->initFixedSlot(ResizableTypedArrayObject::INITIAL_LENGTH_SLOT, + PrivateValue(size_t(0))); + tarray->initFixedSlot(ResizableTypedArrayObject::INITIAL_BYTE_OFFSET_SLOT, + PrivateValue(size_t(0))); + + // Template objects don't need memory for their elements, since there + // won't be any elements to store. + MOZ_ASSERT(tarray->getReservedSlot(DATA_SLOT).isUndefined()); + + return tarray; + } }; template <typename NativeType> @@ -1499,18 +1528,29 @@ static bool GetTemplateObjectForNative(JSContext* cx, return !!res; } + if (!arg.isObject()) { + return true; + } + auto* obj = &arg.toObject(); + // We don't support wrappers, because of the complicated interaction between // wrapped ArrayBuffers and TypedArrays, see |fromBufferWrapped()|. - if (arg.isObject() && !IsWrapper(&arg.toObject())) { - // We don't use the template's length in the object case, so we can create - // the template typed array with an initial length of zero. - uint32_t len = 0; + if (IsWrapper(obj)) { + return true; + } + + // We don't use the template's length in the object case, so we can create + // the template typed array with an initial length of zero. + uint32_t len = 0; + + if (!obj->is<ArrayBufferObjectMaybeShared>() || + !obj->as<ArrayBufferObjectMaybeShared>().isResizable()) { res.set( FixedLengthTypedArrayObjectTemplate<T>::makeTemplateObject(cx, len)); - return !!res; + } else { + res.set(ResizableTypedArrayObjectTemplate<T>::makeTemplateObject(cx)); } - - return true; + return !!res; } /* static */ bool TypedArrayObject::GetTemplateObjectForNative( @@ -2199,131 +2239,6 @@ bool TypedArrayObjectTemplate<uint64_t>::getElement(JSContext* cx, } } /* anonymous namespace */ -/** - * IsIntegerIndexedObjectOutOfBounds ( iieoRecord ) - * - * IsIntegerIndexedObjectOutOfBounds can be rewritten into the following spec - * steps when inlining the call to - * MakeIntegerIndexedObjectWithBufferWitnessRecord. - * - * 1. Let buffer be O.[[ViewedArrayBuffer]]. - * 2. If IsDetachedBuffer(buffer) is true, then - * a. Return true. - * 3. If IsFixedLengthArrayBuffer(buffer) is true, then - * a. Return false. - * 4. Let bufferByteLength be ArrayBufferByteLength(buffer, order). - * 5. Let byteOffsetStart be O.[[ByteOffset]]. - * 6. If byteOffsetStart > bufferByteLength, then - * a. Return true. - * 7. If O.[[ArrayLength]] is auto, then - * a. Return false. - * 8. Let elementSize be TypedArrayElementSize(O). - * 9. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize. - * 10. If byteOffsetEnd > bufferByteLength, then - * a. Return true. - * 11. Return false. - * - * The additional call to IsFixedLengthArrayBuffer is an optimization to skip - * unnecessary validation which don't apply for fixed length typed arrays. - * - * https://tc39.es/ecma262/#sec-isintegerindexedobjectoutofbounds - * https://tc39.es/ecma262/#sec-makeintegerindexedobjectwithbufferwitnessrecord - */ -mozilla::Maybe<size_t> TypedArrayObject::byteOffset() const { - if (MOZ_UNLIKELY(hasDetachedBuffer())) { - return mozilla::Nothing{}; - } - - size_t byteOffsetStart = ArrayBufferViewObject::byteOffset(); - - if (MOZ_LIKELY(is<FixedLengthTypedArrayObject>())) { - return mozilla::Some(byteOffsetStart); - } - - auto* buffer = bufferEither(); - MOZ_ASSERT(buffer->isResizable()); - - size_t bufferByteLength = buffer->byteLength(); - if (byteOffsetStart > bufferByteLength) { - return mozilla::Nothing{}; - } - - if (as<ResizableTypedArrayObject>().isAutoLength()) { - return mozilla::Some(byteOffsetStart); - } - - size_t viewByteLength = rawByteLength(); - size_t byteOffsetEnd = byteOffsetStart + viewByteLength; - if (byteOffsetEnd > bufferByteLength) { - return mozilla::Nothing{}; - } - return mozilla::Some(byteOffsetStart); -} - -/** - * IntegerIndexedObjectLength ( iieoRecord ) - * - * IntegerIndexedObjectLength can be rewritten into the following spec - * steps when inlining the calls to IsIntegerIndexedObjectOutOfBounds and - * MakeIntegerIndexedObjectWithBufferWitnessRecord. - * - * 1. Let buffer be O.[[ViewedArrayBuffer]]. - * 2. If IsDetachedBuffer(buffer) is true, then - * a. Return out-of-bounds. - * 3. If IsFixedLengthArrayBuffer(buffer) is true, then - * a. Return O.[[ArrayLength]]. - * 4. Let bufferByteLength be ArrayBufferByteLength(buffer, order). - * 5. Let byteOffsetStart be O.[[ByteOffset]]. - * 6. If byteOffsetStart > bufferByteLength, then - * a. Return out-of-bounds. - * 7. If O.[[ArrayLength]] is auto, then - * a. Let elementSize be TypedArrayElementSize(O). - * b. Return floor((bufferByteLength - byteOffsetStart) / elementSize). - * 8. Let elementSize be TypedArrayElementSize(O). - * 9. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize. - * 10. If byteOffsetEnd > bufferByteLength, then - * a. Return out-of-bounds. - * 11. Return O.[[ArrayLength]]. - * - * The additional call to IsFixedLengthArrayBuffer is an optimization to skip - * unnecessary validation which don't apply for fixed length typed arrays. - * - * https://tc39.es/ecma262/#sec-integerindexedobjectlength - * https://tc39.es/ecma262/#sec-isintegerindexedobjectoutofbounds - * https://tc39.es/ecma262/#sec-makeintegerindexedobjectwithbufferwitnessrecord - */ -mozilla::Maybe<size_t> TypedArrayObject::length() const { - if (MOZ_UNLIKELY(hasDetachedBuffer())) { - return mozilla::Nothing{}; - } - - if (MOZ_LIKELY(is<FixedLengthTypedArrayObject>())) { - size_t arrayLength = rawLength(); - return mozilla::Some(arrayLength); - } - - auto* buffer = bufferEither(); - MOZ_ASSERT(buffer->isResizable()); - - size_t bufferByteLength = buffer->byteLength(); - size_t byteOffsetStart = ArrayBufferViewObject::byteOffset(); - if (byteOffsetStart > bufferByteLength) { - return mozilla::Nothing{}; - } - - if (as<ResizableTypedArrayObject>().isAutoLength()) { - size_t bytes = bufferByteLength - byteOffsetStart; - return mozilla::Some(bytes / bytesPerElement()); - } - - size_t arrayLength = rawLength(); - size_t byteOffsetEnd = byteOffsetStart + arrayLength * bytesPerElement(); - if (byteOffsetEnd > bufferByteLength) { - return mozilla::Nothing{}; - } - return mozilla::Some(arrayLength); -} - namespace js { template <> diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index b6b1f00e72..46531ec4ee 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -69,27 +69,30 @@ class TypedArrayObject : public ArrayBufferViewObject { static bool ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> typedArray); - protected: - size_t rawByteLength() const { return rawLength() * bytesPerElement(); } - - size_t rawLength() const { - return size_t(getFixedSlot(LENGTH_SLOT).toPrivate()); - } - public: - mozilla::Maybe<size_t> byteOffset() const; + /** + * Return the current length, or |Nothing| if the TypedArray is detached or + * out-of-bounds. + */ + mozilla::Maybe<size_t> length() const { + return ArrayBufferViewObject::length(); + } + /** + * Return the current byteLength, or |Nothing| if the TypedArray is detached + * or out-of-bounds. + */ mozilla::Maybe<size_t> byteLength() const { return length().map( [this](size_t value) { return value * bytesPerElement(); }); } - mozilla::Maybe<size_t> length() const; - // Self-hosted TypedArraySubarray function needs to read [[ByteOffset]], even // when it's currently out-of-bounds. size_t byteOffsetMaybeOutOfBounds() const { - return ArrayBufferViewObject::byteOffset(); + // dataPointerOffset() returns the [[ByteOffset]] spec value, except when + // the buffer is detached. (bug 1840991) + return ArrayBufferViewObject::dataPointerOffset(); } template <AllowGC allowGC> @@ -148,11 +151,13 @@ class FixedLengthTypedArrayObject : public TypedArrayObject { static inline gc::AllocKind AllocKindForLazyBuffer(size_t nbytes); - size_t byteOffset() const { return ArrayBufferViewObject::byteOffset(); } + size_t byteOffset() const { + return ArrayBufferViewObject::byteOffsetSlotValue(); + } - size_t byteLength() const { return rawByteLength(); } + size_t byteLength() const { return length() * bytesPerElement(); } - size_t length() const { return rawLength(); } + size_t length() const { return ArrayBufferViewObject::lengthSlotValue(); } bool hasInlineElements() const; void setInlineElements(); @@ -176,13 +181,7 @@ class FixedLengthTypedArrayObject : public TypedArrayObject { class ResizableTypedArrayObject : public TypedArrayObject { public: - static const uint8_t AUTO_LENGTH_SLOT = TypedArrayObject::RESERVED_SLOTS; - - static const uint8_t RESERVED_SLOTS = TypedArrayObject::RESERVED_SLOTS + 1; - - bool isAutoLength() const { - return getFixedSlot(AUTO_LENGTH_SLOT).toBoolean(); - } + static const uint8_t RESERVED_SLOTS = RESIZABLE_RESERVED_SLOTS; }; extern TypedArrayObject* NewTypedArrayWithTemplateAndLength( diff --git a/js/src/vm/UbiNodeCensus.cpp b/js/src/vm/UbiNodeCensus.cpp index ba3ccd0898..7e33341d30 100644 --- a/js/src/vm/UbiNodeCensus.cpp +++ b/js/src/vm/UbiNodeCensus.cpp @@ -6,6 +6,8 @@ #include "js/UbiNodeCensus.h" +#include "mozilla/ScopeExit.h" + #include "builtin/MapObject.h" #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/Printer.h" @@ -1062,17 +1064,19 @@ JS_PUBLIC_API bool CensusHandler::operator()( /*** Parsing Breakdowns *****************************************************/ -static CountTypePtr ParseChildBreakdown(JSContext* cx, HandleObject breakdown, - PropertyName* prop) { +static CountTypePtr ParseChildBreakdown( + JSContext* cx, HandleObject breakdown, PropertyName* prop, + MutableHandle<GCVector<JSLinearString*>> seen) { RootedValue v(cx); if (!GetProperty(cx, breakdown, breakdown, prop, &v)) { return nullptr; } - return ParseBreakdown(cx, v); + return ParseBreakdown(cx, v, seen); } -JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, - HandleValue breakdownValue) { +JS_PUBLIC_API CountTypePtr +ParseBreakdown(JSContext* cx, HandleValue breakdownValue, + MutableHandle<GCVector<JSLinearString*>> seen) { if (breakdownValue.isUndefined()) { // Construct the default type, { by: 'count' } CountTypePtr simple(cx->new_<SimpleCount>()); @@ -1097,6 +1101,24 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, return nullptr; } + for (auto candidate : seen.get()) { + if (EqualStrings(by, candidate)) { + UniqueChars byBytes = QuoteString(cx, by, '"'); + if (!byBytes) { + return nullptr; + } + + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEBUG_CENSUS_BREAKDOWN_NESTED, + byBytes.get()); + return nullptr; + } + } + if (!seen.append(by)) { + return nullptr; + } + auto popper = mozilla::MakeScopeExit([&]() { seen.popBack(); }); + if (StringEqualsLiteral(by, "count")) { RootedValue countValue(cx), bytesValue(cx); if (!GetProperty(cx, breakdown, breakdown, cx->names().count, @@ -1140,13 +1162,14 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "objectClass")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } CountTypePtr otherType( - ParseChildBreakdown(cx, breakdown, cx->names().other)); + ParseChildBreakdown(cx, breakdown, cx->names().other, seen)); if (!otherType) { return nullptr; } @@ -1156,27 +1179,27 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, if (StringEqualsLiteral(by, "coarseType")) { CountTypePtr objectsType( - ParseChildBreakdown(cx, breakdown, cx->names().objects)); + ParseChildBreakdown(cx, breakdown, cx->names().objects, seen)); if (!objectsType) { return nullptr; } CountTypePtr scriptsType( - ParseChildBreakdown(cx, breakdown, cx->names().scripts)); + ParseChildBreakdown(cx, breakdown, cx->names().scripts, seen)); if (!scriptsType) { return nullptr; } CountTypePtr stringsType( - ParseChildBreakdown(cx, breakdown, cx->names().strings)); + ParseChildBreakdown(cx, breakdown, cx->names().strings, seen)); if (!stringsType) { return nullptr; } CountTypePtr otherType( - ParseChildBreakdown(cx, breakdown, cx->names().other)); + ParseChildBreakdown(cx, breakdown, cx->names().other, seen)); if (!otherType) { return nullptr; } CountTypePtr domNodeType( - ParseChildBreakdown(cx, breakdown, cx->names().domNode)); + ParseChildBreakdown(cx, breakdown, cx->names().domNode, seen)); if (!domNodeType) { return nullptr; } @@ -1186,7 +1209,8 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "internalType")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } @@ -1195,7 +1219,8 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "descriptiveType")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } @@ -1203,12 +1228,13 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "allocationStack")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } CountTypePtr noStackType( - ParseChildBreakdown(cx, breakdown, cx->names().noStack)); + ParseChildBreakdown(cx, breakdown, cx->names().noStack, seen)); if (!noStackType) { return nullptr; } @@ -1217,13 +1243,14 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "filename")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } CountTypePtr noFilenameType( - ParseChildBreakdown(cx, breakdown, cx->names().noFilename)); + ParseChildBreakdown(cx, breakdown, cx->names().noFilename, seen)); if (!noFilenameType) { return nullptr; } @@ -1307,8 +1334,9 @@ JS_PUBLIC_API bool ParseCensusOptions(JSContext* cx, Census& census, return false; } + Rooted<GCVector<JSLinearString*>> seen(cx, cx); outResult = breakdown.isUndefined() ? GetDefaultBreakdown(cx) - : ParseBreakdown(cx, breakdown); + : ParseBreakdown(cx, breakdown, &seen); return !!outResult; } diff --git a/js/src/vm/Value.cpp b/js/src/vm/Value.cpp index 8fcad7ee83..0da89a41c2 100644 --- a/js/src/vm/Value.cpp +++ b/js/src/vm/Value.cpp @@ -10,6 +10,7 @@ #include <inttypes.h> +#include "gc/Cell.h" // js::gc::Cell #include "js/Conversions.h" // JS::NumberToString, JS::MaximumNumberToStringLength #include "js/Printer.h" // js::GenericPrinter, js::Fprinter #include "vm/BigIntType.h" // JS::BigInt @@ -41,6 +42,12 @@ const HandleValue FalseHandleValue = const Handle<mozilla::Maybe<Value>> NothingHandleValue = Handle<mozilla::Maybe<Value>>::fromMarkedLocation(&JSVAL_NOTHING); +#ifdef DEBUG +void JS::Value::assertTraceKindMatches(js::gc::Cell* cell) const { + MOZ_ASSERT(traceKind() == cell->getTraceKind()); +} +#endif + } // namespace JS void js::ReportBadValueTypeAndCrash(const JS::Value& value) { diff --git a/js/src/vm/Watchtower.cpp b/js/src/vm/Watchtower.cpp index 80023d7e81..86c748285d 100644 --- a/js/src/vm/Watchtower.cpp +++ b/js/src/vm/Watchtower.cpp @@ -102,23 +102,24 @@ static void InvalidateMegamorphicCache(JSContext* cx, } void MaybePopReturnFuses(JSContext* cx, Handle<NativeObject*> nobj) { - JSObject* objectProto = &cx->global()->getObjectPrototype(); + GlobalObject* global = &nobj->global(); + JSObject* objectProto = &global->getObjectPrototype(); if (nobj == objectProto) { nobj->realm()->realmFuses.objectPrototypeHasNoReturnProperty.popFuse( cx, nobj->realm()->realmFuses); return; } - JSObject* iteratorProto = cx->global()->maybeGetIteratorPrototype(); + JSObject* iteratorProto = global->maybeGetIteratorPrototype(); if (nobj == iteratorProto) { nobj->realm()->realmFuses.iteratorPrototypeHasNoReturnProperty.popFuse( cx, nobj->realm()->realmFuses); return; } - JSObject* arrayIterProto = cx->global()->maybeGetArrayIteratorPrototype(); + JSObject* arrayIterProto = global->maybeGetArrayIteratorPrototype(); if (nobj == arrayIterProto) { - cx->realm()->realmFuses.arrayIteratorPrototypeHasNoReturnProperty.popFuse( + nobj->realm()->realmFuses.arrayIteratorPrototypeHasNoReturnProperty.popFuse( cx, nobj->realm()->realmFuses); return; } @@ -208,12 +209,12 @@ static bool WatchProtoChangeImpl(JSContext* cx, HandleObject obj) { InvalidateMegamorphicCache(cx, obj.as<NativeObject>()); NativeObject* nobj = &obj->as<NativeObject>(); - if (nobj == cx->global()->maybeGetArrayIteratorPrototype()) { + if (nobj == nobj->global().maybeGetArrayIteratorPrototype()) { nobj->realm()->realmFuses.arrayIteratorPrototypeHasIteratorProto.popFuse( cx, nobj->realm()->realmFuses); } - if (nobj == cx->global()->maybeGetIteratorPrototype()) { + if (nobj == nobj->global().maybeGetIteratorPrototype()) { nobj->realm()->realmFuses.iteratorPrototypeHasObjectProto.popFuse( cx, nobj->realm()->realmFuses); } diff --git a/js/src/wasm/GenerateBuiltinModules.py b/js/src/wasm/GenerateBuiltinModules.py index 17270bc46e..0bd17d8821 100644 --- a/js/src/wasm/GenerateBuiltinModules.py +++ b/js/src/wasm/GenerateBuiltinModules.py @@ -47,6 +47,58 @@ def cppBool(v): return "false" +def specTypeToMIRType(specType): + if specType == "i32" or specType == "i64" or specType == "f32" or specType == "f64": + return f"ValType::{specType}().toMIRType()" + if ( + specType == "externref" + or specType == "anyref" + or specType == "funcref" + or isinstance(specType, dict) + ): + return "MIRType::WasmAnyRef" + raise ValueError() + + +def specHeapTypeToTypeCode(specHeapType): + if specHeapType == "func": + return "Func" + if specHeapType == "any": + return "Any" + if specHeapType == "extern": + return "Extern" + if specHeapType == "array": + return "Array" + if specHeapType == "struct": + return "Struct" + raise ValueError() + + +def specTypeToValType(specType): + if specType == "i32" or specType == "i64" or specType == "f32" or specType == "f64": + return f"ValType::{specType}()" + + if specType == "externref": + return "ValType(RefType::extern_())" + + if specType == "anyref": + return "ValType(RefType::any())" + + if specType == "funcref": + return "ValType(RefType::func())" + + if isinstance(specType, dict): + nullable = cppBool(specType["nullable"]) + if "type" in specType: + ref = specType["type"] + return f"ValType(RefType::fromTypeDef({ref}, {nullable}))" + else: + code = specType["code"] + return f"ValType(RefType::fromTypeCode(TypeCode(RefType::{specHeapTypeToTypeCode(code)}), {nullable}))" + + raise ValueError() + + def main(c_out, yaml_path): data = load_yaml(yaml_path) @@ -64,34 +116,45 @@ def main(c_out, yaml_path): for op in data: # Define DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_<op> as: # `{ValType::I32, ValType::I32, ...}`. + valTypes = ", ".join(specTypeToValType(p) for p in op["params"]) contents += ( f"#define DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_{op['op']} " - f"{{{', '.join(op['params'])}}}\n" + f"{{{valTypes}}}\n" ) - # Define DECLARE_BUILTIN_MODULE_FUNC_PARAM_SASTYPES_<op> as: - # `<num_types>, {_PTR, _I32, ..., _PTR, _END}`. + # Define DECLARE_BUILTIN_MODULE_FUNC_PARAM_MIRTYPES_<op> as: + # `<num_types>, {MIRType::Pointer, _I32, ..., MIRType::Pointer, _END}`. num_types = len(op["params"]) + 1 - sas_types = ( - f"{{_PTR{''.join(', ' + (p + '.toMIRType()') for p in op['params'])}" - ) + mir_types = "{MIRType::Pointer" + mir_types += "".join(", " + specTypeToMIRType(p) for p in op["params"]) if op["uses_memory"]: - sas_types += ", _PTR" + mir_types += ", MIRType::Pointer" num_types += 1 - sas_types += ", _END}" + # Add the end marker + mir_types += ", MIRType::None}" - contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_PARAM_SASTYPES_{op['op']} {num_types}, {sas_types}\n" + contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_PARAM_MIRTYPES_{op['op']} {num_types}, {mir_types}\n" + # Define DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_<op> as: + # `Some(X)` if present, or else `Nothing()`. result_valtype = "" - result_sastype = "" if "result" in op: - result_valtype = f"Some({op['result']})\n" - result_sastype = f"{op['result']}.toMIRType()\n" + result_valtype = f"Some({specTypeToValType(op['result'])})\n" else: result_valtype = "Nothing()" - result_sastype = "_VOID" contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_{op['op']} {result_valtype}\n" - contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_RESULT_SASTYPE_{op['op']} {result_sastype}\n" - contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_{op['op']} _{op['fail_mode']}\n" + + # Define DECLARE_BUILTIN_MODULE_FUNC_RESULT_MIRTYPE_<op> as: + # `X` if present, or else `MIRType::None`. + result_mirtype = "" + if "result" in op: + result_mirtype = specTypeToMIRType(op["result"]) + "\n" + else: + result_mirtype = "MIRType::None" + contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_RESULT_MIRTYPE_{op['op']} {result_mirtype}\n" + + # Define DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_<op> as: + # `FailureMode::X`. + contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_{op['op']} FailureMode::{op['fail_mode']}\n" generate_header(c_out, "wasm_WasmBuiltinModuleGenerated_h", contents) diff --git a/js/src/wasm/WasmBCClass.h b/js/src/wasm/WasmBCClass.h index c216d0ffd5..844ae3381a 100644 --- a/js/src/wasm/WasmBCClass.h +++ b/js/src/wasm/WasmBCClass.h @@ -297,6 +297,10 @@ struct BaseCompiler final { // Flag indicating that the compiler is currently in a dead code region. bool deadCode_; + // Store previously finished note to know if we need to insert a nop in + // finishTryNote. + size_t mostRecentFinishedTryNoteIndex_; + /////////////////////////////////////////////////////////////////////////// // // State for bounds check elimination. @@ -973,7 +977,7 @@ struct BaseCompiler final { bool tailCall, CodeOffset* fastCallOffset, CodeOffset* slowCallOffset); CodeOffset callImport(unsigned instanceDataOffset, const FunctionCall& call); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC void callRef(const Stk& calleeRef, const FunctionCall& call, CodeOffset* fastCallOffset, CodeOffset* slowCallOffset); # ifdef ENABLE_WASM_TAIL_CALLS @@ -1641,7 +1645,7 @@ struct BaseCompiler final { [[nodiscard]] bool emitRefFunc(); [[nodiscard]] bool emitRefNull(); [[nodiscard]] bool emitRefIsNull(); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool emitRefAsNonNull(); [[nodiscard]] bool emitBrOnNull(); [[nodiscard]] bool emitBrOnNonNull(); diff --git a/js/src/wasm/WasmBCMemory.cpp b/js/src/wasm/WasmBCMemory.cpp index f4e19d95e8..b6ef67f24d 100644 --- a/js/src/wasm/WasmBCMemory.cpp +++ b/js/src/wasm/WasmBCMemory.cpp @@ -1212,7 +1212,7 @@ static void PopAndAllocate(BaseCompiler* bc, ValType type, Scalar::Type viewType, AtomicOp op, RegI32* rd, RegI32* rv, Temps* temps) { bc->needI32(bc->specific_.eax); - if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) { + if (op == AtomicOp::Add || op == AtomicOp::Sub) { // We use xadd, so source and destination are the same. Using // eax here is overconstraining, but for byte operations on x86 // we do need something with a byte register. @@ -1246,7 +1246,7 @@ static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, T srcAddr, # else RegI32 temp; ScratchI32 scratch(*bc); - if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) { + if (op != AtomicOp::Add && op != AtomicOp::Sub) { temp = scratch; } # endif @@ -1401,7 +1401,7 @@ namespace atomic_rmw64 { static void PopAndAllocate(BaseCompiler* bc, AtomicOp op, RegI64* rd, RegI64* rv, RegI64* temp) { - if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) { + if (op == AtomicOp::Add || op == AtomicOp::Sub) { // We use xaddq, so input and output must be the same register. *rv = bc->popI64(); *rd = *rv; @@ -1422,7 +1422,7 @@ static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, static void Deallocate(BaseCompiler* bc, AtomicOp op, RegI64 rv, RegI64 temp) { bc->maybeFree(temp); - if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) { + if (op != AtomicOp::Add && op != AtomicOp::Sub) { bc->freeI64(rv); } } diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 196e49f76e..cb0fbde6ec 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -1658,7 +1658,7 @@ bool BaseCompiler::callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex, return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC void BaseCompiler::callRef(const Stk& calleeRef, const FunctionCall& call, CodeOffset* fastCallOffset, CodeOffset* slowCallOffset) { @@ -1788,15 +1788,25 @@ void BaseCompiler::finishTryNote(size_t tryNoteIndex) { masm.nop(); } - // Check the previous try note to ensure that we don't share an edge with - // it that could lead to ambiguity. Insert a nop, if required. - if (tryNotes.length() > 0) { - const TryNote& previous = tryNotes.back(); + // Check the most recent finished try note to ensure that we don't share an + // edge with it that could lead to ambiguity. Insert a nop, if required. + // + // Notice that finishTryNote is called in LIFO order -- using depth-first + // search numbering to see if we are traversing back from a nested try to a + // parent try, where we may need to ensure that the end offsets do not + // coincide. + // + // In the case the tryNodeIndex >= mostRecentFinishedTryNoteIndex_, we have + // finished a try that began after the most recent finished try, and so + // startTryNote will take care of any nops. + if (tryNoteIndex < mostRecentFinishedTryNoteIndex_) { + const TryNote& previous = tryNotes[mostRecentFinishedTryNoteIndex_]; uint32_t currentOffset = masm.currentOffset(); if (previous.tryBodyEnd() == currentOffset) { masm.nop(); } } + mostRecentFinishedTryNoteIndex_ = tryNoteIndex; // Don't set the end of the try note if we've OOM'ed, as the above nop's may // not have been placed. This is okay as this compilation will be thrown @@ -3875,7 +3885,7 @@ bool BaseCompiler::emitBrIf() { return emitBranchPerform(&b); } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC bool BaseCompiler::emitBrOnNull() { MOZ_ASSERT(!hasLatentOp()); @@ -5286,7 +5296,7 @@ bool BaseCompiler::emitReturnCallIndirect() { } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC bool BaseCompiler::emitCallRef() { const FuncType* funcType; Nothing unused_callee; @@ -6289,7 +6299,7 @@ bool BaseCompiler::emitRefIsNull() { return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC bool BaseCompiler::emitRefAsNonNull() { Nothing nothing; if (!iter_.readRefAsNonNull(¬hing)) { @@ -9776,13 +9786,13 @@ bool BaseCompiler::emitCallBuiltinModuleFunc() { return true; } - if (builtinModuleFunc->usesMemory) { + if (builtinModuleFunc->usesMemory()) { // The final parameter of an builtinModuleFunc is implicitly the heap base pushHeapBase(0); } // Call the builtinModuleFunc - return emitInstanceCall(builtinModuleFunc->signature); + return emitInstanceCall(*builtinModuleFunc->sig()); } ////////////////////////////////////////////////////////////////////////////// @@ -9989,36 +9999,18 @@ bool BaseCompiler::emitBody() { case uint16_t(Op::Else): CHECK_NEXT(emitElse()); case uint16_t(Op::Try): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitTry()); case uint16_t(Op::Catch): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitCatch()); case uint16_t(Op::CatchAll): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitCatchAll()); case uint16_t(Op::Delegate): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK(emitDelegate()); iter_.popDelegate(); NEXT(); case uint16_t(Op::Throw): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitThrow()); case uint16_t(Op::Rethrow): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitRethrow()); case uint16_t(Op::ThrowRef): if (!moduleEnv_.exnrefEnabled()) { @@ -10063,16 +10055,15 @@ bool BaseCompiler::emitBody() { } CHECK_NEXT(emitReturnCallIndirect()); #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::CallRef): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitCallRef()); # ifdef ENABLE_WASM_TAIL_CALLS case uint16_t(Op::ReturnCallRef): - if (!moduleEnv_.functionReferencesEnabled() || - !moduleEnv_.tailCallsEnabled()) { + if (!moduleEnv_.gcEnabled() || !moduleEnv_.tailCallsEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitReturnCallRef()); @@ -10609,19 +10600,19 @@ bool BaseCompiler::emitBody() { case uint16_t(Op::MemorySize): CHECK_NEXT(emitMemorySize()); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::RefAsNonNull): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitRefAsNonNull()); case uint16_t(Op::BrOnNull): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitBrOnNull()); case uint16_t(Op::BrOnNonNull): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitBrOnNonNull()); @@ -11484,113 +11475,113 @@ bool BaseCompiler::emitBody() { case uint32_t(ThreadOp::I32AtomicAdd): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicSub): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicAnd): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicOr): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicXor): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXchg): CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Int32)); @@ -11851,6 +11842,8 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& moduleEnv, stackMapGenerator_(stackMaps, trapExitLayout, trapExitLayoutNumWords, *masm), deadCode_(false), + // Init value is selected to ensure proper logic in finishTryNote. + mostRecentFinishedTryNoteIndex_(0), bceSafe_(0), latentOp_(LatentOp::None), latentType_(ValType::I32), @@ -11979,10 +11972,7 @@ bool js::wasm::BaselineCompileFunctions(const ModuleEnvironment& moduleEnv, // Build the local types vector. ValTypeVector locals; - if (!locals.appendAll(moduleEnv.funcs[func.index].type->args())) { - return false; - } - if (!DecodeLocalEntries(d, *moduleEnv.types, moduleEnv.features, &locals)) { + if (!DecodeLocalEntriesWithParams(d, moduleEnv, func.index, &locals)) { return false; } diff --git a/js/src/wasm/WasmBinary.h b/js/src/wasm/WasmBinary.h index 2d41528157..da17a0a864 100644 --- a/js/src/wasm/WasmBinary.h +++ b/js/src/wasm/WasmBinary.h @@ -72,12 +72,18 @@ class Opcode { static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits"); MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit)); } + MOZ_IMPLICIT Opcode(GcOp op) + : bits_((uint32_t(op) << 8) | uint32_t(Op::GcPrefix)) { + static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits"); + MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit)); + } bool isOp() const { return bits_ < uint32_t(Op::FirstPrefix); } bool isMisc() const { return (bits_ & 255) == uint32_t(Op::MiscPrefix); } bool isThread() const { return (bits_ & 255) == uint32_t(Op::ThreadPrefix); } bool isMoz() const { return (bits_ & 255) == uint32_t(Op::MozPrefix); } bool isSimd() const { return (bits_ & 255) == uint32_t(Op::SimdPrefix); } + bool isGc() const { return (bits_ & 255) == uint32_t(Op::GcPrefix); } Op asOp() const { MOZ_ASSERT(isOp()); @@ -99,6 +105,10 @@ class Opcode { MOZ_ASSERT(isSimd()); return SimdOp(bits_ >> 8); } + GcOp asGc() const { + MOZ_ASSERT(isGc()); + return GcOp(bits_ >> 8); + } uint32_t bits() const { return bits_; } @@ -127,6 +137,7 @@ using MaybeSectionRange = Maybe<SectionRange>; class Encoder { Bytes& bytes_; + const TypeContext* types_; template <class T> [[nodiscard]] bool write(const T& v) { @@ -201,7 +212,13 @@ class Encoder { } public: - explicit Encoder(Bytes& bytes) : bytes_(bytes) { MOZ_ASSERT(empty()); } + explicit Encoder(Bytes& bytes) : bytes_(bytes), types_(nullptr) { + MOZ_ASSERT(empty()); + } + explicit Encoder(Bytes& bytes, const TypeContext& types) + : bytes_(bytes), types_(&types) { + MOZ_ASSERT(empty()); + } size_t currentOffset() const { return bytes_.length(); } bool empty() const { return currentOffset() == 0; } @@ -226,9 +243,17 @@ class Encoder { [[nodiscard]] bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); } [[nodiscard]] bool writeValType(ValType type) { static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits"); - // writeValType is only used by asm.js, which doesn't use type - // references - MOZ_RELEASE_ASSERT(!type.isTypeRef(), "NYI"); + if (type.isTypeRef()) { + MOZ_RELEASE_ASSERT(types_, + "writeValType is used, but types were not specified."); + if (!writeFixedU8(uint8_t(type.isNullable() ? TypeCode::NullableRef + : TypeCode::Ref))) { + return false; + } + uint32_t typeIndex = types_->indexOf(*type.typeDef()); + // Encode positive LEB S33 as S64. + return writeVarS64(typeIndex); + } TypeCode tc = type.packed().typeCode(); MOZ_ASSERT(size_t(tc) < size_t(TypeCode::Limit)); return writeFixedU8(uint8_t(tc)); @@ -693,9 +718,9 @@ inline bool Decoder::readPackedType(const TypeContext& types, } case uint8_t(TypeCode::Ref): case uint8_t(TypeCode::NullableRef): { -#ifdef ENABLE_WASM_FUNCTION_REFERENCES - if (!features.functionReferences) { - return fail("(ref T) types not enabled"); +#ifdef ENABLE_WASM_GC + if (!features.gc) { + return fail("gc not enabled"); } bool nullable = code == uint8_t(TypeCode::NullableRef); RefType refType; @@ -718,7 +743,7 @@ inline bool Decoder::readPackedType(const TypeContext& types, case uint8_t(TypeCode::NullAnyRef): { #ifdef ENABLE_WASM_GC if (!features.gc) { - return fail("gc types not enabled"); + return fail("gc not enabled"); } *type = RefType::fromTypeCode(TypeCode(code), true); return true; @@ -784,7 +809,7 @@ inline bool Decoder::readHeapType(const TypeContext& types, case uint8_t(TypeCode::NullExternRef): case uint8_t(TypeCode::NullAnyRef): if (!features.gc) { - return fail("gc types not enabled"); + return fail("gc not enabled"); } *type = RefType::fromTypeCode(TypeCode(code), nullable); return true; @@ -794,8 +819,8 @@ inline bool Decoder::readHeapType(const TypeContext& types, } } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES - if (features.functionReferences) { +#ifdef ENABLE_WASM_GC + if (features.gc) { int32_t x; if (!readVarS32(&x) || x < 0 || uint32_t(x) >= types.length()) { return fail("invalid heap type index"); diff --git a/js/src/wasm/WasmBuiltinModule.cpp b/js/src/wasm/WasmBuiltinModule.cpp index 0748977c8b..044591224e 100644 --- a/js/src/wasm/WasmBuiltinModule.cpp +++ b/js/src/wasm/WasmBuiltinModule.cpp @@ -27,28 +27,29 @@ #include "wasm/WasmJS.h" #include "wasm/WasmModule.h" #include "wasm/WasmOpIter.h" +#include "wasm/WasmStaticTypeDefs.h" #include "wasm/WasmValidate.h" using namespace js; using namespace js::wasm; -#define VISIT_BUILTIN_FUNC(op, export, sa_name, abitype, entry, uses_memory, \ - idx) \ - static const ValType BuiltinModuleFunc##op##_Params[] = \ - DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_##op; \ - \ - const BuiltinModuleFunc BuiltinModuleFunc##op = { \ - export, \ - mozilla::Span<const ValType>(BuiltinModuleFunc##op##_Params), \ - DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_##op, \ - SASig##sa_name, \ - uses_memory, \ - }; - -FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) -#undef VISIT_BUILTIN_FUNC +BuiltinModuleFuncs* BuiltinModuleFuncs::singleton_ = nullptr; + +[[nodiscard]] bool BuiltinModuleFunc::init(const RefPtr<TypeContext>& types, + mozilla::Span<const ValType> params, + Maybe<ValType> result, + bool usesMemory, + const SymbolicAddressSignature* sig, + const char* exportName) { + // This builtin must not have been initialized yet. + MOZ_ASSERT(!recGroup_); + + // Initialize the basic fields + exportName_ = exportName; + sig_ = sig; + usesMemory_ = usesMemory; -bool BuiltinModuleFunc::funcType(FuncType* type) const { + // Create a function type for the given params and result ValTypeVector paramVec; if (!paramVec.append(params.data(), params.data() + params.size())) { return false; @@ -57,21 +58,48 @@ bool BuiltinModuleFunc::funcType(FuncType* type) const { if (result.isSome() && !resultVec.append(*result)) { return false; } - *type = FuncType(std::move(paramVec), std::move(resultVec)); + const TypeDef* typeDef = + types->addType(FuncType(std::move(paramVec), std::move(resultVec))); + if (!typeDef) { + return false; + } + recGroup_ = &typeDef->recGroup(); return true; } -/* static */ -const BuiltinModuleFunc& BuiltinModuleFunc::getFromId(BuiltinModuleFuncId id) { - switch (id) { -#define VISIT_BUILTIN_FUNC(op, ...) \ - case BuiltinModuleFuncId::op: \ - return BuiltinModuleFunc##op; - FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) +bool BuiltinModuleFuncs::init() { + singleton_ = js_new<BuiltinModuleFuncs>(); + if (!singleton_) { + return false; + } + + RefPtr<TypeContext> types = js_new<TypeContext>(); + if (!types) { + return false; + } + +#define VISIT_BUILTIN_FUNC(op, export, sa_name, abitype, entry, uses_memory, \ + ...) \ + const ValType op##Params[] = \ + DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_##op; \ + Maybe<ValType> op##Result = DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_##op; \ + if (!singleton_->funcs_[BuiltinModuleFuncId::op].init( \ + types, mozilla::Span<const ValType>(op##Params), op##Result, \ + uses_memory, &SASig##sa_name, export)) { \ + return false; \ + } + FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) #undef VISIT_BUILTIN_FUNC - default: - MOZ_CRASH("unexpected builtinModuleFunc"); + + return true; +} + +void BuiltinModuleFuncs::destroy() { + if (!singleton_) { + return; } + js_delete(singleton_); + singleton_ = nullptr; } bool EncodeFuncBody(const BuiltinModuleFunc& builtinModuleFunc, @@ -80,7 +108,8 @@ bool EncodeFuncBody(const BuiltinModuleFunc& builtinModuleFunc, if (!EncodeLocalEntries(encoder, ValTypeVector())) { return false; } - for (uint32_t i = 0; i < builtinModuleFunc.params.size(); i++) { + const FuncType* funcType = builtinModuleFunc.funcType(); + for (uint32_t i = 0; i < funcType->args().length(); i++) { if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(i)) { return false; } @@ -145,11 +174,11 @@ bool CompileBuiltinModule(JSContext* cx, for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) { const BuiltinModuleFuncId& id = ids[funcIndex]; const BuiltinModuleFunc& builtinModuleFunc = - BuiltinModuleFunc::getFromId(id); + BuiltinModuleFuncs::getFromId(id); - FuncType type; - if (!builtinModuleFunc.funcType(&type) || - !moduleEnv.types->addType(std::move(type))) { + SharedRecGroup recGroup = builtinModuleFunc.recGroup(); + MOZ_ASSERT(recGroup->numTypes() == 1); + if (!moduleEnv.types->addRecGroup(recGroup)) { ReportOutOfMemory(cx); return false; } @@ -170,10 +199,10 @@ bool CompileBuiltinModule(JSContext* cx, // Add (export "$name" (func $i)) declarations. for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) { const BuiltinModuleFunc& builtinModuleFunc = - BuiltinModuleFunc::getFromId(ids[funcIndex]); + BuiltinModuleFuncs::getFromId(ids[funcIndex]); CacheableName exportName; - if (!CacheableName::fromUTF8Chars(builtinModuleFunc.exportName, + if (!CacheableName::fromUTF8Chars(builtinModuleFunc.exportName(), &exportName) || !moduleEnv.exports.append(Export(std::move(exportName), funcIndex, DefinitionKind::Function))) { @@ -200,7 +229,7 @@ bool CompileBuiltinModule(JSContext* cx, for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) { BuiltinModuleFuncId id = ids[funcIndex]; const BuiltinModuleFunc& builtinModuleFunc = - BuiltinModuleFunc::getFromId(ids[funcIndex]); + BuiltinModuleFuncs::getFromId(ids[funcIndex]); // Compilation may be done using other threads, ModuleGenerator requires // that function bodies live until after finishFuncDefs(). @@ -267,14 +296,16 @@ static BuiltinModuleFuncId IntGemmFuncs[] = { #ifdef ENABLE_WASM_JS_STRING_BUILTINS static BuiltinModuleFuncId JSStringFuncs[] = { - BuiltinModuleFuncId::StringFromWTF16Array, - BuiltinModuleFuncId::StringToWTF16Array, + BuiltinModuleFuncId::StringTest, + BuiltinModuleFuncId::StringCast, + BuiltinModuleFuncId::StringFromCharCodeArray, + BuiltinModuleFuncId::StringIntoCharCodeArray, BuiltinModuleFuncId::StringFromCharCode, BuiltinModuleFuncId::StringFromCodePoint, BuiltinModuleFuncId::StringCharCodeAt, BuiltinModuleFuncId::StringCodePointAt, BuiltinModuleFuncId::StringLength, - BuiltinModuleFuncId::StringConcatenate, + BuiltinModuleFuncId::StringConcat, BuiltinModuleFuncId::StringSubstring, BuiltinModuleFuncId::StringEquals, BuiltinModuleFuncId::StringCompare}; @@ -300,8 +331,8 @@ Maybe<const BuiltinModuleFunc*> wasm::ImportMatchesBuiltinModuleFunc( // Not supported for implicit instantiation yet MOZ_RELEASE_ASSERT(module == BuiltinModuleId::JSString); for (BuiltinModuleFuncId funcId : JSStringFuncs) { - const BuiltinModuleFunc& func = BuiltinModuleFunc::getFromId(funcId); - if (importName == mozilla::MakeStringSpan(func.exportName)) { + const BuiltinModuleFunc& func = BuiltinModuleFuncs::getFromId(funcId); + if (importName == mozilla::MakeStringSpan(func.exportName())) { return Some(&func); } } diff --git a/js/src/wasm/WasmBuiltinModule.h b/js/src/wasm/WasmBuiltinModule.h index 42faffec73..8646e789e6 100644 --- a/js/src/wasm/WasmBuiltinModule.h +++ b/js/src/wasm/WasmBuiltinModule.h @@ -62,25 +62,60 @@ struct MOZ_STACK_CLASS BuiltinModuleInstances { // An builtin module func is a natively implemented function that may be // compiled into a 'builtin module', which may be instantiated with a provided // memory yielding an exported WebAssembly function wrapping the builtin module. -struct BuiltinModuleFunc { +class BuiltinModuleFunc { + private: + SharedRecGroup recGroup_; + const char* exportName_; + const SymbolicAddressSignature* sig_; + bool usesMemory_; + + public: + // Default constructor so this can be used in an EnumeratedArray. + BuiltinModuleFunc() = default; + + // Initialize this builtin. Must only be called once. + [[nodiscard]] bool init(const RefPtr<TypeContext>& types, + mozilla::Span<const ValType> params, + Maybe<ValType> result, bool usesMemory, + const SymbolicAddressSignature* sig, + const char* exportName); + + // The rec group for the function type for this builtin. + const RecGroup* recGroup() const { return recGroup_.get(); } + // The type definition for the function type for this builtin. + const TypeDef* typeDef() const { return &recGroup_->type(0); } + // The function type for this builtin. + const FuncType* funcType() const { return &typeDef()->funcType(); } + // The name of the func as it is exported - const char* exportName; - // The params taken by the func. - mozilla::Span<const ValType> params; - // The optional result returned by the func. - mozilla::Maybe<const ValType> result; - // The signature of the builtin that implements the func - const SymbolicAddressSignature& signature; + const char* exportName() const { return exportName_; } + // The signature of the builtin that implements this function. + const SymbolicAddressSignature* sig() const { return sig_; } // Whether this function takes a pointer to the memory base as a hidden final - // parameter. - bool usesMemory; + // parameter. This parameter will show up in the SymbolicAddressSignature, + // but not the function type. Compilers must pass the memoryBase to the + // function call as the last parameter. + bool usesMemory() const { return usesMemory_; } +}; + +// Static storage for all builtin module funcs in the system. +class BuiltinModuleFuncs { + using Storage = + mozilla::EnumeratedArray<BuiltinModuleFuncId, BuiltinModuleFunc, + size_t(BuiltinModuleFuncId::Limit)>; + Storage funcs_; - // Allocate a FuncType for this func, returning false for OOM - bool funcType(FuncType* type) const; + static BuiltinModuleFuncs* singleton_; + + public: + [[nodiscard]] static bool init(); + static void destroy(); // Get the BuiltinModuleFunc for an BuiltinModuleFuncId. BuiltinModuleFuncId // must be validated. - static const BuiltinModuleFunc& getFromId(BuiltinModuleFuncId id); + static const BuiltinModuleFunc& getFromId(BuiltinModuleFuncId id) { + return singleton_->funcs_[id]; + } }; Maybe<BuiltinModuleId> ImportMatchesBuiltinModule( diff --git a/js/src/wasm/WasmBuiltinModule.yaml b/js/src/wasm/WasmBuiltinModule.yaml index 88c2f5a575..755e0e5e74 100644 --- a/js/src/wasm/WasmBuiltinModule.yaml +++ b/js/src/wasm/WasmBuiltinModule.yaml @@ -12,10 +12,10 @@ entry: Instance::intrI8VecMul export: i8vecmul params: - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -40,12 +40,12 @@ entry: intgemm::IntrI8PrepareB export: int8_prepare_b params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -65,12 +65,12 @@ entry: intgemm::IntrI8PrepareBFromTransposed export: int8_prepare_b_from_transposed params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -90,10 +90,10 @@ entry: intgemm::IntrI8PrepareBFromQuantizedTransposed export: int8_prepare_b_from_quantized_transposed params: - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -116,12 +116,12 @@ entry: intgemm::IntrI8PrepareA export: int8_prepare_a params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -142,15 +142,15 @@ entry: intgemm::IntrI8PrepareBias export: int8_prepare_bias params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -177,18 +177,18 @@ entry: intgemm::IntrI8MultiplyAndAddBias export: int8_multiply_and_add_bias params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -206,12 +206,12 @@ entry: intgemm::IntrI8SelectColumnsOfB export: int8_select_columns_of_b params: - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -219,31 +219,61 @@ #if defined(ENABLE_WASM_JS_STRING_BUILTINS) -- op: StringFromWTF16Array +- op: StringTest symbolic_address: - name: StringFromWTF16Array + name: StringTest + type: Args_Int32_GeneralGeneral + entry: Instance::stringTest + export: test + params: + - 'externref' + result: 'i32' + fail_mode: Infallible + uses_memory: false + +- op: StringCast + symbolic_address: + name: StringCast + type: Args_General2 + entry: Instance::stringCast + export: cast + params: + - 'externref' + result: + code: 'extern' + nullable: false + fail_mode: FailOnNullPtr + uses_memory: false + +- op: StringFromCharCodeArray + symbolic_address: + name: StringFromCharCodeArray type: Args_General_GeneralGeneralInt32Int32 - entry: Instance::stringFromWTF16Array - export: fromWTF16Array + entry: Instance::stringFromCharCodeArray + export: fromCharCodeArray params: - - 'ValType(RefType::any())' - - 'ValType::i32()' - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - type: "StaticTypeDefs::arrayMutI16" + nullable: true + - 'i32' + - 'i32' + result: + code: 'extern' + nullable: false fail_mode: FailOnNullPtr uses_memory: false -- op: StringToWTF16Array +- op: StringIntoCharCodeArray symbolic_address: - name: StringToWTF16Array + name: StringIntoCharCodeArray type: Args_Int32_GeneralGeneralGeneralInt32 - entry: Instance::stringToWTF16Array - export: toWTF16Array + entry: Instance::stringIntoCharCodeArray + export: intoCharCodeArray params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::any())' - - 'ValType::i32()' - result: 'ValType::i32()' + - 'externref' + - type: "StaticTypeDefs::arrayMutI16" + nullable: true + - 'i32' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -254,8 +284,8 @@ entry: Instance::stringFromCharCode export: fromCharCode params: - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - 'i32' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -266,8 +296,8 @@ entry: Instance::stringFromCodePoint export: fromCodePoint params: - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - 'i32' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -278,9 +308,9 @@ entry: Instance::stringCharCodeAt export: charCodeAt params: - - 'ValType(RefType::extern_())' - - 'ValType::i32()' - result: 'ValType::i32()' + - 'externref' + - 'i32' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -291,9 +321,9 @@ entry: Instance::stringCodePointAt export: codePointAt params: - - 'ValType(RefType::extern_())' - - 'ValType::i32()' - result: 'ValType::i32()' + - 'externref' + - 'i32' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -304,21 +334,21 @@ entry: Instance::stringLength export: length params: - - 'ValType(RefType::extern_())' - result: 'ValType::i32()' + - 'externref' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false -- op: StringConcatenate +- op: StringConcat symbolic_address: - name: StringConcatenate + name: StringConcat type: Args_General3 - entry: Instance::stringConcatenate - export: concatenate + entry: Instance::stringConcat + export: concat params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::extern_())' - result: 'ValType(RefType::extern_())' + - 'externref' + - 'externref' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -329,10 +359,10 @@ entry: Instance::stringSubstring export: substring params: - - 'ValType(RefType::extern_())' - - 'ValType::i32()' - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - 'externref' + - 'i32' + - 'i32' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -343,9 +373,9 @@ entry: Instance::stringEquals export: equals params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::extern_())' - result: 'ValType::i32()' + - 'externref' + - 'externref' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -356,9 +386,9 @@ entry: Instance::stringCompare export: compare params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::extern_())' - result: 'ValType::i32()' + - 'externref' + - 'externref' + result: 'i32' fail_mode: FailOnMaxI32 uses_memory: false diff --git a/js/src/wasm/WasmBuiltins.cpp b/js/src/wasm/WasmBuiltins.cpp index 08024c3dfe..7b03494bcd 100644 --- a/js/src/wasm/WasmBuiltins.cpp +++ b/js/src/wasm/WasmBuiltins.cpp @@ -397,9 +397,9 @@ const SymbolicAddressSignature SASigArrayCopy = { #define VISIT_BUILTIN_FUNC(op, export, sa_name, ...) \ const SymbolicAddressSignature SASig##sa_name = { \ SymbolicAddress::sa_name, \ - DECLARE_BUILTIN_MODULE_FUNC_RESULT_SASTYPE_##op, \ + DECLARE_BUILTIN_MODULE_FUNC_RESULT_MIRTYPE_##op, \ DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_##op, \ - DECLARE_BUILTIN_MODULE_FUNC_PARAM_SASTYPES_##op}; + DECLARE_BUILTIN_MODULE_FUNC_PARAM_MIRTYPES_##op}; FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) #undef VISIT_BUILTIN_FUNC @@ -1839,7 +1839,7 @@ using TypedNativeToCodeRangeMap = HashMap<TypedNative, uint32_t, TypedNative, SystemAllocPolicy>; using SymbolicAddressToCodeRangeArray = - EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, uint32_t>; + EnumeratedArray<SymbolicAddress, uint32_t, size_t(SymbolicAddress::Limit)>; struct BuiltinThunks { uint8_t* codeBase; diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index b7aaa1869c..7fe2562ab6 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -1085,6 +1085,23 @@ bool Code::lookupTrap(void* pc, Trap* trapOut, BytecodeOffset* bytecode) const { return false; } +bool Code::lookupFunctionTier(const CodeRange* codeRange, Tier* tier) const { + // This logic only works if the codeRange is a function, and therefore only + // exists in metadata and not a lazy stub tier. Generalizing to access lazy + // stubs would require taking a lock, which is undesirable for the profiler. + MOZ_ASSERT(codeRange->isFunction()); + for (Tier t : tiers()) { + const CodeTier& code = codeTier(t); + const MetadataTier& metadata = code.metadata(); + if (codeRange >= metadata.codeRanges.begin() && + codeRange < metadata.codeRanges.end()) { + *tier = t; + return true; + } + } + return false; +} + struct UnwindInfoPCOffset { const CodeRangeUnwindInfoVector& info; explicit UnwindInfoPCOffset(const CodeRangeUnwindInfoVector& info) diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h index a34a462127..e03a2f596e 100644 --- a/js/src/wasm/WasmCode.h +++ b/js/src/wasm/WasmCode.h @@ -117,8 +117,8 @@ struct LinkData : LinkDataCacheablePod { }; using InternalLinkVector = Vector<InternalLink, 0, SystemAllocPolicy>; - struct SymbolicLinkArray - : EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, Uint32Vector> { + struct SymbolicLinkArray : EnumeratedArray<SymbolicAddress, Uint32Vector, + size_t(SymbolicAddress::Limit)> { size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; @@ -857,6 +857,7 @@ class Code : public ShareableBase<Code> { bool containsCodePC(const void* pc) const; bool lookupTrap(void* pc, Trap* trap, BytecodeOffset* bytecode) const; const CodeRangeUnwindInfo* lookupUnwindInfo(void* pc) const; + bool lookupFunctionTier(const CodeRange* codeRange, Tier* tier) const; // To save memory, profilingLabels_ are generated lazily when profiling mode // is enabled. diff --git a/js/src/wasm/WasmCodegenTypes.h b/js/src/wasm/WasmCodegenTypes.h index 815292dd23..590572ae8a 100644 --- a/js/src/wasm/WasmCodegenTypes.h +++ b/js/src/wasm/WasmCodegenTypes.h @@ -273,7 +273,7 @@ WASM_DECLARE_CACHEABLE_POD(TrapSite); WASM_DECLARE_POD_VECTOR(TrapSite, TrapSiteVector) struct TrapSiteVectorArray - : EnumeratedArray<Trap, Trap::Limit, TrapSiteVector> { + : EnumeratedArray<Trap, TrapSiteVector, size_t(Trap::Limit)> { bool empty() const; void clear(); void swap(TrapSiteVectorArray& rhs); diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index 2418340684..fbf4df3e71 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -726,8 +726,8 @@ void CompilerEnvironment::computeParameters(Decoder& d) { state_ = Computed; } -template <class DecoderT> -static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg, +template <class DecoderT, class ModuleGeneratorT> +static bool DecodeFunctionBody(DecoderT& d, ModuleGeneratorT& mg, uint32_t funcIndex) { uint32_t bodySize; if (!d.readVarU32(&bodySize)) { @@ -751,9 +751,9 @@ static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg, bodyBegin + bodySize); } -template <class DecoderT> +template <class DecoderT, class ModuleGeneratorT> static bool DecodeCodeSection(const ModuleEnvironment& env, DecoderT& d, - ModuleGenerator& mg) { + ModuleGeneratorT& mg) { if (!env.codeSection) { if (env.numFuncDefs() != 0) { return d.fail("expected code section"); @@ -996,3 +996,46 @@ SharedModule wasm::CompileStreaming( return mg.finishModule(*bytecode, streamEnd.tier2Listener); } + +class DumpIonModuleGenerator { + private: + ModuleEnvironment& moduleEnv_; + uint32_t targetFuncIndex_; + IonDumpContents contents_; + GenericPrinter& out_; + UniqueChars* error_; + + public: + DumpIonModuleGenerator(ModuleEnvironment& moduleEnv, uint32_t targetFuncIndex, + IonDumpContents contents, GenericPrinter& out, + UniqueChars* error) + : moduleEnv_(moduleEnv), + targetFuncIndex_(targetFuncIndex), + contents_(contents), + out_(out), + error_(error) {} + + bool finishFuncDefs() { return true; } + bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode, + const uint8_t* begin, const uint8_t* end) { + if (funcIndex != targetFuncIndex_) { + return true; + } + + FuncCompileInput input(funcIndex, lineOrBytecode, begin, end, + Uint32Vector()); + return IonDumpFunction(moduleEnv_, input, contents_, out_, error_); + } +}; + +bool wasm::DumpIonFunctionInModule(const ShareableBytes& bytecode, + uint32_t targetFuncIndex, + IonDumpContents contents, + GenericPrinter& out, UniqueChars* error) { + UniqueCharsVector warnings; + Decoder d(bytecode.bytes, 0, error, &warnings); + ModuleEnvironment moduleEnv(FeatureArgs::allEnabled()); + DumpIonModuleGenerator mg(moduleEnv, targetFuncIndex, contents, out, error); + return moduleEnv.init() && DecodeModuleEnvironment(d, &moduleEnv) && + DecodeCodeSection(moduleEnv, d, mg); +} diff --git a/js/src/wasm/WasmCompile.h b/js/src/wasm/WasmCompile.h index 2b07881eea..f39dc09fb9 100644 --- a/js/src/wasm/WasmCompile.h +++ b/js/src/wasm/WasmCompile.h @@ -93,6 +93,19 @@ SharedModule CompileStreaming(const CompileArgs& args, const Bytes& envBytes, const Atomic<bool>& cancelled, UniqueChars* error, UniqueCharsVector* warnings); +// What to print out from dumping a function from Ion. +enum class IonDumpContents { + UnoptimizedMIR, + OptimizedMIR, + LIR, + + Default = UnoptimizedMIR, +}; + +bool DumpIonFunctionInModule(const ShareableBytes& bytecode, + uint32_t targetFuncIndex, IonDumpContents contents, + GenericPrinter& out, UniqueChars* error); + } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmCompileArgs.h b/js/src/wasm/WasmCompileArgs.h index 1bf9e60d13..af85026b93 100644 --- a/js/src/wasm/WasmCompileArgs.h +++ b/js/src/wasm/WasmCompileArgs.h @@ -103,6 +103,15 @@ struct FeatureArgs { FeatureArgs(FeatureArgs&&) = default; static FeatureArgs build(JSContext* cx, const FeatureOptions& options); + static FeatureArgs allEnabled() { + FeatureArgs args; +#define WASM_FEATURE(NAME, LOWER_NAME, ...) args.LOWER_NAME = true; + JS_FOR_WASM_FEATURES(WASM_FEATURE) +#undef WASM_FEATURE + args.sharedMemory = Shareable::True; + args.simd = true; + return args; + } #define WASM_FEATURE(NAME, LOWER_NAME, ...) bool LOWER_NAME; JS_FOR_WASM_FEATURES(WASM_FEATURE) diff --git a/js/src/wasm/WasmFeatures.cpp b/js/src/wasm/WasmFeatures.cpp index 05804353ae..24ab1c7d51 100644 --- a/js/src/wasm/WasmFeatures.cpp +++ b/js/src/wasm/WasmFeatures.cpp @@ -21,6 +21,7 @@ #include "jit/AtomicOperations.h" #include "jit/JitContext.h" #include "jit/JitOptions.h" +#include "js/Prefs.h" #include "util/StringBuffer.h" #include "vm/JSContext.h" #include "vm/Realm.h" @@ -56,13 +57,13 @@ static inline bool WasmThreadsFlag(JSContext* cx) { JS_FOR_WASM_FEATURES(WASM_FEATURE); #undef WASM_FEATURE -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, ...) \ - static inline bool Wasm##NAME##Flag(JSContext* cx) { \ - if (!(COMPILE_PRED)) { \ - return false; \ - } \ - return ((FLAG_PRED) && cx->options().wasm##NAME()) || (FLAG_FORCE_ON); \ +#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \ + FLAG_FORCE_ON, FLAG_FUZZ_ON, PREF) \ + static inline bool Wasm##NAME##Flag(JSContext* cx) { \ + if (!(COMPILE_PRED)) { \ + return false; \ + } \ + return ((FLAG_PRED) && JS::Prefs::wasm_##PREF()) || (FLAG_FORCE_ON); \ } JS_FOR_WASM_FEATURES(WASM_FEATURE); #undef WASM_FEATURE @@ -219,10 +220,9 @@ bool wasm::AnyCompilerAvailable(JSContext* cx) { // compiler that can support the feature. Subsequent compiler selection must // ensure that only compilers that actually support the feature are used. -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - ...) \ - bool wasm::NAME##Available(JSContext* cx) { \ - return Wasm##NAME##Flag(cx) && (COMPILER_PRED); \ +#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, ...) \ + bool wasm::NAME##Available(JSContext* cx) { \ + return Wasm##NAME##Flag(cx) && (COMPILER_PRED); \ } JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp index 171ac285be..90555720da 100644 --- a/js/src/wasm/WasmFrameIter.cpp +++ b/js/src/wasm/WasmFrameIter.cpp @@ -1882,3 +1882,16 @@ const char* ProfilingFrameIterator::label() const { MOZ_CRASH("bad code range kind"); } + +ProfilingFrameIterator::Category ProfilingFrameIterator::category() const { + if (!exitReason_.isFixed() || !exitReason_.isNone() || + !codeRange_->isFunction()) { + return Category::Other; + } + + Tier tier; + if (!code_->lookupFunctionTier(codeRange_, &tier)) { + return Category::Other; + } + return tier == Tier::Optimized ? Category::Ion : Category::Baseline; +} diff --git a/js/src/wasm/WasmFrameIter.h b/js/src/wasm/WasmFrameIter.h index 014f5de0ef..59590b1b2a 100644 --- a/js/src/wasm/WasmFrameIter.h +++ b/js/src/wasm/WasmFrameIter.h @@ -196,6 +196,12 @@ class ProfilingFrameIterator { ProfilingFrameIterator(const jit::JitActivation& activation, const RegisterState& state); + enum Category { + Baseline, + Ion, + Other, + }; + void operator++(); bool done() const { @@ -213,6 +219,8 @@ class ProfilingFrameIterator { } const char* label() const; + Category category() const; + void* endStackAddress() const { return endStackAddress_; } }; diff --git a/js/src/wasm/WasmGcObject-inl.h b/js/src/wasm/WasmGcObject-inl.h index 17800f41f1..4714aafc06 100644 --- a/js/src/wasm/WasmGcObject-inl.h +++ b/js/src/wasm/WasmGcObject-inl.h @@ -342,8 +342,7 @@ MOZ_ALWAYS_INLINE WasmArrayObject* WasmArrayObject::createArray( calcStorageBytesChecked(typeDefData->arrayElemSize, numElements); if (!storageBytes.isValid() || storageBytes.value() > uint32_t(wasm::MaxArrayPayloadBytes)) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_WASM_ARRAY_IMP_LIMIT); + wasm::ReportTrapError(cx, JSMSG_WASM_ARRAY_IMP_LIMIT); return nullptr; } diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp index a470626de4..338812e1d6 100644 --- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -440,7 +440,7 @@ static bool InRange(uint32_t caller, uint32_t callee) { using OffsetMap = HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>; using TrapMaybeOffsetArray = - EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>>; + EnumeratedArray<Trap, Maybe<uint32_t>, size_t(Trap::Limit)>; bool ModuleGenerator::linkCallSites() { AutoCreatedBy acb(masm_, "linkCallSites"); diff --git a/js/src/wasm/WasmInitExpr.cpp b/js/src/wasm/WasmInitExpr.cpp index e8c49cbc31..581eca2f62 100644 --- a/js/src/wasm/WasmInitExpr.cpp +++ b/js/src/wasm/WasmInitExpr.cpp @@ -74,20 +74,16 @@ class MOZ_STACK_CLASS InitExprInterpreter { return stack.append(Val(RefType::func(), ref)); } -#if defined(ENABLE_WASM_EXTENDED_CONST) || defined(ENABLE_WASM_GC) int32_t popI32() { uint32_t result = stack.back().i32(); stack.popBack(); return int32_t(result); } -#endif -#ifdef ENABLE_WASM_EXTENDED_CONST int64_t popI64() { uint64_t result = stack.back().i64(); stack.popBack(); return int64_t(result); } -#endif bool evalGlobalGet(JSContext* cx, uint32_t index) { RootedVal val(cx); @@ -107,7 +103,6 @@ class MOZ_STACK_CLASS InitExprInterpreter { return pushFuncRef(func); } bool evalRefNull(RefType type) { return pushRef(type, AnyRef::null()); } -#ifdef ENABLE_WASM_EXTENDED_CONST bool evalI32Add() { uint32_t b = popI32(); uint32_t a = popI32(); @@ -138,7 +133,6 @@ class MOZ_STACK_CLASS InitExprInterpreter { uint64_t a = popI64(); return pushI64(a * b); } -#endif // ENABLE_WASM_EXTENDED_CONST #ifdef ENABLE_WASM_GC bool evalStructNew(JSContext* cx, uint32_t typeIndex) { const TypeDef& typeDef = instance().metadata().types->type(typeIndex); @@ -320,7 +314,6 @@ bool InitExprInterpreter::evaluate(JSContext* cx, Decoder& d) { } CHECK(evalRefNull(type)); } -#ifdef ENABLE_WASM_EXTENDED_CONST case uint16_t(Op::I32Add): { if (!d.readBinary()) { return false; @@ -357,7 +350,6 @@ bool InitExprInterpreter::evaluate(JSContext* cx, Decoder& d) { } CHECK(evalI64Mul()); } -#endif #ifdef ENABLE_WASM_GC case uint16_t(Op::GcPrefix): { switch (op.b1) { @@ -449,9 +441,7 @@ bool wasm::DecodeConstantExpression(Decoder& d, ModuleEnvironment* env, return false; } -#if defined(ENABLE_WASM_EXTENDED_CONST) || defined(ENABLE_WASM_GC) Nothing nothing; -#endif NothingVector nothings{}; ResultType unusedType; @@ -542,13 +532,9 @@ bool wasm::DecodeConstantExpression(Decoder& d, ModuleEnvironment* env, *literal = Some(LitVal(ValType(type))); break; } -#ifdef ENABLE_WASM_EXTENDED_CONST case uint16_t(Op::I32Add): case uint16_t(Op::I32Sub): case uint16_t(Op::I32Mul): { - if (!env->extendedConstEnabled()) { - return iter.unrecognizedOpcode(&op); - } if (!iter.readBinary(ValType::I32, ¬hing, ¬hing)) { return false; } @@ -558,16 +544,12 @@ bool wasm::DecodeConstantExpression(Decoder& d, ModuleEnvironment* env, case uint16_t(Op::I64Add): case uint16_t(Op::I64Sub): case uint16_t(Op::I64Mul): { - if (!env->extendedConstEnabled()) { - return iter.unrecognizedOpcode(&op); - } if (!iter.readBinary(ValType::I64, ¬hing, ¬hing)) { return false; } *literal = Nothing(); break; } -#endif #ifdef ENABLE_WASM_GC case uint16_t(Op::GcPrefix): { if (!env->gcEnabled()) { @@ -663,6 +645,7 @@ bool InitExpr::decodeAndValidate(Decoder& d, ModuleEnvironment* env, expr->type_ = expected; if (literal) { + literal->unsafeSetType(expected); expr->kind_ = InitExprKind::Literal; expr->literal_ = *literal; return true; diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp index bf25b58c14..d025c02c16 100644 --- a/js/src/wasm/WasmInstance.cpp +++ b/js/src/wasm/WasmInstance.cpp @@ -1531,8 +1531,10 @@ static bool ArrayCopyFromData(JSContext* cx, Handle<WasmArrayObject*> arrayObj, // Because `numBytesToCopy` is an in-range `CheckedUint32`, the cast to // `size_t` is safe even on a 32-bit target. - memcpy(arrayObj->data_, &seg->bytes[segByteOffset], - size_t(numBytesToCopy.value())); + if (numElements != 0) { + memcpy(arrayObj->data_, &seg->bytes[segByteOffset], + size_t(numBytesToCopy.value())); + } return true; } @@ -1948,35 +1950,42 @@ static bool ArrayCopyFromElem(JSContext* cx, Handle<WasmArrayObject*> arrayObj, // take into account the enclosing recursion group of the type. This is // temporary until builtin module functions can specify a precise array type // for params/results. -static WasmArrayObject* CastToI16Array(HandleAnyRef ref, bool needMutable) { - if (!ref.isJSObject()) { - return nullptr; - } +template <bool isMutable> +static WasmArrayObject* UncheckedCastToArrayI16(HandleAnyRef ref) { JSObject& object = ref.toJSObject(); - if (!object.is<WasmArrayObject>()) { - return nullptr; - } WasmArrayObject& array = object.as<WasmArrayObject>(); - const ArrayType& type = array.typeDef().arrayType(); - if (type.elementType_ != StorageType::I16) { - return nullptr; + DebugOnly<const ArrayType*> type(&array.typeDef().arrayType()); + MOZ_ASSERT(type->elementType_ == StorageType::I16); + MOZ_ASSERT(type->isMutable_ == isMutable); + return &array; +} + +/* static */ +int32_t Instance::stringTest(Instance* instance, void* stringArg) { + AnyRef string = AnyRef::fromCompiledCode(stringArg); + if (string.isNull() || !string.isJSString()) { + return 0; } - if (needMutable && !type.isMutable_) { + return 1; +} + +/* static */ +void* Instance::stringCast(Instance* instance, void* stringArg) { + AnyRef string = AnyRef::fromCompiledCode(stringArg); + if (string.isNull() || !string.isJSString()) { + ReportTrapError(instance->cx(), JSMSG_WASM_BAD_CAST); return nullptr; } - return &array; + return string.forCompiledCode(); } /* static */ -void* Instance::stringFromWTF16Array(Instance* instance, void* arrayArg, - uint32_t arrayStart, uint32_t arrayCount) { +void* Instance::stringFromCharCodeArray(Instance* instance, void* arrayArg, + uint32_t arrayStart, + uint32_t arrayCount) { JSContext* cx = instance->cx(); RootedAnyRef arrayRef(cx, AnyRef::fromCompiledCode(arrayArg)); - Rooted<WasmArrayObject*> array(cx); - if (!(array = CastToI16Array(arrayRef, false))) { - ReportTrapError(cx, JSMSG_WASM_BAD_CAST); - return nullptr; - } + Rooted<WasmArrayObject*> array(cx, UncheckedCastToArrayI16<true>(arrayRef)); CheckedUint32 lastIndexPlus1 = CheckedUint32(arrayStart) + CheckedUint32(arrayCount); @@ -1997,8 +2006,8 @@ void* Instance::stringFromWTF16Array(Instance* instance, void* arrayArg, } /* static */ -int32_t Instance::stringToWTF16Array(Instance* instance, void* stringArg, - void* arrayArg, uint32_t arrayStart) { +int32_t Instance::stringIntoCharCodeArray(Instance* instance, void* stringArg, + void* arrayArg, uint32_t arrayStart) { JSContext* cx = instance->cx(); AnyRef stringRef = AnyRef::fromCompiledCode(stringArg); if (!stringRef.isJSString()) { @@ -2009,11 +2018,7 @@ int32_t Instance::stringToWTF16Array(Instance* instance, void* stringArg, size_t stringLength = string->length(); RootedAnyRef arrayRef(cx, AnyRef::fromCompiledCode(arrayArg)); - Rooted<WasmArrayObject*> array(cx); - if (!(array = CastToI16Array(arrayRef, true))) { - ReportTrapError(cx, JSMSG_WASM_BAD_CAST); - return -1; - } + Rooted<WasmArrayObject*> array(cx, UncheckedCastToArrayI16<true>(arrayRef)); CheckedUint32 lastIndexPlus1 = CheckedUint32(arrayStart) + stringLength; if (!lastIndexPlus1.isValid() || @@ -2120,8 +2125,8 @@ int32_t Instance::stringLength(Instance* instance, void* stringArg) { return (int32_t)stringRef.toJSString()->length(); } -void* Instance::stringConcatenate(Instance* instance, void* firstStringArg, - void* secondStringArg) { +void* Instance::stringConcat(Instance* instance, void* firstStringArg, + void* secondStringArg) { JSContext* cx = instance->cx(); AnyRef firstStringRef = AnyRef::fromCompiledCode(firstStringArg); @@ -2444,11 +2449,10 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, if (global.isIndirect()) { // Initialize the cell - wasm::GCPtrVal& cell = globalObjs[i]->val(); - cell = val.get(); + globalObjs[i]->setVal(val); + // Link to the cell - void* address = (void*)&cell.get().cell(); - *(void**)globalAddr = address; + *(void**)globalAddr = globalObjs[i]->addressOfCell(); } else { val.get().writeToHeapLocation(globalAddr); } @@ -2539,6 +2543,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, size_t numWords = std::max<size_t>((numFuncs + 31) / 32, 1); debugFilter_ = (uint32_t*)js_calloc(numWords, sizeof(uint32_t)); if (!debugFilter_) { + ReportOutOfMemory(cx); return false; } } @@ -2552,6 +2557,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, // Take references to the passive data segments if (!passiveDataSegments_.resize(dataSegments.length())) { + ReportOutOfMemory(cx); return false; } for (size_t i = 0; i < dataSegments.length(); i++) { @@ -2563,6 +2569,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, // Create InstanceElemSegments for any passive element segments, since these // are the ones available at runtime. if (!passiveElemSegments_.resize(elemSegments.length())) { + ReportOutOfMemory(cx); return false; } for (size_t i = 0; i < elemSegments.length(); i++) { @@ -2571,6 +2578,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, passiveElemSegments_[i] = InstanceElemSegment(); InstanceElemSegment& instanceSeg = passiveElemSegments_[i]; if (!instanceSeg.reserve(seg.numElements())) { + ReportOutOfMemory(cx); return false; } diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h index dcc586b14f..074c6212df 100644 --- a/js/src/wasm/WasmInstance.h +++ b/js/src/wasm/WasmInstance.h @@ -571,10 +571,13 @@ class alignas(16) Instance { static int32_t intrI8VecMul(Instance* instance, uint32_t dest, uint32_t src1, uint32_t src2, uint32_t len, uint8_t* memBase); - static void* stringFromWTF16Array(Instance* instance, void* arrayArg, - uint32_t arrayStart, uint32_t arrayCount); - static int32_t stringToWTF16Array(Instance* instance, void* stringArg, - void* arrayArg, uint32_t start); + static int32_t stringTest(Instance* instance, void* stringArg); + static void* stringCast(Instance* instance, void* stringArg); + static void* stringFromCharCodeArray(Instance* instance, void* arrayArg, + uint32_t arrayStart, + uint32_t arrayCount); + static int32_t stringIntoCharCodeArray(Instance* instance, void* stringArg, + void* arrayArg, uint32_t arrayStart); static void* stringFromCharCode(Instance* instance, uint32_t charCode); static void* stringFromCodePoint(Instance* instance, uint32_t codePoint); static int32_t stringCharCodeAt(Instance* instance, void* stringArg, @@ -582,8 +585,8 @@ class alignas(16) Instance { static int32_t stringCodePointAt(Instance* instance, void* stringArg, uint32_t index); static int32_t stringLength(Instance* instance, void* stringArg); - static void* stringConcatenate(Instance* instance, void* firstStringArg, - void* secondStringArg); + static void* stringConcat(Instance* instance, void* firstStringArg, + void* secondStringArg); static void* stringSubstring(Instance* instance, void* stringArg, int32_t startIndex, int32_t endIndex); static int32_t stringEquals(Instance* instance, void* firstStringArg, diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index 6fbfeb3809..0568a95804 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -900,7 +900,7 @@ class FunctionCompiler { return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool brOnNull(uint32_t relativeDepth, const DefVector& values, const ResultType& type, MDefinition* condition) { if (inDeadCode()) { @@ -963,7 +963,7 @@ class FunctionCompiler { return true; } -#endif // ENABLE_WASM_FUNCTION_REFERENCES +#endif // ENABLE_WASM_GC #ifdef ENABLE_WASM_GC MDefinition* refI31(MDefinition* input) { @@ -2006,10 +2006,10 @@ class FunctionCompiler { MOZ_CRASH("Unknown ABIArg kind."); } - template <typename SpanT> - [[nodiscard]] bool passArgs(const DefVector& argDefs, SpanT types, + template <typename VecT> + [[nodiscard]] bool passArgs(const DefVector& argDefs, const VecT& types, CallCompileState* call) { - MOZ_ASSERT(argDefs.length() == types.size()); + MOZ_ASSERT(argDefs.length() == types.length()); for (uint32_t i = 0; i < argDefs.length(); i++) { MDefinition* def = argDefs[i]; ValType type = types[i]; @@ -2447,7 +2447,7 @@ class FunctionCompiler { return collectUnaryCallResult(builtin.retType, def); } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool callRef(const FuncType& funcType, MDefinition* ref, uint32_t lineOrBytecode, const CallCompileState& call, DefVector* results) { @@ -2489,7 +2489,7 @@ class FunctionCompiler { # endif // ENABLE_WASM_TAIL_CALLS -#endif // ENABLE_WASM_FUNCTION_REFERENCES +#endif // ENABLE_WASM_GC /*********************************************** Control flow generation */ @@ -2788,7 +2788,8 @@ class FunctionCompiler { // patches around. for (uint32_t depth = 0; depth < iter().controlStackDepth(); depth++) { LabelKind kind = iter().controlKind(depth); - if (kind != LabelKind::Try && kind != LabelKind::Body) { + if (kind != LabelKind::Try && kind != LabelKind::TryTable && + kind != LabelKind::Body) { continue; } Control& control = iter().controlItem(depth); @@ -5440,7 +5441,7 @@ static bool EmitReturnCallIndirect(FunctionCompiler& f) { } #endif -#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_FUNCTION_REFERENCES) +#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_GC) static bool EmitReturnCallRef(FunctionCompiler& f) { uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); @@ -7090,7 +7091,11 @@ static bool EmitLoadSplatSimd128(FunctionCompiler& f, Scalar::Type viewType, return false; } - f.iter().setResult(f.loadSplatSimd128(viewType, addr, splatOp)); + auto* ins = f.loadSplatSimd128(viewType, addr, splatOp); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7100,7 +7105,11 @@ static bool EmitLoadExtendSimd128(FunctionCompiler& f, wasm::SimdOp op) { return false; } - f.iter().setResult(f.loadExtendSimd128(addr, op)); + auto* ins = f.loadExtendSimd128(addr, op); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7111,7 +7120,11 @@ static bool EmitLoadZeroSimd128(FunctionCompiler& f, Scalar::Type viewType, return false; } - f.iter().setResult(f.loadZeroSimd128(viewType, numBytes, addr)); + auto* ins = f.loadZeroSimd128(viewType, numBytes, addr); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7123,7 +7136,11 @@ static bool EmitLoadLaneSimd128(FunctionCompiler& f, uint32_t laneSize) { return false; } - f.iter().setResult(f.loadLaneSimd128(laneSize, addr, laneIndex, src)); + auto* ins = f.loadLaneSimd128(laneSize, addr, laneIndex, src); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7141,7 +7158,7 @@ static bool EmitStoreLaneSimd128(FunctionCompiler& f, uint32_t laneSize) { #endif // ENABLE_WASM_SIMD -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC static bool EmitRefAsNonNull(FunctionCompiler& f) { MDefinition* ref; if (!f.iter().readRefAsNonNull(&ref)) { @@ -7204,7 +7221,7 @@ static bool EmitCallRef(FunctionCompiler& f) { return true; } -#endif // ENABLE_WASM_FUNCTION_REFERENCES +#endif // ENABLE_WASM_GC #ifdef ENABLE_WASM_GC @@ -7917,18 +7934,18 @@ static bool EmitCallBuiltinModuleFunc(FunctionCompiler& f) { } uint32_t bytecodeOffset = f.readBytecodeOffset(); - const SymbolicAddressSignature& callee = builtinModuleFunc->signature; + const SymbolicAddressSignature& callee = *builtinModuleFunc->sig(); CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; } - if (!f.passArgs(params, builtinModuleFunc->params, &args)) { + if (!f.passArgs(params, builtinModuleFunc->funcType()->args(), &args)) { return false; } - if (builtinModuleFunc->usesMemory) { + if (builtinModuleFunc->usesMemory()) { MDefinition* memoryBase = f.memoryBase(0); if (!f.passArg(memoryBase, MIRType::Pointer, &args)) { return false; @@ -7939,7 +7956,7 @@ static bool EmitCallBuiltinModuleFunc(FunctionCompiler& f) { return false; } - bool hasResult = builtinModuleFunc->result.isSome(); + bool hasResult = !builtinModuleFunc->funcType()->results().empty(); MDefinition* result = nullptr; MDefinition** resultOutParam = hasResult ? &result : nullptr; if (!f.builtinInstanceMethodCall(callee, bytecodeOffset, args, @@ -7996,37 +8013,19 @@ static bool EmitBodyExprs(FunctionCompiler& f) { case uint16_t(Op::Else): CHECK(EmitElse(f)); case uint16_t(Op::Try): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitTry(f)); case uint16_t(Op::Catch): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitCatch(f)); case uint16_t(Op::CatchAll): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitCatchAll(f)); case uint16_t(Op::Delegate): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } if (!EmitDelegate(f)) { return false; } break; case uint16_t(Op::Throw): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitThrow(f)); case uint16_t(Op::Rethrow): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitRethrow(f)); case uint16_t(Op::ThrowRef): if (!f.moduleEnv().exnrefEnabled()) { @@ -8474,36 +8473,35 @@ static bool EmitBodyExprs(FunctionCompiler& f) { } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::RefAsNonNull): - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitRefAsNonNull(f)); case uint16_t(Op::BrOnNull): { - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitBrOnNull(f)); } case uint16_t(Op::BrOnNonNull): { - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitBrOnNonNull(f)); } case uint16_t(Op::CallRef): { - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitCallRef(f)); } #endif -#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_FUNCTION_REFERENCES) +#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_GC) case uint16_t(Op::ReturnCallRef): { - if (!f.moduleEnv().functionReferencesEnabled() || - !f.moduleEnv().tailCallsEnabled()) { + if (!f.moduleEnv().gcEnabled() || !f.moduleEnv().tailCallsEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitReturnCallRef(f)); @@ -9025,114 +9023,91 @@ static bool EmitBodyExprs(FunctionCompiler& f) { CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint32)); case uint32_t(ThreadOp::I32AtomicAdd): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchAddOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchAddOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchAddOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicSub): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchSubOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchSubOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchSubOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicAnd): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchAndOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchAndOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchAndOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicOr): - CHECK( - EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr): - CHECK( - EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr8U): - CHECK( - EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr8U): - CHECK( - EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicXor): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchXorOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchXorOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchXorOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXchg): CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Int32)); @@ -9267,6 +9242,41 @@ static bool EmitBodyExprs(FunctionCompiler& f) { #undef CHECK } +static bool IonBuildMIR(Decoder& d, const ModuleEnvironment& moduleEnv, + const FuncCompileInput& func, + const ValTypeVector& locals, MIRGenerator& mir, + TryNoteVector& tryNotes, FeatureUsage* observedFeatures, + UniqueChars* error) { + // Initialize MIR global information used for optimization + if (moduleEnv.numMemories() > 0) { + if (moduleEnv.memories[0].indexType() == IndexType::I32) { + mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength32()); + } else { + mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength64()); + } + } + + // Build MIR graph + FunctionCompiler f(moduleEnv, d, func, locals, mir, tryNotes); + if (!f.init()) { + return false; + } + + if (!f.startBlock()) { + return false; + } + + if (!EmitBodyExprs(f)) { + return false; + } + + f.finish(); + + *observedFeatures = f.featureUsage(); + + return true; +} + bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, const CompilerEnvironment& compilerEnv, LifoAlloc& lifo, @@ -9307,52 +9317,28 @@ bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, Decoder d(func.begin, func.end, func.lineOrBytecode, error); // Build the local types vector. - - const FuncType& funcType = *moduleEnv.funcs[func.index].type; ValTypeVector locals; - if (!locals.appendAll(funcType.args())) { - return false; - } - if (!DecodeLocalEntries(d, *moduleEnv.types, moduleEnv.features, &locals)) { + if (!DecodeLocalEntriesWithParams(d, moduleEnv, func.index, &locals)) { return false; } // Set up for Ion compilation. - const JitCompileOptions options; MIRGraph graph(&alloc); CompileInfo compileInfo(locals.length()); MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo, IonOptimizations.get(OptimizationLevel::Wasm)); - if (moduleEnv.numMemories() > 0) { - if (moduleEnv.memories[0].indexType() == IndexType::I32) { - mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength32()); - } else { - mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength64()); - } - } // Build MIR graph - { - FunctionCompiler f(moduleEnv, d, func, locals, mir, masm.tryNotes()); - if (!f.init()) { - return false; - } - - if (!f.startBlock()) { - return false; - } - - if (!EmitBodyExprs(f)) { - return false; - } - - f.finish(); - - // Record observed feature usage - code->featureUsage |= f.featureUsage(); + FeatureUsage observedFeatures; + if (!IonBuildMIR(d, moduleEnv, func, locals, mir, masm.tryNotes(), + &observedFeatures, error)) { + return false; } + // Record observed feature usage + code->featureUsage |= observedFeatures; + // Compile MIR graph { jit::SpewBeginWasmFunction(&mir, func.index); @@ -9373,7 +9359,7 @@ bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, BytecodeOffset prologueTrapOffset(func.lineOrBytecode); FuncOffsets offsets; - ArgTypeVector args(funcType); + ArgTypeVector args(*moduleEnv.funcs[func.index].type); if (!codegen.generateWasm(CallIndirectId::forFunc(moduleEnv, func.index), prologueTrapOffset, args, trapExitLayout, trapExitLayoutNumWords, &offsets, @@ -9407,6 +9393,66 @@ bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, return code->swap(masm); } +bool wasm::IonDumpFunction(const ModuleEnvironment& moduleEnv, + const FuncCompileInput& func, + IonDumpContents contents, GenericPrinter& out, + UniqueChars* error) { + LifoAlloc lifo(TempAllocator::PreferredLifoChunkSize); + TempAllocator alloc(&lifo); + JitContext jitContext; + Decoder d(func.begin, func.end, func.lineOrBytecode, error); + + // Decode the locals. + ValTypeVector locals; + if (!DecodeLocalEntriesWithParams(d, moduleEnv, func.index, &locals)) { + return false; + } + + // Set up for Ion compilation. + const JitCompileOptions options; + MIRGraph graph(&alloc); + CompileInfo compileInfo(locals.length()); + MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo, + IonOptimizations.get(OptimizationLevel::Wasm)); + + // Build MIR graph + TryNoteVector tryNotes; + FeatureUsage observedFeatures; + if (!IonBuildMIR(d, moduleEnv, func, locals, mir, tryNotes, &observedFeatures, + error)) { + return false; + } + + if (contents == IonDumpContents::UnoptimizedMIR) { + graph.dump(out); + return true; + } + + // Optimize the MIR graph + if (!OptimizeMIR(&mir)) { + return false; + } + + if (contents == IonDumpContents::OptimizedMIR) { + graph.dump(out); + return true; + } + +#ifdef JS_JITSPEW + // Generate the LIR graph + LIRGraph* lir = GenerateLIR(&mir); + if (!lir) { + return false; + } + + MOZ_ASSERT(contents == IonDumpContents::LIR); + lir->dump(out); +#else + out.printf("cannot dump LIR without --enable-jitspew"); +#endif + return true; +} + bool js::wasm::IonPlatformSupport() { #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || \ defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \ diff --git a/js/src/wasm/WasmIonCompile.h b/js/src/wasm/WasmIonCompile.h index f583cbad1f..4597c08be1 100644 --- a/js/src/wasm/WasmIonCompile.h +++ b/js/src/wasm/WasmIonCompile.h @@ -22,6 +22,9 @@ #include "wasm/WasmGenerator.h" namespace js { + +class GenericPrinter; + namespace wasm { // Return whether IonCompileFunction() can generate code on the current device. @@ -35,6 +38,11 @@ namespace wasm { const FuncCompileInputVector& inputs, CompiledCode* code, UniqueChars* error); +[[nodiscard]] bool IonDumpFunction(const ModuleEnvironment& moduleEnv, + const FuncCompileInput& func, + IonDumpContents contents, + GenericPrinter& out, UniqueChars* error); + } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 6cc9528415..2eb5e355d9 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -768,8 +768,14 @@ static JSObject* GetWasmConstructorPrototype(JSContext* cx, } #ifdef ENABLE_WASM_TYPE_REFLECTIONS -static JSString* UTF8CharsToString(JSContext* cx, const char* chars) { - return NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(chars, strlen(chars))); +template <typename T> +static JSString* TypeToString(JSContext* cx, T type) { + UniqueChars chars = ToString(type, nullptr); + if (!chars) { + return nullptr; + } + return NewStringCopyUTF8Z( + cx, JS::ConstUTF8CharsZ(chars.get(), strlen(chars.get()))); } [[nodiscard]] static JSObject* ValTypesToArray(JSContext* cx, @@ -779,8 +785,7 @@ static JSString* UTF8CharsToString(JSContext* cx, const char* chars) { return nullptr; } for (ValType valType : valTypes) { - RootedString type(cx, - UTF8CharsToString(cx, ToString(valType, nullptr).get())); + RootedString type(cx, TypeToString(cx, valType)); if (!type) { return nullptr; } @@ -809,15 +814,14 @@ static JSObject* FuncTypeToObject(JSContext* cx, const FuncType& type) { return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* TableTypeToObject(JSContext* cx, RefType type, uint32_t initial, Maybe<uint32_t> maximum) { Rooted<IdValueVector> props(cx, IdValueVector(cx)); - RootedString elementType( - cx, UTF8CharsToString(cx, ToString(type, nullptr).get())); + RootedString elementType(cx, TypeToString(cx, type)); if (!elementType || !props.append(IdValuePair(NameToId(cx->names().element), StringValue(elementType)))) { ReportOutOfMemory(cx); @@ -838,7 +842,7 @@ static JSObject* TableTypeToObject(JSContext* cx, RefType type, return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* MemoryTypeToObject(JSContext* cx, bool shared, @@ -892,7 +896,7 @@ static JSObject* MemoryTypeToObject(JSContext* cx, bool shared, return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* GlobalTypeToObject(JSContext* cx, ValType type, @@ -905,15 +909,14 @@ static JSObject* GlobalTypeToObject(JSContext* cx, ValType type, return nullptr; } - RootedString valueType(cx, - UTF8CharsToString(cx, ToString(type, nullptr).get())); + RootedString valueType(cx, TypeToString(cx, type)); if (!valueType || !props.append(IdValuePair(NameToId(cx->names().value), StringValue(valueType)))) { ReportOutOfMemory(cx); return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* TagTypeToObject(JSContext* cx, @@ -928,7 +931,7 @@ static JSObject* TagTypeToObject(JSContext* cx, return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } #endif // ENABLE_WASM_TYPE_REFLECTIONS @@ -1184,8 +1187,7 @@ bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) { } #endif // ENABLE_WASM_TYPE_REFLECTIONS - JSObject* obj = - NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + JSObject* obj = NewPlainObjectWithUniqueNames(cx, props); if (!obj) { return false; } @@ -1288,8 +1290,7 @@ bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) { } #endif // ENABLE_WASM_TYPE_REFLECTIONS - JSObject* obj = - NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + JSObject* obj = NewPlainObjectWithUniqueNames(cx, props); if (!obj) { return false; } @@ -3227,7 +3228,7 @@ void WasmGlobalObject::finalize(JS::GCContext* gcx, JSObject* obj) { // Release the strong reference to the type definitions this global could // be referencing. global->type().Release(); - gcx->delete_(obj, &global->val(), MemoryUse::WasmGlobalCell); + gcx->delete_(obj, &global->mutableVal(), MemoryUse::WasmGlobalCell); } } @@ -3253,7 +3254,9 @@ WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal value, // It's simpler to initialize the cell after the object has been created, // to avoid needing to root the cell before the object creation. - obj->val() = value.get(); + // We don't use `setVal` here because the assumes the cell has already + // been initialized. + obj->mutableVal() = value.get(); // Acquire a strong reference to a type definition this global could // be referencing. obj->type().AddRef(); @@ -3384,7 +3387,7 @@ bool WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) { if (!Val::fromJSValue(cx, global->type(), args.get(0), &val)) { return false; } - global->val() = val.get(); + global->setVal(val); args.rval().setUndefined(); return true; @@ -3417,10 +3420,23 @@ bool WasmGlobalObject::isMutable() const { ValType WasmGlobalObject::type() const { return val().get().type(); } -GCPtrVal& WasmGlobalObject::val() const { +GCPtrVal& WasmGlobalObject::mutableVal() { + return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate()); +} + +const GCPtrVal& WasmGlobalObject::val() const { return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate()); } +void WasmGlobalObject::setVal(wasm::HandleVal value) { + MOZ_ASSERT(type() == value.get().type()); + mutableVal() = value; +} + +void* WasmGlobalObject::addressOfCell() const { + return (void*)&val().get().cell(); +} + #ifdef ENABLE_WASM_TYPE_REFLECTIONS /* static */ bool WasmGlobalObject::typeImpl(JSContext* cx, const CallArgs& args) { @@ -4652,6 +4668,10 @@ static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) { } FeatureOptions options; + if (!options.init(cx, callArgs.get(1))) { + return false; + } + UniqueChars error; bool validated = Validate(cx, *bytecode, options, &error); @@ -5351,15 +5371,13 @@ static bool WebAssemblyClassFinish(JSContext* cx, HandleObject object, } } - if (ExceptionsAvailable(cx)) { - constexpr NameAndProtoKey exceptionEntries[] = { - {"Tag", JSProto_WasmTag}, - {"Exception", JSProto_WasmException}, - }; - for (const auto& entry : exceptionEntries) { - if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) { - return false; - } + constexpr NameAndProtoKey exceptionEntries[] = { + {"Tag", JSProto_WasmTag}, + {"Exception", JSProto_WasmException}, + }; + for (const auto& entry : exceptionEntries) { + if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) { + return false; } } diff --git a/js/src/wasm/WasmJS.h b/js/src/wasm/WasmJS.h index 10c71b436b..27d49701a9 100644 --- a/js/src/wasm/WasmJS.h +++ b/js/src/wasm/WasmJS.h @@ -167,6 +167,8 @@ class WasmGlobalObject : public NativeObject { static bool valueSetterImpl(JSContext* cx, const CallArgs& args); static bool valueSetter(JSContext* cx, unsigned argc, Value* vp); + wasm::GCPtrVal& mutableVal(); + public: static const unsigned RESERVED_SLOTS = 2; static const JSClass class_; @@ -182,7 +184,9 @@ class WasmGlobalObject : public NativeObject { bool isMutable() const; wasm::ValType type() const; - wasm::GCPtrVal& val() const; + const wasm::GCPtrVal& val() const; + void setVal(wasm::HandleVal value); + void* addressOfCell() const; }; // The class of WebAssembly.Instance. Each WasmInstanceObject owns a diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index c2de0429d3..a297e81ad3 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -867,7 +867,7 @@ static bool GetGlobalExport(JSContext* cx, MOZ_RELEASE_ASSERT(!global.isImport()); RootedVal globalVal(cx); instanceObj->instance().constantGlobalGet(globalIndex, &globalVal); - globalObj->val() = globalVal; + globalObj->setVal(globalVal); return true; } diff --git a/js/src/wasm/WasmOpIter.cpp b/js/src/wasm/WasmOpIter.cpp index 102d39639c..d60a87dc12 100644 --- a/js/src/wasm/WasmOpIter.cpp +++ b/js/src/wasm/WasmOpIter.cpp @@ -25,14 +25,14 @@ using namespace js::jit; using namespace js::wasm; #ifdef ENABLE_WASM_GC -# ifndef ENABLE_WASM_FUNCTION_REFERENCES +# ifndef ENABLE_WASM_GC # error "GC types require the function-references feature" # endif #endif #ifdef DEBUG -# ifdef ENABLE_WASM_FUNCTION_REFERENCES +# ifdef ENABLE_WASM_GC # define WASM_FUNCTION_REFERENCES_OP(code) return code # else # define WASM_FUNCTION_REFERENCES_OP(code) break diff --git a/js/src/wasm/WasmOpIter.h b/js/src/wasm/WasmOpIter.h index 1711cc3926..59d494bfbf 100644 --- a/js/src/wasm/WasmOpIter.h +++ b/js/src/wasm/WasmOpIter.h @@ -165,7 +165,7 @@ enum class OpKind { ReturnCall, CallIndirect, ReturnCallIndirect, -# ifdef ENABLE_WASM_FUNCTION_REFERENCES +# ifdef ENABLE_WASM_GC CallRef, ReturnCallRef, # endif @@ -493,7 +493,8 @@ class MOZ_STACK_CLASS OpIter : private Policy { [[nodiscard]] bool getControl(uint32_t relativeDepth, Control** controlEntry); [[nodiscard]] bool checkBranchValueAndPush(uint32_t relativeDepth, ResultType* type, - ValueVector* values); + ValueVector* values, + bool rewriteStackTypes); [[nodiscard]] bool checkBrTableEntryAndPush(uint32_t* relativeDepth, ResultType prevBranchType, ResultType* branchType, @@ -533,7 +534,7 @@ class MOZ_STACK_CLASS OpIter : private Policy { inline bool checkIsSubtypeOf(ResultType params, ResultType results); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC inline bool checkIsSubtypeOf(uint32_t actualTypeIndex, uint32_t expectedTypeIndex); #endif @@ -703,7 +704,7 @@ class MOZ_STACK_CLASS OpIter : private Policy { uint32_t* tableIndex, Value* callee, ValueVector* argValues); #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool readCallRef(const FuncType** funcType, Value* callee, ValueVector* argValues); @@ -932,7 +933,7 @@ inline bool OpIter<Policy>::checkIsSubtypeOf(ResultType params, return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC template <typename Policy> inline bool OpIter<Policy>::checkIsSubtypeOf(uint32_t actualTypeIndex, uint32_t expectedTypeIndex) { @@ -1480,14 +1481,15 @@ inline void OpIter<Policy>::popEnd() { template <typename Policy> inline bool OpIter<Policy>::checkBranchValueAndPush(uint32_t relativeDepth, ResultType* type, - ValueVector* values) { + ValueVector* values, + bool rewriteStackTypes) { Control* block = nullptr; if (!getControl(relativeDepth, &block)) { return false; } *type = block->branchTargetType(); - return checkTopTypeMatches(*type, values, /*rewriteStackTypes=*/false); + return checkTopTypeMatches(*type, values, rewriteStackTypes); } template <typename Policy> @@ -1499,7 +1501,8 @@ inline bool OpIter<Policy>::readBr(uint32_t* relativeDepth, ResultType* type, return fail("unable to read br depth"); } - if (!checkBranchValueAndPush(*relativeDepth, type, values)) { + if (!checkBranchValueAndPush(*relativeDepth, type, values, + /*rewriteStackTypes=*/false)) { return false; } @@ -1520,7 +1523,8 @@ inline bool OpIter<Policy>::readBrIf(uint32_t* relativeDepth, ResultType* type, return false; } - return checkBranchValueAndPush(*relativeDepth, type, values); + return checkBranchValueAndPush(*relativeDepth, type, values, + /*rewriteStackTypes=*/true); } #define UNKNOWN_ARITY UINT32_MAX @@ -2392,10 +2396,10 @@ inline bool OpIter<Policy>::readRefFunc(uint32_t* funcIndex) { "function index is not declared in a section before the code section"); } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC // When function references enabled, push type index on the stack, e.g. for // validation of the call_ref instruction. - if (env_.functionReferencesEnabled()) { + if (env_.gcEnabled()) { const uint32_t typeIndex = env_.funcs[*funcIndex].typeIndex; const TypeDef& typeDef = env_.types->type(typeIndex); return push(RefType::fromTypeDef(&typeDef, false)); @@ -2457,7 +2461,8 @@ inline bool OpIter<Policy>::readBrOnNull(uint32_t* relativeDepth, return false; } - if (!checkBranchValueAndPush(*relativeDepth, type, values)) { + if (!checkBranchValueAndPush(*relativeDepth, type, values, + /*rewriteStackTypes=*/true)) { return false; } @@ -2505,7 +2510,7 @@ inline bool OpIter<Policy>::readBrOnNonNull(uint32_t* relativeDepth, } // Check if the type stack matches the branch target type. - if (!checkTopTypeMatches(*type, values, /*rewriteStackTypes=*/false)) { + if (!checkTopTypeMatches(*type, values, /*rewriteStackTypes=*/true)) { return false; } @@ -2693,7 +2698,7 @@ inline bool OpIter<Policy>::readReturnCallIndirect(uint32_t* funcTypeIndex, } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC template <typename Policy> inline bool OpIter<Policy>::readCallRef(const FuncType** funcType, Value* callee, ValueVector* argValues) { @@ -2719,7 +2724,7 @@ inline bool OpIter<Policy>::readCallRef(const FuncType** funcType, } #endif -#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_FUNCTION_REFERENCES) +#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_GC) template <typename Policy> inline bool OpIter<Policy>::readReturnCallRef(const FuncType** funcType, Value* callee, @@ -4001,7 +4006,7 @@ inline bool OpIter<Policy>::readBrOnCast(bool onSuccess, fallthroughTypes[labelTypeNumValues - 1] = typeOnFallthrough; return checkTopTypeMatches(ResultType::Vector(fallthroughTypes), values, - /*rewriteStackTypes=*/false); + /*rewriteStackTypes=*/true); } template <typename Policy> @@ -4228,18 +4233,18 @@ inline bool OpIter<Policy>::readCallBuiltinModuleFunc( return fail("index out of range"); } - *builtinModuleFunc = &BuiltinModuleFunc::getFromId(BuiltinModuleFuncId(id)); + *builtinModuleFunc = &BuiltinModuleFuncs::getFromId(BuiltinModuleFuncId(id)); - if ((*builtinModuleFunc)->usesMemory && env_.numMemories() == 0) { + if ((*builtinModuleFunc)->usesMemory() && env_.numMemories() == 0) { return fail("can't touch memory without memory"); } - if (!popWithTypes((*builtinModuleFunc)->params, params)) { + + const FuncType& funcType = *(*builtinModuleFunc)->funcType(); + if (!popCallArgs(funcType.args(), params)) { return false; } - if ((*builtinModuleFunc)->result.isNothing()) { - return true; - } - return push(*(*builtinModuleFunc)->result); + + return push(ResultType::Vector(funcType.results())); } } // namespace wasm diff --git a/js/src/wasm/WasmProcess.cpp b/js/src/wasm/WasmProcess.cpp index 427ba42d9d..0436e0a23f 100644 --- a/js/src/wasm/WasmProcess.cpp +++ b/js/src/wasm/WasmProcess.cpp @@ -26,10 +26,12 @@ #include "threading/ExclusiveData.h" #include "vm/MutexIDs.h" #include "vm/Runtime.h" +#include "wasm/WasmBuiltinModule.h" #include "wasm/WasmBuiltins.h" #include "wasm/WasmCode.h" #include "wasm/WasmInstance.h" #include "wasm/WasmModuleTypes.h" +#include "wasm/WasmStaticTypeDefs.h" using namespace js; using namespace wasm; @@ -438,6 +440,15 @@ bool wasm::Init() { oomUnsafe.crash("js::wasm::Init"); } + if (!StaticTypeDefs::init()) { + oomUnsafe.crash("js::wasm::Init"); + } + + // This uses StaticTypeDefs + if (!BuiltinModuleFuncs::init()) { + oomUnsafe.crash("js::wasm::Init"); + } + sProcessCodeSegmentMap = map; if (!InitTagForJSValue()) { @@ -455,6 +466,8 @@ void wasm::ShutDown() { return; } + BuiltinModuleFuncs::destroy(); + StaticTypeDefs::destroy(); PurgeCanonicalTypes(); if (sWrappedJSValueTagType) { diff --git a/js/src/wasm/WasmSerialize.cpp b/js/src/wasm/WasmSerialize.cpp index 62a68c5aff..35f437688c 100644 --- a/js/src/wasm/WasmSerialize.cpp +++ b/js/src/wasm/WasmSerialize.cpp @@ -957,7 +957,7 @@ CoderResult CodeSymbolicLinkArray( template <CoderMode mode> CoderResult CodeLinkData(Coder<mode>& coder, CoderArg<mode, wasm::LinkData> item) { - WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData, 8832); + WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData, 8976); if constexpr (mode == MODE_ENCODE) { MOZ_ASSERT(item->tier == Tier::Serialized); } diff --git a/js/src/wasm/WasmStaticTypeDefs.cpp b/js/src/wasm/WasmStaticTypeDefs.cpp new file mode 100644 index 0000000000..2306339087 --- /dev/null +++ b/js/src/wasm/WasmStaticTypeDefs.cpp @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * + * Copyright 2023 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmStaticTypeDefs.h" + +#include "wasm/WasmTypeDef.h" + +using namespace js; +using namespace js::wasm; + +const TypeDef* StaticTypeDefs::arrayMutI16 = nullptr; + +bool StaticTypeDefs::init() { + RefPtr<TypeContext> types = js_new<TypeContext>(); + if (!types) { + return false; + } + +#ifdef ENABLE_WASM_GC + arrayMutI16 = types->addType(ArrayType(StorageType::I16, true)); + if (!arrayMutI16) { + return false; + } + arrayMutI16->recGroup().AddRef(); +#endif + + return true; +} + +void StaticTypeDefs::destroy() { + if (arrayMutI16) { + arrayMutI16->recGroup().Release(); + arrayMutI16 = nullptr; + } +} diff --git a/js/src/wasm/WasmStaticTypeDefs.h b/js/src/wasm/WasmStaticTypeDefs.h new file mode 100644 index 0000000000..d3a01ad26c --- /dev/null +++ b/js/src/wasm/WasmStaticTypeDefs.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * + * Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_static_type_defs +#define wasm_static_type_defs + +namespace js { +namespace wasm { + +class TypeDef; + +// Simple type definitions used in builtins with a static lifetime. +// +// TODO: this class is very simple and won't scale well with many type +// definitions. Rethink this if we have more than several type definitions. +struct StaticTypeDefs { + static const TypeDef* arrayMutI16; + + [[nodiscard]] static bool init(); + static void destroy(); +}; + +} // namespace wasm +} // namespace js + +#endif // wasm_static_type_defs diff --git a/js/src/wasm/WasmTypeDef.h b/js/src/wasm/WasmTypeDef.h index 7aedbed1f8..3426647095 100644 --- a/js/src/wasm/WasmTypeDef.h +++ b/js/src/wasm/WasmTypeDef.h @@ -1228,13 +1228,16 @@ class TypeContext : public AtomicRefCounted<TypeContext> { } template <typename T> - [[nodiscard]] bool addType(T&& type) { + [[nodiscard]] const TypeDef* addType(T&& type) { MutableRecGroup recGroup = startRecGroup(1); if (!recGroup) { - return false; + return nullptr; } recGroup->type(0) = std::move(type); - return endRecGroup(); + if (!endRecGroup()) { + return nullptr; + } + return &this->type(length() - 1); } const TypeDef& type(uint32_t index) const { return *types_[index]; } diff --git a/js/src/wasm/WasmValType.cpp b/js/src/wasm/WasmValType.cpp index d1874b7131..64ef8ff85a 100644 --- a/js/src/wasm/WasmValType.cpp +++ b/js/src/wasm/WasmValType.cpp @@ -150,94 +150,7 @@ enum class RefTypeResult { Unparsed, }; -static RefTypeResult MaybeToRefType(JSContext* cx, HandleObject obj, - RefType* out) { -#ifdef ENABLE_WASM_FUNCTION_REFERENCES - if (!wasm::FunctionReferencesAvailable(cx)) { - return RefTypeResult::Unparsed; - } - - JSAtom* refAtom = Atomize(cx, "ref", strlen("ref")); - if (!refAtom) { - return RefTypeResult::Failure; - } - RootedId refId(cx, AtomToId(refAtom)); - - RootedValue refVal(cx); - if (!GetProperty(cx, obj, obj, refId, &refVal)) { - return RefTypeResult::Failure; - } - - RootedString typeStr(cx, ToString(cx, refVal)); - if (!typeStr) { - return RefTypeResult::Failure; - } - - Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx)); - if (!typeLinearStr) { - return RefTypeResult::Failure; - } - - if (StringEqualsLiteral(typeLinearStr, "func")) { - *out = RefType::func(); - } else if (StringEqualsLiteral(typeLinearStr, "extern")) { - *out = RefType::extern_(); -# ifdef ENABLE_WASM_EXNREF - } else if (ExnRefAvailable(cx) && StringEqualsLiteral(typeLinearStr, "exn")) { - *out = RefType::exn(); -# endif -# ifdef ENABLE_WASM_GC - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "any")) { - *out = RefType::any(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "eq")) { - *out = RefType::eq(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "i31")) { - *out = RefType::i31(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "struct")) { - *out = RefType::struct_(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "array")) { - *out = RefType::array(); -# endif - } else { - return RefTypeResult::Unparsed; - } - - JSAtom* nullableAtom = Atomize(cx, "nullable", strlen("nullable")); - if (!nullableAtom) { - return RefTypeResult::Failure; - } - RootedId nullableId(cx, AtomToId(nullableAtom)); - RootedValue nullableVal(cx); - if (!GetProperty(cx, obj, obj, nullableId, &nullableVal)) { - return RefTypeResult::Failure; - } - - bool nullable = ToBoolean(nullableVal); - if (!nullable) { - *out = out->asNonNullable(); - } - MOZ_ASSERT(out->isNullable() == nullable); - return RefTypeResult::Parsed; -#else - return RefTypeResult::Unparsed; -#endif -} - bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) { - if (v.isObject()) { - RootedObject obj(cx, &v.toObject()); - RefType refType; - switch (MaybeToRefType(cx, obj, &refType)) { - case RefTypeResult::Failure: - return false; - case RefTypeResult::Parsed: - *out = ValType(refType); - return true; - case RefTypeResult::Unparsed: - break; - } - } - RootedString typeStr(cx, ToString(cx, v)); if (!typeStr) { return false; @@ -274,18 +187,6 @@ bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) { } bool wasm::ToRefType(JSContext* cx, HandleValue v, RefType* out) { - if (v.isObject()) { - RootedObject obj(cx, &v.toObject()); - switch (MaybeToRefType(cx, obj, out)) { - case RefTypeResult::Failure: - return false; - case RefTypeResult::Parsed: - return true; - case RefTypeResult::Unparsed: - break; - } - } - RootedString typeStr(cx, ToString(cx, v)); if (!typeStr) { return false; diff --git a/js/src/wasm/WasmValType.h b/js/src/wasm/WasmValType.h index 0821ee5df9..c98eda28dd 100644 --- a/js/src/wasm/WasmValType.h +++ b/js/src/wasm/WasmValType.h @@ -479,7 +479,7 @@ class StorageTypeTraits { case TypeCode::NullExternRef: case TypeCode::NullAnyRef: #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case AbstractTypeRefCode: #endif return true; @@ -557,7 +557,7 @@ class ValTypeTraits { case TypeCode::NullExternRef: case TypeCode::NullAnyRef: #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case AbstractTypeRefCode: #endif return true; diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp index e964c11d04..98a1423a41 100644 --- a/js/src/wasm/WasmValidate.cpp +++ b/js/src/wasm/WasmValidate.cpp @@ -89,14 +89,19 @@ bool wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals) { return true; } -bool wasm::DecodeLocalEntries(Decoder& d, const TypeContext& types, - const FeatureArgs& features, - ValTypeVector* locals) { +bool wasm::DecodeLocalEntriesWithParams(Decoder& d, + const ModuleEnvironment& env, + uint32_t funcIndex, + ValTypeVector* locals) { uint32_t numLocalEntries; if (!d.readVarU32(&numLocalEntries)) { return d.fail("failed to read number of local entries"); } + if (!locals->appendAll(env.funcs[funcIndex].type->args())) { + return false; + } + for (uint32_t i = 0; i < numLocalEntries; i++) { uint32_t count; if (!d.readVarU32(&count)) { @@ -108,7 +113,7 @@ bool wasm::DecodeLocalEntries(Decoder& d, const TypeContext& types, } ValType type; - if (!d.readValType(types, features, &type)) { + if (!d.readValType(*env.types, env.features, &type)) { return false; } @@ -235,9 +240,9 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, &unusedArgs)); } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::CallRef): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } const FuncType* unusedType; @@ -246,7 +251,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, } # ifdef ENABLE_WASM_TAIL_CALLS case uint16_t(Op::ReturnCallRef): { - if (!env.functionReferencesEnabled() || !env.tailCallsEnabled()) { + if (!env.gcEnabled() || !env.tailCallsEnabled()) { return iter.unrecognizedOpcode(&op); } const FuncType* unusedType; @@ -1240,15 +1245,15 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, } break; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::RefAsNonNull): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } CHECK(iter.readRefAsNonNull(¬hing)); } case uint16_t(Op::BrOnNull): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } uint32_t unusedDepth; @@ -1256,7 +1261,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, iter.readBrOnNull(&unusedDepth, &unusedType, ¬hings, ¬hing)); } case uint16_t(Op::BrOnNonNull): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } uint32_t unusedDepth; @@ -1285,31 +1290,19 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, CHECK(iter.readRefIsNull(¬hing)); } case uint16_t(Op::Try): - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } CHECK(iter.readTry(&unusedType)); case uint16_t(Op::Catch): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } LabelKind unusedKind; uint32_t unusedIndex; CHECK(iter.readCatch(&unusedKind, &unusedIndex, &unusedType, &unusedType, ¬hings)); } case uint16_t(Op::CatchAll): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } LabelKind unusedKind; CHECK(iter.readCatchAll(&unusedKind, &unusedType, &unusedType, ¬hings)); } case uint16_t(Op::Delegate): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } uint32_t unusedDepth; if (!iter.readDelegate(&unusedDepth, &unusedType, ¬hings)) { return false; @@ -1318,16 +1311,10 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, break; } case uint16_t(Op::Throw): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } uint32_t unusedIndex; CHECK(iter.readThrow(&unusedIndex, ¬hings)); } case uint16_t(Op::Rethrow): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } uint32_t unusedDepth; CHECK(iter.readRethrow(&unusedDepth)); } @@ -1541,14 +1528,10 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, bool wasm::ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, uint32_t bodySize, Decoder& d) { - ValTypeVector locals; - if (!locals.appendAll(env.funcs[funcIndex].type->args())) { - return false; - } - const uint8_t* bodyBegin = d.currentPosition(); - if (!DecodeLocalEntries(d, *env.types, env.features, &locals)) { + ValTypeVector locals; + if (!DecodeLocalEntriesWithParams(d, env, funcIndex, &locals)) { return false; } @@ -1624,7 +1607,7 @@ static bool DecodeFuncType(Decoder& d, ModuleEnvironment* env, static bool DecodeStructType(Decoder& d, ModuleEnvironment* env, StructType* structType) { if (!env->gcEnabled()) { - return d.fail("Structure types not enabled"); + return d.fail("gc not enabled"); } uint32_t numFields; @@ -1668,7 +1651,7 @@ static bool DecodeStructType(Decoder& d, ModuleEnvironment* env, static bool DecodeArrayType(Decoder& d, ModuleEnvironment* env, ArrayType* arrayType) { if (!env->gcEnabled()) { - return d.fail("gc types not enabled"); + return d.fail("gc not enabled"); } StorageType elementType; @@ -2247,13 +2230,6 @@ static bool CheckImportsAgainstBuiltinModules(Decoder& d, return true; } - // Allocate a type context for builtin types so we can canonicalize them - // and use them in type comparisons - RefPtr<TypeContext> builtinTypes = js_new<TypeContext>(); - if (!builtinTypes) { - return false; - } - uint32_t importFuncIndex = 0; for (auto& import : env->imports) { Maybe<BuiltinModuleId> builtinModule = @@ -2278,21 +2254,9 @@ static bool CheckImportsAgainstBuiltinModules(Decoder& d, return d.fail("unrecognized builtin module field"); } - // Get a canonicalized type definition for this builtin so we can - // accurately compare it against the import type. - FuncType builtinFuncType; - if (!(*builtinFunc)->funcType(&builtinFuncType)) { - return false; - } - if (!builtinTypes->addType(builtinFuncType)) { - return false; - } - const TypeDef& builtinTypeDef = - builtinTypes->type(builtinTypes->length() - 1); - const TypeDef& importTypeDef = (*env->types)[func.typeIndex]; - if (!TypeDef::isSubTypeOf(&builtinTypeDef, &importTypeDef)) { - return d.failf("type mismatch in %s", (*builtinFunc)->exportName); + if (!TypeDef::isSubTypeOf((*builtinFunc)->typeDef(), &importTypeDef)) { + return d.failf("type mismatch in %s", (*builtinFunc)->exportName()); } break; } @@ -2479,10 +2443,6 @@ static bool DecodeTagSection(Decoder& d, ModuleEnvironment* env) { return true; } - if (!env->exceptionsEnabled()) { - return d.fail("exceptions not enabled"); - } - uint32_t numDefs; if (!d.readVarU32(&numDefs)) { return d.fail("expected number of tags"); diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index 3254e7b74a..8ba08fd088 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -285,11 +285,13 @@ using ValidatingOpIter = OpIter<ValidatingPolicy>; Decoder& d, ValTypeVector* locals); -// This validates the entries. +// This validates the entries. Function params are inserted before the locals +// to generate the full local entries for use in validation -[[nodiscard]] bool DecodeLocalEntries(Decoder& d, const TypeContext& types, - const FeatureArgs& features, - ValTypeVector* locals); +[[nodiscard]] bool DecodeLocalEntriesWithParams(Decoder& d, + const ModuleEnvironment& env, + uint32_t funcIndex, + ValTypeVector* locals); // Returns whether the given [begin, end) prefix of a module's bytecode starts a // code section and, if so, returns the SectionRange of that code section. diff --git a/js/src/wasm/WasmValue.cpp b/js/src/wasm/WasmValue.cpp index 3798f6c3e8..6039b00517 100644 --- a/js/src/wasm/WasmValue.cpp +++ b/js/src/wasm/WasmValue.cpp @@ -642,7 +642,7 @@ bool wasm::ToWebAssemblyValue(JSContext* cx, HandleValue val, ValType type, case ValType::V128: break; case ValType::Ref: -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC if (!type.isNullable() && val.isNull()) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_REF_NONNULLABLE_VALUE); diff --git a/js/src/wasm/WasmValue.h b/js/src/wasm/WasmValue.h index 66de690537..79e20285b9 100644 --- a/js/src/wasm/WasmValue.h +++ b/js/src/wasm/WasmValue.h @@ -224,6 +224,11 @@ class LitVal { Cell& cell() { return cell_; } const Cell& cell() const { return cell_; } + // Updates the type of the LitVal. Does not check that the type is valid for the + // actual value, so make sure the type is definitely correct via validation or + // something. + void unsafeSetType(ValType type) { type_ = type; } + uint32_t i32() const { MOZ_ASSERT(type_ == ValType::I32); return cell_.i32_; @@ -309,11 +314,6 @@ class MOZ_NON_PARAM Val : public LitVal { return cell_.ref_; } - // Updates the type of the Val. Does not check that the type is valid for the - // actual value, so make sure the type is definitely correct via validation or - // something. - void unsafeSetType(ValType type) { type_ = type; } - // Initialize from `loc` which is a rooted location and needs no barriers. void initFromRootedLocation(ValType type, const void* loc); void initFromHeapLocation(ValType type, const void* loc); diff --git a/js/src/wasm/moz.build b/js/src/wasm/moz.build index 8aa23e3516..83fea3b81b 100644 --- a/js/src/wasm/moz.build +++ b/js/src/wasm/moz.build @@ -45,6 +45,7 @@ UNIFIED_SOURCES += [ "WasmRealm.cpp", "WasmSerialize.cpp", "WasmSignalHandlers.cpp", + "WasmStaticTypeDefs.cpp", "WasmStubs.cpp", "WasmSummarizeInsn.cpp", "WasmTable.cpp", |