diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 6bb13ebcb0..1a881981be 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -465,7 +465,7 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx) unsigned lineNum = 0; NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP"); - JS::AutoFilename scriptFilename; + JS::UniqueChars scriptFilename; if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum)) { if (const char *file = scriptFilename.get()) { CopyUTF8toUTF16(nsDependentCString(file), fileName); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 80cd92257e..b19351038d 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -14106,7 +14106,7 @@ nsDocShell::NotifyJSRunToCompletionStart(const char* aReason, RefPtr timelines = TimelineConsumers::Get(); if (timelines && timelines->HasConsumer(this)) { timelines->AddMarkerForDocShell(this, Move( - MakeUnique( + mozilla::MakeUnique( aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START, aAsyncStack, aAsyncCause))); } diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 39e2eb45c8..5e9d7da500 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -1261,7 +1261,7 @@ WebSocket::Constructor(const GlobalObject& aGlobal, } unsigned lineno, column; - JS::AutoFilename file; + JS::UniqueChars file; if (!JS::DescribeScriptedCaller(aGlobal.Context(), &file, &lineno, &column)) { NS_WARNING("Failed to get line number and filename in workers."); @@ -1496,7 +1496,7 @@ WebSocketImpl::Init(JSContext* aCx, MOZ_ASSERT(aCx); unsigned lineno, column; - JS::AutoFilename file; + JS::UniqueChars file; if (JS::DescribeScriptedCaller(aCx, &file, &lineno, &column)) { mScriptFile = file.get(); mScriptLine = lineno; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index a233edf142..31e7527266 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -11141,7 +11141,7 @@ nsGlobalWindow::ShowSlowScriptDialog() } // Check if we should offer the option to debug - JS::AutoFilename filename; + JS::UniqueChars filename; unsigned lineno; bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno); diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index f0e567f44c..1bc9d9f92d 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -37,7 +37,7 @@ bool nsJSUtils::GetCallingLocation(JSContext* aContext, nsACString& aFilename, uint32_t* aLineno, uint32_t* aColumn) { - JS::AutoFilename filename; + JS::UniqueChars filename; if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) { return false; } @@ -50,7 +50,7 @@ bool nsJSUtils::GetCallingLocation(JSContext* aContext, nsAString& aFilename, uint32_t* aLineno, uint32_t* aColumn) { - JS::AutoFilename filename; + JS::UniqueChars filename; if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) { return false; } diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 75e2f4244f..a7b47ab01b 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -643,7 +643,7 @@ ContentSecurityPolicyAllows(JSContext* aCx) nsString fileName; uint32_t lineNum = 0; - JS::AutoFilename file; + JS::UniqueChars file; if (JS::DescribeScriptedCaller(aCx, &file, &lineNum) && file.get()) { fileName = NS_ConvertUTF8toUTF16(file.get()); } else { diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index aa7c680006..f92b52d1eb 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -4325,7 +4325,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, // We're being created outside of a window. Need to figure out the script // that is creating us in order for us to use relative URIs later on. - JS::AutoFilename fileName; + JS::UniqueChars fileName; if (JS::DescribeScriptedCaller(aCx, &fileName)) { // In most cases, fileName is URI. In a few other cases // (e.g. xpcshell), fileName is a file path. Ideally, we would diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index bf7892edce..2aa6647f61 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -895,10 +895,10 @@ void WorkerDebuggerGlobalScope::ReportError(JSContext* aCx, const nsAString& aMessage) { - JS::AutoFilename afn; + JS::UniqueChars chars; uint32_t lineno = 0; - JS::DescribeScriptedCaller(aCx, &afn, &lineno); - nsString filename(NS_ConvertUTF8toUTF16(afn.get())); + JS::DescribeScriptedCaller(aCx, &chars, &lineno); + nsString filename(NS_ConvertUTF8toUTF16(chars.get())); mWorkerPrivate->ReportErrorToDebugger(filename, lineno, aMessage); } diff --git a/js/public/Debug.h b/js/public/Debug.h index e1cf17c712..0a11fd6ef3 100644 --- a/js/public/Debug.h +++ b/js/public/Debug.h @@ -12,7 +12,6 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/UniquePtr.h" #include "jsapi.h" #include "jspubtd.h" @@ -26,9 +25,6 @@ class Debugger; } // namespace js namespace JS { - -using mozilla::UniquePtr; - namespace dbg { // Helping embedding code build objects for Debugger diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 794cda7584..42be696eea 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -7,10 +7,10 @@ #ifndef js_GCAPI_h #define js_GCAPI_h -#include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" #include "js/HeapAPI.h" +#include "js/UniquePtr.h" namespace js { namespace gc { @@ -48,8 +48,6 @@ typedef enum JSGCInvocationKind { namespace JS { -using mozilla::UniquePtr; - #define GCREASONS(D) \ /* Reasons internal to the JS engine */ \ D(API) \ @@ -292,7 +290,7 @@ class GarbageCollectionEvent , collections() { } - using Ptr = UniquePtr>; + using Ptr = js::UniquePtr; static Ptr Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t majorGCNumber); JSObject* toJSObject(JSContext* cx) const; diff --git a/js/public/TraceableVector.h b/js/public/GCVector.h similarity index 85% rename from js/public/TraceableVector.h rename to js/public/GCVector.h index 6601b48521..cc0726cd3f 100644 --- a/js/public/TraceableVector.h +++ b/js/public/GCVector.h @@ -4,8 +4,8 @@ * 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 js_TraceableVector_h -#define js_TraceableVector_h +#ifndef js_GCVector_h +#define js_GCVector_h #include "mozilla/Vector.h" @@ -15,7 +15,7 @@ namespace js { -// A TraceableVector is a Vector with an additional trace method that knows how +// A GCVector is a Vector with an additional trace method that knows how // to visit all of the items stored in the Vector. For vectors that contain GC // things, this is usually more convenient than manually iterating and marking // the contents. @@ -33,20 +33,20 @@ template > -class TraceableVector : public JS::Traceable +class GCVector : public JS::Traceable { mozilla::Vector vector; public: - explicit TraceableVector(AllocPolicy alloc = AllocPolicy()) + explicit GCVector(AllocPolicy alloc = AllocPolicy()) : vector(alloc) {} - TraceableVector(TraceableVector&& vec) + GCVector(GCVector&& vec) : vector(mozilla::Move(vec.vector)) {} - TraceableVector& operator=(TraceableVector&& vec) { + GCVector& operator=(GCVector&& vec) { vector = mozilla::Move(vec.vector); return *this; } @@ -118,7 +118,7 @@ class TraceableVector : public JS::Traceable return vector.sizeOfIncludingThis(mallocSizeOf); } - static void trace(TraceableVector* vec, JSTracer* trc) { vec->trace(trc); } + static void trace(GCVector* vec, JSTracer* trc) { vec->trace(trc); } void trace(JSTracer* trc) { for (auto& elem : vector) @@ -127,9 +127,9 @@ class TraceableVector : public JS::Traceable }; template -class TraceableVectorOperations +class GCVectorOperations { - using Vec = TraceableVector; + using Vec = GCVector; const Vec& vec() const { return static_cast(this)->get(); } public: @@ -147,10 +147,10 @@ class TraceableVectorOperations }; template -class MutableTraceableVectorOperations - : public TraceableVectorOperations +class MutableGCVectorOperations + : public GCVectorOperations { - using Vec = TraceableVector; + using Vec = GCVector; const Vec& vec() const { return static_cast(this)->get(); } Vec& vec() { return static_cast(this)->get(); } @@ -161,10 +161,12 @@ class MutableTraceableVectorOperations T* begin() { return vec().begin(); } const T* end() const { return vec().end(); } T* end() { return vec().end(); } - const T& operator[](size_t aIndex) const { return vec().operator[](aIndex); } const T& back() const { return vec().back(); } T& back() { return vec().back(); } + JS::Handle operator[](size_t aIndex) const { + return JS::Handle::fromMarkedLocation(&vec().operator[](aIndex)); + } JS::MutableHandle operator[](size_t aIndex) { return JS::MutableHandle::fromMarkedLocation(&vec().operator[](aIndex)); } @@ -212,27 +214,27 @@ class MutableTraceableVectorOperations }; template -class RootedBase> - : public MutableTraceableVectorOperations>, T,N,AP,GP> +class RootedBase> + : public MutableGCVectorOperations>, T,N,AP,GP> {}; template -class MutableHandleBase> - : public MutableTraceableVectorOperations>, +class MutableHandleBase> + : public MutableGCVectorOperations>, T,N,AP,GP> {}; template -class HandleBase> - : public TraceableVectorOperations>, T,N,AP,GP> +class HandleBase> + : public GCVectorOperations>, T,N,AP,GP> {}; template -class PersistentRootedBase> - : public MutableTraceableVectorOperations>, +class PersistentRootedBase> + : public MutableGCVectorOperations>, T,N,AP,GP> {}; } // namespace js -#endif // js_TraceableVector_h +#endif // js_GCVector_h diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 7c42758da8..cd4f440931 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -606,21 +606,21 @@ template struct DefaultHasher : PointerHasher::value> {}; -// Specialize hashing policy for mozilla::UniquePtr to proxy the UniquePtr's +// Specialize hashing policy for mozilla::UniquePtr to proxy the UniquePtr's // raw pointer to PointerHasher. -template -struct DefaultHasher> +template +struct DefaultHasher> { - using Lookup = mozilla::UniquePtr; + using Lookup = mozilla::UniquePtr; using PtrHasher = PointerHasher::value>; static HashNumber hash(const Lookup& l) { return PtrHasher::hash(l.get()); } - static bool match(const mozilla::UniquePtr& k, const Lookup& l) { + static bool match(const mozilla::UniquePtr& k, const Lookup& l) { return PtrHasher::match(k.get(), l.get()); } - static void rekey(mozilla::UniquePtr& k, mozilla::UniquePtr&& newKey) { + static void rekey(mozilla::UniquePtr& k, mozilla::UniquePtr&& newKey) { k = mozilla::Move(newKey); } }; diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index 1b0bbac56a..9ea3fce840 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -390,6 +390,14 @@ struct DefaultGCPolicy } }; +template <> +struct DefaultGCPolicy +{ + static void trace(JSTracer* trc, JS::Value* v, const char* name) { + js::UnsafeTraceManuallyBarrieredEdge(trc, v, name); + } +}; + template <> struct DefaultGCPolicy : public IgnoreGCPolicy {}; template <> struct DefaultGCPolicy : public IgnoreGCPolicy {}; diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index e58c7aba69..653fb0a89b 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -15,7 +15,6 @@ #include "mozilla/Move.h" #include "mozilla/RangedPtr.h" #include "mozilla/TypeTraits.h" -#include "mozilla/UniquePtr.h" #include "mozilla/Variant.h" #include "jspubtd.h" @@ -25,6 +24,7 @@ #include "js/RootingAPI.h" #include "js/TracingAPI.h" #include "js/TypeDecls.h" +#include "js/UniquePtr.h" #include "js/Value.h" #include "js/Vector.h" @@ -174,16 +174,6 @@ class StackFrame; } // namespace ubi } // namespace JS -namespace mozilla { - -template<> -class DefaultDelete : public JS::DeletePolicy { }; - -template<> -class DefaultDelete : public JS::DeletePolicy { }; - -} // namespace mozilla - namespace JS { namespace ubi { @@ -191,7 +181,6 @@ using mozilla::Forward; using mozilla::Maybe; using mozilla::Move; using mozilla::RangedPtr; -using mozilla::UniquePtr; using mozilla::Variant; /*** ubi::StackFrame ******************************************************************************/ @@ -597,7 +586,7 @@ class JS_FRIEND_API(Base) { // // If wantNames is true, compute names for edges. Doing so can be expensive // in time and memory. - virtual UniquePtr edges(JSRuntime* rt, bool wantNames) const = 0; + virtual js::UniquePtr edges(JSRuntime* rt, bool wantNames) const = 0; // Return the Zone to which this node's referent belongs, or nullptr if the // referent is not of a type allocated in SpiderMonkey Zones. @@ -633,8 +622,7 @@ class JS_FRIEND_API(Base) { // Otherwise, place nullptr in the out parameter. Caller maintains ownership // of the out parameter. True is returned on success, false is returned on // OOM. - virtual bool jsObjectConstructorName(JSContext* cx, - UniquePtr& outName) const { + virtual bool jsObjectConstructorName(JSContext* cx, UniqueTwoByteChars& outName) const { outName.reset(nullptr); return true; } @@ -780,8 +768,7 @@ class JS_FRIEND_API(Node) { JS::Zone* zone() const { return base()->zone(); } JSCompartment* compartment() const { return base()->compartment(); } const char* jsObjectClassName() const { return base()->jsObjectClassName(); } - bool jsObjectConstructorName(JSContext* cx, - UniquePtr& outName) const { + bool jsObjectConstructorName(JSContext* cx, UniqueTwoByteChars& outName) const { return base()->jsObjectConstructorName(cx, outName); } @@ -796,7 +783,7 @@ class JS_FRIEND_API(Node) { return size; } - UniquePtr edges(JSRuntime* rt, bool wantNames = true) const { + js::UniquePtr edges(JSRuntime* rt, bool wantNames = true) const { return base()->edges(rt, wantNames); } @@ -830,7 +817,7 @@ class JS_FRIEND_API(Node) { /*** Edge and EdgeRange ***************************************************************************/ -using EdgeName = UniquePtr; +using EdgeName = UniqueTwoByteChars; // An outgoing edge to a referent node. class Edge { @@ -1002,7 +989,7 @@ class MOZ_STACK_CLASS JS_FRIEND_API(RootList) { template<> struct JS_FRIEND_API(Concrete) : public Base { - UniquePtr edges(JSRuntime* rt, bool wantNames) const override; + js::UniquePtr edges(JSRuntime* rt, bool wantNames) const override; const char16_t* typeName() const override { return concreteTypeName; } protected: @@ -1019,7 +1006,7 @@ struct JS_FRIEND_API(Concrete) : public Base { template class JS_FRIEND_API(TracerConcrete) : public Base { const char16_t* typeName() const override { return concreteTypeName; } - UniquePtr edges(JSRuntime* rt, bool wantNames) const override; + js::UniquePtr edges(JSRuntime* rt, bool wantNames) const override; JS::Zone* zone() const override; protected: @@ -1077,8 +1064,7 @@ template<> struct Concrete : TracerConcreteWithCompartment { template<> class JS_FRIEND_API(Concrete) : public TracerConcreteWithCompartment { const char* jsObjectClassName() const override; - bool jsObjectConstructorName(JSContext* cx, - UniquePtr& outName) const override; + bool jsObjectConstructorName(JSContext* cx, UniqueTwoByteChars& outName) const override; Size size(mozilla::MallocSizeOf mallocSizeOf) const override; bool hasAllocationStack() const override; @@ -1113,7 +1099,7 @@ template<> class JS_FRIEND_API(Concrete) : public Base { const char16_t* typeName() const override; Size size(mozilla::MallocSizeOf mallocSizeOf) const override; - UniquePtr edges(JSRuntime* rt, bool wantNames) const override; + js::UniquePtr edges(JSRuntime* rt, bool wantNames) const override; JS::Zone* zone() const override; JSCompartment* compartment() const override; CoarseType coarseType() const final; diff --git a/js/public/UbiNodeCensus.h b/js/public/UbiNodeCensus.h index 004d1b26e3..e38b8c1b50 100644 --- a/js/public/UbiNodeCensus.h +++ b/js/public/UbiNodeCensus.h @@ -83,7 +83,7 @@ struct JS_FRIEND_API(CountDeleter) { void operator()(CountBase*); }; -using CountBasePtr = UniquePtr; +using CountBasePtr = js::UniquePtr; // Abstract base class for CountType nodes. struct JS_FRIEND_API(CountType) { @@ -111,7 +111,7 @@ struct JS_FRIEND_API(CountType) { virtual bool report(JSContext* cx, CountBase& count, MutableHandleValue report) = 0; }; -using CountTypePtr = UniquePtr>; +using CountTypePtr = js::UniquePtr; // An abstract base class for count tree nodes. class JS_FRIEND_API(CountBase) { diff --git a/js/public/UbiNodePostOrder.h b/js/public/UbiNodePostOrder.h index 5943ee6505..52c3214259 100644 --- a/js/public/UbiNodePostOrder.h +++ b/js/public/UbiNodePostOrder.h @@ -89,7 +89,7 @@ struct PostOrder { mozilla::DebugOnly traversed; private: - bool fillEdgesFromRange(EdgeVector& edges, UniquePtr& range) { + bool fillEdgesFromRange(EdgeVector& edges, js::UniquePtr& range) { MOZ_ASSERT(range); for ( ; !range->empty(); range->popFront()) { if (!edges.append(mozilla::Move(range->front()))) diff --git a/js/public/UniquePtr.h b/js/public/UniquePtr.h new file mode 100644 index 0000000000..0236bab425 --- /dev/null +++ b/js/public/UniquePtr.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 js_UniquePtr_h +#define js_UniquePtr_h + +#include "mozilla/UniquePtr.h" + +#include "js/Utility.h" + +namespace js { + +// Replacement for mozilla::UniquePtr that defaults to js::DefaultDelete. +template > +using UniquePtr = mozilla::UniquePtr; + +namespace detail { + +template +struct UniqueSelector +{ + typedef UniquePtr SingleObject; +}; + +template +struct UniqueSelector +{ + typedef UniquePtr UnknownBound; +}; + +template +struct UniqueSelector +{ + typedef UniquePtr KnownBound; +}; + +} // namespace detail + +// Replacement for mozilla::MakeUnique that correctly calls js_new and produces +// a js::UniquePtr. +template +typename detail::UniqueSelector::SingleObject +MakeUnique(Args&&... aArgs) +{ + return UniquePtr(js_new(mozilla::Forward(aArgs)...)); +} + +template +typename detail::UniqueSelector::UnknownBound +MakeUnique(decltype(sizeof(int)) aN) = delete; + +template +typename detail::UniqueSelector::KnownBound +MakeUnique(Args&&... aArgs) = delete; + +} // namespace js + +#endif /* js_UniquePtr_h */ diff --git a/js/public/Utility.h b/js/public/Utility.h index fc7c42abb9..470c4bec09 100644 --- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -460,6 +460,14 @@ namespace JS { template struct DeletePolicy { + MOZ_CONSTEXPR DeletePolicy() {} + + template + MOZ_IMPLICIT DeletePolicy(DeletePolicy other, + typename mozilla::EnableIf::value, + int>::Type dummy = 0) + {} + void operator()(const T* ptr) { js_delete(const_cast(ptr)); } @@ -472,6 +480,9 @@ struct FreePolicy } }; +typedef mozilla::UniquePtr UniqueChars; +typedef mozilla::UniquePtr UniqueTwoByteChars; + } // namespace JS namespace js { @@ -480,13 +491,6 @@ namespace js { typedef uint32_t HashNumber; const unsigned HashNumberSizeBits = 32; -typedef mozilla::UniquePtr UniqueChars; - -static inline UniqueChars make_string_copy(const char* str) -{ - return UniqueChars(js_strdup(str)); -} - namespace detail { /* diff --git a/js/src/NamespaceImports.h b/js/src/NamespaceImports.h index 05a0e5dcf5..3db0bb42f4 100644 --- a/js/src/NamespaceImports.h +++ b/js/src/NamespaceImports.h @@ -13,7 +13,7 @@ // These includes are needed these for some typedefs (e.g. HandleValue) and // functions (e.g. NullValue())... #include "js/CallNonGenericMethod.h" -#include "js/TraceableVector.h" +#include "js/GCVector.h" #include "js/TypeDecls.h" #include "js/Value.h" @@ -36,9 +36,9 @@ typedef AutoVectorRooter AutoIdVector; typedef AutoVectorRooter AutoObjectVector; typedef AutoVectorRooter AutoVector; -using ValueVector = js::TraceableVector; -using IdVector = js::TraceableVector; -using ScriptVector = js::TraceableVector; +using ValueVector = js::GCVector; +using IdVector = js::GCVector; +using ScriptVector = js::GCVector; template class AutoVectorRooter; template class AutoHashMapRooter; @@ -77,6 +77,8 @@ using JS::TwoByteChars; using JS::TwoByteCharsZ; using JS::UTF8Chars; using JS::UTF8CharsZ; +using JS::UniqueChars; +using JS::UniqueTwoByteChars; using JS::AutoVectorRooter; typedef AutoVectorRooter AutoValueVector; diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 023da12003..af20e3da63 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -18,11 +18,13 @@ #include "asmjs/AsmJS.h" +#include "mozilla/Attributes.h" #include "mozilla/Compression.h" #include "mozilla/MathAlgorithms.h" #include "jsmath.h" #include "jsprf.h" +#include "jsstr.h" #include "jsutil.h" #include "jswrapper.h" @@ -54,7 +56,6 @@ using mozilla::Compression::LZ4; using mozilla::HashGeneric; using mozilla::IsNaN; using mozilla::IsNegativeZero; -using mozilla::MallocSizeOf; using mozilla::Move; using mozilla::PodCopy; using mozilla::PodEqual; @@ -66,6 +67,15 @@ using JS::GenericNaN; /*****************************************************************************/ // asm.js module object +typedef PropertyName* PropertyNamePtr; + +static void +TraceNameField(JSTracer* trc, const PropertyNamePtr* name, const char* label) +{ + if (*name) + TraceManuallyBarrieredEdge(trc, const_cast(name), label); +} + // The asm.js spec recognizes this set of builtin Math functions. enum AsmJSMathBuiltinFunction { @@ -123,593 +133,336 @@ enum AsmJSSimdOperation #undef ASMJSSIMDOPERATION }; -// An AsmJSModule extends (via containment) a wasm::Module with the extra -// persistent state necessary to represent a compiled asm.js module. -class js::AsmJSModule +// An AsmJSGlobal represents a JS global variable in the asm.js module function. +class AsmJSGlobal { public: - class Global - { - public: - enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction, - AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation }; - enum VarInitKind { InitConstant, InitImport }; - enum ConstantKind { GlobalConstant, MathConstant }; - - private: - struct CacheablePod { - Which which_; - union { - struct { - uint32_t globalDataOffset_; - VarInitKind initKind_; - union { - wasm::ValType importType_; - wasm::Val val_; - } u; - } var; - uint32_t ffiIndex_; - Scalar::Type viewType_; - AsmJSMathBuiltinFunction mathBuiltinFunc_; - AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_; - AsmJSSimdType simdCtorType_; - struct { - AsmJSSimdType type_; - AsmJSSimdOperation which_; - } simdOp; - struct { - ConstantKind kind_; - double value_; - } constant; - } u; - } pod; - PropertyName* name_; - - friend class AsmJSModule; - - Global(Which which, PropertyName* name) { - mozilla::PodZero(&pod); // zero padding for Valgrind - pod.which_ = which; - name_ = name; - MOZ_ASSERT_IF(name_, name_->isTenured()); - } - - void trace(JSTracer* trc) { - if (name_) - TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name"); - } - - public: - Global() {} - Which which() const { - return pod.which_; - } - uint32_t varGlobalDataOffset() const { - MOZ_ASSERT(pod.which_ == Variable); - return pod.u.var.globalDataOffset_; - } - VarInitKind varInitKind() const { - MOZ_ASSERT(pod.which_ == Variable); - return pod.u.var.initKind_; - } - wasm::Val varInitVal() const { - MOZ_ASSERT(pod.which_ == Variable); - MOZ_ASSERT(pod.u.var.initKind_ == InitConstant); - return pod.u.var.u.val_; - } - wasm::ValType varInitImportType() const { - MOZ_ASSERT(pod.which_ == Variable); - MOZ_ASSERT(pod.u.var.initKind_ == InitImport); - return pod.u.var.u.importType_; - } - PropertyName* varImportField() const { - MOZ_ASSERT(pod.which_ == Variable); - MOZ_ASSERT(pod.u.var.initKind_ == InitImport); - return name_; - } - PropertyName* ffiField() const { - MOZ_ASSERT(pod.which_ == FFI); - return name_; - } - uint32_t ffiIndex() const { - MOZ_ASSERT(pod.which_ == FFI); - return pod.u.ffiIndex_; - } - // When a view is created from an imported constructor: - // var I32 = stdlib.Int32Array; - // var i32 = new I32(buffer); - // the second import has nothing to validate and thus has a null field. - PropertyName* maybeViewName() const { - MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); - return name_; - } - Scalar::Type viewType() const { - MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); - return pod.u.viewType_; - } - PropertyName* mathName() const { - MOZ_ASSERT(pod.which_ == MathBuiltinFunction); - return name_; - } - PropertyName* atomicsName() const { - MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); - return name_; - } - AsmJSMathBuiltinFunction mathBuiltinFunction() const { - MOZ_ASSERT(pod.which_ == MathBuiltinFunction); - return pod.u.mathBuiltinFunc_; - } - AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const { - MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); - return pod.u.atomicsBuiltinFunc_; - } - AsmJSSimdType simdCtorType() const { - MOZ_ASSERT(pod.which_ == SimdCtor); - return pod.u.simdCtorType_; - } - PropertyName* simdCtorName() const { - MOZ_ASSERT(pod.which_ == SimdCtor); - return name_; - } - PropertyName* simdOperationName() const { - MOZ_ASSERT(pod.which_ == SimdOperation); - return name_; - } - AsmJSSimdOperation simdOperation() const { - MOZ_ASSERT(pod.which_ == SimdOperation); - return pod.u.simdOp.which_; - } - AsmJSSimdType simdOperationType() const { - MOZ_ASSERT(pod.which_ == SimdOperation); - return pod.u.simdOp.type_; - } - PropertyName* constantName() const { - MOZ_ASSERT(pod.which_ == Constant); - return name_; - } - ConstantKind constantKind() const { - MOZ_ASSERT(pod.which_ == Constant); - return pod.u.constant.kind_; - } - double constantValue() const { - MOZ_ASSERT(pod.which_ == Constant); - return pod.u.constant.value_; - } - - WASM_DECLARE_SERIALIZABLE(Global); - }; - - typedef Vector GlobalVector; - - // An import is slightly different than an asm.js FFI function: a single - // asm.js FFI function can be called with many different signatures. When - // compiled to wasm, each unique FFI function paired with signature - // generates a wasm import. - class Import - { - uint32_t ffiIndex_; - public: - Import() = default; - explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {} - uint32_t ffiIndex() const { return ffiIndex_; } - }; - - typedef Vector ImportVector; - - class Export - { - PropertyName* name_; - PropertyName* maybeFieldName_; - struct CacheablePod { - uint32_t startOffsetInModule_; // Store module-start-relative offsets - uint32_t endOffsetInModule_; // so preserved by serialization. - } pod; - - public: - Export() {} - Export(PropertyName* name, PropertyName* maybeFieldName, - uint32_t startOffsetInModule, uint32_t endOffsetInModule) - : name_(name), - maybeFieldName_(maybeFieldName) - { - MOZ_ASSERT(name_->isTenured()); - MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured()); - pod.startOffsetInModule_ = startOffsetInModule; - pod.endOffsetInModule_ = endOffsetInModule; - } - - void trace(JSTracer* trc) { - TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name"); - if (maybeFieldName_) - TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field"); - } - - PropertyName* name() const { - return name_; - } - PropertyName* maybeFieldName() const { - return maybeFieldName_; - } - uint32_t startOffsetInModule() const { - return pod.startOffsetInModule_; - } - uint32_t endOffsetInModule() const { - return pod.endOffsetInModule_; - } - - WASM_DECLARE_SERIALIZABLE(Export) - }; - - typedef Vector ExportVector; - - typedef JS::UniquePtr> UniqueWasmModule; + enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction, + AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation }; + enum VarInitKind { InitConstant, InitImport }; + enum ConstantKind { GlobalConstant, MathConstant }; private: - UniqueWasmModule wasmModule_; - wasm::UniqueStaticLinkData linkData_; struct CacheablePod { - uint32_t minHeapLength_; - uint32_t numFFIs_; - uint32_t srcLength_; - uint32_t srcLengthWithRightBrace_; - bool strict_; - bool hasArrayView_; - bool isSharedView_; + Which which_; + union { + struct { + uint32_t globalDataOffset_; + VarInitKind initKind_; + union { + ValType importType_; + Val val_; + } u; + } var; + uint32_t ffiIndex_; + Scalar::Type viewType_; + AsmJSMathBuiltinFunction mathBuiltinFunc_; + AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_; + AsmJSSimdType simdCtorType_; + struct { + AsmJSSimdType type_; + AsmJSSimdOperation which_; + } simdOp; + struct { + ConstantKind kind_; + double value_; + } constant; + } u; } pod; - const ScriptSourceHolder scriptSource_; - const uint32_t srcStart_; - const uint32_t srcBodyStart_; - GlobalVector globals_; - ImportVector imports_; - ExportVector exports_; - PropertyName* globalArgumentName_; - PropertyName* importArgumentName_; - PropertyName* bufferArgumentName_; + PropertyName* name_; + + friend class ModuleValidator; public: - explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart, - bool strict) - : scriptSource_(scriptSource), - srcStart_(srcStart), - srcBodyStart_(srcBodyStart), - globalArgumentName_(nullptr), - importArgumentName_(nullptr), - bufferArgumentName_(nullptr) + AsmJSGlobal() {} + AsmJSGlobal(Which which, PropertyName* name) { + mozilla::PodZero(&pod); // zero padding for Valgrind + pod.which_ = which; + name_ = name; + MOZ_ASSERT_IF(name_, name_->isTenured()); + } + void trace(JSTracer* trc) const { + TraceNameField(trc, &name_, "asm.js global name"); + } + Which which() const { + return pod.which_; + } + uint32_t varGlobalDataOffset() const { + MOZ_ASSERT(pod.which_ == Variable); + return pod.u.var.globalDataOffset_; + } + VarInitKind varInitKind() const { + MOZ_ASSERT(pod.which_ == Variable); + return pod.u.var.initKind_; + } + Val varInitVal() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitConstant); + return pod.u.var.u.val_; + } + ValType varInitImportType() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitImport); + return pod.u.var.u.importType_; + } + PropertyName* varImportField() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitImport); + return name_; + } + PropertyName* ffiField() const { + MOZ_ASSERT(pod.which_ == FFI); + return name_; + } + uint32_t ffiIndex() const { + MOZ_ASSERT(pod.which_ == FFI); + return pod.u.ffiIndex_; + } + // When a view is created from an imported constructor: + // var I32 = stdlib.Int32Array; + // var i32 = new I32(buffer); + // the second import has nothing to validate and thus has a null field. + PropertyName* maybeViewName() const { + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); + return name_; + } + Scalar::Type viewType() const { + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); + return pod.u.viewType_; + } + PropertyName* mathName() const { + MOZ_ASSERT(pod.which_ == MathBuiltinFunction); + return name_; + } + PropertyName* atomicsName() const { + MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); + return name_; + } + AsmJSMathBuiltinFunction mathBuiltinFunction() const { + MOZ_ASSERT(pod.which_ == MathBuiltinFunction); + return pod.u.mathBuiltinFunc_; + } + AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const { + MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); + return pod.u.atomicsBuiltinFunc_; + } + AsmJSSimdType simdCtorType() const { + MOZ_ASSERT(pod.which_ == SimdCtor); + return pod.u.simdCtorType_; + } + PropertyName* simdCtorName() const { + MOZ_ASSERT(pod.which_ == SimdCtor); + return name_; + } + PropertyName* simdOperationName() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return name_; + } + AsmJSSimdOperation simdOperation() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return pod.u.simdOp.which_; + } + AsmJSSimdType simdOperationType() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return pod.u.simdOp.type_; + } + PropertyName* constantName() const { + MOZ_ASSERT(pod.which_ == Constant); + return name_; + } + ConstantKind constantKind() const { + MOZ_ASSERT(pod.which_ == Constant); + return pod.u.constant.kind_; + } + double constantValue() const { + MOZ_ASSERT(pod.which_ == Constant); + return pod.u.constant.value_; + } + + WASM_DECLARE_SERIALIZABLE(AsmJSGlobal); +}; + +typedef Vector AsmJSGlobalVector; + +// An AsmJSImport is slightly different than an asm.js FFI function: a single +// asm.js FFI function can be called with many different signatures. When +// compiled to wasm, each unique FFI function paired with signature generates a +// wasm import. +class AsmJSImport +{ + uint32_t ffiIndex_; + public: + AsmJSImport() = default; + explicit AsmJSImport(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {} + uint32_t ffiIndex() const { return ffiIndex_; } +}; + +typedef Vector AsmJSImportVector; + +// An AsmJSExport logically extends Export with the extra information needed for +// an asm.js exported function, viz., the offsets in module's source chars in +// case the function is toString()ed. +class AsmJSExport +{ + // All fields are treated as cacheable POD: + uint32_t startOffsetInModule_; // Store module-start-relative offsets + uint32_t endOffsetInModule_; // so preserved by serialization. + + public: + AsmJSExport() { PodZero(this); } + AsmJSExport(uint32_t startOffsetInModule, uint32_t endOffsetInModule) + : startOffsetInModule_(startOffsetInModule), + endOffsetInModule_(endOffsetInModule) + {} + uint32_t startOffsetInModule() const { + return startOffsetInModule_; + } + uint32_t endOffsetInModule() const { + return endOffsetInModule_; + } +}; + +typedef Vector AsmJSExportVector; + +// Holds the trivially-memcpy()able, serializable portion of AsmJSModuleData. +struct AsmJSModuleCacheablePod +{ + uint32_t minHeapLength; + uint32_t numFFIs; + uint32_t srcLength; + uint32_t srcLengthWithRightBrace; +}; + +// Holds the immutable guts of an AsmJSModule. This struct is mutably built up +// by ModuleValidator and then handed over to the AsmJSModule constructor in +// finish(). +struct AsmJSModuleData : AsmJSModuleCacheablePod +{ + AsmJSGlobalVector globals; + AsmJSImportVector imports; + AsmJSExportVector exports; + ExportMap exportMap; + PropertyName* globalArgumentName; + PropertyName* importArgumentName; + PropertyName* bufferArgumentName; + + // These values are not serialized since they are relative to the + // containing script which can be different between serialization and + // deserialization contexts. Thus, they must be set explicitly using the + // ambient Parser/ScriptSource after deserialization. Cloning, however, + // preserves the same exact parsing context and can copy these values. + uint32_t srcStart; + uint32_t srcBodyStart; + bool strict; + ScriptSourceHolder scriptSource; + + AsmJSModuleData() + : globalArgumentName(nullptr), + importArgumentName(nullptr), + bufferArgumentName(nullptr), + srcStart(0), + srcBodyStart(0), + strict(false) { - mozilla::PodZero(&pod); - pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0); - pod.strict_ = strict; - - MOZ_ASSERT(srcStart_ <= srcBodyStart_); - - // AsmJSCheckedImmediateRange should be defined to be at most the minimum - // heap length so that offsets can be folded into bounds checks. - MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_); + PodZero(&pod()); } - void trace(JSTracer* trc) { - if (wasmModule_) - wasmModule_->trace(trc); - for (Global& global : globals_) + AsmJSModuleCacheablePod& pod() { return *this; } + const AsmJSModuleCacheablePod& pod() const { return *this; } + + void trace(JSTracer* trc) const { + for (const AsmJSGlobal& global : globals) global.trace(trc); - for (Export& exp : exports_) - exp.trace(trc); - if (globalArgumentName_) - TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name"); - if (importArgumentName_) - TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name"); - if (bufferArgumentName_) - TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name"); + TraceNameField(trc, &globalArgumentName, "asm.js global argument name"); + TraceNameField(trc, &importArgumentName, "asm.js import argument name"); + TraceNameField(trc, &bufferArgumentName, "asm.js buffer argument name"); } - /*************************************************************************/ - // These functions may be used as soon as the module is constructed: + WASM_DECLARE_SERIALIZABLE(AsmJSModuleData) +}; - ScriptSource* scriptSource() const { - return scriptSource_.get(); +typedef UniquePtr UniqueAsmJSModuleData; + +// An AsmJSModule is-a Module with the extra persistent state necessary to +// represent a compiled asm.js module. +class js::AsmJSModule final : public Module +{ + typedef UniquePtr UniqueConstAsmJSModuleData; + typedef UniquePtr UniqueConstStaticLinkData; + + const UniqueConstStaticLinkData link_; + const UniqueConstAsmJSModuleData module_; + + public: + AsmJSModule(UniqueModuleData base, + UniqueStaticLinkData link, + UniqueAsmJSModuleData module) + : Module(Move(base), Module::IsAsmJS), + link_(Move(link)), + module_(Move(module)) + {} + + virtual void trace(JSTracer* trc) override { + Module::trace(trc); + module_->trace(trc); } - bool strict() const { - return pod.strict_; + virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) override { + Module::addSizeOfMisc(mallocSizeOf, code, data); + *data += mallocSizeOf(link_.get()) + link_->sizeOfExcludingThis(mallocSizeOf); + *data += mallocSizeOf(module_.get()) + module_->sizeOfExcludingThis(mallocSizeOf); } + uint32_t minHeapLength() const { return module_->minHeapLength; } + uint32_t numFFIs() const { return module_->numFFIs; } + bool strict() const { return module_->strict; } + ScriptSource* scriptSource() const { return module_->scriptSource.get(); } + const AsmJSGlobalVector& asmJSGlobals() const { return module_->globals; } + const AsmJSImportVector& asmJSImports() const { return module_->imports; } + const AsmJSExportVector& asmJSExports() const { return module_->exports; } + const ExportMap& exportMap() const { return module_->exportMap; } + PropertyName* globalArgumentName() const { return module_->globalArgumentName; } + PropertyName* importArgumentName() const { return module_->importArgumentName; } + PropertyName* bufferArgumentName() const { return module_->bufferArgumentName; } + // srcStart() refers to the offset in the ScriptSource to the beginning of // the asm.js module function. If the function has been created with the // Function constructor, this will be the first character in the function // source. Otherwise, it will be the opening parenthesis of the arguments // list. uint32_t srcStart() const { - return srcStart_; + return module_->srcStart; + } + uint32_t srcEndBeforeCurly() const { + return module_->srcStart + module_->srcLength; + } + uint32_t srcEndAfterCurly() const { + return module_->srcStart + module_->srcLengthWithRightBrace; } // srcBodyStart() refers to the offset in the ScriptSource to the end // of the 'use asm' string-literal token. uint32_t srcBodyStart() const { - return srcBodyStart_; + return module_->srcBodyStart; } - // While these functions may be accessed at any time, their values will - // change as the module is compiled. - uint32_t minHeapLength() const { - return pod.minHeapLength_; - } - - void initGlobalArgumentName(PropertyName* n) { - MOZ_ASSERT(!isFinished()); - MOZ_ASSERT_IF(n, n->isTenured()); - globalArgumentName_ = n; - } - void initImportArgumentName(PropertyName* n) { - MOZ_ASSERT(!isFinished()); - MOZ_ASSERT_IF(n, n->isTenured()); - importArgumentName_ = n; - } - void initBufferArgumentName(PropertyName* n) { - MOZ_ASSERT(!isFinished()); - MOZ_ASSERT_IF(n, n->isTenured()); - bufferArgumentName_ = n; - } - PropertyName* globalArgumentName() const { - return globalArgumentName_; - } - PropertyName* importArgumentName() const { - return importArgumentName_; - } - PropertyName* bufferArgumentName() const { - return bufferArgumentName_; - } - - bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) { - MOZ_ASSERT(!isFinished()); - Global g(Global::Variable, nullptr); - g.pod.u.var.initKind_ = Global::InitConstant; - g.pod.u.var.u.val_ = v; - g.pod.u.var.globalDataOffset_ = globalDataOffset; - return globals_.append(g); - } - bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) { - MOZ_ASSERT(!isFinished()); - Global g(Global::Variable, name); - g.pod.u.var.initKind_ = Global::InitImport; - g.pod.u.var.u.importType_ = importType; - g.pod.u.var.globalDataOffset_ = globalDataOffset; - return globals_.append(g); - } - // See Import comment above for FFI vs. Import. - bool addFFI(PropertyName* field, uint32_t* ffiIndex) { - MOZ_ASSERT(!isFinished()); - if (pod.numFFIs_ == UINT32_MAX) - return false; - Global g(Global::FFI, field); - g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++; - return globals_.append(g); - } - bool addArrayView(Scalar::Type vt, PropertyName* maybeField) { - MOZ_ASSERT(!isFinished()); - pod.hasArrayView_ = true; - pod.isSharedView_ = false; - Global g(Global::ArrayView, maybeField); - g.pod.u.viewType_ = vt; - return globals_.append(g); - } - bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) { - MOZ_ASSERT(!isFinished()); - MOZ_ASSERT(field); - pod.isSharedView_ = false; - Global g(Global::ArrayViewCtor, field); - g.pod.u.viewType_ = vt; - return globals_.append(g); - } - bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) { - MOZ_ASSERT(!isFinished()); - Global g(Global::MathBuiltinFunction, field); - g.pod.u.mathBuiltinFunc_ = func; - return globals_.append(g); - } - bool addMathBuiltinConstant(double value, PropertyName* field) { - MOZ_ASSERT(!isFinished()); - Global g(Global::Constant, field); - g.pod.u.constant.value_ = value; - g.pod.u.constant.kind_ = Global::MathConstant; - return globals_.append(g); - } - bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) { - MOZ_ASSERT(!isFinished()); - Global g(Global::AtomicsBuiltinFunction, field); - g.pod.u.atomicsBuiltinFunc_ = func; - return globals_.append(g); - } - bool addSimdCtor(AsmJSSimdType type, PropertyName* field) { - MOZ_ASSERT(!isFinished()); - Global g(Global::SimdCtor, field); - g.pod.u.simdCtorType_ = type; - return globals_.append(g); - } - bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) { - MOZ_ASSERT(!isFinished()); - Global g(Global::SimdOperation, field); - g.pod.u.simdOp.type_ = type; - g.pod.u.simdOp.which_ = op; - return globals_.append(g); - } - bool addGlobalConstant(double value, PropertyName* name) { - MOZ_ASSERT(!isFinished()); - Global g(Global::Constant, name); - g.pod.u.constant.value_ = value; - g.pod.u.constant.kind_ = Global::GlobalConstant; - return globals_.append(g); - } - // See Import comment above for FFI vs. Import. - bool addImport(uint32_t ffiIndex, uint32_t importIndex) { - MOZ_ASSERT(imports_.length() == importIndex); - return imports_.emplaceBack(ffiIndex); - } - bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t begin, uint32_t end) { - // The begin/end offsets are given relative to the ScriptSource (the - // entire file) and ExportedFunctions store offsets relative to the - // beginning of the module (so that they are caching-invariant). - MOZ_ASSERT(!isFinished()); - MOZ_ASSERT(srcStart_ < begin); - MOZ_ASSERT(begin < end); - uint32_t startOffsetInModule = begin - srcStart_; - uint32_t endOffsetInModule = end - srcStart_; - return exports_.emplaceBack(name, maybeFieldName, startOffsetInModule, endOffsetInModule); - } - - const GlobalVector& globals() const { - return globals_; - } - const ImportVector& imports() const { - return imports_; - } - const ExportVector& exports() const { - return exports_; - } - - void setViewsAreShared() { - if (pod.hasArrayView_) - pod.isSharedView_ = true; - } - bool hasArrayView() const { - return pod.hasArrayView_; - } - bool isSharedView() const { - return pod.isSharedView_; - } - void requireHeapLengthToBeAtLeast(uint32_t len) { - MOZ_ASSERT(!isFinished()); - len = RoundUpToNextValidAsmJSHeapLength(len); - if (len > pod.minHeapLength_) - pod.minHeapLength_ = len; - } - - /*************************************************************************/ - // A module isFinished() when compilation completes. After being finished, - // a module must be statically and dynamically linked before execution. - - bool isFinished() const { - return !!wasmModule_; - } - void finish(wasm::Module* wasmModule, wasm::UniqueStaticLinkData linkData, - uint32_t endBeforeCurly, uint32_t endAfterCurly) - { - MOZ_ASSERT(!isFinished()); - - wasmModule_.reset(wasmModule); - linkData_ = Move(linkData); - - MOZ_ASSERT(endBeforeCurly >= srcBodyStart_); - MOZ_ASSERT(endAfterCurly >= srcBodyStart_); - pod.srcLength_ = endBeforeCurly - srcStart_; - pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_; - - MOZ_ASSERT(isFinished()); - } - - /*************************************************************************/ - // These accessor functions can only be used after finish(): - - wasm::Module& wasmModule() const { - MOZ_ASSERT(isFinished()); - return *wasmModule_; - } - uint32_t numFFIs() const { - MOZ_ASSERT(isFinished()); - return pod.numFFIs_; - } - uint32_t srcEndBeforeCurly() const { - MOZ_ASSERT(isFinished()); - return srcStart_ + pod.srcLength_; - } - uint32_t srcEndAfterCurly() const { - MOZ_ASSERT(isFinished()); - return srcStart_ + pod.srcLengthWithRightBrace_; - } bool staticallyLink(ExclusiveContext* cx) { - return wasmModule_->staticallyLink(cx, *linkData_); + return Module::staticallyLink(cx, *link_); } - // See WASM_DECLARE_SERIALIZABLE. + // Clone this AsmJSModule into a new AsmJSModule that isn't statically or + // dynamically linked for cases where a single asm.js module function is + // linked several times. + bool clone(JSContext* cx, MutableHandle moduleObj) const; + + // These are the top-level serialization functions used by caching to + // cache an AsmJSModule. size_t serializedSize() const; uint8_t* serialize(uint8_t* cursor) const; - const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor); - bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const; - void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data); + static const uint8_t* + deserialize(ExclusiveContext* cx, const uint8_t* cursor, AsmJSParser& parser, + MutableHandle moduleObj); }; -static void -AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj) -{ - AsmJSModuleObject& moduleObj = obj->as(); - if (moduleObj.hasModule()) - fop->delete_(&moduleObj.module()); -} - -static void -AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj) -{ - AsmJSModuleObject& moduleObj = obj->as(); - if (moduleObj.hasModule()) - moduleObj.module().trace(trc); -} - -const Class AsmJSModuleObject::class_ = { - "AsmJSModuleObject", - JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK | - JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS), - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - AsmJSModuleObject_finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - AsmJSModuleObject_trace -}; - -static AsmJSModuleObject* -NewAsmJSModuleObject(ExclusiveContext* cx) -{ - AutoSetNewObjectMetadata metadata(cx); - JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr); - if (!obj) - return nullptr; - - return &obj->as(); -} - -bool -AsmJSModuleObject::hasModule() const -{ - MOZ_ASSERT(is()); - return !getReservedSlot(MODULE_SLOT).isUndefined(); -} - -void -AsmJSModuleObject::setModule(AsmJSModule* newModule) -{ - MOZ_ASSERT(is()); - if (hasModule()) - js_delete(&module()); - setReservedSlot(MODULE_SLOT, PrivateValue(newModule)); -} - -AsmJSModule& -AsmJSModuleObject::module() const -{ - MOZ_ASSERT(is()); - return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate(); -} - -void -AsmJSModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) -{ - module().addSizeOfMisc(mallocSizeOf, code, data); -} - /*****************************************************************************/ // ParseNode utilities @@ -1245,7 +998,7 @@ class NumLit return which_ == Int32x4 || which_ == Float32x4 || which_ == Bool32x4; } - const jit::SimdConstant& simdValue() const { + const SimdConstant& simdValue() const { MOZ_ASSERT(isSimd()); return u.simd_; } @@ -1579,8 +1332,6 @@ class Type static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024; -namespace { - // The ModuleValidator encapsulates the entire validation of an asm.js module. // Its lifetime goes from the validation of the top components of an asm.js // module (all the globals), the emission of bytecode for all the functions in @@ -1843,77 +1594,35 @@ class MOZ_STACK_CLASS ModuleValidator typedef HashMap AtomicsNameMap; typedef HashMap SimdOperationNameMap; typedef Vector ArrayViewVector; - - public: typedef HashMap ImportMap; - private: - ExclusiveContext* cx_; - AsmJSParser& parser_; + ExclusiveContext* cx_; + AsmJSParser& parser_; + ParseNode* moduleFunctionNode_; + PropertyName* moduleFunctionName_; + MathNameMap standardLibraryMathNames_; + AtomicsNameMap standardLibraryAtomicsNames_; + SimdOperationNameMap standardLibrarySimdOpNames_; - ModuleGenerator mg_; - AsmJSModule* module_; + // Validation-internal state: + LifoAlloc validationLifo_; + FuncVector functions_; + FuncPtrTableVector funcPtrTables_; + GlobalMap globalMap_; + ImportMap importMap_; + ArrayViewVector arrayViews_; + bool atomicsPresent_; - LifoAlloc validationLifo_; - FuncVector functions_; - FuncPtrTableVector funcPtrTables_; - GlobalMap globals_; - ArrayViewVector arrayViews_; - ImportMap imports_; + // State used to build the AsmJSModule in finish(): + ModuleGenerator mg_; + UniqueAsmJSModuleData module_; - MathNameMap standardLibraryMathNames_; - AtomicsNameMap standardLibraryAtomicsNames_; - SimdOperationNameMap standardLibrarySimdOpNames_; + // Error reporting: + UniqueChars errorString_; + uint32_t errorOffset_; + bool errorOverRecursed_; - ParseNode* moduleFunctionNode_; - PropertyName* moduleFunctionName_; - - UniqueChars errorString_; - uint32_t errorOffset_; - bool errorOverRecursed_; - - bool supportsSimd_; - bool atomicsPresent_; - - public: - ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser) - : cx_(cx), - parser_(parser), - mg_(cx), - validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE), - functions_(cx), - funcPtrTables_(cx), - globals_(cx), - arrayViews_(cx), - imports_(cx), - standardLibraryMathNames_(cx), - standardLibraryAtomicsNames_(cx), - standardLibrarySimdOpNames_(cx), - moduleFunctionNode_(parser.pc->maybeFunction), - moduleFunctionName_(nullptr), - errorString_(nullptr), - errorOffset_(UINT32_MAX), - errorOverRecursed_(false), - supportsSimd_(cx->jitSupportsSimd()), - atomicsPresent_(false) - { - MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox()); - } - - ~ModuleValidator() { - if (errorString_) { - MOZ_ASSERT(errorOffset_ != UINT32_MAX); - tokenStream().reportAsmJSError(errorOffset_, - JSMSG_USE_ASM_TYPE_FAIL, - errorString_.get()); - } - if (errorOverRecursed_) - ReportOverRecursed(cx_); - } - - private: - - // Helpers + // Helpers: bool addStandardLibraryMathName(const char* name, AsmJSMathBuiltinFunction func) { JSAtom* atom = Atomize(cx_, name, strlen(name)); if (!atom) @@ -1942,9 +1651,50 @@ class MOZ_STACK_CLASS ModuleValidator } public: + ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* moduleFunctionNode) + : cx_(cx), + parser_(parser), + moduleFunctionNode_(moduleFunctionNode), + moduleFunctionName_(FunctionName(moduleFunctionNode)), + standardLibraryMathNames_(cx), + standardLibraryAtomicsNames_(cx), + standardLibrarySimdOpNames_(cx), + validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE), + functions_(cx), + funcPtrTables_(cx), + globalMap_(cx), + importMap_(cx), + arrayViews_(cx), + atomicsPresent_(false), + mg_(cx), + errorString_(nullptr), + errorOffset_(UINT32_MAX), + errorOverRecursed_(false) + {} - bool init(HandleAsmJSModule moduleObj) { - if (!globals_.init() || !imports_.init()) + ~ModuleValidator() { + if (errorString_) { + MOZ_ASSERT(errorOffset_ != UINT32_MAX); + tokenStream().reportAsmJSError(errorOffset_, + JSMSG_USE_ASM_TYPE_FAIL, + errorString_.get()); + } + if (errorOverRecursed_) + ReportOverRecursed(cx_); + } + + bool init() { + module_ = cx_->make_unique(); + if (!module_) + return false; + + module_->minHeapLength = RoundUpToNextValidAsmJSHeapLength(0); + module_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin; + module_->srcBodyStart = parser_.tokenStream.currentToken().pos.end; + module_->strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict(); + module_->scriptSource.reset(parser_.ss); + + if (!globalMap_.init() || !importMap_.init()) return false; if (!standardLibraryMathNames_.init() || @@ -2003,73 +1753,42 @@ class MOZ_STACK_CLASS ModuleValidator } #undef ADDSTDLIBSIMDOPNAME - uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin; - uint32_t srcBodyStart = tokenStream().currentToken().pos.end; - - // "use strict" should be added to the source if we are in an implicit - // strict context, see also comment above addUseStrict in - // js::FunctionToString. - bool strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict(); - - module_ = cx_->new_(parser_.ss, srcStart, srcBodyStart, strict); - if (!module_) - return false; - - moduleObj->setModule(module_); - return mg_.init(); } - bool finish(SlowFunctionVector* slowFuncs) { - uint32_t endBeforeCurly = tokenStream().currentToken().pos.end; - TokenPos pos; - JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand)); - uint32_t endAfterCurly = pos.end; + ExclusiveContext* cx() const { return cx_; } + PropertyName* moduleFunctionName() const { return moduleFunctionName_; } + PropertyName* globalArgumentName() const { return module_->globalArgumentName; } + PropertyName* importArgumentName() const { return module_->importArgumentName; } + PropertyName* bufferArgumentName() const { return module_->bufferArgumentName; } + ModuleGenerator& mg() { return mg_; } + AsmJSParser& parser() const { return parser_; } + TokenStream& tokenStream() const { return parser_.tokenStream; } + bool supportsSimd() const { return cx_->jitSupportsSimd(); } + bool atomicsPresent() const { return atomicsPresent_; } + uint32_t minHeapLength() const { return module_->minHeapLength; } - HeapUsage heapUsage = module_->hasArrayView() - ? module_->isSharedView() - ? HeapUsage::Shared - : HeapUsage::Unshared - : HeapUsage::None; - - auto mutedErrors = Module::MutedBool(parser_.ss->mutedErrors()); - - CacheableChars filename; - if (parser_.ss->filename()) { - filename = make_string_copy(parser_.ss->filename()); - if (!filename) - return false; - } - - CacheableTwoByteChars displayURL; - if (parser_.ss->hasDisplayURL()) { - uint32_t length = js_strlen(parser_.ss->displayURL()); - displayURL.reset(js_pod_calloc(length + 1)); - if (!displayURL) - return false; - PodCopy(displayURL.get(), parser_.ss->displayURL(), length); - } - - UniqueStaticLinkData linkData; - Module* wasm = mg_.finish(heapUsage, mutedErrors, Move(filename), Move(displayURL), - &linkData, slowFuncs); - if (!wasm) - return false; - - module_->finish(wasm, Move(linkData), endBeforeCurly, endAfterCurly); - return true; + void initModuleFunctionName(PropertyName* name) { + MOZ_ASSERT(!moduleFunctionName_); + moduleFunctionName_ = name; + } + void initGlobalArgumentName(PropertyName* n) { + MOZ_ASSERT(n->isTenured()); + module_->globalArgumentName = n; + } + void initImportArgumentName(PropertyName* n) { + MOZ_ASSERT(n->isTenured()); + module_->importArgumentName = n; + } + void initBufferArgumentName(PropertyName* n) { + MOZ_ASSERT(n->isTenured()); + module_->bufferArgumentName = n; } - - // Mutable interface. - void initModuleFunctionName(PropertyName* name) { moduleFunctionName_ = name; } - void initGlobalArgumentName(PropertyName* n) { module().initGlobalArgumentName(n); } - void initImportArgumentName(PropertyName* n) { module().initImportArgumentName(n); } - void initBufferArgumentName(PropertyName* n) { module().initBufferArgumentName(n); } - bool addGlobalVarInit(PropertyName* var, const NumLit& lit, bool isConst) { uint32_t globalDataOffset; if (!mg_.allocateGlobalVar(lit.type(), &globalDataOffset)) return false; + Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable; Global* global = validationLifo_.new_(which); if (!global) @@ -2078,31 +1797,49 @@ class MOZ_STACK_CLASS ModuleValidator global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : Type::var(lit.type())).which(); if (isConst) global->u.varOrConst.literalValue_ = lit; - return globals_.putNew(var, global) && - module().addGlobalVarInit(lit.value(), globalDataOffset); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::Variable, nullptr); + g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant; + g.pod.u.var.u.val_ = lit.value(); + g.pod.u.var.globalDataOffset_ = globalDataOffset; + return module_->globals.append(g); } bool addGlobalVarImport(PropertyName* var, PropertyName* field, ValType type, bool isConst) { uint32_t globalDataOffset; if (!mg_.allocateGlobalVar(type, &globalDataOffset)) return false; + Global::Which which = isConst ? Global::ConstantImport : Global::Variable; Global* global = validationLifo_.new_(which); if (!global) return false; global->u.varOrConst.globalDataOffset_ = globalDataOffset; global->u.varOrConst.type_ = Type::var(type).which(); - return globals_.putNew(var, global) && - module().addGlobalVarImport(field, type, globalDataOffset); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::Variable, field); + g.pod.u.var.initKind_ = AsmJSGlobal::InitImport; + g.pod.u.var.u.importType_ = type; + g.pod.u.var.globalDataOffset_ = globalDataOffset; + return module_->globals.append(g); } bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) { if (!arrayViews_.append(ArrayView(var, vt))) return false; + Global* global = validationLifo_.new_(Global::ArrayView); if (!global) return false; global->u.viewInfo.viewType_ = vt; - return globals_.putNew(var, global) && - module().addArrayView(vt, maybeField); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::ArrayView, maybeField); + g.pod.u.viewType_ = vt; + return module_->globals.append(g); } bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func, PropertyName* field) @@ -2111,8 +1848,12 @@ class MOZ_STACK_CLASS ModuleValidator if (!global) return false; global->u.mathBuiltinFunc_ = func; - return globals_.putNew(var, global) && - module().addMathBuiltinFunction(func, field); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::MathBuiltinFunction, field); + g.pod.u.mathBuiltinFunc_ = func; + return module_->globals.append(g); } private: bool addGlobalDoubleConstant(PropertyName* var, double constant) { @@ -2121,35 +1862,54 @@ class MOZ_STACK_CLASS ModuleValidator return false; global->u.varOrConst.type_ = Type::Double; global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant)); - return globals_.putNew(var, global); + return globalMap_.putNew(var, global); } public: bool addMathBuiltinConstant(PropertyName* var, double constant, PropertyName* field) { - return addGlobalDoubleConstant(var, constant) && - module().addMathBuiltinConstant(constant, field); + if (!addGlobalDoubleConstant(var, constant)) + return false; + + AsmJSGlobal g(AsmJSGlobal::Constant, field); + g.pod.u.constant.value_ = constant; + g.pod.u.constant.kind_ = AsmJSGlobal::MathConstant; + return module_->globals.append(g); } bool addGlobalConstant(PropertyName* var, double constant, PropertyName* field) { - return addGlobalDoubleConstant(var, constant) && - module().addGlobalConstant(constant, field); + if (!addGlobalDoubleConstant(var, constant)) + return false; + + AsmJSGlobal g(AsmJSGlobal::Constant, field); + g.pod.u.constant.value_ = constant; + g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant; + return module_->globals.append(g); } bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func, PropertyName* field) { + atomicsPresent_ = true; + Global* global = validationLifo_.new_(Global::AtomicsBuiltinFunction); if (!global) return false; - atomicsPresent_ = true; global->u.atomicsBuiltinFunc_ = func; - return globals_.putNew(var, global) && - module().addAtomicsBuiltinFunction(func, field); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::AtomicsBuiltinFunction, field); + g.pod.u.atomicsBuiltinFunc_ = func; + return module_->globals.append(g); } bool addSimdCtor(PropertyName* var, AsmJSSimdType type, PropertyName* field) { Global* global = validationLifo_.new_(Global::SimdCtor); if (!global) return false; global->u.simdCtorType_ = type; - return globals_.putNew(var, global) && - module().addSimdCtor(type, field); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::SimdCtor, field); + g.pod.u.simdCtorType_ = type; + return module_->globals.append(g); } bool addSimdOperation(PropertyName* var, AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* opName) @@ -2159,34 +1919,77 @@ class MOZ_STACK_CLASS ModuleValidator return false; global->u.simdOp.type_ = type; global->u.simdOp.which_ = op; - return globals_.putNew(var, global) && - module().addSimdOperation(type, op, opName); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::SimdOperation, opName); + g.pod.u.simdOp.type_ = type; + g.pod.u.simdOp.which_ = op; + return module_->globals.append(g); } bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) { Global* global = validationLifo_.new_(Global::ArrayViewCtor); if (!global) return false; global->u.viewInfo.viewType_ = vt; - return globals_.putNew(var, global) && - module().addArrayViewCtor(vt, field); + if (!globalMap_.putNew(var, global)) + return false; + + AsmJSGlobal g(AsmJSGlobal::ArrayViewCtor, field); + g.pod.u.viewType_ = vt; + return module_->globals.append(g); } bool addFFI(PropertyName* var, PropertyName* field) { + if (module_->numFFIs == UINT32_MAX) + return false; + uint32_t ffiIndex = module_->numFFIs++; + Global* global = validationLifo_.new_(Global::FFI); if (!global) return false; - uint32_t index; - if (!module().addFFI(field, &index)) + global->u.ffiIndex_ = ffiIndex; + if (!globalMap_.putNew(var, global)) return false; - global->u.ffiIndex_ = index; - return globals_.putNew(var, global); + + AsmJSGlobal g(AsmJSGlobal::FFI, field); + g.pod.u.ffiIndex_ = ffiIndex; + return module_->globals.append(g); } - bool addExport(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) { + bool addExportField(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) { + // Record the field name of this export. + CacheableChars fieldName; + if (maybeFieldName) + fieldName = StringToNewUTF8CharsZ(cx_, *maybeFieldName); + else + fieldName = DuplicateString(""); + if (!fieldName || !module_->exportMap.fieldNames.append(Move(fieldName))) + return false; + + // Declare which function is exported which gives us an index into the + // module ExportVector. MallocSig::ArgVector args; if (!args.appendAll(func.sig().args())) return false; - MallocSig sig(Move(args), func.sig().ret()); - return mg_.declareExport(Move(sig), func.index()) && - module().addExport(func.name(), maybeFieldName, func.srcBegin(), func.srcEnd()); + uint32_t exportIndex; + if (!mg_.declareExport(MallocSig(Move(args), func.sig().ret()), func.index(), &exportIndex)) + return false; + + // Add a mapping from the given field to the Export's index. + if (!module_->exportMap.fieldsToExports.append(exportIndex)) + return false; + + // The exported function might have already been exported in which case + // the index will refer into the range of AsmJSExports. + MOZ_ASSERT(exportIndex <= module_->exports.length()); + if (exportIndex < module_->exports.length()) + return true; + + // If this is a new export, record the src info for later toString. + CacheableChars exportName = StringToNewUTF8CharsZ(cx_, *func.name()); + return exportName && + module_->exportMap.exportNames.emplaceBack(Move(exportName)) && + module_->exports.emplaceBack(func.srcBegin() - module_->srcStart, + func.srcEnd() - module_->srcStart); } private: const LifoSig* getLifoSig(const LifoSig& sig) { @@ -2202,7 +2005,7 @@ class MOZ_STACK_CLASS ModuleValidator if (!global) return false; global->u.funcIndex_ = funcIndex; - if (!globals_.putNew(name, global)) + if (!globalMap_.putNew(name, global)) return false; const LifoSig* lifoSig = getLifoSig(sig); if (!lifoSig) @@ -2221,7 +2024,7 @@ class MOZ_STACK_CLASS ModuleValidator if (!global) return false; global->u.funcPtrTableIndex_ = *index; - if (!globals_.putNew(name, global)) + if (!globalMap_.putNew(name, global)) return false; const LifoSig* lifoSig = getLifoSig(sig); if (!lifoSig) @@ -2241,7 +2044,7 @@ class MOZ_STACK_CLASS ModuleValidator const LifoSig** lifoSig) { ImportDescriptor::Lookup lookup(name, sig); - ImportMap::AddPtr p = imports_.lookupForAdd(lookup); + ImportMap::AddPtr p = importMap_.lookupForAdd(lookup); if (p) { *lifoSig = &p->key().sig(); *importIndex = p->value(); @@ -2250,24 +2053,25 @@ class MOZ_STACK_CLASS ModuleValidator *lifoSig = getLifoSig(sig); if (!*lifoSig) return false; - return mg_.declareImport(Move(sig), importIndex) && - imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) && - module().addImport(ffiIndex, *importIndex); + if (!mg_.declareImport(Move(sig), importIndex)) + return false; + if (!importMap_.add(p, ImportDescriptor(name, **lifoSig), *importIndex)) + return false; + MOZ_ASSERT(module_->imports.length() == *importIndex); + return module_->imports.emplaceBack(ffiIndex); } bool tryConstantAccess(uint64_t start, uint64_t width) { MOZ_ASSERT(UINT64_MAX - start > width); - uint64_t end = start + width; - if (end > uint64_t(INT32_MAX) + 1) + uint64_t len = start + width; + if (len > uint64_t(INT32_MAX) + 1) return false; - module().requireHeapLengthToBeAtLeast(end); + len = RoundUpToNextValidAsmJSHeapLength(len); + if (len > module_->minHeapLength) + module_->minHeapLength = len; return true; } - bool usesSharedMemory() const { - return atomicsPresent_; - } - // Error handling. bool hasAlreadyFailed() const { return !!errorString_; @@ -2278,7 +2082,7 @@ class MOZ_STACK_CLASS ModuleValidator MOZ_ASSERT(errorOffset_ == UINT32_MAX); MOZ_ASSERT(str); errorOffset_ = offset; - errorString_ = make_string_copy(str); + errorString_ = DuplicateString(str); return false; } @@ -2329,16 +2133,6 @@ class MOZ_STACK_CLASS ModuleValidator return false; } - // Read-only interface - ExclusiveContext* cx() const { return cx_; } - ParseNode* moduleFunctionNode() const { return moduleFunctionNode_; } - PropertyName* moduleFunctionName() const { return moduleFunctionName_; } - ModuleGenerator& mg() { return mg_; } - AsmJSModule& module() const { return *module_; } - AsmJSParser& parser() const { return parser_; } - TokenStream& tokenStream() const { return parser_.tokenStream; } - bool supportsSimd() const { return supportsSimd_; } - unsigned numArrayViews() const { return arrayViews_.length(); } @@ -2359,13 +2153,13 @@ class MOZ_STACK_CLASS ModuleValidator } const Global* lookupGlobal(PropertyName* name) const { - if (GlobalMap::Ptr p = globals_.lookup(name)) + if (GlobalMap::Ptr p = globalMap_.lookup(name)) return p->value(); return nullptr; } Func* lookupFunction(PropertyName* name) { - if (GlobalMap::Ptr p = globals_.lookup(name)) { + if (GlobalMap::Ptr p = globalMap_.lookup(name)) { Global* value = p->value(); if (value->which() == Global::Function) return functions_[value->funcIndex()]; @@ -2397,9 +2191,7 @@ class MOZ_STACK_CLASS ModuleValidator bool startFunctionBodies() { if (atomicsPresent_) { -#if defined(ENABLE_SHARED_ARRAY_BUFFER) - module().setViewsAreShared(); -#else +#if !defined(ENABLE_SHARED_ARRAY_BUFFER) return failOffset(parser_.tokenStream.currentToken().pos.begin, "shared memory and atomics not supported by this build"); #endif @@ -2409,9 +2201,49 @@ class MOZ_STACK_CLASS ModuleValidator bool finishFunctionBodies() { return mg_.finishFuncs(); } -}; + bool finish(MutableHandle moduleObj, SlowFunctionVector* slowFuncs) { + HeapUsage heap = arrayViews_.empty() + ? HeapUsage::None + : atomicsPresent_ + ? HeapUsage::Shared + : HeapUsage::Unshared; -} // namespace + auto muted = MutedErrorsBool(parser_.ss->mutedErrors()); + + CacheableChars filename; + if (parser_.ss->filename()) { + filename = DuplicateString(parser_.ss->filename()); + if (!filename) + return false; + } + + CacheableTwoByteChars displayURL; + if (parser_.ss->hasDisplayURL()) { + displayURL = DuplicateString(parser_.ss->displayURL()); + if (!displayURL) + return false; + } + + uint32_t endBeforeCurly = tokenStream().currentToken().pos.end; + module_->srcLength = endBeforeCurly - module_->srcStart; + + TokenPos pos; + JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand)); + uint32_t endAfterCurly = pos.end; + module_->srcLengthWithRightBrace = endAfterCurly - module_->srcStart; + + UniqueModuleData base; + UniqueStaticLinkData link; + if (!mg_.finish(heap, muted, Move(filename), Move(displayURL), &base, &link, slowFuncs)) + return false; + + moduleObj.set(WasmModuleObject::create(cx_)); + if (!moduleObj) + return false; + + return moduleObj->init(cx_->new_(Move(base), Move(link), Move(module_))); + } +}; /*****************************************************************************/ // Numeric literal utilities @@ -2544,6 +2376,7 @@ IsSimdLiteral(ModuleValidator& m, ParseNode* pn) case AsmJSSimdType_bool32x4: if (!IsLiteralInt(m, arg, &_)) return false; + MOZ_FALLTHROUGH; case AsmJSSimdType_float32x4: if (!IsNumericNonFloatLiteral(arg)) return false; @@ -2729,6 +2562,7 @@ class MOZ_STACK_CLASS FunctionValidator ParseNode* fn_; FunctionGenerator fg_; + Encoder encoder_; LocalMap locals_; LabelMap labels_; @@ -2746,19 +2580,19 @@ class MOZ_STACK_CLASS FunctionValidator {} ModuleValidator& m() const { return m_; } - const AsmJSModule& module() const { return m_.module(); } - FuncIR& funcIR() const { return fg_.func(); } ExclusiveContext* cx() const { return m_.cx(); } ParseNode* fn() const { return fn_; } bool init(PropertyName* name, unsigned line, unsigned column) { - return locals_.init() && - labels_.init() && - m_.mg().startFunc(name, line, column, &fg_); + UniqueBytecode recycled; + return m_.mg().startFunc(name, line, column, &recycled, &fg_) && + encoder_.init(Move(recycled)) && + locals_.init() && + labels_.init(); } bool finish(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime) { - return m_.mg().finishFunc(funcIndex, sig, generateTime, &fg_); + return m_.mg().finishFunc(funcIndex, sig, encoder().finish(), generateTime, &fg_); } bool fail(ParseNode* pn, const char* str) { @@ -2788,7 +2622,7 @@ class MOZ_STACK_CLASS FunctionValidator bool addVariable(ParseNode* pn, PropertyName* name, ValType type) { return addFormal(pn, name, type) && - funcIR().addVariable(type); + fg_.addVariable(type); } /****************************** For consistency of returns in a function */ @@ -2841,73 +2675,65 @@ class MOZ_STACK_CLASS FunctionValidator size_t numLocals() const { return locals_.count(); } + /************************************************* Packing interface */ + Encoder& encoder() { return encoder_; } + bool noteLineCol(ParseNode* pn) { uint32_t line, column; m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column); - return funcIR().addSourceCoords(line, column); - } - - /************************************************* Packing interface */ - - bool startedPacking() const { - return funcIR().size() != 0; + return fg_.addSourceCoords(encoder().bytecodeOffset(), line, column); } template - size_t writeOp(T op) { + MOZ_WARN_UNUSED_RESULT + bool writeOp(T op) { static_assert(sizeof(T) == sizeof(uint8_t), "opcodes must be uint8"); - return funcIR().writeU8(uint8_t(op)); + return encoder().writeU8(uint8_t(op)); } - void writeDebugCheckPoint() { + MOZ_WARN_UNUSED_RESULT + bool writeDebugCheckPoint() { #ifdef DEBUG - writeOp(Stmt::DebugCheckPoint); + return writeOp(Stmt::DebugCheckPoint); #endif + return true; } - size_t writeU8(uint8_t u) { - return funcIR().writeU8(u); + MOZ_WARN_UNUSED_RESULT + bool writeU8(uint8_t u) { + return encoder().writeU8(u); } - size_t writeU32(uint32_t u) { - return funcIR().writeU32(u); + MOZ_WARN_UNUSED_RESULT + bool writeU32(uint32_t u) { + return encoder().writeU32(u); } - size_t writeI32(int32_t u) { - return funcIR().writeI32(u); + MOZ_WARN_UNUSED_RESULT + bool writeI32(int32_t u) { + return encoder().writeI32(u); + } + MOZ_WARN_UNUSED_RESULT + bool writeInt32Lit(int32_t i) { + return writeOp(I32::Literal) && encoder().writeI32(i); } - void writeInt32Lit(int32_t i) { - writeOp(I32::Literal); - funcIR().writeI32(i); - } - - void writeLit(NumLit lit) { + MOZ_WARN_UNUSED_RESULT + bool writeLit(NumLit lit) { switch (lit.which()) { case NumLit::Fixnum: case NumLit::NegativeInt: case NumLit::BigUnsigned: - writeInt32Lit(lit.toInt32()); - return; + return writeInt32Lit(lit.toInt32()); case NumLit::Float: - writeOp(F32::Literal); - funcIR().writeF32(lit.toFloat()); - return; + return writeOp(F32::Literal) && encoder().writeF32(lit.toFloat()); case NumLit::Double: - writeOp(F64::Literal); - funcIR().writeF64(lit.toDouble()); - return; + return writeOp(F64::Literal) && encoder().writeF64(lit.toDouble()); case NumLit::Int32x4: - writeOp(I32X4::Literal); - funcIR().writeI32X4(lit.simdValue().asInt32x4()); - return; + return writeOp(I32X4::Literal) && encoder().writeI32X4(lit.simdValue().asInt32x4()); case NumLit::Float32x4: - writeOp(F32X4::Literal); - funcIR().writeF32X4(lit.simdValue().asFloat32x4()); - return; + return writeOp(F32X4::Literal) && encoder().writeF32X4(lit.simdValue().asFloat32x4()); case NumLit::Bool32x4: // Boolean vectors use the Int32x4 memory representation. - writeOp(B32X4::Literal); - funcIR().writeI32X4(lit.simdValue().asInt32x4()); - return; + return writeOp(B32X4::Literal) && encoder().writeI32X4(lit.simdValue().asInt32x4()); case NumLit::OutOfRangeInt: break; } @@ -2917,37 +2743,47 @@ class MOZ_STACK_CLASS FunctionValidator template void patchOp(size_t pos, T stmt) { static_assert(sizeof(T) == sizeof(uint8_t), "opcodes must be uint8"); - funcIR().patchU8(pos, uint8_t(stmt)); + encoder().patchU8(pos, uint8_t(stmt)); } void patchU8(size_t pos, uint8_t u8) { - funcIR().patchU8(pos, u8); + encoder().patchU8(pos, u8); } template void patch32(size_t pos, T val) { static_assert(sizeof(T) == sizeof(uint32_t), "patch32 is used for 4-bytes long ops"); - funcIR().patch32(pos, val); + encoder().patch32(pos, val); } void patchSig(size_t pos, const LifoSig* ptr) { - funcIR().patchSig(pos, ptr); + encoder().patchSig(pos, ptr); } - size_t tempU8() { - return funcIR().writeU8(uint8_t(Stmt::Bad)); + MOZ_WARN_UNUSED_RESULT + bool tempU8(size_t* offset) { + return encoder().writeU8(uint8_t(Stmt::Bad), offset); } - size_t tempOp() { - return tempU8(); + MOZ_WARN_UNUSED_RESULT + bool tempOp(size_t* offset) { + return tempU8(offset); } - size_t temp32() { - size_t ret = funcIR().writeU8(uint8_t(Stmt::Bad)); - for (size_t i = 1; i < 4; i++) - funcIR().writeU8(uint8_t(Stmt::Bad)); - return ret; + MOZ_WARN_UNUSED_RESULT + bool temp32(size_t* offset) { + if (!encoder().writeU8(uint8_t(Stmt::Bad), offset)) + return false; + for (size_t i = 1; i < 4; i++) { + if (!encoder().writeU8(uint8_t(Stmt::Bad))) + return false; + } + return true; } - size_t tempPtr() { - size_t ret = funcIR().writeU8(uint8_t(Stmt::Bad)); - for (size_t i = 1; i < sizeof(intptr_t); i++) - funcIR().writeU8(uint8_t(Stmt::Bad)); - return ret; + MOZ_WARN_UNUSED_RESULT + bool tempPtr(size_t* offset) { + if (!encoder().writeU8(uint8_t(Stmt::Bad), offset)) + return false; + for (size_t i = 1; i < sizeof(intptr_t); i++) { + if (!encoder().writeU8(uint8_t(Stmt::Bad))) + return false; + } + return true; } /************************************************** End of build helpers */ }; @@ -2972,9 +2808,9 @@ CheckModuleLevelName(ModuleValidator& m, ParseNode* usepn, PropertyName* name) return false; if (name == m.moduleFunctionName() || - name == m.module().globalArgumentName() || - name == m.module().importArgumentName() || - name == m.module().bufferArgumentName() || + name == m.globalArgumentName() || + name == m.importArgumentName() || + name == m.bufferArgumentName() || m.lookupGlobal(name)) { return m.failName(usepn, "duplicate name '%s' not allowed", name); @@ -3036,17 +2872,17 @@ CheckModuleArguments(ModuleValidator& m, ParseNode* fn) return m.fail(fn, "asm.js modules takes at most 3 argument"); PropertyName* arg1Name = nullptr; - if (numFormals >= 1 && !CheckModuleArgument(m, arg1, &arg1Name)) + if (arg1 && !CheckModuleArgument(m, arg1, &arg1Name)) return false; m.initGlobalArgumentName(arg1Name); PropertyName* arg2Name = nullptr; - if (numFormals >= 2 && !CheckModuleArgument(m, arg2, &arg2Name)) + if (arg2 && !CheckModuleArgument(m, arg2, &arg2Name)) return false; m.initImportArgumentName(arg2Name); PropertyName* arg3Name = nullptr; - if (numFormals >= 3 && !CheckModuleArgument(m, arg3, &arg3Name)) + if (arg3 && !CheckModuleArgument(m, arg3, &arg3Name)) return false; m.initBufferArgumentName(arg3Name); @@ -3102,6 +2938,7 @@ CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode, ValType* coerce case PNK_CALL: { if (IsCoercionCall(m, coercionNode, coerceTo, coercedExpr)) return true; + break; } default:; } @@ -3119,7 +2956,7 @@ CheckGlobalVariableImportExpr(ModuleValidator& m, PropertyName* varName, ValType ParseNode* base = DotBase(coercedExpr); PropertyName* field = DotMember(coercedExpr); - PropertyName* importName = m.module().importArgumentName(); + PropertyName* importName = m.importArgumentName(); if (!importName) return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter"); if (!IsUseOfName(base, importName)) @@ -3181,11 +3018,11 @@ CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* buf static bool CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) { - PropertyName* globalName = m.module().globalArgumentName(); + PropertyName* globalName = m.globalArgumentName(); if (!globalName) return m.fail(newExpr, "cannot create array view without an asm.js global parameter"); - PropertyName* bufferName = m.module().bufferArgumentName(); + PropertyName* bufferName = m.bufferArgumentName(); if (!bufferName) return m.fail(newExpr, "cannot create array view without an asm.js heap parameter"); @@ -3339,7 +3176,7 @@ CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initN ParseNode* global = DotBase(base); PropertyName* mathOrAtomicsOrSimd = DotMember(base); - PropertyName* globalName = m.module().globalArgumentName(); + PropertyName* globalName = m.globalArgumentName(); if (!globalName) return m.fail(base, "import statement requires the module have a stdlib parameter"); @@ -3363,7 +3200,7 @@ CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initN if (!base->isKind(PNK_NAME)) return m.fail(base, "expected name of variable or parameter"); - if (base->name() == m.module().globalArgumentName()) { + if (base->name() == m.globalArgumentName()) { if (field == m.cx()->names().NaN) return m.addGlobalConstant(varName, GenericNaN(), field); if (field == m.cx()->names().Infinity) @@ -3376,7 +3213,7 @@ CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initN return m.failName(initNode, "'%s' is not a standard constant or typed array name", field); } - if (base->name() == m.module().importArgumentName()) + if (base->name() == m.importArgumentName()) return m.addFFI(varName, field); const ModuleValidator::Global* global = m.lookupGlobal(base->name()); @@ -3556,16 +3393,14 @@ CheckFinalReturn(FunctionValidator& f, ParseNode* lastNonEmptyStmt) { if (!f.hasAlreadyReturned()) { f.setReturnedType(ExprType::Void); - f.writeOp(Stmt::Ret); - return true; + return f.writeOp(Stmt::Ret); } if (!lastNonEmptyStmt->isKind(PNK_RETURN)) { if (!IsVoid(f.returnedType())) return f.fail(lastNonEmptyStmt, "void incompatible with previous return type"); - f.writeOp(Stmt::Ret); - return true; + return f.writeOp(Stmt::Ret); } return true; @@ -3575,11 +3410,10 @@ template static bool SetLocal(FunctionValidator& f, Stmt exprStmt, T setLocal, NumLit lit) { - f.writeOp(exprStmt); - f.writeOp(setLocal); - f.writeU32(f.numLocals()); - f.writeLit(lit); - return true; + return f.writeOp(exprStmt) && + f.writeOp(setLocal) && + f.writeU32(f.numLocals()) && + f.writeLit(lit); } static bool @@ -3668,9 +3502,8 @@ CheckNumericLiteral(FunctionValidator& f, ParseNode* num, Type* type) NumLit lit = ExtractNumericLiteral(f.m(), num); if (!lit.valid()) return f.fail(num, "numeric literal out of representable integer range"); - f.writeLit(lit); *type = Type::lit(lit); - return true; + return f.writeLit(lit); } static bool @@ -3680,15 +3513,16 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type) if (const FunctionValidator::Local* local = f.lookupLocal(name)) { switch (local->type) { - case ValType::I32: f.writeOp(I32::GetLocal); break; + case ValType::I32: if (!f.writeOp(I32::GetLocal)) return false; break; case ValType::I64: MOZ_CRASH("no int64 in asm.js"); - case ValType::F32: f.writeOp(F32::GetLocal); break; - case ValType::F64: f.writeOp(F64::GetLocal); break; - case ValType::I32x4: f.writeOp(I32X4::GetLocal); break; - case ValType::F32x4: f.writeOp(F32X4::GetLocal); break; - case ValType::B32x4: f.writeOp(B32X4::GetLocal); break; + case ValType::F32: if (!f.writeOp(F32::GetLocal)) return false; break; + case ValType::F64: if (!f.writeOp(F64::GetLocal)) return false; break; + case ValType::I32x4: if (!f.writeOp(I32X4::GetLocal)) return false; break; + case ValType::F32x4: if (!f.writeOp(F32X4::GetLocal)) return false; break; + case ValType::B32x4: if (!f.writeOp(B32X4::GetLocal)) return false; break; } - f.writeU32(local->slot); + if (!f.writeU32(local->slot)) + return false; *type = Type::var(local->type); return true; } @@ -3696,25 +3530,23 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type) if (const ModuleValidator::Global* global = f.lookupGlobal(name)) { switch (global->which()) { case ModuleValidator::Global::ConstantLiteral: - f.writeLit(global->constLiteralValue()); *type = global->varOrConstType(); - break; + return f.writeLit(global->constLiteralValue()); case ModuleValidator::Global::ConstantImport: case ModuleValidator::Global::Variable: { - switch (global->varOrConstType().which()) { - case Type::Int: f.writeOp(I32::GetGlobal); break; - case Type::Double: f.writeOp(F64::GetGlobal); break; - case Type::Float: f.writeOp(F32::GetGlobal); break; - case Type::Int32x4: f.writeOp(I32X4::GetGlobal); break; - case Type::Float32x4: f.writeOp(F32X4::GetGlobal); break; - case Type::Bool32x4: f.writeOp(B32X4::GetGlobal); break; + *type = global->varOrConstType(); + switch (type->which()) { + case Type::Int: if (!f.writeOp(I32::GetGlobal)) return false; break; + case Type::Double: if (!f.writeOp(F64::GetGlobal)) return false; break; + case Type::Float: if (!f.writeOp(F32::GetGlobal)) return false; break; + case Type::Int32x4: if (!f.writeOp(I32X4::GetGlobal)) return false; break; + case Type::Float32x4: if (!f.writeOp(F32X4::GetGlobal)) return false; break; + case Type::Bool32x4: if (!f.writeOp(B32X4::GetGlobal)) return false; break; default: MOZ_CRASH("unexpected global type"); } - - f.writeU32(global->varOrConstGlobalDataOffset()); - f.writeU8(uint8_t(global->isConst())); - *type = global->varOrConstType(); - break; + if (!f.writeU32(global->varOrConstGlobalDataOffset())) + return false; + return f.writeU8(uint8_t(global->isConst())); } case ModuleValidator::Global::Function: case ModuleValidator::Global::FFI: @@ -3725,9 +3557,9 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type) case ModuleValidator::Global::ArrayViewCtor: case ModuleValidator::Global::SimdCtor: case ModuleValidator::Global::SimdOperation: - return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name); + break; } - return true; + return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name); } return f.failName(varRef, "'%s' not found in local or asm.js module scope", name); @@ -3759,7 +3591,7 @@ FoldMaskedArrayIndex(FunctionValidator& f, ParseNode** indexExpr, int32_t* mask, // constraint. The unsigned maximum of a masked index is the mask // itself, so check that the mask is not negative and compare the mask // to the known minimum heap length. - if (int32_t(mask2) >= 0 && mask2 < f.m().module().minHeapLength()) + if (int32_t(mask2) >= 0 && mask2 < f.m().minHeapLength()) *needsBoundsCheck = NO_BOUNDS_CHECK; *mask &= mask2; *indexExpr = indexNode; @@ -3794,8 +3626,7 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr *mask = NoMask; *needsBoundsCheck = NO_BOUNDS_CHECK; - f.writeInt32Lit(byteOffset); - return true; + return f.writeInt32Lit(byteOffset); } // Mask off the low bits to account for the clearing effect of a right shift @@ -3858,7 +3689,9 @@ static bool CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr, Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask) { - size_t prepareAt = f.tempOp(); + size_t prepareAt; + if (!f.tempOp(&prepareAt)) + return false; if (!CheckArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask)) return false; @@ -3867,11 +3700,10 @@ CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* // a shift of zero or a SIMD access. if (*mask != NoMask) { f.patchOp(prepareAt, I32::BitAnd); - f.writeInt32Lit(*mask); - } else { - f.patchOp(prepareAt, I32::Id); + return f.writeInt32Lit(*mask); } + f.patchOp(prepareAt, I32::Id); return true; } @@ -3882,8 +3714,10 @@ CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type) NeedsBoundsCheck needsBoundsCheck; int32_t mask; - size_t opcodeAt = f.tempOp(); - size_t needsBoundsCheckAt = f.tempU8(); + size_t opcodeAt; + size_t needsBoundsCheckAt; + if (!f.tempOp(&opcodeAt) || !f.tempU8(&needsBoundsCheckAt)) + return false; if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &needsBoundsCheck, &mask)) return false; @@ -3926,8 +3760,10 @@ CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type) static bool CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type) { - size_t opcodeAt = f.tempOp(); - size_t needsBoundsCheckAt = f.tempU8(); + size_t opcodeAt; + size_t needsBoundsCheckAt; + if (!f.tempOp(&opcodeAt) || !f.tempU8(&needsBoundsCheckAt)) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -4000,8 +3836,10 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type { RootedPropertyName name(f.cx(), lhs->name()); - size_t opcodeAt = f.tempOp(); - size_t indexAt = f.temp32(); + size_t opcodeAt; + size_t indexAt; + if (!f.tempOp(&opcodeAt) || !f.temp32(&indexAt)) + return false; Type rhsType; if (!CheckExpr(f, rhs, &rhsType)) @@ -4081,7 +3919,8 @@ CheckMathIMul(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* lhs = CallArgList(call); ParseNode* rhs = NextNode(lhs); - f.writeOp(I32::Mul); + if (!f.writeOp(I32::Mul)) + return false; Type lhsType; if (!CheckExpr(f, lhs, &lhsType)) @@ -4106,7 +3945,8 @@ CheckMathClz32(FunctionValidator& f, ParseNode* call, Type* type) if (CallArgListLength(call) != 1) return f.fail(call, "Math.clz32 must be passed 1 argument"); - f.writeOp(I32::Clz); + if (!f.writeOp(I32::Clz)) + return false; ParseNode* arg = CallArgList(call); @@ -4129,7 +3969,9 @@ CheckMathAbs(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* arg = CallArgList(call); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type argType; if (!CheckExpr(f, arg, &argType)) @@ -4164,7 +4006,9 @@ CheckMathSqrt(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* arg = CallArgList(call); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type argType; if (!CheckExpr(f, arg, &argType)) @@ -4191,8 +4035,10 @@ CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* typ if (CallArgListLength(callNode) < 2) return f.fail(callNode, "Math.min/max must be passed at least 2 arguments"); - size_t opcodeAt = f.tempOp(); - size_t numArgsAt = f.tempU8(); + size_t opcodeAt; + size_t numArgsAt; + if (!f.tempOp(&opcodeAt) || !f.tempU8(&numArgsAt)) + return false; ParseNode* firstArg = CallArgList(callNode); Type firstType; @@ -4239,12 +4085,12 @@ CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNod if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask)) return false; - // Atomic accesses may be made on shared integer arrays only. - // The global will be sane, CheckArrayAccess checks it. const ModuleValidator::Global* global = f.lookupGlobal(viewName->name()); - if (global->which() != ModuleValidator::Global::ArrayView || !f.m().module().isSharedView()) - return f.fail(viewName, "base of array access must be a shared typed array view name"); + if (global->which() != ModuleValidator::Global::ArrayView) + return f.fail(viewName, "base of array access must be a typed array view"); + + MOZ_ASSERT(f.m().atomicsPresent()); switch (*viewType) { case Scalar::Int8: @@ -4267,9 +4113,17 @@ CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type) if (CallArgListLength(call) != 0) return f.fail(call, "Atomics.fence must be passed 0 arguments"); - f.writeOp(Stmt::AtomicsFence); *type = Type::Void; - return true; + return f.writeOp(Stmt::AtomicsFence); +} + +template +static bool +WriteAtomicOperator(FunctionValidator& f, T opcode, size_t* needsBoundsCheckAt, size_t* viewTypeAt) +{ + return f.writeOp(opcode) && + f.tempU8(needsBoundsCheckAt) && + f.tempU8(viewTypeAt); } static bool @@ -4281,9 +4135,10 @@ CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* arrayArg = CallArgList(call); ParseNode* indexArg = NextNode(arrayArg); - f.writeOp(I32::AtomicsLoad); - size_t needsBoundsCheckAt = f.tempU8(); - size_t viewTypeAt = f.tempU8(); + size_t needsBoundsCheckAt; + size_t viewTypeAt; + if (!WriteAtomicOperator(f, I32::AtomicsLoad, &needsBoundsCheckAt, &viewTypeAt)) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -4308,9 +4163,10 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* indexArg = NextNode(arrayArg); ParseNode* valueArg = NextNode(indexArg); - f.writeOp(I32::AtomicsStore); - size_t needsBoundsCheckAt = f.tempU8(); - size_t viewTypeAt = f.tempU8(); + size_t needsBoundsCheckAt; + size_t viewTypeAt; + if (!WriteAtomicOperator(f, I32::AtomicsStore, &needsBoundsCheckAt, &viewTypeAt)) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -4342,10 +4198,12 @@ CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op ParseNode* indexArg = NextNode(arrayArg); ParseNode* valueArg = NextNode(indexArg); - f.writeOp(I32::AtomicsBinOp); - size_t needsBoundsCheckAt = f.tempU8(); - size_t viewTypeAt = f.tempU8(); - f.writeU8(uint8_t(op)); + size_t needsBoundsCheckAt; + size_t viewTypeAt; + if (!WriteAtomicOperator(f, I32::AtomicsBinOp, &needsBoundsCheckAt, &viewTypeAt)) + return false; + if (!f.writeU8(uint8_t(op))) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -4379,9 +4237,8 @@ CheckAtomicsIsLockFree(FunctionValidator& f, ParseNode* call, Type* type) if (!IsLiteralInt(f.m(), sizeArg, &size)) return f.fail(sizeArg, "Atomics.isLockFree requires an integer literal argument"); - f.writeInt32Lit(AtomicOperations::isLockfree(size)); *type = Type::Int; - return true; + return f.writeInt32Lit(AtomicOperations::isLockfree(size)); } static bool @@ -4395,9 +4252,10 @@ CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* oldValueArg = NextNode(indexArg); ParseNode* newValueArg = NextNode(oldValueArg); - f.writeOp(I32::AtomicsCompareExchange); - size_t needsBoundsCheckAt = f.tempU8(); - size_t viewTypeAt = f.tempU8(); + size_t needsBoundsCheckAt; + size_t viewTypeAt; + if (!WriteAtomicOperator(f, I32::AtomicsCompareExchange, &needsBoundsCheckAt, &viewTypeAt)) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -4436,9 +4294,10 @@ CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* indexArg = NextNode(arrayArg); ParseNode* valueArg = NextNode(indexArg); - f.writeOp(I32::AtomicsExchange); - size_t needsBoundsCheckAt = f.tempU8(); - size_t viewTypeAt = f.tempU8(); + size_t needsBoundsCheckAt; + size_t viewTypeAt; + if (!WriteAtomicOperator(f, I32::AtomicsExchange, &needsBoundsCheckAt, &viewTypeAt)) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -4570,20 +4429,22 @@ CheckInternalCall(FunctionValidator& f, ParseNode* callNode, PropertyName* calle ExprType ret, Type* type) { switch (ret) { - case ExprType::Void: f.writeOp(Stmt::CallInternal); break; - case ExprType::I32: f.writeOp(I32::CallInternal); break; + case ExprType::Void: if (!f.writeOp(Stmt::CallInternal)) return false; break; + case ExprType::I32: if (!f.writeOp(I32::CallInternal)) return false; break; case ExprType::I64: MOZ_CRASH("no int64 in asm.js"); - case ExprType::F32: f.writeOp(F32::CallInternal); break; - case ExprType::F64: f.writeOp(F64::CallInternal); break; - case ExprType::I32x4: f.writeOp(I32X4::CallInternal); break; - case ExprType::F32x4: f.writeOp(F32X4::CallInternal); break; - case ExprType::B32x4: f.writeOp(B32X4::CallInternal); break; + case ExprType::F32: if (!f.writeOp(F32::CallInternal)) return false; break; + case ExprType::F64: if (!f.writeOp(F64::CallInternal)) return false; break; + case ExprType::I32x4: if (!f.writeOp(I32X4::CallInternal)) return false; break; + case ExprType::F32x4: if (!f.writeOp(F32X4::CallInternal)) return false; break; + case ExprType::B32x4: if (!f.writeOp(B32X4::CallInternal)) return false; break; } // Function's index, to find out the function's entry - size_t funcIndexAt = f.temp32(); + size_t funcIndexAt; // Function's signature in lifo - size_t sigAt = f.tempPtr(); + size_t sigAt; + if (!f.temp32(&funcIndexAt) || !f.tempPtr(&sigAt)) + return false; if (!f.noteLineCol(callNode)) return false; @@ -4661,22 +4522,26 @@ CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* // Opcode switch (ret) { - case ExprType::Void: f.writeOp(Stmt::CallIndirect); break; - case ExprType::I32: f.writeOp(I32::CallIndirect); break; + case ExprType::Void: if (!f.writeOp(Stmt::CallIndirect)) return false; break; + case ExprType::I32: if (!f.writeOp(I32::CallIndirect)) return false; break; case ExprType::I64: MOZ_CRASH("no in64 in asm.js"); - case ExprType::F32: f.writeOp(F32::CallIndirect); break; - case ExprType::F64: f.writeOp(F64::CallIndirect); break; - case ExprType::I32x4: f.writeOp(I32X4::CallIndirect); break; - case ExprType::F32x4: f.writeOp(F32X4::CallIndirect); break; - case ExprType::B32x4: f.writeOp(B32X4::CallIndirect); break; + case ExprType::F32: if (!f.writeOp(F32::CallIndirect)) return false; break; + case ExprType::F64: if (!f.writeOp(F64::CallIndirect)) return false; break; + case ExprType::I32x4: if (!f.writeOp(I32X4::CallIndirect)) return false; break; + case ExprType::F32x4: if (!f.writeOp(F32X4::CallIndirect)) return false; break; + case ExprType::B32x4: if (!f.writeOp(B32X4::CallIndirect)) return false; break; } - // Table's mask - f.writeU32(mask); + if (!f.writeU32(mask)) + return false; // Global data offset - size_t globalDataOffsetAt = f.temp32(); + size_t globalDataOffsetAt; + if (!f.temp32(&globalDataOffsetAt)) + return false; // Signature - size_t sigAt = f.tempPtr(); + size_t sigAt; + if (!f.tempPtr(&sigAt)) + return false; if (!f.noteLineCol(callNode)) return false; @@ -4725,20 +4590,24 @@ CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprT return f.fail(callNode, "FFI calls can't return SIMD values"); switch (ret) { - case ExprType::Void: f.writeOp(Stmt::CallImport); break; - case ExprType::I32: f.writeOp(I32::CallImport); break; + case ExprType::Void: if (!f.writeOp(Stmt::CallImport)) return false; break; + case ExprType::I32: if (!f.writeOp(I32::CallImport)) return false; break; case ExprType::I64: MOZ_CRASH("no int64 in asm.js"); - case ExprType::F32: f.writeOp(F32::CallImport); break; - case ExprType::F64: f.writeOp(F64::CallImport); break; - case ExprType::I32x4: f.writeOp(I32X4::CallImport); break; - case ExprType::F32x4: f.writeOp(F32X4::CallImport); break; - case ExprType::B32x4: f.writeOp(B32X4::CallImport); break; + case ExprType::F32: if (!f.writeOp(F32::CallImport)) return false; break; + case ExprType::F64: if (!f.writeOp(F64::CallImport)) return false; break; + case ExprType::I32x4: if (!f.writeOp(I32X4::CallImport)) return false; break; + case ExprType::F32x4: if (!f.writeOp(F32X4::CallImport)) return false; break; + case ExprType::B32x4: if (!f.writeOp(B32X4::CallImport)) return false; break; } // Global data offset - size_t offsetAt = f.temp32(); + size_t offsetAt; + if (!f.temp32(&offsetAt)) + return false; // Pointer to the import's signature in the module's lifo - size_t sigAt = f.tempPtr(); + size_t sigAt; + if (!f.tempPtr(&sigAt)) + return false; if (!f.noteLineCol(callNode)) return false; @@ -4795,7 +4664,9 @@ CheckCoercionArg(FunctionValidator& f, ParseNode* arg, ValType expected, Type* t if (arg->isKind(PNK_CALL)) return CheckCoercedCall(f, arg, ret, type); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type argType; if (!CheckExpr(f, arg, &argType)) @@ -4882,7 +4753,9 @@ CheckMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltin if (actualArity != arity) return f.failf(callNode, "call passed %u arguments, expected %u", actualArity, arity); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; if (!f.noteLineCol(callNode)) return false; @@ -4958,7 +4831,9 @@ CheckSimdCallArgsPatchable(FunctionValidator& f, ParseNode* call, unsigned expec for (size_t i = 0; i < numArgs; i++, arg = NextNode(arg)) { MOZ_ASSERT(!!arg); Type argType; - size_t patchAt = f.tempOp(); + size_t patchAt; + if (!f.tempOp(&patchAt)) + return false; if (!CheckExpr(f, arg, &argType)) return false; if (!checkArg(f, arg, i, argType, patchAt)) @@ -5171,13 +5046,13 @@ class CheckSimdReplaceLaneArgs } // namespace -static void +static bool SwitchPackOp(FunctionValidator& f, AsmJSSimdType type, I32X4 i32x4, F32X4 f32x4, B32X4 b32x4) { switch (type) { - case AsmJSSimdType_int32x4: f.writeOp(i32x4); return; - case AsmJSSimdType_float32x4: f.writeOp(f32x4); return; - case AsmJSSimdType_bool32x4: f.writeOp(b32x4); return; + case AsmJSSimdType_int32x4: return f.writeOp(i32x4); + case AsmJSSimdType_float32x4: return f.writeOp(f32x4); + case AsmJSSimdType_bool32x4: return f.writeOp(b32x4); } MOZ_CRASH("unexpected simd type"); } @@ -5186,8 +5061,10 @@ static bool CheckSimdUnary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, MSimdUnaryArith::Operation op, Type* type) { - SwitchPackOp(f, opType, I32X4::Unary, F32X4::Unary, B32X4::Unary); - f.writeU8(uint8_t(op)); + if (!SwitchPackOp(f, opType, I32X4::Unary, F32X4::Unary, B32X4::Unary)) + return false; + if (!f.writeU8(uint8_t(op))) + return false; if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType))) return false; *type = opType; @@ -5199,7 +5076,8 @@ inline bool CheckSimdBinaryGuts(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, OpKind op, Type* type) { - f.writeU8(uint8_t(op)); + if (!f.writeU8(uint8_t(op))) + return false; if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType))) return false; *type = opType; @@ -5210,16 +5088,16 @@ static bool CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, MSimdBinaryArith::Operation op, Type* type) { - SwitchPackOp(f, opType, I32X4::Binary, F32X4::Binary, B32X4::Binary); - return CheckSimdBinaryGuts(f, call, opType, op, type); + return SwitchPackOp(f, opType, I32X4::Binary, F32X4::Binary, B32X4::Binary) && + CheckSimdBinaryGuts(f, call, opType, op, type); } static bool CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, MSimdBinaryBitwise::Operation op, Type* type) { - SwitchPackOp(f, opType, I32X4::BinaryBitwise, F32X4::Bad, B32X4::BinaryBitwise); - return CheckSimdBinaryGuts(f, call, opType, op, type); + return SwitchPackOp(f, opType, I32X4::BinaryBitwise, F32X4::Bad, B32X4::BinaryBitwise) && + CheckSimdBinaryGuts(f, call, opType, op, type); } static bool @@ -5228,15 +5106,18 @@ CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, { switch (opType) { case AsmJSSimdType_int32x4: - f.writeOp(B32X4::BinaryCompI32X4); + if (!f.writeOp(B32X4::BinaryCompI32X4)) + return false; break; case AsmJSSimdType_float32x4: - f.writeOp(B32X4::BinaryCompF32X4); + if (!f.writeOp(B32X4::BinaryCompF32X4)) + return false; break; case AsmJSSimdType_bool32x4: MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Can't compare boolean vectors"); } - f.writeU8(uint8_t(op)); + if (!f.writeU8(uint8_t(op))) + return false; if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType))) return false; *type = Type::Bool32x4; @@ -5247,8 +5128,8 @@ static bool CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, MSimdShift::Operation op, Type* type) { - f.writeOp(I32X4::BinaryShift); - f.writeU8(uint8_t(op)); + if (!f.writeOp(I32X4::BinaryShift) || !f.writeU8(uint8_t(op))) + return false; if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType))) return false; *type = Type::Int32x4; @@ -5260,15 +5141,18 @@ CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType { switch (opType) { case AsmJSSimdType_int32x4: - f.writeOp(I32::I32X4ExtractLane); + if (!f.writeOp(I32::I32X4ExtractLane)) + return false; *type = Type::Signed; break; case AsmJSSimdType_float32x4: - f.writeOp(F32::F32X4ExtractLane); + if (!f.writeOp(F32::F32X4ExtractLane)) + return false; *type = Type::Float; break; case AsmJSSimdType_bool32x4: - f.writeOp(I32::B32X4ExtractLane); + if (!f.writeOp(I32::B32X4ExtractLane)) + return false; *type = Type::Int; break; } @@ -5278,7 +5162,8 @@ CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType static bool CheckSimdReplaceLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type) { - SwitchPackOp(f, opType, I32X4::ReplaceLane, F32X4::ReplaceLane, B32X4::ReplaceLane); + if (!SwitchPackOp(f, opType, I32X4::ReplaceLane, F32X4::ReplaceLane, B32X4::ReplaceLane)) + return false; if (!CheckSimdCallArgsPatchable(f, call, 3, CheckSimdReplaceLaneArgs(opType))) return false; *type = opType; @@ -5294,10 +5179,13 @@ static bool CheckSimdCast(FunctionValidator& f, ParseNode* call, AsmJSSimdType fromType, AsmJSSimdType toType, bool bitcast, Type* type) { - SwitchPackOp(f, toType, + if (!SwitchPackOp(f, toType, bitcast ? I32X4::FromF32X4Bits : I32X4::FromF32X4, bitcast ? F32X4::FromI32X4Bits : F32X4::FromI32X4, - B32X4::Bad); + B32X4::Bad)) + { + return false; + } if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(fromType))) return false; *type = toType; @@ -5327,7 +5215,8 @@ CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty if (numArgs != 5) return f.failf(call, "expected 5 arguments to SIMD swizzle, got %u", numArgs); - SwitchPackOp(f, opType, I32X4::Swizzle, F32X4::Swizzle, B32X4::Bad); + if (!SwitchPackOp(f, opType, I32X4::Swizzle, F32X4::Swizzle, B32X4::Bad)) + return false; Type retType = opType; ParseNode* vec = CallArgList(call); @@ -5341,8 +5230,10 @@ CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty if (!CheckSimdShuffleSelectors(f, NextNode(vec), lanes, 4)) return false; - for (unsigned i = 0; i < 4; i++) - f.writeU8(uint8_t(lanes[i])); + for (unsigned i = 0; i < 4; i++) { + if (!f.writeU8(uint8_t(lanes[i]))) + return false; + } *type = retType; return true; @@ -5355,7 +5246,8 @@ CheckSimdShuffle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty if (numArgs != 6) return f.failf(call, "expected 6 arguments to SIMD shuffle, got %u", numArgs); - SwitchPackOp(f, opType, I32X4::Shuffle, F32X4::Shuffle, B32X4::Bad); + if (!SwitchPackOp(f, opType, I32X4::Shuffle, F32X4::Shuffle, B32X4::Bad)) + return false; Type retType = opType; ParseNode* arg = CallArgList(call); @@ -5371,8 +5263,10 @@ CheckSimdShuffle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty if (!CheckSimdShuffleSelectors(f, arg, lanes, 8)) return false; - for (unsigned i = 0; i < 4; i++) - f.writeU8(uint8_t(lanes[i])); + for (unsigned i = 0; i < 4; i++) { + if (!f.writeU8(uint8_t(lanes[i]))) + return false; + } *type = retType; return true; @@ -5409,8 +5303,7 @@ CheckSimdLoadStoreArgs(FunctionValidator& f, ParseNode* call, AsmJSSimdType opTy return f.fail(indexExpr, "constant index out of range"); *needsBoundsCheck = NO_BOUNDS_CHECK; - f.writeInt32Lit(indexLit); - return true; + return f.writeInt32Lit(indexLit); } Type indexType; @@ -5431,10 +5324,13 @@ CheckSimdLoad(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, if (numArgs != 2) return f.failf(call, "expected 2 arguments to SIMD load, got %u", numArgs); - SwitchPackOp(f, opType, I32X4::Load, F32X4::Load, B32X4::Bad); - size_t viewTypeAt = f.tempU8(); - size_t needsBoundsCheckAt = f.tempU8(); - f.writeU8(numElems); + if (!SwitchPackOp(f, opType, I32X4::Load, F32X4::Load, B32X4::Bad)) + return false; + + size_t viewTypeAt; + size_t needsBoundsCheckAt; + if (!f.tempU8(&viewTypeAt) || !f.tempU8(&needsBoundsCheckAt) || !f.writeU8(numElems)) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -5456,10 +5352,13 @@ CheckSimdStore(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, if (numArgs != 3) return f.failf(call, "expected 3 arguments to SIMD store, got %u", numArgs); - SwitchPackOp(f, opType, I32X4::Store, F32X4::Store, B32X4::Bad); - size_t viewTypeAt = f.tempU8(); - size_t needsBoundsCheckAt = f.tempU8(); - f.writeU8(numElems); + if (!SwitchPackOp(f, opType, I32X4::Store, F32X4::Store, B32X4::Bad)) + return false; + + size_t viewTypeAt; + size_t needsBoundsCheckAt; + if (!f.tempU8(&viewTypeAt) || !f.tempU8(&needsBoundsCheckAt) || !f.writeU8(numElems)) + return false; Scalar::Type viewType; NeedsBoundsCheck needsBoundsCheck; @@ -5484,7 +5383,8 @@ CheckSimdStore(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, static bool CheckSimdSelect(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type) { - SwitchPackOp(f, opType, I32X4::Select, F32X4::Select, B32X4::Bad); + if (!SwitchPackOp(f, opType, I32X4::Select, F32X4::Select, B32X4::Bad)) + return false; if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(opType))) return false; *type = opType; @@ -5496,7 +5396,8 @@ CheckSimdAllTrue(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty { switch (opType) { case AsmJSSimdType_bool32x4: - f.writeOp(I32::B32X4AllTrue); + if (!f.writeOp(I32::B32X4AllTrue)) + return false; break; case AsmJSSimdType_int32x4: case AsmJSSimdType_float32x4: @@ -5513,7 +5414,8 @@ CheckSimdAnyTrue(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty { switch (opType) { case AsmJSSimdType_bool32x4: - f.writeOp(I32::B32X4AnyTrue); + if (!f.writeOp(I32::B32X4AnyTrue)) + return false; break; case AsmJSSimdType_int32x4: case AsmJSSimdType_float32x4: @@ -5538,7 +5440,8 @@ CheckSimdCheck(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type static bool CheckSimdSplat(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type) { - SwitchPackOp(f, opType, I32X4::Splat, F32X4::Splat, B32X4::Splat); + if (!SwitchPackOp(f, opType, I32X4::Splat, F32X4::Splat, B32X4::Splat)) + return false; if (!CheckSimdCallArgsPatchable(f, call, 1, CheckSimdScalarArgs(opType))) return false; *type = opType; @@ -5665,7 +5568,8 @@ CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator:: MOZ_ASSERT(call->isKind(PNK_CALL)); AsmJSSimdType simdType = global->simdCtorType(); - SwitchPackOp(f, simdType, I32X4::Ctor, F32X4::Ctor, B32X4::Ctor); + if (!SwitchPackOp(f, simdType, I32X4::Ctor, F32X4::Ctor, B32X4::Ctor)) + return false; unsigned length = SimdTypeToLength(simdType); if (!CheckSimdCallArgsPatchable(f, call, length, CheckSimdScalarArgs(simdType))) @@ -5771,7 +5675,9 @@ static bool CheckCoercedMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func, ExprType ret, Type* type) { - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type actual; if (!CheckMathBuiltinCall(f, callNode, func, &actual)) return false; @@ -5782,7 +5688,9 @@ static bool CheckCoercedSimdCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global, ExprType ret, Type* type) { - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type actual; if (global->isSimdCtor()) { @@ -5802,7 +5710,9 @@ static bool CheckCoercedAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsBuiltinFunction func, ExprType ret, Type* type) { - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type actual; if (!CheckAtomicsBuiltinCall(f, callNode, func, &actual)) return false; @@ -5815,9 +5725,12 @@ CheckCoercedCall(FunctionValidator& f, ParseNode* call, ExprType ret, Type* type JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); if (IsNumericLiteral(f.m(), call)) { - size_t coerceOp = f.tempOp(); + size_t coerceOp; + if (!f.tempOp(&coerceOp)) + return false; NumLit lit = ExtractNumericLiteral(f.m(), call); - f.writeLit(lit); + if (!f.writeLit(lit)) + return false; return CoerceResult(f, call, ret, Type::lit(lit), coerceOp, type); } @@ -5866,7 +5779,10 @@ CheckPos(FunctionValidator& f, ParseNode* pos, Type* type) if (operand->isKind(PNK_CALL)) return CheckCoercedCall(f, operand, ExprType::F64, type); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; + Type actual; if (!CheckExpr(f, operand, &actual)) return false; @@ -5880,7 +5796,8 @@ CheckNot(FunctionValidator& f, ParseNode* expr, Type* type) MOZ_ASSERT(expr->isKind(PNK_NOT)); ParseNode* operand = UnaryKid(expr); - f.writeOp(I32::Not); + if (!f.writeOp(I32::Not)) + return false; Type operandType; if (!CheckExpr(f, operand, &operandType)) @@ -5899,7 +5816,9 @@ CheckNeg(FunctionValidator& f, ParseNode* expr, Type* type) MOZ_ASSERT(expr->isKind(PNK_NEG)); ParseNode* operand = UnaryKid(expr); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type operandType; if (!CheckExpr(f, operand, &operandType)) @@ -5932,7 +5851,9 @@ CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type) MOZ_ASSERT(expr->isKind(PNK_BITNOT)); ParseNode* operand = UnaryKid(expr); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type operandType; if (!CheckExpr(f, operand, &operandType)) @@ -5961,7 +5882,8 @@ CheckBitNot(FunctionValidator& f, ParseNode* neg, Type* type) if (operand->isKind(PNK_BITNOT)) return CheckCoerceToInt(f, operand, type); - f.writeOp(I32::BitNot); + if (!f.writeOp(I32::BitNot)) + return false; Type operandType; if (!CheckExpr(f, operand, &operandType)) @@ -5983,8 +5905,9 @@ CheckComma(FunctionValidator& f, ParseNode* comma, Type* type) MOZ_ASSERT(comma->isKind(PNK_COMMA)); ParseNode* operands = ListHead(comma); - size_t commaAt = f.tempOp(); - f.writeU32(ListLength(comma)); + size_t commaAt; + if (!f.tempOp(&commaAt) || !f.writeU32(ListLength(comma))) + return false; ParseNode* pn = operands; for (; NextNode(pn); pn = NextNode(pn)) { @@ -6018,7 +5941,9 @@ CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type) { MOZ_ASSERT(ternary->isKind(PNK_CONDITIONAL)); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; ParseNode* cond = TernaryKid1(ternary); ParseNode* thenExpr = TernaryKid2(ternary); @@ -6099,7 +6024,9 @@ CheckMultiply(FunctionValidator& f, ParseNode* star, Type* type) ParseNode* lhs = MultiplyLeft(star); ParseNode* rhs = MultiplyRight(star); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type lhsType; if (!CheckExpr(f, lhs, &lhsType)) @@ -6144,7 +6071,9 @@ CheckAddOrSub(FunctionValidator& f, ParseNode* expr, Type* type, unsigned* numAd Type lhsType, rhsType; unsigned lhsNumAddOrSub, rhsNumAddOrSub; - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) { if (!CheckAddOrSub(f, lhs, &lhsType, &lhsNumAddOrSub)) @@ -6196,7 +6125,9 @@ CheckDivOrMod(FunctionValidator& f, ParseNode* expr, Type* type) { MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD)); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; ParseNode* lhs = DivOrModLeft(expr); ParseNode* rhs = DivOrModRight(expr); @@ -6244,7 +6175,9 @@ CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type) MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) || comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE)); - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; ParseNode* lhs = ComparisonLeft(comp); ParseNode* rhs = ComparisonRight(comp); @@ -6355,12 +6288,12 @@ CheckBitwise(FunctionValidator& f, ParseNode* bitwise, Type* type) } switch (bitwise->getKind()) { - case PNK_BITOR: f.writeOp(I32::BitOr); break; - case PNK_BITAND: f.writeOp(I32::BitAnd); break; - case PNK_BITXOR: f.writeOp(I32::BitXor); break; - case PNK_LSH: f.writeOp(I32::Lsh); break; - case PNK_RSH: f.writeOp(I32::ArithRsh); break; - case PNK_URSH: f.writeOp(I32::LogicRsh); break; + case PNK_BITOR: if (!f.writeOp(I32::BitOr)) return false; break; + case PNK_BITAND: if (!f.writeOp(I32::BitAnd)) return false; break; + case PNK_BITXOR: if (!f.writeOp(I32::BitXor)) return false; break; + case PNK_LSH: if (!f.writeOp(I32::Lsh)) return false; break; + case PNK_RSH: if (!f.writeOp(I32::ArithRsh)) return false; break; + case PNK_URSH: if (!f.writeOp(I32::LogicRsh)) return false; break; default: MOZ_CRASH("not a bitwise op"); } @@ -6438,7 +6371,9 @@ CheckAsExprStatement(FunctionValidator& f, ParseNode* expr) return CheckCoercedCall(f, expr, ExprType::Void, &_); } - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; Type type; if (!CheckExpr(f, expr, &type)) @@ -6467,12 +6402,8 @@ CheckExprStatement(FunctionValidator& f, ParseNode* exprStmt) { MOZ_ASSERT(exprStmt->isKind(PNK_SEMI)); ParseNode* expr = UnaryKid(exprStmt); - - if (!expr) { - f.writeOp(Stmt::Noop); - return true; - } - + if (!expr) + return f.writeOp(Stmt::Noop); return CheckAsExprStatement(f, expr); } @@ -6481,21 +6412,27 @@ enum class InterruptCheckPosition { Loop }; -static void +static bool MaybeAddInterruptCheck(FunctionValidator& f, InterruptCheckPosition pos, ParseNode* pn) { if (f.m().mg().args().useSignalHandlersForInterrupt) - return; + return true; switch (pos) { - case InterruptCheckPosition::Head: f.writeOp(Stmt::InterruptCheckHead); break; - case InterruptCheckPosition::Loop: f.writeOp(Stmt::InterruptCheckLoop); break; + case InterruptCheckPosition::Head: + if (!f.writeOp(Stmt::InterruptCheckHead)) + return false; + break; + case InterruptCheckPosition::Loop: + if (!f.writeOp(Stmt::InterruptCheckLoop)) + return false; + break; } unsigned lineno = 0, column = 0; f.m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &lineno, &column); - f.writeU32(lineno); - f.writeU32(column); + return f.writeU32(lineno) && + f.writeU32(column); } static bool @@ -6505,7 +6442,8 @@ CheckWhile(FunctionValidator& f, ParseNode* whileStmt) ParseNode* cond = BinaryLeft(whileStmt); ParseNode* body = BinaryRight(whileStmt); - f.writeOp(Stmt::While); + if (!f.writeOp(Stmt::While)) + return false; Type condType; if (!CheckExpr(f, cond, &condType)) @@ -6513,9 +6451,8 @@ CheckWhile(FunctionValidator& f, ParseNode* whileStmt) if (!condType.isInt()) return f.failf(cond, "%s is not a subtype of int", condType.toChars()); - MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, whileStmt); - - return CheckStatement(f, body); + return MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, whileStmt) && + CheckStatement(f, body); } static bool @@ -6532,8 +6469,10 @@ CheckFor(FunctionValidator& f, ParseNode* forStmt) ParseNode* maybeCond = TernaryKid2(forHead); ParseNode* maybeInc = TernaryKid3(forHead); - f.writeOp(maybeInit ? (maybeInc ? Stmt::ForInitInc : Stmt::ForInitNoInc) - : (maybeInc ? Stmt::ForNoInitInc : Stmt::ForNoInitNoInc)); + Stmt stmt = maybeInit ? (maybeInc ? Stmt::ForInitInc : Stmt::ForInitNoInc) + : (maybeInc ? Stmt::ForNoInitInc : Stmt::ForNoInitNoInc); + if (!f.writeOp(stmt)) + return false; if (maybeInit && !CheckAsExprStatement(f, maybeInit)) return false; @@ -6544,11 +6483,12 @@ CheckFor(FunctionValidator& f, ParseNode* forStmt) return false; if (!condType.isInt()) return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars()); - } else { - f.writeInt32Lit(1); + } else if (!f.writeInt32Lit(1)) { + return false; } - MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, forStmt); + if (!MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, forStmt)) + return false; if (!CheckStatement(f, body)) return false; @@ -6556,8 +6496,7 @@ CheckFor(FunctionValidator& f, ParseNode* forStmt) if (maybeInc && !CheckAsExprStatement(f, maybeInc)) return false; - f.writeDebugCheckPoint(); - return true; + return f.writeDebugCheckPoint(); } static bool @@ -6567,9 +6506,11 @@ CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt) ParseNode* body = BinaryLeft(whileStmt); ParseNode* cond = BinaryRight(whileStmt); - f.writeOp(Stmt::DoWhile); + if (!f.writeOp(Stmt::DoWhile)) + return false; - MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, cond); + if (!MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, cond)) + return false; if (!CheckStatement(f, body)) return false; @@ -6590,13 +6531,15 @@ CheckLabel(FunctionValidator& f, ParseNode* labeledStmt) PropertyName* label = LabeledStatementLabel(labeledStmt); ParseNode* stmt = LabeledStatementStatement(labeledStmt); - f.writeOp(Stmt::Label); + if (!f.writeOp(Stmt::Label)) + return false; uint32_t labelId; if (!f.addLabel(label, &labelId)) return false; - f.writeU32(labelId); + if (!f.writeU32(labelId)) + return false; if (!CheckStatement(f, stmt)) return false; @@ -6609,7 +6552,9 @@ static bool CheckIf(FunctionValidator& f, ParseNode* ifStmt) { recurse: - size_t opcodeAt = f.tempOp(); + size_t opcodeAt; + if (!f.tempOp(&opcodeAt)) + return false; MOZ_ASSERT(ifStmt->isKind(PNK_IF)); ParseNode* cond = TernaryKid1(ifStmt); @@ -6732,13 +6677,24 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) { MOZ_ASSERT(switchStmt->isKind(PNK_SWITCH)); - f.writeOp(Stmt::Switch); + if (!f.writeOp(Stmt::Switch)) + return false; + // Has default - size_t hasDefaultAt = f.tempU8(); + size_t hasDefaultAt; + if (!f.tempU8(&hasDefaultAt)) + return false; + // Low / High / Num cases - size_t lowAt = f.temp32(); - size_t highAt = f.temp32(); - size_t numCasesAt = f.temp32(); + size_t lowAt; + if (!f.temp32(&lowAt)) + return false; + size_t highAt; + if (!f.temp32(&highAt)) + return false; + size_t numCasesAt; + if (!f.temp32(&numCasesAt)) + return false; ParseNode* switchExpr = BinaryLeft(switchStmt); ParseNode* switchBody = BinaryRight(switchStmt); @@ -6781,7 +6737,8 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) cases[caseIndex] = true; numCases += 1; - f.writeI32(caseValue); + if (!f.writeI32(caseValue)) + return false; if (!CheckStatement(f, CaseBody(stmt))) return false; @@ -6819,7 +6776,8 @@ CheckReturn(FunctionValidator& f, ParseNode* returnStmt) { ParseNode* expr = ReturnExpr(returnStmt); - f.writeOp(Stmt::Ret); + if (!f.writeOp(Stmt::Ret)) + return false; if (!expr) return CheckReturnType(f, returnStmt, ExprType::Void); @@ -6854,34 +6812,31 @@ CheckStatementList(FunctionValidator& f, ParseNode* stmtList) { MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); - f.writeOp(Stmt::Block); - f.writeU32(ListLength(stmtList)); + if (!f.writeOp(Stmt::Block) || !f.writeU32(ListLength(stmtList))) + return false; for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) { if (!CheckStatement(f, stmt)) return false; } - f.writeDebugCheckPoint(); - return true; + return f.writeDebugCheckPoint(); } static bool CheckBreakOrContinue(FunctionValidator& f, PropertyName* maybeLabel, Stmt withoutLabel, Stmt withLabel) { - if (!maybeLabel) { - f.writeOp(withoutLabel); - return true; - } + if (!maybeLabel) + return f.writeOp(withoutLabel); - f.writeOp(withLabel); + if (!f.writeOp(withLabel)) + return false; uint32_t labelId = f.lookupLabel(maybeLabel); MOZ_ASSERT(labelId != uint32_t(-1)); - f.writeU32(labelId); - return true; + return f.writeU32(labelId); } static bool @@ -7002,8 +6957,8 @@ CheckFunction(ModuleValidator& m) if (!CheckArguments(f, &stmtIter, &args)) return false; - MOZ_ASSERT(!f.startedPacking(), "No bytecode should be written at this point."); - MaybeAddInterruptCheck(f, InterruptCheckPosition::Head, fn); + if (!MaybeAddInterruptCheck(f, InterruptCheckPosition::Head, fn)) + return false; if (!CheckVariables(f, &stmtIter)) return false; @@ -7158,7 +7113,7 @@ CheckModuleExportFunction(ModuleValidator& m, ParseNode* pn, PropertyName* maybe if (global->which() != ModuleValidator::Global::Function) return m.failName(pn, "'%s' is not a function", funcName); - return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName); + return m.addExportField(pn, m.function(global->funcIndex()), maybeFieldName); } static bool @@ -7239,25 +7194,23 @@ CheckModuleEnd(ModuleValidator &m) } static bool -CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, HandleAsmJSModule obj, - unsigned* time, SlowFunctionVector* slowFuncs) +CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, + MutableHandle moduleObj, unsigned* time, + SlowFunctionVector* slowFuncs) { int64_t before = PRMJ_Now(); - ModuleValidator m(cx, parser); - if (!m.init(obj)) + ParseNode* moduleFunctionNode = parser.pc->maybeFunction; + MOZ_ASSERT(moduleFunctionNode); + + ModuleValidator m(cx, parser, moduleFunctionNode); + if (!m.init()) return false; - if (PropertyName* moduleFunctionName = FunctionName(m.moduleFunctionNode())) { - if (!CheckModuleLevelName(m, m.moduleFunctionNode(), moduleFunctionName)) - return false; - m.initModuleFunctionName(moduleFunctionName); - } - - if (!CheckFunctionHead(m, m.moduleFunctionNode())) + if (!CheckFunctionHead(m, moduleFunctionNode)) return false; - if (!CheckModuleArguments(m, m.moduleFunctionNode())) + if (!CheckModuleArguments(m, moduleFunctionNode)) return false; if (!CheckPrecedingStatements(m, stmtList)) @@ -7287,7 +7240,7 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, Hand if (!CheckModuleEnd(m)) return false; - if (!m.finish(slowFuncs)) + if (!m.finish(moduleObj, slowFuncs)) return false; *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC; @@ -7297,34 +7250,6 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, Hand /*****************************************************************************/ // Link-time validation -static AsmJSModuleObject& -FunctionToModuleObject(JSFunction* fun) -{ - MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun)); - const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_MODULE_SLOT); - return v.toObject().as(); -} - -static unsigned -FunctionToExportIndex(JSFunction* fun) -{ - MOZ_ASSERT(IsAsmJSFunction(fun)); - const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT); - return v.toInt32(); -} - -static bool -CallAsmJS(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - RootedFunction callee(cx, &args.callee().as()); - - AsmJSModule& module = FunctionToModuleObject(callee).module(); - uint32_t exportIndex = FunctionToExportIndex(callee); - - return module.wasmModule().callExport(cx, exportIndex, args); -} - static bool LinkFail(JSContext* cx, const char* str) { @@ -7383,13 +7308,13 @@ HasPureCoercion(JSContext* cx, HandleValue v) } static bool -ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData, +ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, uint8_t* globalData, HandleValue importVal) { void* datum = globalData + global.varGlobalDataOffset(); switch (global.varInitKind()) { - case AsmJSModule::Global::InitConstant: { + case AsmJSGlobal::InitConstant: { Val v = global.varInitVal(); switch (v.type()) { case ValType::I32: @@ -7415,7 +7340,7 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t break; } - case AsmJSModule::Global::InitImport: { + case AsmJSGlobal::InitImport: { RootedPropertyName field(cx, global.varImportField()); RootedValue v(cx); if (!GetDataProperty(cx, importVal, field, &v)) @@ -7470,8 +7395,8 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t } static bool -ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal, - AutoVectorRooter* ffis) +ValidateFFI(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal, + MutableHandle ffis) { RootedPropertyName field(cx, global.ffiField()); RootedValue v(cx); @@ -7481,12 +7406,12 @@ ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue import if (!v.isObject() || !v.toObject().is()) return LinkFail(cx, "FFI imports must be functions"); - (*ffis)[global.ffiIndex()].set(&v.toObject().as()); + ffis[global.ffiIndex()].set(&v.toObject().as()); return true; } static bool -ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +ValidateArrayView(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal) { RootedPropertyName field(cx, global.maybeViewName()); if (!field) @@ -7504,7 +7429,7 @@ ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue } static bool -ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +ValidateMathBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal) { RootedValue v(cx); if (!GetDataProperty(cx, globalVal, cx->names().Math, &v)) @@ -7566,7 +7491,7 @@ AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type) } static bool -ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal, +ValidateSimdType(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal, MutableHandleValue out) { RootedValue v(cx); @@ -7574,7 +7499,7 @@ ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue g return false; AsmJSSimdType type; - if (global.which() == AsmJSModule::Global::SimdCtor) + if (global.which() == AsmJSGlobal::SimdCtor) type = global.simdCtorType(); else type = global.simdOperationType(); @@ -7598,14 +7523,14 @@ ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue g } static bool -ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +ValidateSimdType(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal) { RootedValue _(cx); return ValidateSimdType(cx, global, globalVal, &_); } static bool -ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal) { // SIMD operations are loaded from the SIMD type, so the type must have been // validated before the operation. @@ -7658,7 +7583,7 @@ ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleVa } static bool -ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal) { RootedValue v(cx); if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v)) @@ -7689,12 +7614,12 @@ ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, } static bool -ValidateConstant(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +ValidateConstant(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal) { RootedPropertyName field(cx, global.constantName()); RootedValue v(cx, globalVal); - if (global.constantKind() == AsmJSModule::Global::MathConstant) { + if (global.constantKind() == AsmJSGlobal::MathConstant) { if (!GetDataProperty(cx, v, cx->names().Math, &v)) return false; } @@ -7721,10 +7646,10 @@ static bool CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal, MutableHandle buffer) { - if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal)) + if (module.hasSharedHeap() && !IsSharedArrayBuffer(bufferVal)) return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer"); - if (!module.isSharedView() && !IsArrayBuffer(bufferVal)) + if (!module.hasSharedHeap() && !IsArrayBuffer(bufferVal)) return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer"); buffer.set(&AsAnyArrayBuffer(bufferVal)); @@ -7754,12 +7679,12 @@ CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal, // Shell builtins may have disabled signal handlers since the module we're // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers // as well for the caching case. - if (module.wasmModule().compileArgs() != CompileArgs(cx)) + if (module.compileArgs() != CompileArgs(cx)) return LinkFail(cx, "Signals have been toggled since compilation"); if (buffer->is()) { Rooted abheap(cx, &buffer->as()); - bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB; + bool useSignalHandlers = module.compileArgs().useSignalHandlersForOOB; if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers)) return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use"); } @@ -7775,77 +7700,58 @@ DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module) HandleValue bufferVal = args.get(2); Rooted buffer(cx); - if (module.hasArrayView() && !CheckBuffer(cx, module, bufferVal, &buffer)) + if (module.usesHeap() && !CheckBuffer(cx, module, bufferVal, &buffer)) return false; - AutoVectorRooter ffis(cx); + Rooted ffis(cx, FunctionVector(cx)); if (!ffis.resize(module.numFFIs())) return false; - for (const AsmJSModule::Global& global : module.globals()) { + for (const AsmJSGlobal& global : module.asmJSGlobals()) { switch (global.which()) { - case AsmJSModule::Global::Variable: - if (!ValidateGlobalVariable(cx, global, module.wasmModule().globalData(), importVal)) + case AsmJSGlobal::Variable: + if (!ValidateGlobalVariable(cx, global, module.globalData(), importVal)) return false; break; - case AsmJSModule::Global::FFI: + case AsmJSGlobal::FFI: if (!ValidateFFI(cx, global, importVal, &ffis)) return false; break; - case AsmJSModule::Global::ArrayView: - case AsmJSModule::Global::ArrayViewCtor: + case AsmJSGlobal::ArrayView: + case AsmJSGlobal::ArrayViewCtor: if (!ValidateArrayView(cx, global, globalVal)) return false; break; - case AsmJSModule::Global::MathBuiltinFunction: + case AsmJSGlobal::MathBuiltinFunction: if (!ValidateMathBuiltinFunction(cx, global, globalVal)) return false; break; - case AsmJSModule::Global::AtomicsBuiltinFunction: + case AsmJSGlobal::AtomicsBuiltinFunction: if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal)) return false; break; - case AsmJSModule::Global::Constant: + case AsmJSGlobal::Constant: if (!ValidateConstant(cx, global, globalVal)) return false; break; - case AsmJSModule::Global::SimdCtor: + case AsmJSGlobal::SimdCtor: if (!ValidateSimdType(cx, global, globalVal)) return false; break; - case AsmJSModule::Global::SimdOperation: + case AsmJSGlobal::SimdOperation: if (!ValidateSimdOperation(cx, global, globalVal)) return false; break; } } - AutoVectorRooter imports(cx); - for (const AsmJSModule::Import& import : module.imports()) { + Rooted imports(cx, FunctionVector(cx)); + for (const AsmJSImport& import : module.asmJSImports()) { if (!imports.append(ffis[import.ffiIndex()])) return false; } - return module.wasmModule().dynamicallyLink(cx, buffer, imports); -} - -static JSFunction* -NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func, - HandleObject moduleObj, unsigned exportIndex) -{ - unsigned numArgs = module.wasmModule().exports()[exportIndex].sig().args().length(); - - RootedPropertyName name(cx, func.name()); - JSFunction* fun = - NewNativeConstructor(cx, CallAsmJS, numArgs, name, - gc::AllocKind::FUNCTION_EXTENDED, GenericObject, - JSFunction::ASMJS_CTOR); - if (!fun) - return nullptr; - - fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj)); - fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex)); - return fun; + return module.dynamicallyLink(cx, buffer, imports); } static bool @@ -7917,38 +7823,12 @@ HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& modul return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT); } -static JSObject* -CreateExportObject(JSContext* cx, HandleAsmJSModule moduleObj) +static WasmModuleObject* +AsmJSModuleToModuleObject(JSFunction* fun) { - AsmJSModule& module = moduleObj->module(); - const AsmJSModule::ExportVector& exports = module.exports(); - - if (exports.length() == 1) { - const AsmJSModule::Export& func = exports[0]; - if (!func.maybeFieldName()) - return NewExportedFunction(cx, module, func, moduleObj, 0); - } - - gc::AllocKind allocKind = gc::GetGCObjectKind(exports.length()); - RootedPlainObject obj(cx, NewBuiltinClassInstance(cx, allocKind)); - if (!obj) - return nullptr; - - for (unsigned i = 0; i < exports.length(); i++) { - const AsmJSModule::Export& func = exports[i]; - - RootedFunction fun(cx, NewExportedFunction(cx, module, func, moduleObj, i)); - if (!fun) - return nullptr; - - MOZ_ASSERT(func.maybeFieldName() != nullptr); - RootedId id(cx, NameToId(func.maybeFieldName())); - RootedValue val(cx, ObjectValue(*fun)); - if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE)) - return nullptr; - } - - return obj; + MOZ_ASSERT(IsAsmJSModule(fun)); + const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_MODULE_SLOT); + return &v.toObject().as(); } // Implements the semantics of an asm.js module function that has been successfully validated. @@ -7960,43 +7840,41 @@ LinkAsmJS(JSContext* cx, unsigned argc, JS::Value* vp) // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended // function and stores its module in an extended slot. RootedFunction fun(cx, &args.callee().as()); - Rooted moduleObj(cx, &FunctionToModuleObject(fun)); + Rooted moduleObj(cx, AsmJSModuleToModuleObject(fun)); + AsmJSModule* module = &moduleObj->module().asAsmJS(); // When a module is linked, it is dynamically specialized to the given // arguments (buffer, ffis). Thus, if the module is linked again (it is just // a function so it can be called multiple times), we need to clone a new // module. - if (moduleObj->module().wasmModule().dynamicallyLinked()) { - Rooted clone(cx, NewAsmJSModuleObject(cx)); - if (!clone) + if (module->dynamicallyLinked()) { + if (!module->clone(cx, &moduleObj)) return false; - if (!moduleObj->module().clone(cx, clone)) - return false; + module = &moduleObj->module().asAsmJS(); - moduleObj = clone; + if (!module->staticallyLink(cx)) + return false; } - AsmJSModule& module = moduleObj->module(); - // Link the module by performing the link-time validation checks in the // asm.js spec and then patching the generated module to associate it with // the given heap (ArrayBuffer) and a new global data segment (the closure // state shared by the inner asm.js functions). - if (!DynamicallyLinkModule(cx, args, module)) { + if (!DynamicallyLinkModule(cx, args, *module)) { // Linking failed, so reparse the entire asm.js module from scratch to // get normal interpreted bytecode which we can simply Invoke. Very slow. RootedPropertyName name(cx, fun->name()); - return HandleDynamicLinkFailure(cx, args, module, name); + return HandleDynamicLinkFailure(cx, args, *module, name); } - // Link-time validation succeeded, so wrap all the exported functions with - // CallAsmJS builtins that trampoline into the generated code. - JSObject* obj = CreateExportObject(cx, moduleObj); - if (!obj) + // Link-time validation succeed! + + RootedObject exportObj(cx); + if (!module->createExportObject(cx, moduleObj, module->exportMap(), &exportObj)) return false; - args.rval().set(ObjectValue(*obj)); + args.rval().set(ObjectValue(*exportObj)); return true; } @@ -8015,6 +7893,8 @@ NewModuleFunction(ExclusiveContext* cx, JSFunction* origFun, HandleObject module return nullptr; moduleFun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj)); + + MOZ_ASSERT(IsAsmJSModule(moduleFun)); return moduleFun; } @@ -8022,7 +7902,7 @@ NewModuleFunction(ExclusiveContext* cx, JSFunction* origFun, HandleObject module // Caching and cloning uint8_t* -AsmJSModule::Global::serialize(uint8_t* cursor) const +AsmJSGlobal::serialize(uint8_t* cursor) const { cursor = WriteBytes(cursor, &pod, sizeof(pod)); cursor = SerializeName(cursor, name_); @@ -8030,14 +7910,14 @@ AsmJSModule::Global::serialize(uint8_t* cursor) const } size_t -AsmJSModule::Global::serializedSize() const +AsmJSGlobal::serializedSize() const { return sizeof(pod) + SerializedNameSize(name_); } const uint8_t* -AsmJSModule::Global::deserialize(ExclusiveContext* cx, const uint8_t* cursor) +AsmJSGlobal::deserialize(ExclusiveContext* cx, const uint8_t* cursor) { (cursor = ReadBytes(cursor, &pod, sizeof(pod))) && (cursor = DeserializeName(cx, cursor, &name_)); @@ -8045,147 +7925,170 @@ AsmJSModule::Global::deserialize(ExclusiveContext* cx, const uint8_t* cursor) } bool -AsmJSModule::Global::clone(JSContext* cx, Global* out) const +AsmJSGlobal::clone(JSContext* cx, AsmJSGlobal* out) const { *out = *this; return true; } -uint8_t* -AsmJSModule::Export::serialize(uint8_t* cursor) const +size_t +AsmJSModuleData::serializedSize() const { - cursor = SerializeName(cursor, name_); - cursor = SerializeName(cursor, maybeFieldName_); - cursor = WriteBytes(cursor, &pod, sizeof(pod)); + return sizeof(pod()) + + SerializedVectorSize(globals) + + SerializedPodVectorSize(imports) + + SerializedPodVectorSize(exports) + + exportMap.serializedSize() + + SerializedNameSize(globalArgumentName) + + SerializedNameSize(importArgumentName) + + SerializedNameSize(bufferArgumentName); +} + +uint8_t* +AsmJSModuleData::serialize(uint8_t* cursor) const +{ + cursor = WriteBytes(cursor, &pod(), sizeof(pod())); + cursor = SerializeVector(cursor, globals); + cursor = SerializePodVector(cursor, imports); + cursor = SerializePodVector(cursor, exports); + cursor = exportMap.serialize(cursor); + cursor = SerializeName(cursor, globalArgumentName); + cursor = SerializeName(cursor, importArgumentName); + cursor = SerializeName(cursor, bufferArgumentName); return cursor; } -size_t -AsmJSModule::Export::serializedSize() const -{ - return SerializedNameSize(name_) + - SerializedNameSize(maybeFieldName_) + - sizeof(pod); -} - const uint8_t* -AsmJSModule::Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor) +AsmJSModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor) { - (cursor = DeserializeName(cx, cursor, &name_)) && - (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) && - (cursor = ReadBytes(cursor, &pod, sizeof(pod))); + (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) && + (cursor = DeserializeVector(cx, cursor, &globals)) && + (cursor = DeserializePodVector(cx, cursor, &imports)) && + (cursor = DeserializePodVector(cx, cursor, &exports)) && + (cursor = exportMap.deserialize(cx, cursor)) && + (cursor = DeserializeName(cx, cursor, &globalArgumentName)) && + (cursor = DeserializeName(cx, cursor, &importArgumentName)) && + (cursor = DeserializeName(cx, cursor, &bufferArgumentName)); return cursor; } bool -AsmJSModule::Export::clone(JSContext* cx, Export* out) const +AsmJSModuleData::clone(JSContext* cx, AsmJSModuleData* out) const { - out->name_ = name_; - out->maybeFieldName_ = maybeFieldName_; - out->pod = pod; - return true; + out->pod() = pod(); + out->globalArgumentName = globalArgumentName; + out->importArgumentName = importArgumentName; + out->bufferArgumentName = bufferArgumentName; + out->srcStart = srcStart; + out->srcBodyStart = srcBodyStart; + out->strict = strict; + out->scriptSource.reset(scriptSource.get()); + return CloneVector(cx, globals, &out->globals) && + ClonePodVector(cx, imports, &out->imports) && + ClonePodVector(cx, exports, &out->exports) && + exportMap.clone(cx, &out->exportMap); +} + +size_t +AsmJSModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const +{ + return globals.sizeOfExcludingThis(mallocSizeOf) + + imports.sizeOfExcludingThis(mallocSizeOf) + + exports.sizeOfExcludingThis(mallocSizeOf) + + exportMap.sizeOfExcludingThis(mallocSizeOf); } size_t AsmJSModule::serializedSize() const { - MOZ_ASSERT(isFinished()); - return wasmModule_->serializedSize() + - linkData_->serializedSize() + - sizeof(pod) + - SerializedVectorSize(globals_) + - SerializedPodVectorSize(imports_) + - SerializedVectorSize(exports_) + - SerializedNameSize(globalArgumentName_) + - SerializedNameSize(importArgumentName_) + - SerializedNameSize(bufferArgumentName_); + return base().serializedSize() + + link_->serializedSize() + + module_->serializedSize(); } uint8_t* AsmJSModule::serialize(uint8_t* cursor) const { - MOZ_ASSERT(isFinished()); - cursor = wasmModule_->serialize(cursor); - cursor = linkData_->serialize(cursor); - cursor = WriteBytes(cursor, &pod, sizeof(pod)); - cursor = SerializeVector(cursor, globals_); - cursor = SerializePodVector(cursor, imports_); - cursor = SerializeVector(cursor, exports_); - cursor = SerializeName(cursor, globalArgumentName_); - cursor = SerializeName(cursor, importArgumentName_); - cursor = SerializeName(cursor, bufferArgumentName_); + cursor = base().serialize(cursor); + cursor = link_->serialize(cursor); + cursor = module_->serialize(cursor); return cursor; } -const uint8_t* -AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor) +/* static */ const uint8_t* +AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor, AsmJSParser& parser, + MutableHandle moduleObj) { - linkData_ = cx->make_unique(); - if (!linkData_) - return nullptr; - - // To avoid GC-during-deserialization corner cases, prevent atoms from - // being collected. + // Deserialization GC-allocates a bunch of atoms and stores them in unrooted + // Vectors so, for simplicity, inhibit GC of the atoms zone. AutoKeepAtoms aka(cx->perThreadData); - (cursor = Module::deserialize(cx, cursor, &wasmModule_)) && - (cursor = linkData_->deserialize(cx, cursor)) && - (cursor = ReadBytes(cursor, &pod, sizeof(pod))) && - (cursor = DeserializeVector(cx, cursor, &globals_)) && - (cursor = DeserializePodVector(cx, cursor, &imports_)) && - (cursor = DeserializeVector(cx, cursor, &exports_)) && - (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) && - (cursor = DeserializeName(cx, cursor, &importArgumentName_)) && - (cursor = DeserializeName(cx, cursor, &bufferArgumentName_)); + UniqueModuleData base = cx->make_unique(); + if (!base) + return nullptr; + cursor = base->deserialize(cx, cursor); + if (!cursor) + return nullptr; + + MOZ_ASSERT(!base->loadedFromCache); + base->loadedFromCache = true; + + UniqueStaticLinkData link = cx->make_unique(); + if (!link) + return nullptr; + cursor = link->deserialize(cx, cursor); + if (!cursor) + return nullptr; + + UniqueAsmJSModuleData module = cx->make_unique(); + if (!module) + return nullptr; + cursor = module->deserialize(cx, cursor); + if (!cursor) + return nullptr; + + // See AsmJSModuleData comment as well as ModuleValidator::init(). + module->srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin; + module->srcBodyStart = parser.tokenStream.currentToken().pos.end; + module->strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict(); + module->scriptSource.reset(parser.ss); + + moduleObj.set(WasmModuleObject::create(cx)); + if (!moduleObj) + return nullptr; + + if (!moduleObj->init(cx->new_(Move(base), Move(link), Move(module)))) + return nullptr; return cursor; } bool -AsmJSModule::clone(JSContext* cx, HandleAsmJSModule obj) const +AsmJSModule::clone(JSContext* cx, MutableHandle moduleObj) const { - auto out = cx->new_(scriptSource(), srcStart_, srcBodyStart_, pod.strict_); - if (!out) + // Prevent any GC that may move the temporarily-unrooted atoms being cloned. + AutoKeepAtoms aka(cx->perThreadData); + + UniqueModuleData base = cx->make_unique(); + if (!base || !this->base().clone(cx, base.get())) return false; - obj->setModule(out); - - out->wasmModule_ = wasmModule_->clone(cx, *linkData_); - if (!out->wasmModule_) + UniqueStaticLinkData link = cx->make_unique(); + if (!link || !link_->clone(cx, link.get())) return false; - out->linkData_ = cx->make_unique(); - if (!out->linkData_ || !linkData_->clone(cx, out->linkData_.get())) + UniqueAsmJSModuleData module = cx->make_unique(); + if (!module || !module_->clone(cx, module.get())) return false; - out->pod = pod; - - if (!CloneVector(cx, globals_, &out->globals_) || - !ClonePodVector(cx, imports_, &out->imports_) || - !CloneVector(cx, exports_, &out->exports_)) - { + moduleObj.set(WasmModuleObject::create(cx)); + if (!moduleObj) return false; - } - out->globalArgumentName_ = globalArgumentName_; - out->importArgumentName_ = importArgumentName_; - out->bufferArgumentName_ = bufferArgumentName_; - return true; -} + if (!moduleObj->init(cx->new_(Move(base), Move(link), Move(module)))) + return false; -void -AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data) -{ - if (wasmModule_) - wasmModule_->addSizeOfMisc(mallocSizeOf, code, data); - - if (linkData_) - *data += linkData_->sizeOfExcludingThis(mallocSizeOf); - - *data += mallocSizeOf(this) + - globals_.sizeOfExcludingThis(mallocSizeOf) + - imports_.sizeOfExcludingThis(mallocSizeOf) + - exports_.sizeOfExcludingThis(mallocSizeOf); + return Module::clone(cx, *link_, &moduleObj->module()); } namespace { @@ -8402,8 +8305,9 @@ StoreAsmJSModuleInCache(AsmJSParser& parser, const AsmJSModule& module, Exclusiv } static bool -LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj, - bool* loadedFromCache, UniqueChars* compilationTimeReport) +LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loadedFromCache, + MutableHandle moduleObj, + UniqueChars* compilationTimeReport) { int64_t usecBefore = PRMJ_Now(); @@ -8438,17 +8342,7 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSM if (!moduleChars.match(parser)) return true; - uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin; - uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end; - bool strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict(); - - AsmJSModule* module = cx->new_(parser.ss, srcStart, srcBodyStart, strict); - if (!module) - return false; - - moduleObj->setModule(module); - - cursor = module->deserialize(cx, cursor); + cursor = AsmJSModule::deserialize(cx, cursor, parser, moduleObj); if (!cursor) return false; @@ -8457,12 +8351,15 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSM if (!atEnd) return true; - if (module->wasmModule().compileArgs() != CompileArgs(cx)) + AsmJSModule& module = moduleObj->module().asAsmJS(); + + if (module.compileArgs() != CompileArgs(cx)) return true; - module->staticallyLink(cx); + if (!module.staticallyLink(cx)) + return false; - if (!parser.tokenStream.advance(module->srcEndBeforeCurly())) + if (!parser.tokenStream.advance(module.srcEndBeforeCurly())) return false; *loadedFromCache = true; @@ -8591,7 +8488,7 @@ BuildConsoleMessage(ExclusiveContext* cx, AsmJSModule& module, unsigned time, return UniqueChars(JS_smprintf("total compilation time %dms; %s%s", time, cacheString, slowText ? slowText.get() : "")); #else - return make_string_copy(""); + return DuplicateString(""); #endif } @@ -8604,32 +8501,30 @@ js::CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, if (!EstablishPreconditions(cx, parser)) return NoExceptionPending(cx); - Rooted moduleObj(cx, NewAsmJSModuleObject(cx)); - if (!moduleObj) - return false; // Before spending any time parsing the module, try to look it up in the // embedding's cache using the chars about to be parsed as the key. + Rooted moduleObj(cx); bool loadedFromCache; UniqueChars message; - if (!LookupAsmJSModuleInCache(cx, parser, moduleObj, &loadedFromCache, &message)) + if (!LookupAsmJSModuleInCache(cx, parser, &loadedFromCache, &moduleObj, &message)) return false; // If not present in the cache, parse, validate and generate code in a // single linear pass over the chars of the asm.js module. if (!loadedFromCache) { // "Checking" parses, validates and compiles, producing a fully compiled - // AsmJSModuleObject as result. + // WasmModuleObject as result. unsigned time; SlowFunctionVector slowFuncs(cx); - if (!CheckModule(cx, parser, stmtList, moduleObj, &time, &slowFuncs)) + if (!CheckModule(cx, parser, stmtList, &moduleObj, &time, &slowFuncs)) return NoExceptionPending(cx); // Try to store the AsmJSModule in the embedding's cache. The // AsmJSModule must be stored before static linking since static linking // specializes the AsmJSModule to the current process's address space // and therefore must be executed after a cache hit. - AsmJSModule& module = moduleObj->module(); + AsmJSModule& module = moduleObj->module().asAsmJS(); JS::AsmJSCacheResult cacheResult = StoreAsmJSModuleInCache(parser, module, cx); if (!module.staticallyLink(cx)) return false; @@ -8660,7 +8555,7 @@ js::CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, } /*****************************************************************************/ -// asm.js module/export queries +// asm.js testing functions bool js::IsAsmJSModuleNative(Native native) @@ -8671,18 +8566,17 @@ js::IsAsmJSModuleNative(Native native) bool js::IsAsmJSModule(JSFunction* fun) { - return fun->isNative() && fun->maybeNative() == LinkAsmJS; + return fun->maybeNative() == LinkAsmJS; } bool js::IsAsmJSFunction(JSFunction* fun) { - return fun->isNative() && fun->maybeNative() == CallAsmJS; + if (IsExportedFunction(fun)) + return ExportedFunctionToModuleObject(fun)->module().isAsmJS(); + return false; } -/*****************************************************************************/ -// asm.js testing natives: - bool js::IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, Value* vp) { @@ -8701,30 +8595,31 @@ js::IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, Value* vp) return true; } -static bool -IsMaybeWrappedNativeFunction(const Value& v, Native native, JSFunction** fun = nullptr) +static JSFunction* +MaybeWrappedNativeFunction(const Value& v) { if (!v.isObject()) - return false; + return nullptr; JSObject* obj = CheckedUnwrap(&v.toObject()); if (!obj) - return false; + return nullptr; if (!obj->is()) - return false; + return nullptr; - if (fun) - *fun = &obj->as(); - - return obj->as().maybeNative() == native; + return &obj->as(); } bool js::IsAsmJSModule(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS); + + bool rval = false; + if (JSFunction* fun = MaybeWrappedNativeFunction(args.get(0))) + rval = IsAsmJSModule(fun); + args.rval().set(BooleanValue(rval)); return true; } @@ -8733,7 +8628,11 @@ bool js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS); + + bool rval = false; + if (JSFunction* fun = MaybeWrappedNativeFunction(args.get(0))) + rval = IsAsmJSFunction(fun); + args.rval().set(BooleanValue(rval)); return true; } @@ -8743,15 +8642,15 @@ js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSFunction* fun; - if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) { + JSFunction* fun = MaybeWrappedNativeFunction(args.get(0)); + if (!fun || !IsAsmJSModule(fun)) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL, "argument passed to isAsmJSModuleLoadedFromCache is not a " "validated asm.js module"); return false; } - bool loadedFromCache = FunctionToModuleObject(fun).module().wasmModule().loadedFromCache(); + bool loadedFromCache = AsmJSModuleToModuleObject(fun)->module().loadedFromCache(); args.rval().set(BooleanValue(loadedFromCache)); return true; @@ -8784,8 +8683,9 @@ AppendUseStrictSource(JSContext* cx, HandleFunction fun, Handle s JSString* js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda) { - AsmJSModule& module = FunctionToModuleObject(fun).module(); + MOZ_ASSERT(IsAsmJSModule(fun)); + AsmJSModule& module = AsmJSModuleToModuleObject(fun)->module().asAsmJS(); uint32_t begin = module.srcStart(); uint32_t end = module.srcEndAfterCurly(); ScriptSource* source = module.scriptSource(); @@ -8857,8 +8757,10 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda JSString* js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun) { - AsmJSModule& module = FunctionToModuleObject(fun).module(); - const AsmJSModule::Export& f = module.exports()[FunctionToExportIndex(fun)]; + MOZ_ASSERT(IsAsmJSFunction(fun)); + + AsmJSModule& module = ExportedFunctionToModuleObject(fun)->module().asAsmJS(); + const AsmJSExport& f = module.asmJSExports()[ExportedFunctionToIndex(fun)]; uint32_t begin = module.srcStart() + f.startOffsetInModule(); uint32_t end = module.srcStart() + f.endOffsetInModule(); @@ -8921,7 +8823,7 @@ static_assert(MinHeapLength % AsmJSPageSize == 0, "Invalid page size"); // Targets define AsmJSImmediateRange to be the size of an address immediate, // and AsmJSCheckedImmediateRange, to be the size of an address immediate that // can be supported by signal-handler OOB handling. -static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange, +static_assert(AsmJSCheckedImmediateRange <= AsmJSImmediateRange, "AsmJSImmediateRange should be the size of an unconstrained " "address immediate"); @@ -8934,7 +8836,7 @@ static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange, // might also try to access a few bytes after this limit, so just inflate it by // AsmJSPageSize. const size_t js::AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL + - jit::AsmJSImmediateRange + + AsmJSImmediateRange + AsmJSPageSize; #endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB diff --git a/js/src/asmjs/AsmJS.h b/js/src/asmjs/AsmJS.h index b3d05b7583..b928dbe408 100644 --- a/js/src/asmjs/AsmJS.h +++ b/js/src/asmjs/AsmJS.h @@ -19,11 +19,10 @@ #ifndef asmjs_asmjs_h #define asmjs_asmjs_h -#include "vm/NativeObject.h" +#include "NamespaceImports.h" namespace js { -class AsmJSModule; class ExclusiveContext; namespace frontend { template class Parser; @@ -34,29 +33,6 @@ namespace frontend { typedef frontend::Parser AsmJSParser; typedef frontend::ParseContext AsmJSParseContext; -// An AsmJSModuleObject is an internal implementation object (i.e., not exposed -// directly to user script) which traces and owns an AsmJSModule. The -// AsmJSModuleObject is referenced by the extended slots of the content-visible -// module and export JSFunctions. - -class AsmJSModuleObject : public NativeObject -{ - static const unsigned MODULE_SLOT = 0; - - public: - static const unsigned RESERVED_SLOTS = 1; - - bool hasModule() const; - void setModule(AsmJSModule* module); - AsmJSModule& module() const; - - void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data); - - static const Class class_; -}; - -typedef Handle HandleAsmJSModule; - // This function takes over parsing of a function starting with "use asm". The // return value indicates whether an error was reported which the caller should // propagate. If no error was reported, the function may still fail to validate diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h new file mode 100644 index 0000000000..02460fccbf --- /dev/null +++ b/js/src/asmjs/WasmBinary.h @@ -0,0 +1,689 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * 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_binary_h +#define wasm_binary_h + +#include "asmjs/WasmTypes.h" + +namespace js { + +class PropertyName; + +namespace wasm { + +enum class Stmt : uint8_t +{ + Ret, + + Block, + + IfThen, + IfElse, + Switch, + + While, + DoWhile, + + ForInitInc, + ForInitNoInc, + ForNoInitNoInc, + ForNoInitInc, + + Label, + Continue, + ContinueLabel, + Break, + BreakLabel, + + CallInternal, + CallIndirect, + CallImport, + + AtomicsFence, + + // asm.js specific + // Expression statements (to be removed in the future) + I32Expr, + F32Expr, + F64Expr, + I32X4Expr, + F32X4Expr, + B32X4Expr, + + Id, + Noop, + InterruptCheckHead, + InterruptCheckLoop, + + DebugCheckPoint, + + Bad +}; + +enum class I32 : uint8_t +{ + // Common opcodes + GetLocal, + SetLocal, + GetGlobal, + SetGlobal, + + CallInternal, + CallIndirect, + CallImport, + + Conditional, + Comma, + + Literal, + + // Binary arith opcodes + Add, + Sub, + Mul, + SDiv, + SMod, + UDiv, + UMod, + Min, + Max, + + // Unary arith opcodes + Not, + Neg, + + // Bitwise opcodes + BitOr, + BitAnd, + BitXor, + BitNot, + + Lsh, + ArithRsh, + LogicRsh, + + // Conversion opcodes + FromF32, + FromF64, + + // Math builtin opcodes + Clz, + Abs, + + // Comparison opcodes + // Ordering matters (EmitComparison expects signed opcodes to be placed + // before unsigned opcodes) + EqI32, + NeI32, + SLtI32, + SLeI32, + SGtI32, + SGeI32, + ULtI32, + ULeI32, + UGtI32, + UGeI32, + + EqF32, + NeF32, + LtF32, + LeF32, + GtF32, + GeF32, + + EqF64, + NeF64, + LtF64, + LeF64, + GtF64, + GeF64, + + // Heap accesses opcodes + SLoad8, + SLoad16, + SLoad32, + ULoad8, + ULoad16, + ULoad32, + Store8, + Store16, + Store32, + + // Atomics opcodes + AtomicsCompareExchange, + AtomicsExchange, + AtomicsLoad, + AtomicsStore, + AtomicsBinOp, + + // SIMD opcodes + I32X4ExtractLane, + B32X4ExtractLane, + B32X4AllTrue, + B32X4AnyTrue, + + // Specific to AsmJS + Id, + + Bad +}; + +enum class F32 : uint8_t +{ + // Common opcodes + GetLocal, + SetLocal, + GetGlobal, + SetGlobal, + + CallInternal, + CallIndirect, + CallImport, + + Conditional, + Comma, + + Literal, + + // Binary arith opcodes + Add, + Sub, + Mul, + Div, + Min, + Max, + Neg, + + // Math builtin opcodes + Abs, + Sqrt, + Ceil, + Floor, + + // Conversion opcodes + FromF64, + FromS32, + FromU32, + + // Heap accesses opcodes + Load, + StoreF32, + StoreF64, + + // SIMD opcodes + F32X4ExtractLane, + + // asm.js specific + Id, + Bad +}; + +enum class F64 : uint8_t +{ + // Common opcodes + GetLocal, + SetLocal, + GetGlobal, + SetGlobal, + + CallInternal, + CallIndirect, + CallImport, + + Conditional, + Comma, + + Literal, + + // Binary arith opcodes + Add, + Sub, + Mul, + Div, + Min, + Max, + Mod, + Neg, + + // Math builtin opcodes + Abs, + Sqrt, + Ceil, + Floor, + Sin, + Cos, + Tan, + Asin, + Acos, + Atan, + Exp, + Log, + Pow, + Atan2, + + // Conversions opcodes + FromF32, + FromS32, + FromU32, + + // Heap accesses opcodes + Load, + StoreF32, + StoreF64, + + // asm.js specific + Id, + Bad +}; + +enum class I32X4 : uint8_t +{ + // Common opcodes + GetLocal, + SetLocal, + + GetGlobal, + SetGlobal, + + CallInternal, + CallIndirect, + CallImport, + + Conditional, + Comma, + + Literal, + + // Specific opcodes + Ctor, + + Unary, + + Binary, + BinaryBitwise, + BinaryShift, + + ReplaceLane, + + FromF32X4, + FromF32X4Bits, + + Swizzle, + Shuffle, + Select, + Splat, + + Load, + Store, + + // asm.js specific + Id, + Bad +}; + +enum class F32X4 : uint8_t +{ + // Common opcodes + GetLocal, + SetLocal, + + GetGlobal, + SetGlobal, + + CallInternal, + CallIndirect, + CallImport, + + Conditional, + Comma, + + Literal, + + // Specific opcodes + Ctor, + + Unary, + + Binary, + + ReplaceLane, + + FromI32X4, + FromI32X4Bits, + Swizzle, + Shuffle, + Select, + Splat, + + Load, + Store, + + // asm.js specific + Id, + Bad +}; + +enum class B32X4 : uint8_t +{ + // Common opcodes + GetLocal, + SetLocal, + + GetGlobal, + SetGlobal, + + CallInternal, + CallIndirect, + CallImport, + + Conditional, + Comma, + + Literal, + + // Specific opcodes + Ctor, + + Unary, + + Binary, + BinaryCompI32X4, + BinaryCompF32X4, + BinaryBitwise, + + ReplaceLane, + + Splat, + + // asm.js specific + Id, + Bad +}; + +enum NeedsBoundsCheck : uint8_t +{ + NO_BOUNDS_CHECK, + NEEDS_BOUNDS_CHECK +}; + +typedef Vector Bytecode; +typedef UniquePtr UniqueBytecode; + +// The Encoder class recycles (through its constructor) or creates a new Bytecode (through its +// init() method). Its Bytecode is released when it's done building the wasm IR in finish(). +class Encoder +{ + UniqueBytecode bytecode_; + mozilla::DebugOnly done_; + + template + MOZ_WARN_UNUSED_RESULT + bool write(T v, size_t* offset) { + if (offset) + *offset = bytecode_->length(); + return bytecode_->append(reinterpret_cast(&v), sizeof(T)); + } + + public: + Encoder() + : bytecode_(nullptr), + done_(false) + {} + + bool init(UniqueBytecode bytecode) { + if (bytecode) { + bytecode_ = mozilla::Move(bytecode); + bytecode_->clear(); + return true; + } + bytecode_ = MakeUnique(); + return !!bytecode_; + } + + size_t bytecodeOffset() const { return bytecode_->length(); } + bool empty() const { return bytecodeOffset() == 0; } + + UniqueBytecode finish() { + MOZ_ASSERT(!done_); + done_ = true; + return mozilla::Move(bytecode_); + } + + MOZ_WARN_UNUSED_RESULT bool + writeU8(uint8_t i, size_t* offset = nullptr) { return write(i, offset); } + MOZ_WARN_UNUSED_RESULT bool + writeI32(int32_t i, size_t* offset = nullptr) { return write(i, offset); } + MOZ_WARN_UNUSED_RESULT bool + writeU32(uint32_t i, size_t* offset = nullptr) { return write(i, offset); } + MOZ_WARN_UNUSED_RESULT bool + writeF32(float f, size_t* offset = nullptr) { return write(f, offset); } + MOZ_WARN_UNUSED_RESULT bool + writeF64(double d, size_t* offset = nullptr) { return write(d, offset); } + + MOZ_WARN_UNUSED_RESULT bool + writeI32X4(const int32_t* i4, size_t* offset = nullptr) { + if (!writeI32(i4[0], offset)) + return false; + for (size_t i = 1; i < 4; i++) { + if (!writeI32(i4[i])) + return false; + } + return true; + } + MOZ_WARN_UNUSED_RESULT bool + writeF32X4(const float* f4, size_t* offset = nullptr) { + if (!writeF32(f4[0], offset)) + return false; + for (size_t i = 1; i < 4; i++) { + if (!writeF32(f4[i])) + return false; + } + return true; + } + +#ifdef DEBUG + bool pcIsPatchable(size_t pc, unsigned size) const { + bool patchable = true; + for (unsigned i = 0; patchable && i < size; i++) + patchable &= Stmt((*bytecode_)[pc]) == Stmt::Bad; + return patchable; + } +#endif + + void patchU8(size_t pc, uint8_t i) { + MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t))); + (*bytecode_)[pc] = i; + } + + template + void patch32(size_t pc, T i) { + static_assert(sizeof(T) == sizeof(uint32_t), + "patch32 must be used with 32-bits wide types"); + MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t))); + memcpy(&(*bytecode_)[pc], &i, sizeof(uint32_t)); + } + + void patchSig(size_t pc, const LifoSig* ptr) { + MOZ_ASSERT(pcIsPatchable(pc, sizeof(LifoSig*))); + memcpy(&(*bytecode_)[pc], &ptr, sizeof(LifoSig*)); + } +}; + +class Decoder +{ + const Bytecode& bytecode_; + size_t cur_; + + template + MOZ_WARN_UNUSED_RESULT + bool read(T* out) { + if (uintptr_t(bytecode_.length() - cur_) < sizeof(T)) + return false; + memcpy((void*)out, &bytecode_[cur_], sizeof(T)); + cur_ += sizeof(T); + return true; + } + + template + T uncheckedRead() { + MOZ_ASSERT(uintptr_t(bytecode_.length() - cur_) >= sizeof(T)); + T ret; + memcpy(&ret, &bytecode_[cur_], sizeof(T)); + cur_ += sizeof(T); + return ret; + } + + public: + explicit Decoder(const Bytecode& bytecode) + : bytecode_(bytecode), + cur_(0) + {} + + bool done() const { return cur_ == bytecode_.length(); } + void assertCurrentIs(const DebugOnly offset) const { + MOZ_ASSERT(offset == cur_); + } + + // The fallible unpacking API should be used when we're not assuming + // anything about the bytecode, in particular if it is well-formed. + MOZ_WARN_UNUSED_RESULT bool readU8 (uint8_t* i) { return read(i); } + MOZ_WARN_UNUSED_RESULT bool readI32(int32_t* i) { return read(i); } + MOZ_WARN_UNUSED_RESULT bool readF32(float* f) { return read(f); } + MOZ_WARN_UNUSED_RESULT bool readU32(uint32_t* u) { return read(u); } + MOZ_WARN_UNUSED_RESULT bool readF64(double* d) { return read(d); } + MOZ_WARN_UNUSED_RESULT bool readSig(const LifoSig* sig) { return read(sig); } + + MOZ_WARN_UNUSED_RESULT bool readI32X4(jit::SimdConstant* c) { + int32_t v[4] = { 0, 0, 0, 0 }; + for (size_t i = 0; i < 4; i++) { + if (!readI32(&v[i])) + return false; + } + *c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); + return true; + } + MOZ_WARN_UNUSED_RESULT bool readF32X4(jit::SimdConstant* c) { + float v[4] = { 0., 0., 0., 0. }; + for (size_t i = 0; i < 4; i++) { + if (!readF32(&v[i])) + return false; + } + *c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); + return true; + } + + // The unfallible unpacking API should be used when we are sure that the + // bytecode is well-formed. + uint8_t uncheckedReadU8 () { return uncheckedRead(); } + int32_t uncheckedReadI32() { return uncheckedRead(); } + float uncheckedReadF32() { return uncheckedRead(); } + uint32_t uncheckedReadU32() { return uncheckedRead(); } + double uncheckedReadF64() { return uncheckedRead(); } + const LifoSig* uncheckedReadSig() { return uncheckedRead(); } + + jit::SimdConstant uncheckedReadI32X4() { + int32_t v[4] = { 0, 0, 0, 0 }; + for (size_t i = 0; i < 4; i++) + v[i] = uncheckedReadI32(); + return jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); + } + jit::SimdConstant uncheckedReadF32X4() { + float v[4] = { 0., 0., 0., 0. }; + for (size_t i = 0; i < 4; i++) + v[i] = uncheckedReadF32(); + return jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); + } +}; + +// Source coordinates for a call site. As they're read sequentially, we +// don't need to store the call's bytecode offset, unless we want to +// check its correctness in debug mode. +struct SourceCoords { + DebugOnly offset; // after call opcode + uint32_t line; + uint32_t column; +}; + +typedef Vector SourceCoordsVector; +typedef Vector ValTypeVector; + +// The FuncBytecode class contains the intermediate representation of a +// parsed/decoded and validated asm.js/WebAssembly function. The FuncBytecode +// lives only until it is fully compiled. +class FuncBytecode +{ + // Note: this unrooted field assumes AutoKeepAtoms via TokenStream via + // asm.js compilation. + PropertyName* name_; + unsigned line_; + unsigned column_; + + SourceCoordsVector callSourceCoords_; + + uint32_t index_; + const LifoSig& sig_; + UniqueBytecode bytecode_; + ValTypeVector localVars_; + unsigned generateTime_; + + public: + FuncBytecode(PropertyName* name, + unsigned line, + unsigned column, + SourceCoordsVector&& sourceCoords, + uint32_t index, + const LifoSig& sig, + UniqueBytecode bytecode, + ValTypeVector&& localVars, + unsigned generateTime) + : name_(name), + line_(line), + column_(column), + callSourceCoords_(mozilla::Move(sourceCoords)), + index_(index), + sig_(sig), + bytecode_(mozilla::Move(bytecode)), + localVars_(mozilla::Move(localVars)), + generateTime_(generateTime) + {} + + UniqueBytecode recycleBytecode() { return mozilla::Move(bytecode_); } + + PropertyName* name() const { return name_; } + unsigned line() const { return line_; } + unsigned column() const { return column_; } + const SourceCoords& sourceCoords(size_t i) const { return callSourceCoords_[i]; } + + uint32_t index() const { return index_; } + const LifoSig& sig() const { return sig_; } + const Bytecode& bytecode() const { return *bytecode_; } + + size_t numLocalVars() const { return localVars_.length(); } + ValType localVarType(size_t i) const { return localVars_[i]; } + size_t numLocals() const { return sig_.args().length() + numLocalVars(); } + + unsigned generateTime() const { return generateTime_; } +}; + +typedef UniquePtr UniqueFuncBytecode; + +} // namespace wasm +} // namespace js + +#endif // wasm_binary_h diff --git a/js/src/asmjs/WasmFrameIterator.cpp b/js/src/asmjs/WasmFrameIterator.cpp index 0f92513198..e94424374d 100644 --- a/js/src/asmjs/WasmFrameIterator.cpp +++ b/js/src/asmjs/WasmFrameIterator.cpp @@ -18,8 +18,6 @@ #include "asmjs/WasmFrameIterator.h" -#include "jsatom.h" - #include "asmjs/WasmModule.h" #include "jit/MacroAssembler-inl.h" @@ -109,16 +107,7 @@ FrameIterator::functionDisplayAtom() const MOZ_ASSERT(!done()); const char* chars = module_->functionName(codeRange_->funcNameIndex()); - UTF8Chars utf8(chars, strlen(chars)); - - size_t twoByteLength; - UniquePtr twoByte(JS::UTF8CharsToNewTwoByteCharsZ(cx_, utf8, &twoByteLength).get()); - if (!twoByte) { - cx_->clearPendingException(); - return cx_->names().empty; - } - - JSAtom* atom = AtomizeChars(cx_, twoByte.get(), twoByteLength); + JSAtom* atom = AtomizeUTF8Chars(cx_, chars, strlen(chars)); if (!atom) { cx_->clearPendingException(); return cx_->names().empty; diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp index 38bc192073..9849c07bba 100644 --- a/js/src/asmjs/WasmGenerator.cpp +++ b/js/src/asmjs/WasmGenerator.cpp @@ -35,21 +35,19 @@ static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024; ModuleGenerator::ModuleGenerator(ExclusiveContext* cx) : cx_(cx), - args_(cx), - globalBytes_(InitialGlobalDataBytes), slowFuncs_(cx), lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE), jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())), alloc_(&lifo_), masm_(MacroAssembler::AsmJSToken(), alloc_), sigs_(cx), + funcEntryOffsets_(cx), + exportFuncIndices_(cx), + funcIndexToExport_(cx), parallel_(false), outstanding_(0), tasks_(cx), freeTasks_(cx), - funcBytes_(0), - funcEntryOffsets_(cx), - exportFuncIndices_(cx), activeFunc_(nullptr), finishedFuncs_(false) { @@ -109,11 +107,18 @@ ParallelCompilationEnabled(ExclusiveContext* cx) bool ModuleGenerator::init() { - staticLinkData_ = cx_->make_unique(); - if (!staticLinkData_) + module_ = cx_->make_unique(); + if (!module_) return false; - if (!sigs_.init()) + module_->globalBytes = InitialGlobalDataBytes; + module_->compileArgs = CompileArgs(cx_); + + link_ = cx_->make_unique(); + if (!link_) + return false; + + if (!sigs_.init() || !funcIndexToExport_.init()) return false; uint32_t numTasks; @@ -139,7 +144,7 @@ ModuleGenerator::init() return false; JSRuntime* runtime = cx_->compartment()->runtimeFromAnyThread(); for (size_t i = 0; i < numTasks; i++) - tasks_.infallibleEmplaceBack(runtime, args_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE); + tasks_.infallibleEmplaceBack(runtime, args(), COMPILATION_LIFO_DEFAULT_CHUNK_SIZE); if (!freeTasks_.reserve(numTasks)) return false; @@ -152,13 +157,17 @@ ModuleGenerator::init() bool ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset) { - uint32_t pad = ComputeByteAlignment(globalBytes_, align); - if (UINT32_MAX - globalBytes_ < pad + bytes) + uint32_t globalBytes = module_->globalBytes; + + uint32_t pad = ComputeByteAlignment(globalBytes, align); + if (UINT32_MAX - globalBytes < pad + bytes) return false; - globalBytes_ += pad; - *globalDataOffset = globalBytes_; - globalBytes_ += bytes; + globalBytes += pad; + *globalDataOffset = globalBytes; + globalBytes += bytes; + + module_->globalBytes = globalBytes; return true; } @@ -192,7 +201,7 @@ ModuleGenerator::finishOutstandingTask() bool ModuleGenerator::finishTask(IonCompileTask* task) { - const FuncIR& func = task->func(); + const FuncBytecode& func = task->func(); FuncCompileResults& results = task->results(); // Offset the recorded FuncOffsets by the offset of the function in the @@ -217,10 +226,10 @@ ModuleGenerator::finishTask(IonCompileTask* task) CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func.name()); if (!funcName) return false; - uint32_t nameIndex = funcNames_.length(); - if (!funcNames_.emplaceBack(Move(funcName))) + uint32_t nameIndex = module_->funcNames.length(); + if (!module_->funcNames.emplaceBack(Move(funcName))) return false; - if (!codeRanges_.emplaceBack(nameIndex, func.line(), results.offsets())) + if (!module_->codeRanges.emplaceBack(nameIndex, func.line(), results.offsets())) return false; // Keep a record of slow functions for printing in the final console message. @@ -230,7 +239,6 @@ ModuleGenerator::finishTask(IonCompileTask* task) return false; } - task->reset(); freeTasks_.infallibleAppend(task); return true; } @@ -280,42 +288,50 @@ ModuleGenerator::declareImport(MallocSig&& sig, unsigned* index) if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset)) return false; - *index = unsigned(imports_.length()); - return imports_.emplaceBack(Move(sig), globalDataOffset); + *index = unsigned(module_->imports.length()); + return module_->imports.emplaceBack(Move(sig), globalDataOffset); } uint32_t -ModuleGenerator::numDeclaredImports() const +ModuleGenerator::numImports() const { - return imports_.length(); + return module_->imports.length(); } uint32_t ModuleGenerator::importExitGlobalDataOffset(uint32_t index) const { - return imports_[index].exitGlobalDataOffset(); + return module_->imports[index].exitGlobalDataOffset(); } const MallocSig& ModuleGenerator::importSig(uint32_t index) const { - return imports_[index].sig(); + return module_->imports[index].sig(); } bool ModuleGenerator::defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit) { - Import& import = imports_[index]; + Import& import = module_->imports[index]; import.initInterpExitOffset(interpExit.begin); import.initJitExitOffset(jitExit.begin); - return codeRanges_.emplaceBack(CodeRange::ImportInterpExit, interpExit) && - codeRanges_.emplaceBack(CodeRange::ImportJitExit, jitExit); + return module_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExit) && + module_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExit); } bool -ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex) +ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* exportIndex) { - return exports_.emplaceBack(Move(sig)) && + FuncIndexMap::AddPtr p = funcIndexToExport_.lookupForAdd(funcIndex); + if (p) { + *exportIndex = p->value(); + return true; + } + + *exportIndex = module_->exports.length(); + return funcIndexToExport_.add(p, funcIndex, *exportIndex) && + module_->exports.append(Move(sig)) && exportFuncIndices_.append(funcIndex); } @@ -328,25 +344,25 @@ ModuleGenerator::exportFuncIndex(uint32_t index) const const MallocSig& ModuleGenerator::exportSig(uint32_t index) const { - return exports_[index].sig(); + return module_->exports[index].sig(); } uint32_t -ModuleGenerator::numDeclaredExports() const +ModuleGenerator::numExports() const { - return exports_.length(); + return module_->exports.length(); } bool ModuleGenerator::defineExport(uint32_t index, Offsets offsets) { - exports_[index].initStubOffset(offsets.begin); - return codeRanges_.emplaceBack(CodeRange::Entry, offsets); + module_->exports[index].initStubOffset(offsets.begin); + return module_->codeRanges.emplaceBack(CodeRange::Entry, offsets); } bool ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column, - FunctionGenerator* fg) + UniqueBytecode* recycled, FunctionGenerator* fg) { MOZ_ASSERT(!activeFunc_); MOZ_ASSERT(!finishedFuncs_); @@ -355,25 +371,38 @@ ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column, return false; IonCompileTask* task = freeTasks_.popCopy(); - FuncIR* func = task->lifo().new_(task->lifo(), name, line, column); - if (!func) - return false; - task->init(*func); + task->reset(recycled); + + fg->name_= name; + fg->line_ = line; + fg->column_ = column; fg->m_ = this; fg->task_ = task; - fg->func_ = func; activeFunc_ = fg; return true; } bool -ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime, - FunctionGenerator* fg) +ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, UniqueBytecode bytecode, + unsigned generateTime, FunctionGenerator* fg) { MOZ_ASSERT(activeFunc_ == fg); - fg->func_->finish(funcIndex, sig, generateTime); + UniqueFuncBytecode func = cx_->make_unique(fg->name_, + fg->line_, + fg->column_, + Move(fg->callSourceCoords_), + funcIndex, + sig, + Move(bytecode), + Move(fg->localVars_), + generateTime + ); + if (!func) + return false; + + fg->task_->init(Move(func)); if (parallel_) { if (!StartOffThreadWasmCompile(cx_, fg->task_)) @@ -388,7 +417,6 @@ ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned gen fg->m_ = nullptr; fg->task_ = nullptr; - fg->func_ = nullptr; activeFunc_ = nullptr; return true; } @@ -419,7 +447,7 @@ ModuleGenerator::finishFuncs() masm_.patchCall(callerOffset, calleeOffset); } - funcBytes_ = masm_.size(); + module_->functionBytes = masm_.size(); finishedFuncs_ = true; return true; } @@ -439,7 +467,7 @@ ModuleGenerator::declareFuncPtrTable(uint32_t numElems, uint32_t* index) if (!allocateGlobalBytes(numElems * sizeof(void*), sizeof(void*), &globalDataOffset)) return false; - StaticLinkData::FuncPtrTableVector& tables = staticLinkData_->funcPtrTables; + StaticLinkData::FuncPtrTableVector& tables = link_->funcPtrTables; *index = tables.length(); if (!tables.emplaceBack(globalDataOffset)) @@ -454,7 +482,7 @@ ModuleGenerator::declareFuncPtrTable(uint32_t numElems, uint32_t* index) uint32_t ModuleGenerator::funcPtrTableGlobalDataOffset(uint32_t index) const { - return staticLinkData_->funcPtrTables[index].globalDataOffset; + return link_->funcPtrTables[index].globalDataOffset; } void @@ -462,7 +490,7 @@ ModuleGenerator::defineFuncPtrTable(uint32_t index, const Vector& elem { MOZ_ASSERT(finishedFuncs_); - StaticLinkData::FuncPtrTable& table = staticLinkData_->funcPtrTables[index]; + StaticLinkData::FuncPtrTable& table = link_->funcPtrTables[index]; MOZ_ASSERT(table.elemOffsets.length() == elemFuncIndices.length()); for (size_t i = 0; i < elemFuncIndices.length(); i++) @@ -473,69 +501,74 @@ bool ModuleGenerator::defineInlineStub(Offsets offsets) { MOZ_ASSERT(finishedFuncs_); - return codeRanges_.emplaceBack(CodeRange::Inline, offsets); + return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets); } bool ModuleGenerator::defineSyncInterruptStub(ProfilingOffsets offsets) { MOZ_ASSERT(finishedFuncs_); - return codeRanges_.emplaceBack(CodeRange::Interrupt, offsets); + return module_->codeRanges.emplaceBack(CodeRange::Interrupt, offsets); } bool ModuleGenerator::defineAsyncInterruptStub(Offsets offsets) { MOZ_ASSERT(finishedFuncs_); - staticLinkData_->pod.interruptOffset = offsets.begin; - return codeRanges_.emplaceBack(CodeRange::Inline, offsets); + link_->pod.interruptOffset = offsets.begin; + return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets); } bool ModuleGenerator::defineOutOfBoundsStub(Offsets offsets) { MOZ_ASSERT(finishedFuncs_); - staticLinkData_->pod.outOfBoundsOffset = offsets.begin; - return codeRanges_.emplaceBack(CodeRange::Inline, offsets); + link_->pod.outOfBoundsOffset = offsets.begin; + return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets); } -Module* +bool ModuleGenerator::finish(HeapUsage heapUsage, - Module::MutedBool mutedErrors, + MutedErrorsBool mutedErrors, CacheableChars filename, CacheableTwoByteChars displayURL, - UniqueStaticLinkData* staticLinkData, + UniqueModuleData* module, + UniqueStaticLinkData* linkData, SlowFunctionVector* slowFuncs) { MOZ_ASSERT(!activeFunc_); MOZ_ASSERT(finishedFuncs_); + module_->heapUsage = heapUsage; + module_->mutedErrors = mutedErrors; + module_->filename = Move(filename); + if (!GenerateStubs(*this, UsesHeap(heapUsage))) - return nullptr; + return false; masm_.finish(); if (masm_.oom()) - return nullptr; + return false; // Start global data on a new page so JIT code may be given independent // protection flags. Note assumption that global data starts right after // code below. - uint32_t codeBytes = AlignBytes(masm_.bytesNeeded(), AsmJSPageSize); + module_->codeBytes = AlignBytes(masm_.bytesNeeded(), AsmJSPageSize); // Inflate the global bytes up to page size so that the total bytes are a // page size (as required by the allocator functions). - globalBytes_ = AlignBytes(globalBytes_, AsmJSPageSize); - uint32_t totalBytes = codeBytes + globalBytes_; + module_->globalBytes = AlignBytes(module_->globalBytes, AsmJSPageSize); // Allocate the code (guarded by a UniquePtr until it is given to the Module). - UniqueCodePtr code = AllocateCode(cx_, totalBytes); - if (!code) - return nullptr; + module_->code = AllocateCode(cx_, module_->totalBytes()); + if (!module_->code) + return false; // Delay flushing until Module::dynamicallyLink. The flush-inhibited range // is set by executableCopy. AutoFlushICache afc("ModuleGenerator::finish", /* inhibit = */ true); - masm_.executableCopy(code.get()); + uint8_t* code = module_->code.get(); + masm_.executableCopy(code); // c.f. JitCode::copyFrom MOZ_ASSERT(masm_.jumpRelocationTableBytes() == 0); @@ -545,16 +578,18 @@ ModuleGenerator::finish(HeapUsage heapUsage, // Convert the CallSiteAndTargetVector (needed during generation) to a // CallSiteVector (what is stored in the Module). - CallSiteVector callSites; - if (!callSites.appendAll(masm_.callSites())) - return nullptr; + if (!module_->callSites.appendAll(masm_.callSites())) + return false; + + // The MacroAssembler has accumulated all the heap accesses during codegen. + module_->heapAccesses = masm_.extractHeapAccesses(); // Add links to absolute addresses identified symbolically. - StaticLinkData::SymbolicLinkArray& symbolicLinks = staticLinkData_->symbolicLinks; + StaticLinkData::SymbolicLinkArray& symbolicLinks = link_->symbolicLinks; for (size_t i = 0; i < masm_.numAsmJSAbsoluteAddresses(); i++) { AsmJSAbsoluteAddress src = masm_.asmJSAbsoluteAddress(i); if (!symbolicLinks[src.target].append(src.patchAt.offset())) - return nullptr; + return false; } // Relative link metadata: absolute addresses that refer to another point within @@ -567,8 +602,8 @@ ModuleGenerator::finish(HeapUsage heapUsage, StaticLinkData::InternalLink link(StaticLinkData::InternalLink::CodeLabel); link.patchAtOffset = masm_.labelToPatchOffset(*cl.patchAt()); link.targetOffset = cl.target()->offset(); - if (!staticLinkData_->internalLinks.append(link)) - return nullptr; + if (!link_->internalLinks.append(link)) + return false; } #if defined(JS_CODEGEN_X86) @@ -579,9 +614,9 @@ ModuleGenerator::finish(HeapUsage heapUsage, AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i); StaticLinkData::InternalLink link(StaticLinkData::InternalLink::RawPointer); link.patchAtOffset = masm_.labelToPatchOffset(a.patchAt); - link.targetOffset = codeBytes + a.globalDataOffset; - if (!staticLinkData_->internalLinks.append(link)) - return nullptr; + link.targetOffset = module_->codeBytes + a.globalDataOffset; + if (!link_->internalLinks.append(link)) + return false; } #endif @@ -593,38 +628,24 @@ ModuleGenerator::finish(HeapUsage heapUsage, size_t off = masm_.longJump(i); StaticLinkData::InternalLink link(StaticLinkData::InternalLink::InstructionImmediate); link.patchAtOffset = off; - link.targetOffset = Assembler::ExtractInstructionImmediate(code.get() + off) - - uintptr_t(code.get()); - if (!staticLinkData_->internalLinks.append(link)) - return nullptr; + link.targetOffset = Assembler::ExtractInstructionImmediate(code + off) - uintptr_t(code); + if (!link_->internalLinks.append(link)) + return false; } #endif #if defined(JS_CODEGEN_X64) // Global data accesses on x64 use rip-relative addressing and thus do // not need patching after deserialization. - uint8_t* globalData = code.get() + codeBytes; + uint8_t* globalData = code + module_->codeBytes; for (size_t i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) { AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i); - masm_.patchAsmJSGlobalAccess(a.patchAt, code.get(), globalData, a.globalDataOffset); + masm_.patchAsmJSGlobalAccess(a.patchAt, code, globalData, a.globalDataOffset); } #endif - *staticLinkData = Move(staticLinkData_); + *module = Move(module_); + *linkData = Move(link_); *slowFuncs = Move(slowFuncs_); - return cx_->new_(args_, - funcBytes_, - codeBytes, - globalBytes_, - heapUsage, - mutedErrors, - Move(code), - Move(imports_), - Move(exports_), - masm_.extractHeapAccesses(), - Move(codeRanges_), - Move(callSites), - Move(funcNames_), - Move(filename), - Move(displayURL)); + return true; } diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h index 93eb6babb7..c9363301f6 100644 --- a/js/src/asmjs/WasmGenerator.h +++ b/js/src/asmjs/WasmGenerator.h @@ -19,8 +19,8 @@ #ifndef wasm_generator_h #define wasm_generator_h +#include "asmjs/WasmBinary.h" #include "asmjs/WasmIonCompile.h" -#include "asmjs/WasmIR.h" #include "asmjs/WasmModule.h" #include "jit/MacroAssembler.h" @@ -55,6 +55,7 @@ class MOZ_STACK_CLASS ModuleGenerator { typedef Vector FuncOffsetVector; typedef Vector FuncIndexVector; + typedef HashMap FuncIndexMap; struct SigHashPolicy { @@ -65,17 +66,10 @@ class MOZ_STACK_CLASS ModuleGenerator typedef HashSet SigSet; ExclusiveContext* cx_; - CompileArgs args_; - - // Data handed over to the Module in finish() - uint32_t globalBytes_; - ImportVector imports_; - ExportVector exports_; - CodeRangeVector codeRanges_; - CacheableCharsVector funcNames_; // Data handed back to the caller in finish() - UniqueStaticLinkData staticLinkData_; + UniqueModuleData module_; + UniqueStaticLinkData link_; SlowFunctionVector slowFuncs_; // Data scoped to the ModuleGenerator's lifetime @@ -84,6 +78,9 @@ class MOZ_STACK_CLASS ModuleGenerator jit::TempAllocator alloc_; jit::MacroAssembler masm_; SigSet sigs_; + FuncOffsetVector funcEntryOffsets_; + FuncIndexVector exportFuncIndices_; + FuncIndexMap funcIndexToExport_; // Parallel compilation bool parallel_; @@ -91,10 +88,7 @@ class MOZ_STACK_CLASS ModuleGenerator Vector tasks_; Vector freeTasks_; - // Function compilation - uint32_t funcBytes_; - FuncOffsetVector funcEntryOffsets_; - FuncIndexVector exportFuncIndices_; + // Assertions DebugOnly activeFunc_; DebugOnly finishedFuncs_; @@ -108,7 +102,7 @@ class MOZ_STACK_CLASS ModuleGenerator bool init(); - CompileArgs args() const { return args_; } + CompileArgs args() const { return module_->compileArgs; } jit::MacroAssembler& masm() { return masm_; } const FuncOffsetVector& funcEntryOffsets() const { return funcEntryOffsets_; } @@ -119,21 +113,23 @@ class MOZ_STACK_CLASS ModuleGenerator // Imports: bool declareImport(MallocSig&& sig, uint32_t* index); - uint32_t numDeclaredImports() const; + uint32_t numImports() const; uint32_t importExitGlobalDataOffset(uint32_t index) const; const MallocSig& importSig(uint32_t index) const; bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit); // Exports: - bool declareExport(MallocSig&& sig, uint32_t funcIndex); - uint32_t numDeclaredExports() const; + bool declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* exportIndex); + uint32_t numExports() const; uint32_t exportFuncIndex(uint32_t index) const; const MallocSig& exportSig(uint32_t index) const; bool defineExport(uint32_t index, Offsets offsets); // Functions: - bool startFunc(PropertyName* name, unsigned line, unsigned column, FunctionGenerator* fg); - bool finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime, FunctionGenerator* fg); + bool startFunc(PropertyName* name, unsigned line, unsigned column, UniqueBytecode* recycled, + FunctionGenerator* fg); + bool finishFunc(uint32_t funcIndex, const LifoSig& sig, UniqueBytecode bytecode, + unsigned generateTime, FunctionGenerator* fg); bool finishFuncs(); // Function-pointer tables: @@ -147,14 +143,16 @@ class MOZ_STACK_CLASS ModuleGenerator bool defineAsyncInterruptStub(Offsets offsets); bool defineOutOfBoundsStub(Offsets offsets); - // Null return indicates failure. The caller must immediately root a - // non-null return value. - Module* finish(HeapUsage heapUsage, - Module::MutedBool mutedErrors, - CacheableChars filename, - CacheableTwoByteChars displayURL, - UniqueStaticLinkData* staticLinkData, - SlowFunctionVector* slowFuncs); + // Return a ModuleData object which may be used to construct a Module, the + // StaticLinkData required to call Module::staticallyLink, and the list of + // functions that took a long time to compile. + bool finish(HeapUsage heapUsage, + MutedErrorsBool mutedErrors, + CacheableChars filename, + CacheableTwoByteChars displayURL, + UniqueModuleData* module, + UniqueStaticLinkData* staticLinkData, + SlowFunctionVector* slowFuncs); }; // A FunctionGenerator encapsulates the generation of a single function body. @@ -166,13 +164,36 @@ class MOZ_STACK_CLASS FunctionGenerator { friend class ModuleGenerator; - ModuleGenerator* m_; - IonCompileTask* task_; - FuncIR* func_; + ModuleGenerator* m_; + IonCompileTask* task_; + + // Function metadata created during function generation, then handed over + // to the FuncBytecode in ModuleGenerator::finishFunc(). + SourceCoordsVector callSourceCoords_; + ValTypeVector localVars_; + + // Note: this unrooted field assumes AutoKeepAtoms via TokenStream via + // asm.js compilation. + PropertyName* name_; + unsigned line_; + unsigned column_; public: - FunctionGenerator() : m_(nullptr), task_(nullptr), func_(nullptr) {} - FuncIR& func() const { MOZ_ASSERT(func_); return *func_; } + FunctionGenerator() + : m_(nullptr), + task_(nullptr), + name_(nullptr), + line_(0), + column_(0) + {} + + bool addSourceCoords(size_t byteOffset, uint32_t line, uint32_t column) { + SourceCoords sc = { byteOffset, line, column }; + return callSourceCoords_.append(sc); + } + bool addVariable(ValType v) { + return localVars_.append(v); + } }; } // namespace wasm diff --git a/js/src/asmjs/WasmIR.h b/js/src/asmjs/WasmIR.h deleted file mode 100644 index 090a8ed12a..0000000000 --- a/js/src/asmjs/WasmIR.h +++ /dev/null @@ -1,594 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * - * 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_ir_h -#define wasm_ir_h - -#include "asmjs/WasmTypes.h" - -namespace js { - -class PropertyName; - -namespace wasm { - -enum class Stmt : uint8_t -{ - Ret, - - Block, - - IfThen, - IfElse, - Switch, - - While, - DoWhile, - - ForInitInc, - ForInitNoInc, - ForNoInitNoInc, - ForNoInitInc, - - Label, - Continue, - ContinueLabel, - Break, - BreakLabel, - - CallInternal, - CallIndirect, - CallImport, - - AtomicsFence, - - // asm.js specific - // Expression statements (to be removed in the future) - I32Expr, - F32Expr, - F64Expr, - I32X4Expr, - F32X4Expr, - B32X4Expr, - - Id, - Noop, - InterruptCheckHead, - InterruptCheckLoop, - - DebugCheckPoint, - - Bad -}; - -enum class I32 : uint8_t -{ - // Common opcodes - GetLocal, - SetLocal, - GetGlobal, - SetGlobal, - - CallInternal, - CallIndirect, - CallImport, - - Conditional, - Comma, - - Literal, - - // Binary arith opcodes - Add, - Sub, - Mul, - SDiv, - SMod, - UDiv, - UMod, - Min, - Max, - - // Unary arith opcodes - Not, - Neg, - - // Bitwise opcodes - BitOr, - BitAnd, - BitXor, - BitNot, - - Lsh, - ArithRsh, - LogicRsh, - - // Conversion opcodes - FromF32, - FromF64, - - // Math builtin opcodes - Clz, - Abs, - - // Comparison opcodes - // Ordering matters (EmitComparison expects signed opcodes to be placed - // before unsigned opcodes) - EqI32, - NeI32, - SLtI32, - SLeI32, - SGtI32, - SGeI32, - ULtI32, - ULeI32, - UGtI32, - UGeI32, - - EqF32, - NeF32, - LtF32, - LeF32, - GtF32, - GeF32, - - EqF64, - NeF64, - LtF64, - LeF64, - GtF64, - GeF64, - - // Heap accesses opcodes - SLoad8, - SLoad16, - SLoad32, - ULoad8, - ULoad16, - ULoad32, - Store8, - Store16, - Store32, - - // Atomics opcodes - AtomicsCompareExchange, - AtomicsExchange, - AtomicsLoad, - AtomicsStore, - AtomicsBinOp, - - // SIMD opcodes - I32X4ExtractLane, - B32X4ExtractLane, - B32X4AllTrue, - B32X4AnyTrue, - - // Specific to AsmJS - Id, - - Bad -}; - -enum class F32 : uint8_t -{ - // Common opcodes - GetLocal, - SetLocal, - GetGlobal, - SetGlobal, - - CallInternal, - CallIndirect, - CallImport, - - Conditional, - Comma, - - Literal, - - // Binary arith opcodes - Add, - Sub, - Mul, - Div, - Min, - Max, - Neg, - - // Math builtin opcodes - Abs, - Sqrt, - Ceil, - Floor, - - // Conversion opcodes - FromF64, - FromS32, - FromU32, - - // Heap accesses opcodes - Load, - StoreF32, - StoreF64, - - // SIMD opcodes - F32X4ExtractLane, - - // asm.js specific - Id, - Bad -}; - -enum class F64 : uint8_t -{ - // Common opcodes - GetLocal, - SetLocal, - GetGlobal, - SetGlobal, - - CallInternal, - CallIndirect, - CallImport, - - Conditional, - Comma, - - Literal, - - // Binary arith opcodes - Add, - Sub, - Mul, - Div, - Min, - Max, - Mod, - Neg, - - // Math builtin opcodes - Abs, - Sqrt, - Ceil, - Floor, - Sin, - Cos, - Tan, - Asin, - Acos, - Atan, - Exp, - Log, - Pow, - Atan2, - - // Conversions opcodes - FromF32, - FromS32, - FromU32, - - // Heap accesses opcodes - Load, - StoreF32, - StoreF64, - - // asm.js specific - Id, - Bad -}; - -enum class I32X4 : uint8_t -{ - // Common opcodes - GetLocal, - SetLocal, - - GetGlobal, - SetGlobal, - - CallInternal, - CallIndirect, - CallImport, - - Conditional, - Comma, - - Literal, - - // Specific opcodes - Ctor, - - Unary, - - Binary, - BinaryBitwise, - BinaryShift, - - ReplaceLane, - - FromF32X4, - FromF32X4Bits, - - Swizzle, - Shuffle, - Select, - Splat, - - Load, - Store, - - // asm.js specific - Id, - Bad -}; - -enum class F32X4 : uint8_t -{ - // Common opcodes - GetLocal, - SetLocal, - - GetGlobal, - SetGlobal, - - CallInternal, - CallIndirect, - CallImport, - - Conditional, - Comma, - - Literal, - - // Specific opcodes - Ctor, - - Unary, - - Binary, - - ReplaceLane, - - FromI32X4, - FromI32X4Bits, - Swizzle, - Shuffle, - Select, - Splat, - - Load, - Store, - - // asm.js specific - Id, - Bad -}; - -enum class B32X4 : uint8_t -{ - // Common opcodes - GetLocal, - SetLocal, - - GetGlobal, - SetGlobal, - - CallInternal, - CallIndirect, - CallImport, - - Conditional, - Comma, - - Literal, - - // Specific opcodes - Ctor, - - Unary, - - Binary, - BinaryCompI32X4, - BinaryCompF32X4, - BinaryBitwise, - - ReplaceLane, - - Splat, - - // asm.js specific - Id, - Bad -}; - -enum NeedsBoundsCheck : uint8_t -{ - NO_BOUNDS_CHECK, - NEEDS_BOUNDS_CHECK -}; - -// The FuncIR class contains the intermediate representation of a parsed/decoded -// and validated asm.js/WebAssembly function. The FuncIR lives only until it -// is fully compiled. Its contents are assumed to be well-formed; all validation -// of untrusted content must happen before FuncIR generation. A FuncIR object is -// associated with a LifoAlloc allocation which contains all the memory -// referenced by the FuncIR. -class FuncIR -{ - typedef Vector> ValTypeVector; - typedef Vector> Bytecode; - - public: - // Source coordinates for a call site. As they're read sequentially, we - // don't need to store the call's bytecode offset, unless we want to - // check its consistency in debug mode. - struct SourceCoords { - DebugOnly offset; // after call opcode - uint32_t line; - uint32_t column; - }; - - private: - typedef Vector> SourceCoordsVector; - - // Note: this unrooted field assumes AutoKeepAtoms via TokenStream via - // asm.js compilation. - PropertyName* name_; - unsigned line_; - unsigned column_; - - uint32_t index_; - const LifoSig* sig_; - ValTypeVector localVars_; - SourceCoordsVector callSourceCoords_; - Bytecode bytecode_; - unsigned generateTime_; - - public: - FuncIR(LifoAlloc& alloc, PropertyName* name, unsigned line, unsigned column) - : name_(name), - line_(line), - column_(column), - index_(UINT_MAX), - sig_(nullptr), - localVars_(alloc), - callSourceCoords_(alloc), - bytecode_(alloc), - generateTime_(UINT_MAX) - {} - - bool addVariable(ValType v) { - return localVars_.append(v); - } - bool addSourceCoords(uint32_t line, uint32_t column) { - SourceCoords sc = { bytecode_.length(), line, column }; - return callSourceCoords_.append(sc); - } - - void finish(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime) { - MOZ_ASSERT(index_ == UINT_MAX); - MOZ_ASSERT(!sig_); - MOZ_ASSERT(generateTime_ == UINT_MAX); - index_ = funcIndex; - sig_ = &sig; - generateTime_ = generateTime; - } - - private: - template size_t writePrimitive(T v) { - size_t writeAt = bytecode_.length(); - if (!bytecode_.append(reinterpret_cast(&v), sizeof(T))) - return -1; - return writeAt; - } - - template T readPrimitive(size_t* pc) const { - MOZ_ASSERT(*pc + sizeof(T) <= bytecode_.length()); - T ret; - memcpy(&ret, &bytecode_[*pc], sizeof(T)); - *pc += sizeof(T); - return ret; - } - - public: - size_t writeU8(uint8_t i) { return writePrimitive(i); } - size_t writeI32(int32_t i) { return writePrimitive(i); } - size_t writeU32(uint32_t i) { return writePrimitive(i); } - size_t writeF32(float f) { return writePrimitive(f); } - size_t writeF64(double d) { return writePrimitive(d); } - - size_t writeI32X4(const int32_t* i4) { - size_t pos = bytecode_.length(); - for (size_t i = 0; i < 4; i++) - writePrimitive(i4[i]); - return pos; - } - size_t writeF32X4(const float* f4) { - size_t pos = bytecode_.length(); - for (size_t i = 0; i < 4; i++) - writePrimitive(f4[i]); - return pos; - } - - uint8_t readU8 (size_t* pc) const { return readPrimitive(pc); } - int32_t readI32(size_t* pc) const { return readPrimitive(pc); } - float readF32(size_t* pc) const { return readPrimitive(pc); } - uint32_t readU32(size_t* pc) const { return readPrimitive(pc); } - double readF64(size_t* pc) const { return readPrimitive(pc); } - const LifoSig* readSig(size_t* pc) const { return readPrimitive(pc); } - - jit::SimdConstant readI32X4(size_t* pc) const { - int32_t x = readI32(pc); - int32_t y = readI32(pc); - int32_t z = readI32(pc); - int32_t w = readI32(pc); - return jit::SimdConstant::CreateX4(x, y, z, w); - } - jit::SimdConstant readF32X4(size_t* pc) const { - float x = readF32(pc); - float y = readF32(pc); - float z = readF32(pc); - float w = readF32(pc); - return jit::SimdConstant::CreateX4(x, y, z, w); - } - -#ifdef DEBUG - bool pcIsPatchable(size_t pc, unsigned size) const { - bool patchable = true; - for (unsigned i = 0; patchable && i < size; i++) - patchable &= Stmt(bytecode_[pc]) == Stmt::Bad; - return patchable; - } -#endif - - void patchU8(size_t pc, uint8_t i) { - MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t))); - bytecode_[pc] = i; - } - - template - void patch32(size_t pc, T i) { - static_assert(sizeof(T) == sizeof(uint32_t), - "patch32 must be used with 32-bits wide types"); - MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t))); - memcpy(&bytecode_[pc], &i, sizeof(uint32_t)); - } - - void patchSig(size_t pc, const LifoSig* ptr) { - MOZ_ASSERT(pcIsPatchable(pc, sizeof(LifoSig*))); - memcpy(&bytecode_[pc], &ptr, sizeof(LifoSig*)); - } - - // Read-only interface - PropertyName* name() const { return name_; } - unsigned line() const { return line_; } - unsigned column() const { return column_; } - uint32_t index() const { MOZ_ASSERT(index_ != UINT32_MAX); return index_; } - size_t size() const { return bytecode_.length(); } - const LifoSig& sig() const { MOZ_ASSERT(sig_); return *sig_; } - size_t numLocalVars() const { return localVars_.length(); } - ValType localVarType(size_t i) const { return localVars_[i]; } - size_t numLocals() const { return sig_->args().length() + numLocalVars(); } - unsigned generateTime() const { MOZ_ASSERT(generateTime_ != UINT_MAX); return generateTime_; } - const SourceCoords& sourceCoords(size_t i) const { return callSourceCoords_[i]; } -}; - -} // namespace wasm -} // namespace js - -#endif // wasm_ir_h diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index 99a205e02a..a5c9541ddc 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -39,8 +39,9 @@ class FunctionCompiler typedef HashMap, SystemAllocPolicy> UnlabeledBlockMap; typedef Vector PositionStack; - const FuncIR& func_; - size_t pc_; + const FuncBytecode& func_; + Decoder decoder_; + size_t nextId_; size_t lastReadCallSite_; TempAllocator& alloc_; @@ -60,9 +61,10 @@ class FunctionCompiler FuncCompileResults& compileResults_; public: - FunctionCompiler(const FuncIR& func, MIRGenerator& mirGen, FuncCompileResults& compileResults) + FunctionCompiler(const FuncBytecode& func, MIRGenerator& mirGen, FuncCompileResults& compileResults) : func_(func), - pc_(0), + decoder_(func.bytecode()), + nextId_(0), lastReadCallSite_(0), alloc_(mirGen.alloc()), graph_(mirGen.graph()), @@ -91,6 +93,8 @@ class FunctionCompiler const LifoSig::ArgVector& args = func_.sig().args(); unsigned firstVarSlot = args.length(); + if (!mirGen_.ensureBallast()) + return false; if (!newBlock(/* pred = */ nullptr, &curBlock_)) return false; @@ -145,7 +149,7 @@ class FunctionCompiler MOZ_ASSERT(labeledBreaks_.empty()); MOZ_ASSERT(labeledContinues_.empty()); MOZ_ASSERT(inDeadCode()); - MOZ_ASSERT(pc_ == func_.size(), "all bytecode must be consumed"); + MOZ_ASSERT(decoder_.done(), "all bytecode must be consumed"); } /************************* Read-only interface (after local scope setup) */ @@ -889,9 +893,9 @@ class FunctionCompiler return curBlock_->pop(); } - bool startPendingLoop(size_t pos, MBasicBlock** loopEntry) + bool startPendingLoop(size_t id, MBasicBlock** loopEntry) { - if (!loopStack_.append(pos) || !breakableStack_.append(pos)) + if (!loopStack_.append(id) || !breakableStack_.append(id)) return false; if (inDeadCode()) { *loopEntry = nullptr; @@ -934,10 +938,10 @@ class FunctionCompiler private: size_t popLoop() { - size_t pos = loopStack_.popCopy(); - MOZ_ASSERT(!unlabeledContinues_.has(pos)); + size_t id = loopStack_.popCopy(); + MOZ_ASSERT(!unlabeledContinues_.has(id)); breakableStack_.popBack(); - return pos; + return id; } void fixupRedundantPhis(MBasicBlock* b) @@ -998,11 +1002,11 @@ class FunctionCompiler public: bool closeLoop(MBasicBlock* loopEntry, MBasicBlock* afterLoop) { - size_t pos = popLoop(); + size_t id = popLoop(); if (!loopEntry) { MOZ_ASSERT(!afterLoop); MOZ_ASSERT(inDeadCode()); - MOZ_ASSERT(!unlabeledBreaks_.has(pos)); + MOZ_ASSERT(!unlabeledBreaks_.has(id)); return true; } MOZ_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1); @@ -1016,15 +1020,15 @@ class FunctionCompiler curBlock_ = afterLoop; if (curBlock_) mirGraph().moveBlockToEnd(curBlock_); - return bindUnlabeledBreaks(pos); + return bindUnlabeledBreaks(id); } bool branchAndCloseDoWhileLoop(MDefinition* cond, MBasicBlock* loopEntry) { - size_t pos = popLoop(); + size_t id = popLoop(); if (!loopEntry) { MOZ_ASSERT(inDeadCode()); - MOZ_ASSERT(!unlabeledBreaks_.has(pos)); + MOZ_ASSERT(!unlabeledBreaks_.has(id)); return true; } MOZ_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1); @@ -1053,13 +1057,13 @@ class FunctionCompiler curBlock_ = afterLoop; } } - return bindUnlabeledBreaks(pos); + return bindUnlabeledBreaks(id); } - bool bindContinues(size_t pos, const LabelVector* maybeLabels) + bool bindContinues(size_t id, const LabelVector* maybeLabels) { bool createdJoinBlock = false; - if (UnlabeledBlockMap::Ptr p = unlabeledContinues_.lookup(pos)) { + if (UnlabeledBlockMap::Ptr p = unlabeledContinues_.lookup(id)) { if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock)) return false; unlabeledContinues_.remove(p); @@ -1085,10 +1089,10 @@ class FunctionCompiler return addBreakOrContinue(loopStack_.back(), &unlabeledContinues_); } - bool startSwitch(size_t pos, MDefinition* expr, int32_t low, int32_t high, + bool startSwitch(size_t id, MDefinition* expr, int32_t low, int32_t high, MBasicBlock** switchBlock) { - if (!breakableStack_.append(pos)) + if (!breakableStack_.append(id)) return false; if (inDeadCode()) { *switchBlock = nullptr; @@ -1129,7 +1133,7 @@ class FunctionCompiler bool joinSwitch(MBasicBlock* switchBlock, const BlockVector& cases, MBasicBlock* defaultBlock) { - size_t pos = breakableStack_.popCopy(); + size_t id = breakableStack_.popCopy(); if (!switchBlock) return true; MTableSwitch* mir = switchBlock->lastIns()->toTableSwitch(); @@ -1155,24 +1159,29 @@ class FunctionCompiler curBlock_->end(MGoto::New(alloc(), next)); curBlock_ = next; } - return bindUnlabeledBreaks(pos); + return bindUnlabeledBreaks(id); } + // Provides unique identifiers for internal uses in the control flow stacks; + // these ids have to grow monotonically. + unsigned nextId() { return nextId_++; } + /************************************************************ DECODING ***/ - uint8_t readU8() { return func_.readU8(&pc_); } - uint32_t readU32() { return func_.readU32(&pc_); } - int32_t readI32() { return func_.readI32(&pc_); } - float readF32() { return func_.readF32(&pc_); } - double readF64() { return func_.readF64(&pc_); } - const LifoSig* readSig() { return func_.readSig(&pc_); } - SimdConstant readI32X4() { return func_.readI32X4(&pc_); } - SimdConstant readF32X4() { return func_.readF32X4(&pc_); } + uint8_t readU8() { return decoder_.uncheckedReadU8(); } + uint32_t readU32() { return decoder_.uncheckedReadU32(); } + int32_t readI32() { return decoder_.uncheckedReadI32(); } + float readF32() { return decoder_.uncheckedReadF32(); } + double readF64() { return decoder_.uncheckedReadF64(); } + const LifoSig* readSig() { return decoder_.uncheckedReadSig(); } + SimdConstant readI32X4() { return decoder_.uncheckedReadI32X4(); } + SimdConstant readF32X4() { return decoder_.uncheckedReadF32X4(); } + Stmt readStmtOp() { return Stmt(readU8()); } void readCallLineCol(uint32_t* line, uint32_t* column) { - const FuncIR::SourceCoords& sc = func_.sourceCoords(lastReadCallSite_++); - MOZ_ASSERT(pc_ == sc.offset); + const SourceCoords& sc = func_.sourceCoords(lastReadCallSite_++); + decoder_.assertCurrentIs(sc.offset); *line = sc.line; *column = sc.column; } @@ -1181,8 +1190,7 @@ class FunctionCompiler MOZ_ASSERT(Stmt(readU8()) == Stmt::DebugCheckPoint); } - bool done() const { return pc_ == func_.size(); } - size_t pc() const { return pc_; } + bool done() const { return decoder_.done(); } /*************************************************************************/ private: @@ -1265,10 +1273,10 @@ class FunctionCompiler return true; } - bool bindUnlabeledBreaks(size_t pos) + bool bindUnlabeledBreaks(size_t id) { bool createdJoinBlock = false; - if (UnlabeledBlockMap::Ptr p = unlabeledBreaks_.lookup(pos)) { + if (UnlabeledBlockMap::Ptr p = unlabeledBreaks_.lookup(id)) { if (!bindBreaksOrContinues(&p->value(), &createdJoinBlock)) return false; unlabeledBreaks_.remove(p); @@ -2286,10 +2294,10 @@ EmitInterruptCheckLoop(FunctionCompiler& f) static bool EmitWhile(FunctionCompiler& f, const LabelVector* maybeLabels) { - size_t headPc = f.pc(); + size_t headId = f.nextId(); MBasicBlock* loopEntry; - if (!f.startPendingLoop(headPc, &loopEntry)) + if (!f.startPendingLoop(headId, &loopEntry)) return false; MDefinition* condDef; @@ -2303,7 +2311,7 @@ EmitWhile(FunctionCompiler& f, const LabelVector* maybeLabels) if (!EmitStatement(f)) return false; - if (!f.bindContinues(headPc, maybeLabels)) + if (!f.bindContinues(headId, maybeLabels)) return false; return f.closeLoop(loopEntry, afterLoop); @@ -2314,7 +2322,7 @@ EmitFor(FunctionCompiler& f, Stmt stmt, const LabelVector* maybeLabels) { MOZ_ASSERT(stmt == Stmt::ForInitInc || stmt == Stmt::ForInitNoInc || stmt == Stmt::ForNoInitInc || stmt == Stmt::ForNoInitNoInc); - size_t headPc = f.pc(); + size_t headId = f.nextId(); if (stmt == Stmt::ForInitInc || stmt == Stmt::ForInitNoInc) { if (!EmitStatement(f)) @@ -2322,7 +2330,7 @@ EmitFor(FunctionCompiler& f, Stmt stmt, const LabelVector* maybeLabels) } MBasicBlock* loopEntry; - if (!f.startPendingLoop(headPc, &loopEntry)) + if (!f.startPendingLoop(headId, &loopEntry)) return false; MDefinition* condDef; @@ -2336,7 +2344,7 @@ EmitFor(FunctionCompiler& f, Stmt stmt, const LabelVector* maybeLabels) if (!EmitStatement(f)) return false; - if (!f.bindContinues(headPc, maybeLabels)) + if (!f.bindContinues(headId, maybeLabels)) return false; if (stmt == Stmt::ForInitInc || stmt == Stmt::ForNoInitInc) { @@ -2352,16 +2360,16 @@ EmitFor(FunctionCompiler& f, Stmt stmt, const LabelVector* maybeLabels) static bool EmitDoWhile(FunctionCompiler& f, const LabelVector* maybeLabels) { - size_t headPc = f.pc(); + size_t headId = f.nextId(); MBasicBlock* loopEntry; - if (!f.startPendingLoop(headPc, &loopEntry)) + if (!f.startPendingLoop(headId, &loopEntry)) return false; if (!EmitStatement(f)) return false; - if (!f.bindContinues(headPc, maybeLabels)) + if (!f.bindContinues(headId, maybeLabels)) return false; MDefinition* condDef; @@ -2464,7 +2472,7 @@ EmitSwitch(FunctionCompiler& f) return false; MBasicBlock* switchBlock; - if (!f.startSwitch(f.pc(), exprDef, low, high, &switchBlock)) + if (!f.startSwitch(f.nextId(), exprDef, low, high, &switchBlock)) return false; while (numCases--) { @@ -3039,7 +3047,7 @@ wasm::IonCompileFunction(IonCompileTask* task) { int64_t before = PRMJ_Now(); - const FuncIR& func = task->func(); + const FuncBytecode& func = task->func(); FuncCompileResults& results = task->results(); JitContext jitContext(CompileRuntime::get(task->runtime()), &results.alloc()); diff --git a/js/src/asmjs/WasmIonCompile.h b/js/src/asmjs/WasmIonCompile.h index fba424c003..b0acd50a14 100644 --- a/js/src/asmjs/WasmIonCompile.h +++ b/js/src/asmjs/WasmIonCompile.h @@ -19,7 +19,7 @@ #ifndef wasm_ion_compile_h #define wasm_ion_compile_h -#include "asmjs/WasmIR.h" +#include "asmjs/WasmBinary.h" #include "jit/MacroAssembler.h" namespace js { @@ -63,7 +63,7 @@ class IonCompileTask JSRuntime* const runtime_; const CompileArgs args_; LifoAlloc lifo_; - const FuncIR* func_; + UniqueFuncBytecode func_; mozilla::Maybe results_; IonCompileTask(const IonCompileTask&) = delete; @@ -85,19 +85,22 @@ class IonCompileTask CompileArgs args() const { return args_; } - void init(const FuncIR& func) { - func_ = &func; + void init(UniqueFuncBytecode func) { + MOZ_ASSERT(!func_); + func_ = mozilla::Move(func); results_.emplace(lifo_); } - const FuncIR& func() const { + const FuncBytecode& func() const { MOZ_ASSERT(func_); return *func_; } FuncCompileResults& results() { return *results_; } - void reset() { - func_ = nullptr; + void reset(UniqueBytecode* recycled) { + if (func_) + *recycled = func_->recycleBytecode(); + func_.reset(nullptr); results_.reset(); lifo_.releaseAll(); } diff --git a/js/src/asmjs/WasmModule.cpp b/js/src/asmjs/WasmModule.cpp index 408de20f1a..3d8c3c0a35 100644 --- a/js/src/asmjs/WasmModule.cpp +++ b/js/src/asmjs/WasmModule.cpp @@ -76,6 +76,7 @@ wasm::AllocateCode(ExclusiveContext* cx, size_t bytes) void CodeDeleter::operator()(uint8_t* p) { + MOZ_ASSERT(bytes_ != 0); DeallocateExecutableMemory(p, bytes_, AsmJSPageSize); } @@ -462,6 +463,13 @@ CacheableUniquePtr::clone(JSContext* cx, CacheableUniquePtr* out) const return true; } +template +size_t +CacheableUniquePtr::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const +{ + return mallocSizeOf(this->get()); +} + namespace js { namespace wasm { template struct CacheableUniquePtr; @@ -469,24 +477,132 @@ template struct CacheableUniquePtr; } } -class Module::AutoMutateCode +size_t +ExportMap::serializedSize() const { - AutoWritableJitCode awjc_; - AutoFlushICache afc_; + return SerializedVectorSize(exportNames) + + SerializedVectorSize(fieldNames) + + SerializedPodVectorSize(fieldsToExports); +} - public: - AutoMutateCode(JSContext* cx, Module& module, const char* name) - : awjc_(cx->runtime(), module.code(), module.pod.codeBytes_), - afc_(name) - { - AutoFlushICache::setRange(uintptr_t(module.code()), module.pod.codeBytes_); - } -}; - -uint32_t -Module::totalBytes() const +uint8_t* +ExportMap::serialize(uint8_t* cursor) const { - return pod.codeBytes_ + pod.globalBytes_; + cursor = SerializeVector(cursor, exportNames); + cursor = SerializeVector(cursor, fieldNames); + cursor = SerializePodVector(cursor, fieldsToExports); + return cursor; +} + +const uint8_t* +ExportMap::deserialize(ExclusiveContext* cx, const uint8_t* cursor) +{ + (cursor = DeserializeVector(cx, cursor, &exportNames)) && + (cursor = DeserializeVector(cx, cursor, &fieldNames)) && + (cursor = DeserializePodVector(cx, cursor, &fieldsToExports)); + return cursor; +} + +bool +ExportMap::clone(JSContext* cx, ExportMap* map) const +{ + return CloneVector(cx, exportNames, &map->exportNames) && + CloneVector(cx, fieldNames, &map->fieldNames) && + ClonePodVector(cx, fieldsToExports, &map->fieldsToExports); +} + +size_t +ExportMap::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const +{ + return SizeOfVectorExcludingThis(exportNames, mallocSizeOf) && + SizeOfVectorExcludingThis(fieldNames, mallocSizeOf) && + fieldsToExports.sizeOfExcludingThis(mallocSizeOf); +} + +size_t +ModuleData::serializedSize() const +{ + return sizeof(pod()) + + codeBytes + + SerializedVectorSize(imports) + + SerializedVectorSize(exports) + + SerializedPodVectorSize(heapAccesses) + + SerializedPodVectorSize(codeRanges) + + SerializedPodVectorSize(callSites) + + SerializedVectorSize(funcNames) + + filename.serializedSize() + + displayURL.serializedSize(); +} + +uint8_t* +ModuleData::serialize(uint8_t* cursor) const +{ + cursor = WriteBytes(cursor, &pod(), sizeof(pod())); + cursor = WriteBytes(cursor, code.get(), codeBytes); + cursor = SerializeVector(cursor, imports); + cursor = SerializeVector(cursor, exports); + cursor = SerializePodVector(cursor, heapAccesses); + cursor = SerializePodVector(cursor, codeRanges); + cursor = SerializePodVector(cursor, callSites); + cursor = SerializeVector(cursor, funcNames); + cursor = filename.serialize(cursor); + cursor = displayURL.serialize(cursor); + return cursor; +} + +/* static */ const uint8_t* +ModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor) +{ + cursor = ReadBytes(cursor, &pod(), sizeof(pod())); + + code = AllocateCode(cx, totalBytes()); + if (!code) + return nullptr; + cursor = ReadBytes(cursor, code.get(), codeBytes); + + (cursor = DeserializeVector(cx, cursor, &imports)) && + (cursor = DeserializeVector(cx, cursor, &exports)) && + (cursor = DeserializePodVector(cx, cursor, &heapAccesses)) && + (cursor = DeserializePodVector(cx, cursor, &codeRanges)) && + (cursor = DeserializePodVector(cx, cursor, &callSites)) && + (cursor = DeserializeVector(cx, cursor, &funcNames)) && + (cursor = filename.deserialize(cx, cursor)) && + (cursor = displayURL.deserialize(cx, cursor)); + return cursor; +} + +bool +ModuleData::clone(JSContext* cx, ModuleData* out) const +{ + out->pod() = pod(); + + out->code = AllocateCode(cx, totalBytes()); + if (!out->code) + return false; + memcpy(out->code.get(), code.get(), codeBytes); + + return CloneVector(cx, imports, &out->imports) && + CloneVector(cx, exports, &out->exports) && + ClonePodVector(cx, heapAccesses, &out->heapAccesses) && + ClonePodVector(cx, codeRanges, &out->codeRanges) && + ClonePodVector(cx, callSites, &out->callSites) && + CloneVector(cx, funcNames, &out->funcNames) && + filename.clone(cx, &out->filename) && + displayURL.clone(cx, &out->displayURL); +} + +size_t +ModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const +{ + // Module::addSizeOfMisc takes care of code and global memory. + return SizeOfVectorExcludingThis(imports, mallocSizeOf) + + SizeOfVectorExcludingThis(exports, mallocSizeOf) + + heapAccesses.sizeOfExcludingThis(mallocSizeOf) + + codeRanges.sizeOfExcludingThis(mallocSizeOf) + + callSites.sizeOfExcludingThis(mallocSizeOf) + + funcNames.sizeOfExcludingThis(mallocSizeOf) + + filename.sizeOfExcludingThis(mallocSizeOf) + + displayURL.sizeOfExcludingThis(mallocSizeOf); } uint8_t* @@ -524,7 +640,7 @@ Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap) // i.e. ptr > heapLength - data-type-byte-size - offset. data-type-byte-size // and offset are already included in the addend so we // just have to add the heap length here. - for (const HeapAccess& access : heapAccesses_) { + for (const HeapAccess& access : module_->heapAccesses) { if (access.hasLengthCheck()) X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength); void* addr = access.patchHeapPtrImmAt(code()); @@ -540,14 +656,14 @@ Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap) // checks at the right places. All accesses that have been recorded are the // only ones that need bound checks (see also // CodeGeneratorX64::visitAsmJS{Load,Store,CompareExchange,Exchange,AtomicBinop}Heap) - for (const HeapAccess& access : heapAccesses_) { + for (const HeapAccess& access : module_->heapAccesses) { // See comment above for x86 codegen. if (access.hasLengthCheck()) X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength); } #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) - for (const HeapAccess& access : heapAccesses_) + for (const HeapAccess& access : module_->heapAccesses) Assembler::UpdateBoundsCheck(heapLength, (Instruction*)(access.insnOffset() + code())); #endif @@ -567,8 +683,8 @@ Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap) #if defined(JS_CODEGEN_X86) uint32_t heapLength = heap->byteLength(); uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - used for value*/); - for (unsigned i = 0; i < heapAccesses_.length(); i++) { - const HeapAccess& access = heapAccesses_[i]; + for (unsigned i = 0; i < module_->heapAccesses.length(); i++) { + const HeapAccess& access = module_->heapAccesses[i]; if (access.hasLengthCheck()) X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength); void* addr = access.patchHeapPtrImmAt(code()); @@ -578,8 +694,8 @@ Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap) } #elif defined(JS_CODEGEN_X64) uint32_t heapLength = heap->byteLength(); - for (unsigned i = 0; i < heapAccesses_.length(); i++) { - const HeapAccess& access = heapAccesses_[i]; + for (unsigned i = 0; i < module_->heapAccesses.length(); i++) { + const HeapAccess& access = module_->heapAccesses[i]; if (access.hasLengthCheck()) X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength); } @@ -594,17 +710,17 @@ Module::sendCodeRangesToProfiler(JSContext* cx) { #ifdef JS_ION_PERF if (PerfFuncEnabled()) { - for (const CodeRange& codeRange : codeRanges_) { + for (const CodeRange& codeRange : module_->codeRanges) { if (!codeRange.isFunction()) continue; uintptr_t start = uintptr_t(code() + codeRange.begin()); uintptr_t end = uintptr_t(code() + codeRange.end()); uintptr_t size = end - start; - const char* file = filename_.get(); + const char* file = module_->filename.get(); unsigned line = codeRange.funcLineNumber(); unsigned column = 0; - const char* name = funcNames_[codeRange.funcNameIndex()].get(); + const char* name = module_->funcNames[codeRange.funcNameIndex()].get(); writePerfSpewerAsmJSFunctionMap(start, size, file, line, column, name); } @@ -612,14 +728,14 @@ Module::sendCodeRangesToProfiler(JSContext* cx) #endif #ifdef MOZ_VTUNE if (IsVTuneProfilingActive()) { - for (const CodeRange& codeRange : codeRanges_) { + for (const CodeRange& codeRange : module_->codeRanges) { if (!codeRange.isFunction()) continue; uintptr_t start = uintptr_t(code() + codeRange.begin()); uintptr_t end = uintptr_t(code() + codeRange.end()); uintptr_t size = end - start; - const char* name = funcNames_[codeRange.funcNameIndex()].get(); + const char* name = module_->funcNames[codeRange.funcNameIndex()].get(); unsigned method_id = iJIT_GetNewMethodID(); if (method_id == 0) @@ -654,16 +770,16 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled) // do it now since, once we start sampling, we'll be in a signal-handing // context where we cannot malloc. if (enabled) { - if (!funcLabels_.resize(funcNames_.length())) { + if (!funcLabels_.resize(module_->funcNames.length())) { ReportOutOfMemory(cx); return false; } - for (const CodeRange& codeRange : codeRanges_) { + for (const CodeRange& codeRange : module_->codeRanges) { if (!codeRange.isFunction()) continue; unsigned lineno = codeRange.funcLineNumber(); - const char* name = funcNames_[codeRange.funcNameIndex()].get(); - UniqueChars label(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno)); + const char* name = module_->funcNames[codeRange.funcNameIndex()].get(); + UniqueChars label(JS_smprintf("%s (%s:%u)", name, module_->filename.get(), lineno)); if (!label) { ReportOutOfMemory(cx); return false; @@ -674,14 +790,16 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled) funcLabels_.clear(); } - // Patch callsites and returns to execute profiling prologues/epililogues. + // Patch callsites and returns to execute profiling prologues/epilogues. { - AutoMutateCode amc(cx, *this, "Module::setProfilingEnabled"); + AutoWritableJitCode awjc(cx->runtime(), code(), codeBytes()); + AutoFlushICache afc("Module::setProfilingEnabled"); + AutoFlushICache::setRange(uintptr_t(code()), codeBytes()); - for (const CallSite& callSite : callSites_) + for (const CallSite& callSite : module_->callSites) EnableProfilingPrologue(*this, callSite, enabled); - for (const CodeRange& codeRange : codeRanges_) + for (const CodeRange& codeRange : module_->codeRanges) EnableProfilingEpilogue(*this, codeRange, enabled); } @@ -709,114 +827,70 @@ Module::importToExit(const Import& import) return *reinterpret_cast(globalData() + import.exitGlobalDataOffset()); } -/* static */ Module::CacheablePod -Module::zeroPod() +bool +Module::clone(JSContext* cx, const StaticLinkData& link, Module* out) const { - CacheablePod pod = {0, 0, 0, HeapUsage::None, false, false, false}; - return pod; + MOZ_ASSERT(dynamicallyLinked_); + + // The out->module_ field was already cloned and initialized when 'out' was + // constructed. This function should clone the rest. + MOZ_ASSERT(out->module_); + + out->isAsmJS_ = isAsmJS_; + out->profilingEnabled_ = profilingEnabled_; + + if (!CloneVector(cx, funcLabels_, &out->funcLabels_)) + return false; + +#ifdef DEBUG + // Put the symbolic links back to -1 so PatchDataWithValueCheck assertions + // in Module::staticallyLink are valid. + for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) { + void* callee = AddressOf(imm, cx); + const StaticLinkData::OffsetVector& offsets = link.symbolicLinks[imm]; + for (uint32_t offset : offsets) { + jit::Assembler::PatchDataWithValueCheck(jit::CodeLocationLabel(out->code() + offset), + jit::PatchedImmPtr((void*)-1), + jit::PatchedImmPtr(callee)); + } + } +#endif + + // If the copied machine code has been specialized to the heap, it must be + // unspecialized in the copy. + if (usesHeap()) + out->despecializeFromHeap(heap_); + + return true; } -void -Module::init() -{ - staticallyLinked_ = false; - interrupt_ = nullptr; - outOfBounds_ = nullptr; - dynamicallyLinked_ = false; +Module::Module(UniqueModuleData module, AsmJSBool isAsmJS) + : module_(Move(module)), + isAsmJS_(bool(isAsmJS)), + staticallyLinked_(false), + interrupt_(nullptr), + outOfBounds_(nullptr), + dynamicallyLinked_(false), + profilingEnabled_(false) +{ *(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN(); *(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN(); } -// Private constructor used for deserialization and cloning. -Module::Module(const CacheablePod& pod, - UniqueCodePtr code, - ImportVector&& imports, - ExportVector&& exports, - HeapAccessVector&& heapAccesses, - CodeRangeVector&& codeRanges, - CallSiteVector&& callSites, - CacheableCharsVector&& funcNames, - CacheableChars filename, - CacheableTwoByteChars displayURL, - CacheBool loadedFromCache, - ProfilingBool profilingEnabled, - FuncLabelVector&& funcLabels) - : pod(pod), - code_(Move(code)), - imports_(Move(imports)), - exports_(Move(exports)), - heapAccesses_(Move(heapAccesses)), - codeRanges_(Move(codeRanges)), - callSites_(Move(callSites)), - funcNames_(Move(funcNames)), - filename_(Move(filename)), - displayURL_(Move(displayURL)), - loadedFromCache_(loadedFromCache), - profilingEnabled_(profilingEnabled), - funcLabels_(Move(funcLabels)) -{ - MOZ_ASSERT_IF(!profilingEnabled, funcLabels_.empty()); - MOZ_ASSERT_IF(profilingEnabled, funcNames_.length() == funcLabels_.length()); - init(); -} - -// Public constructor for compilation. -Module::Module(CompileArgs args, - uint32_t functionBytes, - uint32_t codeBytes, - uint32_t globalBytes, - HeapUsage heapUsage, - MutedBool mutedErrors, - UniqueCodePtr code, - ImportVector&& imports, - ExportVector&& exports, - HeapAccessVector&& heapAccesses, - CodeRangeVector&& codeRanges, - CallSiteVector&& callSites, - CacheableCharsVector&& funcNames, - CacheableChars filename, - CacheableTwoByteChars displayURL) - : pod(zeroPod()), - code_(Move(code)), - imports_(Move(imports)), - exports_(Move(exports)), - heapAccesses_(Move(heapAccesses)), - codeRanges_(Move(codeRanges)), - callSites_(Move(callSites)), - funcNames_(Move(funcNames)), - filename_(Move(filename)), - displayURL_(Move(displayURL)), - loadedFromCache_(false), - profilingEnabled_(false) -{ - // Work around MSVC 2013 bug around {} member initialization. - const_cast(pod.functionBytes_) = functionBytes; - const_cast(pod.codeBytes_) = codeBytes; - const_cast(pod.globalBytes_) = globalBytes; - const_cast(pod.heapUsage_) = heapUsage; - const_cast(pod.mutedErrors_) = bool(mutedErrors); - const_cast(pod.usesSignalHandlersForOOB_) = args.useSignalHandlersForOOB; - const_cast(pod.usesSignalHandlersForInterrupt_) = args.useSignalHandlersForInterrupt; - - init(); -} - Module::~Module() { - if (code_) { - for (unsigned i = 0; i < imports_.length(); i++) { - ImportExit& exit = importToExit(imports_[i]); - if (exit.baselineScript) - exit.baselineScript->removeDependentWasmModule(*this, i); - } + for (unsigned i = 0; i < imports().length(); i++) { + ImportExit& exit = importToExit(imports()[i]); + if (exit.baselineScript) + exit.baselineScript->removeDependentWasmModule(*this, i); } } void Module::trace(JSTracer* trc) { - for (const Import& import : imports_) { + for (const Import& import : imports()) { if (importToExit(import).fun) TraceEdge(trc, &importToExit(import).fun, "wasm function import"); } @@ -825,25 +899,16 @@ Module::trace(JSTracer* trc) TraceEdge(trc, &heap_, "wasm buffer"); } -CompileArgs -Module::compileArgs() const -{ - CompileArgs args; - args.useSignalHandlersForOOB = pod.usesSignalHandlersForOOB_; - args.useSignalHandlersForInterrupt = pod.usesSignalHandlersForInterrupt_; - return args; -} - bool Module::containsFunctionPC(void* pc) const { - return pc >= code() && pc < (code() + pod.functionBytes_); + return pc >= code() && pc < (code() + module_->functionBytes); } bool Module::containsCodePC(void* pc) const { - return pc >= code() && pc < (code() + pod.codeBytes_); + return pc >= code() && pc < (code() + codeBytes()); } struct CallSiteRetAddrOffset @@ -860,13 +925,13 @@ Module::lookupCallSite(void* returnAddress) const { uint32_t target = ((uint8_t*)returnAddress) - code(); size_t lowerBound = 0; - size_t upperBound = callSites_.length(); + size_t upperBound = module_->callSites.length(); size_t match; - if (!BinarySearch(CallSiteRetAddrOffset(callSites_), lowerBound, upperBound, target, &match)) + if (!BinarySearch(CallSiteRetAddrOffset(module_->callSites), lowerBound, upperBound, target, &match)) return nullptr; - return &callSites_[match]; + return &module_->callSites[match]; } const CodeRange* @@ -874,13 +939,13 @@ Module::lookupCodeRange(void* pc) const { CodeRange::PC target((uint8_t*)pc - code()); size_t lowerBound = 0; - size_t upperBound = codeRanges_.length(); + size_t upperBound = module_->codeRanges.length(); size_t match; - if (!BinarySearch(codeRanges_, lowerBound, upperBound, target, &match)) + if (!BinarySearch(module_->codeRanges, lowerBound, upperBound, target, &match)) return nullptr; - return &codeRanges_[match]; + return &module_->codeRanges[match]; } struct HeapAccessOffset @@ -899,13 +964,13 @@ Module::lookupHeapAccess(void* pc) const uint32_t target = ((uint8_t*)pc) - code(); size_t lowerBound = 0; - size_t upperBound = heapAccesses_.length(); + size_t upperBound = module_->heapAccesses.length(); size_t match; - if (!BinarySearch(HeapAccessOffset(heapAccesses_), lowerBound, upperBound, target, &match)) + if (!BinarySearch(HeapAccessOffset(module_->heapAccesses), lowerBound, upperBound, target, &match)) return nullptr; - return &heapAccesses_[match]; + return &module_->heapAccesses[match]; } bool @@ -920,7 +985,7 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData) JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())); MOZ_ASSERT(IsCompilingAsmJS()); AutoFlushICache afc("Module::staticallyLink", /* inhibit = */ true); - AutoFlushICache::setRange(uintptr_t(code()), pod.codeBytes_); + AutoFlushICache::setRange(uintptr_t(code()), codeBytes()); interrupt_ = code() + linkData.pod.interruptOffset; outOfBounds_ = code() + linkData.pod.outOfBoundsOffset; @@ -983,7 +1048,7 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData) bool Module::dynamicallyLink(JSContext* cx, Handle heap, - const AutoVectorRooter& imports) + Handle importArgs) { MOZ_ASSERT(staticallyLinked_); MOZ_ASSERT(!dynamicallyLinked_); @@ -994,15 +1059,15 @@ Module::dynamicallyLink(JSContext* cx, Handle hea JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())); MOZ_ASSERT(IsCompilingAsmJS()); AutoFlushICache afc("Module::dynamicallyLink"); - AutoFlushICache::setRange(uintptr_t(code()), pod.codeBytes_); + AutoFlushICache::setRange(uintptr_t(code()), codeBytes()); // Initialize imports with actual imported values. - MOZ_ASSERT(imports.length() == imports_.length()); - for (size_t i = 0; i < imports_.length(); i++) { - const Import& import = imports_[i]; + MOZ_ASSERT(importArgs.length() == imports().length()); + for (size_t i = 0; i < imports().length(); i++) { + const Import& import = imports()[i]; ImportExit& exit = importToExit(import); exit.code = code() + import.interpExitCodeOffset(); - exit.fun = imports[i]; + exit.fun = importArgs[i]; exit.baselineScript = nullptr; } @@ -1011,7 +1076,7 @@ Module::dynamicallyLink(JSContext* cx, Handle hea specializeToHeap(heap); // See AllocateCode comment above. - if (!ExecutableAllocator::makeExecutable(code(), pod.codeBytes_)) { + if (!ExecutableAllocator::makeExecutable(code(), codeBytes())) { ReportOutOfMemory(cx); return false; } @@ -1020,6 +1085,92 @@ Module::dynamicallyLink(JSContext* cx, Handle hea return true; } +static bool +WasmCall(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedFunction callee(cx, &args.callee().as()); + + Module& module = ExportedFunctionToModuleObject(callee)->module(); + uint32_t exportIndex = ExportedFunctionToIndex(callee); + + return module.callExport(cx, exportIndex, args); +} + +static JSFunction* +NewExportedFunction(JSContext* cx, Handle moduleObj, const ExportMap& map, + uint32_t exportIndex) +{ + unsigned numArgs = moduleObj->module().exports()[exportIndex].sig().args().length(); + + const char* chars = map.exportNames[exportIndex].get(); + RootedAtom name(cx, AtomizeUTF8Chars(cx, chars, strlen(chars))); + if (!name) + return nullptr; + + JSFunction* fun = NewNativeConstructor(cx, WasmCall, numArgs, name, + gc::AllocKind::FUNCTION_EXTENDED, GenericObject, + JSFunction::ASMJS_CTOR); + if (!fun) + return nullptr; + + fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj)); + fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex)); + return fun; +} + +bool +Module::createExportObject(JSContext* cx, Handle moduleObj, + const ExportMap& map, MutableHandleObject exportObj) +{ + MOZ_ASSERT(this == &moduleObj->module()); + MOZ_ASSERT(map.exportNames.length() == exports().length()); + MOZ_ASSERT(map.fieldNames.length() == map.fieldsToExports.length()); + + for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) { + const char* fieldName = map.fieldNames[fieldIndex].get(); + if (!*fieldName) { + MOZ_ASSERT_IF(isAsmJS(), exports().length() == 1); + MOZ_ASSERT(!exportObj); + uint32_t exportIndex = map.fieldsToExports[fieldIndex]; + exportObj.set(NewExportedFunction(cx, moduleObj, map, exportIndex)); + if (!exportObj) + return false; + break; + } + } + + Rooted vals(cx, ValueVector(cx)); + for (size_t exportIndex = 0; exportIndex < exports().length(); exportIndex++) { + JSFunction* fun = NewExportedFunction(cx, moduleObj, map, exportIndex); + if (!fun || !vals.append(ObjectValue(*fun))) + return false; + } + + if (!exportObj) { + exportObj.set(JS_NewPlainObject(cx)); + if (!exportObj) + return false; + } + + for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) { + const char* fieldName = map.fieldNames[fieldIndex].get(); + if (!*fieldName) + continue; + + JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName)); + if (!atom) + return false; + + RootedId id(cx, AtomToId(atom)); + HandleValue val = vals[map.fieldsToExports[fieldIndex]]; + if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE)) + return false; + } + + return true; +} + SharedMem Module::heap() const { @@ -1043,7 +1194,7 @@ void Module::deoptimizeImportExit(uint32_t importIndex) { MOZ_ASSERT(dynamicallyLinked_); - const Import& import = imports_[importIndex]; + const Import& import = imports()[importIndex]; ImportExit& exit = importToExit(import); exit.code = code() + import.interpExitCodeOffset(); exit.baselineScript = nullptr; @@ -1054,7 +1205,7 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args) { MOZ_ASSERT(dynamicallyLinked_); - const Export& exp = exports_[exportIndex]; + const Export& exp = exports()[exportIndex]; // Enable/disable profiling in the Module to match the current global // profiling state. Don't do this if the Module is already active on the @@ -1190,7 +1341,7 @@ Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Val { MOZ_ASSERT(dynamicallyLinked_); - const Import& import = imports_[importIndex]; + const Import& import = imports()[importIndex]; RootedValue fval(cx, ObjectValue(*importToExit(import).fun)); if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval)) @@ -1264,211 +1415,114 @@ Module::profilingLabel(uint32_t funcIndex) const return funcLabels_[funcIndex].get(); } -size_t -Module::serializedSize() const +void +Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) { - return sizeof(pod) + - pod.codeBytes_ + - SerializedVectorSize(imports_) + - SerializedVectorSize(exports_) + - SerializedPodVectorSize(heapAccesses_) + - SerializedPodVectorSize(codeRanges_) + - SerializedPodVectorSize(callSites_) + - SerializedVectorSize(funcNames_) + - filename_.serializedSize() + - displayURL_.serializedSize(); + *code += codeBytes(); + *data += mallocSizeOf(this) + + globalBytes() + + mallocSizeOf(module_.get()) + + module_->sizeOfExcludingThis(mallocSizeOf) + + funcPtrTables_.sizeOfExcludingThis(mallocSizeOf) + + SizeOfVectorExcludingThis(funcLabels_, mallocSizeOf); } -uint8_t* -Module::serialize(uint8_t* cursor) const -{ - MOZ_ASSERT(!profilingEnabled_, "assumed by Module::deserialize"); +const Class WasmModuleObject::class_ = { + "WasmModuleObject", + JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK | + JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS), + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + WasmModuleObject::finalize, + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + WasmModuleObject::trace +}; - cursor = WriteBytes(cursor, &pod, sizeof(pod)); - cursor = WriteBytes(cursor, code(), pod.codeBytes_); - cursor = SerializeVector(cursor, imports_); - cursor = SerializeVector(cursor, exports_); - cursor = SerializePodVector(cursor, heapAccesses_); - cursor = SerializePodVector(cursor, codeRanges_); - cursor = SerializePodVector(cursor, callSites_); - cursor = SerializeVector(cursor, funcNames_); - cursor = filename_.serialize(cursor); - cursor = displayURL_.serialize(cursor); - return cursor; +bool +WasmModuleObject::hasModule() const +{ + MOZ_ASSERT(is()); + return !getReservedSlot(MODULE_SLOT).isUndefined(); } -/* static */ const uint8_t* -Module::deserialize(ExclusiveContext* cx, const uint8_t* cursor, UniqueModule* out) +/* static */ void +WasmModuleObject::finalize(FreeOp* fop, JSObject* obj) { - CacheablePod pod = zeroPod(); - cursor = ReadBytes(cursor, &pod, sizeof(pod)); - if (!cursor) - return nullptr; - - UniqueCodePtr code = AllocateCode(cx, pod.codeBytes_ + pod.globalBytes_); - if (!code) - return nullptr; - - cursor = ReadBytes(cursor, code.get(), pod.codeBytes_); - - ImportVector imports; - cursor = DeserializeVector(cx, cursor, &imports); - if (!cursor) - return nullptr; - - ExportVector exports; - cursor = DeserializeVector(cx, cursor, &exports); - if (!cursor) - return nullptr; - - HeapAccessVector heapAccesses; - cursor = DeserializePodVector(cx, cursor, &heapAccesses); - if (!cursor) - return nullptr; - - CodeRangeVector codeRanges; - cursor = DeserializePodVector(cx, cursor, &codeRanges); - if (!cursor) - return nullptr; - - CallSiteVector callSites; - cursor = DeserializePodVector(cx, cursor, &callSites); - if (!cursor) - return nullptr; - - CacheableCharsVector funcNames; - cursor = DeserializeVector(cx, cursor, &funcNames); - if (!cursor) - return nullptr; - - CacheableChars filename; - cursor = filename.deserialize(cx, cursor); - if (!cursor) - return nullptr; - - CacheableTwoByteChars displayURL; - cursor = displayURL.deserialize(cx, cursor); - if (!cursor) - return nullptr; - - *out = cx->make_unique(pod, - Move(code), - Move(imports), - Move(exports), - Move(heapAccesses), - Move(codeRanges), - Move(callSites), - Move(funcNames), - Move(filename), - Move(displayURL), - Module::LoadedFromCache, - Module::ProfilingDisabled, - FuncLabelVector()); - - return cursor; + WasmModuleObject& moduleObj = obj->as(); + if (moduleObj.hasModule()) + fop->delete_(&moduleObj.module()); } -Module::UniqueModule -Module::clone(JSContext* cx, const StaticLinkData& linkData) const +/* static */ void +WasmModuleObject::trace(JSTracer* trc, JSObject* obj) { - MOZ_ASSERT(dynamicallyLinked_); + WasmModuleObject& moduleObj = obj->as(); + if (moduleObj.hasModule()) + moduleObj.module().trace(trc); +} - UniqueCodePtr code = AllocateCode(cx, totalBytes()); - if (!code) +/* static */ WasmModuleObject* +WasmModuleObject::create(ExclusiveContext* cx) +{ + AutoSetNewObjectMetadata metadata(cx); + JSObject* obj = NewObjectWithGivenProto(cx, &WasmModuleObject::class_, nullptr); + if (!obj) return nullptr; - memcpy(code.get(), this->code(), pod.codeBytes_); + return &obj->as(); +} -#ifdef DEBUG - // Put the symbolic links back to -1 so PatchDataWithValueCheck assertions - // in Module::staticallyLink are valid. - for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) { - void* callee = AddressOf(imm, cx); - const StaticLinkData::OffsetVector& offsets = linkData.symbolicLinks[imm]; - for (uint32_t offset : offsets) { - jit::Assembler::PatchDataWithValueCheck(jit::CodeLocationLabel(code.get() + offset), - jit::PatchedImmPtr((void*)-1), - jit::PatchedImmPtr(callee)); - } - } -#endif +bool +WasmModuleObject::init(Module* module) +{ + MOZ_ASSERT(is()); + MOZ_ASSERT(!hasModule()); + if (!module) + return false; + setReservedSlot(MODULE_SLOT, PrivateValue(module)); + return true; +} - ImportVector imports; - if (!CloneVector(cx, imports_, &imports)) - return nullptr; - - ExportVector exports; - if (!CloneVector(cx, exports_, &exports)) - return nullptr; - - HeapAccessVector heapAccesses; - if (!ClonePodVector(cx, heapAccesses_, &heapAccesses)) - return nullptr; - - CodeRangeVector codeRanges; - if (!ClonePodVector(cx, codeRanges_, &codeRanges)) - return nullptr; - - CallSiteVector callSites; - if (!ClonePodVector(cx, callSites_, &callSites)) - return nullptr; - - CacheableCharsVector funcNames; - if (!CloneVector(cx, funcNames_, &funcNames)) - return nullptr; - - CacheableChars filename; - if (!filename_.clone(cx, &filename)) - return nullptr; - - CacheableTwoByteChars displayURL; - if (!displayURL_.clone(cx, &displayURL)) - return nullptr; - - FuncLabelVector funcLabels; - if (!CloneVector(cx, funcLabels_, &funcLabels)) - return nullptr; - - // Must not GC between Module allocation and (successful) return. - auto out = cx->make_unique(pod, - Move(code), - Move(imports), - Move(exports), - Move(heapAccesses), - Move(codeRanges), - Move(callSites), - Move(funcNames), - Move(filename), - Move(displayURL), - CacheBool::NotLoadedFromCache, - ProfilingBool(profilingEnabled_), - Move(funcLabels)); - if (!out) - return nullptr; - - // If the copied machine code has been specialized to the heap, it must be - // unspecialized in the copy. - if (usesHeap()) - out->despecializeFromHeap(heap_); - - if (!out->staticallyLink(cx, linkData)) - return nullptr; - - return Move(out); +Module& +WasmModuleObject::module() const +{ + MOZ_ASSERT(is()); + MOZ_ASSERT(hasModule()); + return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate(); } void -Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode, size_t* asmJSModuleData) +WasmModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) { - *asmJSModuleCode += pod.codeBytes_; - *asmJSModuleData += mallocSizeOf(this) + - pod.globalBytes_ + - SizeOfVectorExcludingThis(imports_, mallocSizeOf) + - SizeOfVectorExcludingThis(exports_, mallocSizeOf) + - heapAccesses_.sizeOfExcludingThis(mallocSizeOf) + - codeRanges_.sizeOfExcludingThis(mallocSizeOf) + - callSites_.sizeOfExcludingThis(mallocSizeOf) + - funcNames_.sizeOfExcludingThis(mallocSizeOf) + - funcPtrTables_.sizeOfExcludingThis(mallocSizeOf); + if (hasModule()) + module().addSizeOfMisc(mallocSizeOf, code, data); } +bool +wasm::IsExportedFunction(JSFunction* fun) +{ + return fun->maybeNative() == WasmCall; +} + +WasmModuleObject* +wasm::ExportedFunctionToModuleObject(JSFunction* fun) +{ + MOZ_ASSERT(IsExportedFunction(fun)); + const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_MODULE_SLOT); + return &v.toObject().as(); +} + +uint32_t +wasm::ExportedFunctionToIndex(JSFunction* fun) +{ + MOZ_ASSERT(IsExportedFunction(fun)); + const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT); + return v.toInt32(); +} diff --git a/js/src/asmjs/WasmModule.h b/js/src/asmjs/WasmModule.h index 102966cfd0..a4b2989a02 100644 --- a/js/src/asmjs/WasmModule.h +++ b/js/src/asmjs/WasmModule.h @@ -26,7 +26,9 @@ namespace js { +class AsmJSModule; class WasmActivation; +class WasmModuleObject; namespace jit { struct BaselineScript; } namespace wasm { @@ -35,8 +37,7 @@ namespace wasm { // deserialization and cloning. Some data can be simply copied as raw bytes and, // as a convention, is stored in an inline CacheablePod struct. Everything else // should implement the below methods which are called recusively by the -// containing Module. The implementation of all these methods are grouped -// together in WasmSerialize.cpp. +// containing Module. See comments for these methods in wasm::Module. #define WASM_DECLARE_SERIALIZABLE(Type) \ size_t serializedSize() const; \ @@ -95,10 +96,10 @@ struct StaticLinkData WASM_DECLARE_SERIALIZABLE(StaticLinkData) }; -typedef UniquePtr> UniqueStaticLinkData; +typedef UniquePtr UniqueStaticLinkData; -// An Export describes an export from a wasm module. Currently only functions -// can be exported. +// An Export represents a single function inside a wasm Module that has been +// exported one or more times. class Export { @@ -195,6 +196,7 @@ typedef Vector ImportVector; class CodeRange { + // All fields are treated as cacheable POD: uint32_t nameIndex_; uint32_t lineNumber_; uint32_t begin_; @@ -291,9 +293,9 @@ typedef Vector CodeRangeVector; // A CacheableUniquePtr is used to cacheably store strings in Module. template -struct CacheableUniquePtr : public UniquePtr +struct CacheableUniquePtr : public UniquePtr { - typedef UniquePtr UPtr; + typedef UniquePtr UPtr; explicit CacheableUniquePtr(CharT* ptr) : UPtr(ptr) {} MOZ_IMPLICIT CacheableUniquePtr(UPtr&& rhs) : UPtr(Move(rhs)) {} CacheableUniquePtr() = default; @@ -306,6 +308,20 @@ typedef CacheableUniquePtr CacheableChars; typedef CacheableUniquePtr CacheableTwoByteChars; typedef Vector CacheableCharsVector; +// The ExportMap describes how Exports are mapped to the fields of the export +// object. This allows a single Export to be used in multiple fields. + +struct ExportMap +{ + typedef Vector FieldToExportVector; + + CacheableCharsVector exportNames; + CacheableCharsVector fieldNames; + FieldToExportVector fieldsToExports; + + WASM_DECLARE_SERIALIZABLE(ExportMap) +}; + // A UniqueCodePtr owns allocated executable code. Code passed to the Module // constructor must be allocated via AllocateCode. @@ -313,10 +329,11 @@ class CodeDeleter { uint32_t bytes_; public: + CodeDeleter() : bytes_(0) {} explicit CodeDeleter(uint32_t bytes) : bytes_(bytes) {} void operator()(uint8_t* p); }; -typedef JS::UniquePtr UniqueCodePtr; +typedef UniquePtr UniqueCodePtr; UniqueCodePtr AllocateCode(ExclusiveContext* cx, size_t bytes); @@ -337,6 +354,55 @@ UsesHeap(HeapUsage heapUsage) return bool(heapUsage); } +// See mutedErrors comment in jsapi.h. + +enum class MutedErrorsBool +{ + DontMuteErrors = false, + MuteErrors = true +}; + +// ModuleCacheablePod holds the trivially-memcpy()able serializable portion of +// ModuleData. + +struct ModuleCacheablePod +{ + uint32_t functionBytes; + uint32_t codeBytes; + uint32_t globalBytes; + HeapUsage heapUsage; + MutedErrorsBool mutedErrors; + CompileArgs compileArgs; + + uint32_t totalBytes() const { return codeBytes + globalBytes; } +}; + +// ModuleData holds the guts of a Module. ModuleData is mutably built up by +// ModuleGenerator and then handed over to the Module constructor in finish(), +// where it is stored immutably. + +struct ModuleData : ModuleCacheablePod +{ + ModuleData() : loadedFromCache(false) { mozilla::PodZero(&pod()); } + ModuleCacheablePod& pod() { return *this; } + const ModuleCacheablePod& pod() const { return *this; } + + UniqueCodePtr code; + ImportVector imports; + ExportVector exports; + HeapAccessVector heapAccesses; + CodeRangeVector codeRanges; + CallSiteVector callSites; + CacheableCharsVector funcNames; + CacheableChars filename; + CacheableTwoByteChars displayURL; + bool loadedFromCache; + + WASM_DECLARE_SERIALIZABLE(ModuleData); +}; + +typedef UniquePtr UniqueModuleData; + // Module represents a compiled WebAssembly module which lives until the last // reference to any exported functions is dropped. Modules must be wrapped by a // rooted JSObject immediately after creation so that Module::trace() is called @@ -359,6 +425,7 @@ UsesHeap(HeapUsage heapUsage) class Module { + typedef UniquePtr UniqueConstModuleData; struct ImportExit { void* code; jit::BaselineScript* baselineScript; @@ -383,25 +450,8 @@ class Module typedef RelocatablePtrArrayBufferObjectMaybeShared BufferPtr; // Initialized when constructed: - struct CacheablePod { - const uint32_t functionBytes_; - const uint32_t codeBytes_; - const uint32_t globalBytes_; - const HeapUsage heapUsage_; - const bool mutedErrors_; - const bool usesSignalHandlersForOOB_; - const bool usesSignalHandlersForInterrupt_; - } pod; - const UniqueCodePtr code_; - const ImportVector imports_; - const ExportVector exports_; - const HeapAccessVector heapAccesses_; - const CodeRangeVector codeRanges_; - const CallSiteVector callSites_; - const CacheableCharsVector funcNames_; - const CacheableChars filename_; - const CacheableTwoByteChars displayURL_; - const bool loadedFromCache_; + const UniqueConstModuleData module_; + bool isAsmJS_; // Initialized during staticallyLink: bool staticallyLinked_; @@ -417,9 +467,6 @@ class Module bool profilingEnabled_; FuncLabelVector funcLabels_; - class AutoMutateCode; - - uint32_t totalBytes() const; uint8_t* rawHeapPtr() const; uint8_t*& rawHeapPtr(); WasmActivation*& activation(); @@ -429,70 +476,50 @@ class Module MOZ_WARN_UNUSED_RESULT bool setProfilingEnabled(JSContext* cx, bool enabled); ImportExit& importToExit(const Import& import); - enum CacheBool { NotLoadedFromCache = false, LoadedFromCache = true }; - enum ProfilingBool { ProfilingDisabled = false, ProfilingEnabled = true }; - - static CacheablePod zeroPod(); - void init(); - Module(const CacheablePod& pod, - UniqueCodePtr code, - ImportVector&& imports, - ExportVector&& exports, - HeapAccessVector&& heapAccesses, - CodeRangeVector&& codeRanges, - CallSiteVector&& callSites, - CacheableCharsVector&& funcNames, - CacheableChars filename, - CacheableTwoByteChars displayURL, - CacheBool loadedFromCache, - ProfilingBool profilingEnabled, - FuncLabelVector&& funcLabels); - - template friend struct js::MallocProvider; friend class js::WasmActivation; + protected: + enum AsmJSBool { NotAsmJS = false, IsAsmJS = true }; + const ModuleData& base() const { return *module_; } + bool clone(JSContext* cx, const StaticLinkData& link, Module* clone) const; + public: static const unsigned SizeOfImportExit = sizeof(ImportExit); static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun); static const unsigned SizeOfEntryArg = sizeof(EntryArg); - enum MutedBool { DontMuteErrors = false, MuteErrors = true }; + explicit Module(UniqueModuleData module, AsmJSBool = NotAsmJS); + virtual ~Module(); + virtual void trace(JSTracer* trc); + virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data); - Module(CompileArgs args, - uint32_t functionBytes, - uint32_t codeBytes, - uint32_t globalBytes, - HeapUsage heapUsage, - MutedBool mutedErrors, - UniqueCodePtr code, - ImportVector&& imports, - ExportVector&& exports, - HeapAccessVector&& heapAccesses, - CodeRangeVector&& codeRanges, - CallSiteVector&& callSites, - CacheableCharsVector&& funcNames, - CacheableChars filename, - CacheableTwoByteChars displayURL); - ~Module(); - void trace(JSTracer* trc); - - uint8_t* code() const { return code_.get(); } - uint8_t* globalData() const { return code() + pod.codeBytes_; } - uint32_t globalBytes() const { return pod.globalBytes_; } - HeapUsage heapUsage() const { return pod.heapUsage_; } - bool usesHeap() const { return UsesHeap(pod.heapUsage_); } - bool hasSharedHeap() const { return pod.heapUsage_ == HeapUsage::Shared; } - bool mutedErrors() const { return pod.mutedErrors_; } - CompileArgs compileArgs() const; - const ImportVector& imports() const { return imports_; } - const ExportVector& exports() const { return exports_; } - const char* functionName(uint32_t i) const { return funcNames_[i].get(); } - const char* filename() const { return filename_.get(); } - const char16_t* displayURL() const { return displayURL_.get(); } - bool loadedFromCache() const { return loadedFromCache_; } + uint8_t* code() const { return module_->code.get(); } + uint32_t codeBytes() const { return module_->codeBytes; } + uint8_t* globalData() const { return code() + module_->codeBytes; } + uint32_t globalBytes() const { return module_->globalBytes; } + HeapUsage heapUsage() const { return module_->heapUsage; } + bool usesHeap() const { return UsesHeap(module_->heapUsage); } + bool hasSharedHeap() const { return module_->heapUsage == HeapUsage::Shared; } + bool mutedErrors() const { return bool(module_->mutedErrors); } + CompileArgs compileArgs() const { return module_->compileArgs; } + const ImportVector& imports() const { return module_->imports; } + const ExportVector& exports() const { return module_->exports; } + const char* functionName(uint32_t i) const { return module_->funcNames[i].get(); } + const char* filename() const { return module_->filename.get(); } + const char16_t* displayURL() const { return module_->displayURL.get(); } + bool loadedFromCache() const { return module_->loadedFromCache; } bool staticallyLinked() const { return staticallyLinked_; } bool dynamicallyLinked() const { return dynamicallyLinked_; } + // Some wasm::Module's have the most-derived type AsmJSModule. The + // AsmJSModule stores the extra metadata necessary to implement asm.js (JS) + // semantics. The asAsmJS() member may be used as a checked downcast when + // isAsmJS() is true. + + bool isAsmJS() const { return isAsmJS_; } + AsmJSModule& asAsmJS() { MOZ_ASSERT(isAsmJS_); return *(AsmJSModule*)this; } + const AsmJSModule& asAsmJS() const { MOZ_ASSERT(isAsmJS_); return *(const AsmJSModule*)this; } + // The range [0, functionBytes) is a subrange of [0, codeBytes) that // contains only function body code, not the stub code. This distinction is // used by the async interrupt handler to only interrupt when the pc is in @@ -509,7 +536,7 @@ class Module // statically-linked state. The given StaticLinkData must have come from the // compilation of this module. - bool staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData); + bool staticallyLink(ExclusiveContext* cx, const StaticLinkData& link); // This function transitions the module from a statically-linked state to a // dynamically-linked state. If this module usesHeap(), a non-null heap @@ -517,7 +544,13 @@ class Module // ImportVector. bool dynamicallyLink(JSContext* cx, Handle heap, - const AutoVectorRooter& imports); + Handle imports); + + // This function creates and returns a new export object for this module. + // The lengths of exports() and map.exportNames must be the same. + + bool createExportObject(JSContext* cx, Handle moduleObj, + const ExportMap& map, MutableHandleObject exportObj); // The wasm heap, established by dynamicallyLink. @@ -554,19 +587,44 @@ class Module bool profilingEnabled() const { return profilingEnabled_; } const char* profilingLabel(uint32_t funcIndex) const; +}; - // See WASM_DECLARE_SERIALIZABLE. - size_t serializedSize() const; - uint8_t* serialize(uint8_t* cursor) const; - typedef UniquePtr> UniqueModule; - static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor, - UniqueModule* out); - UniqueModule clone(JSContext* cx, const StaticLinkData& linkData) const; - void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode, - size_t* asmJSModuleData); +typedef UniquePtr UniqueModule; + +// Tests to query and access the exported JS functions produced by +// Module::createExportObject. + +extern bool +IsExportedFunction(JSFunction* fun); + +extern WasmModuleObject* +ExportedFunctionToModuleObject(JSFunction* fun); + +extern uint32_t +ExportedFunctionToIndex(JSFunction* fun); + +} // namespace wasm + +// An WasmModuleObject is an internal object (i.e., not exposed directly to user +// code) which traces and owns a wasm::Module. The WasmModuleObject is +// referenced by the extended slots of exported JSFunctions and serves to keep +// the wasm::Module alive until its last GC reference is dead. + +class WasmModuleObject : public NativeObject +{ + static const unsigned MODULE_SLOT = 0; + bool hasModule() const; + static void finalize(FreeOp* fop, JSObject* obj); + static void trace(JSTracer* trc, JSObject* obj); + public: + static const unsigned RESERVED_SLOTS = 1; + static WasmModuleObject* create(ExclusiveContext* cx); + bool init(wasm::Module* module); + wasm::Module& module() const; + void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data); + static const Class class_; }; } // namespace js -} // namespace wasm #endif // wasm_module_h diff --git a/js/src/asmjs/WasmStubs.cpp b/js/src/asmjs/WasmStubs.cpp index 05635d4b40..dccd6c6697 100644 --- a/js/src/asmjs/WasmStubs.cpp +++ b/js/src/asmjs/WasmStubs.cpp @@ -262,7 +262,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap) MOZ_CRASH("no int64 in asm.js"); case ExprType::F32: masm.convertFloat32ToDouble(ReturnFloat32Reg, ReturnDoubleReg); - // Fall through as ReturnDoubleReg now contains a Double + MOZ_FALLTHROUGH; // as ReturnDoubleReg now contains a Double case ExprType::F64: masm.canonicalizeDouble(ReturnDoubleReg); masm.storeDouble(ReturnDoubleReg, Address(argv, 0)); @@ -1074,14 +1074,14 @@ GenerateThrowStub(ModuleGenerator& mg, Label* throwLabel) bool wasm::GenerateStubs(ModuleGenerator& mg, bool usesHeap) { - for (unsigned i = 0; i < mg.numDeclaredExports(); i++) { + for (unsigned i = 0; i < mg.numExports(); i++) { if (!GenerateEntry(mg, i, usesHeap)) return false; } Label onThrow; - for (size_t i = 0; i < mg.numDeclaredImports(); i++) { + for (size_t i = 0; i < mg.numImports(); i++) { ProfilingOffsets interp; if (!GenerateInterpExitStub(mg, i, &onThrow, &interp)) return false; diff --git a/js/src/asmjs/WasmTypes.h b/js/src/asmjs/WasmTypes.h index 5da09914ea..fb38203c43 100644 --- a/js/src/asmjs/WasmTypes.h +++ b/js/src/asmjs/WasmTypes.h @@ -22,10 +22,12 @@ #include "mozilla/DebugOnly.h" #include "mozilla/HashFunctions.h" #include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" + +#include "NamespaceImports.h" #include "ds/LifoAlloc.h" #include "jit/IonTypes.h" +#include "js/UniquePtr.h" #include "js/Utility.h" #include "js/Vector.h" @@ -37,7 +39,6 @@ namespace wasm { using mozilla::Move; using mozilla::DebugOnly; -using mozilla::UniquePtr; using mozilla::MallocSizeOf; // The ValType enum represents the WebAssembly "value type", which are used to diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 561152e2a6..77664b05a5 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -1166,7 +1166,7 @@ MakeElementValue(JSObject *object) } template -ArrayObject* ModuleBuilder::createArray(const TraceableVector& vector) +ArrayObject* ModuleBuilder::createArray(const GCVector& vector) { uint32_t length = vector.length(); RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length)); diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index f0352955b9..024d8291b8 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -12,7 +12,7 @@ #include "gc/Zone.h" -#include "js/TraceableVector.h" +#include "js/GCVector.h" #include "vm/NativeObject.h" #include "vm/ProxyObject.h" @@ -196,7 +196,7 @@ struct FunctionDeclaration RelocatablePtrFunction fun; }; -using FunctionDeclarationVector = TraceableVector; +using FunctionDeclarationVector = GCVector; class ModuleObject : public NativeObject { @@ -282,7 +282,7 @@ class MOZ_STACK_CLASS ModuleBuilder bool hasExportedName(JSAtom* name) const; - using ExportEntryVector = TraceableVector; + using ExportEntryVector = GCVector; const ExportEntryVector& localExportEntries() const { return localExportEntries_; } @@ -291,9 +291,9 @@ class MOZ_STACK_CLASS ModuleBuilder bool initModule(); private: - using AtomVector = TraceableVector; + using AtomVector = GCVector; using RootedAtomVector = JS::Rooted; - using ImportEntryVector = TraceableVector; + using ImportEntryVector = GCVector; using RootedImportEntryVector = JS::Rooted; using RootedExportEntryVector = JS::Rooted; @@ -316,7 +316,7 @@ class MOZ_STACK_CLASS ModuleBuilder bool maybeAppendRequestedModule(HandleAtom module); template - ArrayObject* createArray(const TraceableVector& vector); + ArrayObject* createArray(const GCVector& vector); }; bool InitModuleClasses(JSContext* cx, HandleObject obj); diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 4c425f1990..16a554c235 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -7,13 +7,13 @@ #include "builtin/Object.h" #include "mozilla/ArrayUtils.h" -#include "mozilla/UniquePtr.h" #include "jscntxt.h" #include "builtin/Eval.h" #include "frontend/BytecodeCompiler.h" #include "jit/InlinableNatives.h" +#include "js/UniquePtr.h" #include "vm/StringBuffer.h" #include "jsobjinlines.h" @@ -25,7 +25,6 @@ using namespace js; using js::frontend::IsIdentifier; using mozilla::ArrayLength; -using mozilla::UniquePtr; bool js::obj_construct(JSContext* cx, unsigned argc, Value* vp) @@ -631,8 +630,7 @@ js::obj_create(JSContext* cx, unsigned argc, Value* vp) if (!args[0].isObjectOrNull()) { RootedValue v(cx, args[0]); - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr); if (!bytes) return false; JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 6c468695e4..d498eddf98 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2448,7 +2448,7 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) pn = pn->pn_expr; if (!pn->isKind(PNK_STATEMENTLIST)) return statement(pn, dst); - /* FALL THROUGH */ + MOZ_FALLTHROUGH; case PNK_STATEMENTLIST: return blockStatement(pn, dst); diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 6226dec2e2..dde68f3f2b 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -7,7 +7,8 @@ #include "builtin/TestingFunctions.h" #include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" + +#include #include "jsapi.h" #include "jscntxt.h" @@ -25,6 +26,7 @@ #include "js/StructuredClone.h" #include "js/UbiNode.h" #include "js/UbiNodeBreadthFirst.h" +#include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" @@ -43,7 +45,6 @@ using namespace js; using mozilla::ArrayLength; using mozilla::Move; -using mozilla::UniquePtr; // If fuzzingSafe is set, remove functionality that could cause problems with // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE. @@ -320,22 +321,45 @@ MinorGC(JSContext* cx, unsigned argc, Value* vp) return true; } -static const struct ParamPair { +#define FOR_EACH_GC_PARAM(_) \ + _("maxBytes", JSGC_MAX_BYTES, true, false) \ + _("maxMallocBytes", JSGC_MAX_MALLOC_BYTES, true, false) \ + _("gcBytes", JSGC_BYTES, false, false) \ + _("gcNumber", JSGC_NUMBER, false, false) \ + _("mode", JSGC_MODE, true, true) \ + _("unusedChunks", JSGC_UNUSED_CHUNKS, false, false) \ + _("totalChunks", JSGC_TOTAL_CHUNKS, false, false) \ + _("sliceTimeBudget", JSGC_SLICE_TIME_BUDGET, true, false) \ + _("markStackLimit", JSGC_MARK_STACK_LIMIT, true, false) \ + _("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true, false) \ + _("highFrequencyLowLimit", JSGC_HIGH_FREQUENCY_LOW_LIMIT, true, false) \ + _("highFrequencyHighLimit", JSGC_HIGH_FREQUENCY_HIGH_LIMIT, true, false) \ + _("highFrequencyHeapGrowthMax", JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, true, false) \ + _("highFrequencyHeapGrowthMin", JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, true, false) \ + _("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true, false) \ + _("dynamicHeapGrowth", JSGC_DYNAMIC_HEAP_GROWTH, true, true) \ + _("dynamicMarkSlice", JSGC_DYNAMIC_MARK_SLICE, true, true) \ + _("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true, false) \ + _("decommitThreshold", JSGC_DECOMMIT_THRESHOLD, true, false) \ + _("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true, true) \ + _("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true, true) \ + _("compactingEnabled", JSGC_COMPACTING_ENABLED, true, true) + +static const struct ParamInfo { const char* name; JSGCParamKey param; + bool writable; + bool allowZero; } paramMap[] = { - {"maxBytes", JSGC_MAX_BYTES }, - {"maxMallocBytes", JSGC_MAX_MALLOC_BYTES}, - {"gcBytes", JSGC_BYTES}, - {"gcNumber", JSGC_NUMBER}, - {"sliceTimeBudget", JSGC_SLICE_TIME_BUDGET}, - {"markStackLimit", JSGC_MARK_STACK_LIMIT}, - {"minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT}, - {"maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT} +#define DEFINE_PARAM_INFO(name, key, writable, allowZero) \ + {name, key, writable, allowZero}, +FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO) +#undef DEFINE_PARAM_INFO }; -// Keep this in sync with above params. -#define GC_PARAMETER_ARGS_LIST "maxBytes, maxMallocBytes, gcBytes, gcNumber, sliceTimeBudget, markStackLimit, minEmptyChunkCount or maxEmptyChunkCount" +#define PARAM_NAME_LIST_ENTRY(name, key, writable, allowZero) \ + " " name +#define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY) static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) @@ -354,13 +378,14 @@ GCParameter(JSContext* cx, unsigned argc, Value* vp) for (;; paramIndex++) { if (paramIndex == ArrayLength(paramMap)) { JS_ReportError(cx, - "the first argument must be one of " GC_PARAMETER_ARGS_LIST); + "the first argument must be one of:" GC_PARAMETER_ARGS_LIST); return false; } if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name)) break; } - JSGCParamKey param = paramMap[paramIndex].param; + const ParamInfo& info = paramMap[paramIndex]; + JSGCParamKey param = info.param; // Request mode. if (args.length() == 1) { @@ -369,9 +394,8 @@ GCParameter(JSContext* cx, unsigned argc, Value* vp) return true; } - if (param == JSGC_NUMBER || param == JSGC_BYTES) { - JS_ReportError(cx, "Attempt to change read-only parameter %s", - paramMap[paramIndex].name); + if (!info.writable) { + JS_ReportError(cx, "Attempt to change read-only parameter %s", info.name); return false; } @@ -380,11 +404,17 @@ GCParameter(JSContext* cx, unsigned argc, Value* vp) return true; } - uint32_t value; - if (!ToUint32(cx, args[1], &value)) + double d; + if (!ToNumber(cx, args[1], &d)) return false; - if (!value) { + if (d < 0 || d > UINT32_MAX) { + JS_ReportError(cx, "Parameter value out of range"); + return false; + } + + uint32_t value = floor(d); + if (!info.allowZero && value == 0) { JS_ReportError(cx, "the second argument must be convertable to uint32_t " "with non-zero value"); return false; @@ -479,7 +509,7 @@ IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) !args[0].toObject().is()) { JS_ReportError(cx, "The first argument should be a function."); - return true; + return false; } JSFunction* fun = &args[0].toObject().as(); @@ -2266,7 +2296,7 @@ ReportLargeAllocationFailure(JSContext* cx, unsigned argc, Value* vp) namespace heaptools { -typedef UniquePtr EdgeName; +typedef UniqueTwoByteChars EdgeName; // An edge to a node from its predecessor in a path through the graph. class BackEdge { @@ -2505,10 +2535,10 @@ EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) size_t srclen = chars.length(); const char16_t* src = chars.start().get(); - JS::AutoFilename filename; + JS::UniqueChars filename; unsigned lineno; - DescribeScriptedCaller(cx, &filename, &lineno); + JS::DescribeScriptedCaller(cx, &filename, &lineno); JS::CompileOptions options(cx); options.setFileAndLine(filename.get(), lineno); @@ -2591,10 +2621,10 @@ ShellCloneAndExecuteScript(JSContext* cx, unsigned argc, Value* vp) size_t srclen = chars.length(); const char16_t* src = chars.start().get(); - JS::AutoFilename filename; + JS::UniqueChars filename; unsigned lineno; - DescribeScriptedCaller(cx, &filename, &lineno); + JS::DescribeScriptedCaller(cx, &filename, &lineno); JS::CompileOptions options(cx); options.setFileAndLine(filename.get(), lineno); @@ -2958,32 +2988,6 @@ SetGCCallback(JSContext* cx, unsigned argc, Value* vp) return true; } -static bool -SetARMHwCapFlags(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() != 1) { - JS_ReportError(cx, "Wrong number of arguments"); - return false; - } - - RootedString flagsListString(cx, JS::ToString(cx, args.get(0))); - if (!flagsListString) - return false; - -#if defined(JS_CODEGEN_ARM) - JSAutoByteString flagsList(cx, flagsListString); - if (!flagsList) - return false; - - jit::ParseARMHwCapFlags(flagsList.ptr()); -#endif - - args.rval().setUndefined(); - return true; -} - static bool GetLcovInfo(JSContext* cx, unsigned argc, Value* vp) { @@ -3165,7 +3169,7 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gcparam", GCParameter, 2, 0, "gcparam(name [, value])", -" Wrapper for JS_[GS]etGCParameter. The name is one of " GC_PARAMETER_ARGS_LIST), +" Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST), JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0, "relazifyFunctions(...)", @@ -3616,11 +3620,6 @@ gc::ZealModeHelpText), " 'minorGC' - run a nursery collection\n" " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"), - JS_FN_HELP("setARMHwCapFlags", SetARMHwCapFlags, 1, 0, -"setARMHwCapFlags(\"flag1,flag2 flag3\")", -" On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n" -" flags is available by calling this function with \"help\" as the flag's name"), - JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0, "getLcovInfo(global)", " Generate LCOV tracefile for the given compartment. If no global are provided then\n" diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp index f948728538..7da1347f71 100644 --- a/js/src/builtin/WeakMapObject.cpp +++ b/js/src/builtin/WeakMapObject.cpp @@ -14,8 +14,6 @@ using namespace js; using namespace js::gc; -using mozilla::UniquePtr; - MOZ_ALWAYS_INLINE bool IsWeakMap(HandleValue v) { @@ -191,8 +189,7 @@ WeakMap_set_impl(JSContext* cx, const CallArgs& args) MOZ_ASSERT(IsWeakMap(args.thisv())); if (!args.get(0).isObject()) { - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr); if (!bytes) return false; JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); @@ -377,7 +374,7 @@ WeakMap_construct(JSContext* cx, unsigned argc, Value* vp) // Steps 12k-l. if (isOriginalAdder) { if (keyVal.isPrimitive()) { - UniquePtr bytes = + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr); if (!bytes) return false; diff --git a/js/src/builtin/WeakSetObject.cpp b/js/src/builtin/WeakSetObject.cpp index 6a55de0678..56486ea25d 100644 --- a/js/src/builtin/WeakSetObject.cpp +++ b/js/src/builtin/WeakSetObject.cpp @@ -22,8 +22,6 @@ using namespace js; -using mozilla::UniquePtr; - const Class WeakSetObject::class_ = { "WeakSet", JSCLASS_HAS_CACHED_PROTO(JSProto_WeakSet) | @@ -127,7 +125,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp) if (isOriginalAdder) { if (keyVal.isPrimitive()) { - UniquePtr bytes = + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr); if (!bytes) return false; diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index e024da19be..9cea50b872 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -4093,7 +4093,8 @@ CType::Finalize(JSFreeOp* fop, JSObject* obj) } } - // Fall through. + MOZ_FALLTHROUGH; + case TYPE_array: { // Free the ffi_type info. slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); @@ -6872,7 +6873,7 @@ CClosure::Create(JSContext* cx, // we might be unable to convert the value to the proper type. If so, we want // the caller to know about it _now_, rather than some uncertain time in the // future when the error sentinel is actually needed. - mozilla::UniquePtr errResult; + UniquePtr errResult; if (!errVal.isUndefined()) { // Make sure the callback returns something. diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 9577b3698c..f92b467875 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -6,7 +6,6 @@ #ifndef ctypes_CTypes_h #define ctypes_CTypes_h -#include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" #include "ffi.h" @@ -16,6 +15,7 @@ #include "ctypes/typedefs.h" #include "js/GCHashTable.h" +#include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/String.h" @@ -467,7 +467,7 @@ namespace PointerType { JSObject* GetBaseType(JSObject* obj); } // namespace PointerType -typedef mozilla::UniquePtr> UniquePtrFFIType; +typedef UniquePtr UniquePtrFFIType; namespace ArrayType { JSObject* CreateInternal(JSContext* cx, HandleObject baseType, size_t length, diff --git a/js/src/ds/IdValuePair.h b/js/src/ds/IdValuePair.h index 8ca5f29947..203beb2799 100644 --- a/js/src/ds/IdValuePair.h +++ b/js/src/ds/IdValuePair.h @@ -11,8 +11,8 @@ #include "NamespaceImports.h" #include "gc/Tracer.h" +#include "js/GCVector.h" #include "js/Id.h" -#include "js/TraceableVector.h" namespace js { @@ -37,7 +37,7 @@ struct IdValuePair } }; -using IdValueVector = TraceableVector; +using IdValueVector = GCVector; } /* namespace js */ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index ae93ad7171..763338a8b9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -15,7 +15,6 @@ #include "mozilla/FloatingPoint.h" #include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include @@ -54,7 +53,6 @@ using mozilla::Some; using mozilla::DebugOnly; using mozilla::NumberIsInt32; using mozilla::PodCopy; -using mozilla::UniquePtr; struct frontend::StmtInfoBCE : public StmtInfoBase { @@ -2109,6 +2107,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) // Any subexpression of a comma expression could be effectful. case PNK_COMMA: MOZ_ASSERT(pn->pn_count > 0); + MOZ_FALLTHROUGH; // Subcomponents of a literal may be effectful. case PNK_ARRAY: case PNK_OBJECT: @@ -7284,6 +7283,76 @@ BytecodeEmitter::emitSelfHostedAllowContentSpread(ParseNode* pn) return emitTree(pn->pn_head->pn_next); } +bool +BytecodeEmitter::isRestParameter(ParseNode* pn, bool* result) +{ + if (!sc->isFunctionBox()) { + *result = false; + return true; + } + + RootedFunction fun(cx, sc->asFunctionBox()->function()); + if (!fun->hasRest()) { + *result = false; + return true; + } + + if (!pn->isKind(PNK_NAME)) { + if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(PNK_CALL)) { + ParseNode* pn2 = pn->pn_head; + if (pn2->getKind() == PNK_NAME && pn2->name() == cx->names().allowContentSpread) + return isRestParameter(pn2->pn_next, result); + } + *result = false; + return true; + } + + if (!bindNameToSlot(pn)) + return false; + + *result = pn->getOp() == JSOP_GETARG && pn->pn_scopecoord.slot() == fun->nargs() - 1; + return true; +} + +bool +BytecodeEmitter::emitOptimizeSpread(ParseNode* arg0, ptrdiff_t* jmp, bool* emitted) +{ + // Emit a pereparation code to optimize the spread call with a rest + // parameter: + // + // function f(...args) { + // g(...args); + // } + // + // If the spread operand is a rest parameter and it's optimizable array, + // skip spread operation and pass it directly to spread call operation. + // See the comment in OptimizeSpreadCall in Interpreter.cpp for the + // optimizable conditons. + bool result = false; + if (!isRestParameter(arg0, &result)) + return false; + + if (!result) { + *emitted = false; + return true; + } + + if (!emitTree(arg0)) + return false; + + if (!emit1(JSOP_OPTIMIZE_SPREADCALL)) + return false; + + if (!emitJump(JSOP_IFNE, 0, jmp)) + return false; + + if (!emit1(JSOP_POP)) + return false; + + *emitted = true; + return true; +} + bool BytecodeEmitter::emitCallOrNew(ParseNode* pn) { @@ -7436,9 +7505,20 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) } } } else { - if (!emitArray(pn2->pn_next, argc, JSOP_SPREADCALLARRAY)) + ParseNode* args = pn2->pn_next; + ptrdiff_t jmp; + bool optCodeEmitted = false; + if (argc == 1) { + if (!emitOptimizeSpread(args->pn_kid, &jmp, &optCodeEmitted)) + return false; + } + + if (!emitArray(args, argc, JSOP_SPREADCALLARRAY)) return false; + if (optCodeEmitted) + setJumpOffsetAt(jmp); + if (isNewOp) { if (pn->isKind(PNK_SUPERCALL)) { if (!emit1(JSOP_NEWTARGET)) @@ -8805,7 +8885,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) break; case PNK_POSHOLDER: - MOZ_ASSERT_UNREACHABLE("Should never try to emit PNK_POSHOLDER"); + MOZ_FALLTHROUGH_ASSERT("Should never try to emit PNK_POSHOLDER"); default: MOZ_ASSERT(0); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index c21d3c1337..cb8c6f5ac7 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -607,6 +607,9 @@ struct BytecodeEmitter bool emitConditionalExpression(ConditionalExpression& conditional); + bool isRestParameter(ParseNode* pn, bool* result); + bool emitOptimizeSpread(ParseNode* arg0, ptrdiff_t* jmp, bool* emitted); + bool emitCallOrNew(ParseNode* pn); bool emitDebugOnlyCheckSelfHosted(); bool emitSelfHostedCallFunction(ParseNode* pn); diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index d9f97c1c52..46b50adcc6 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -156,7 +156,7 @@ class NameResolver * flagged as a contributor. */ pos--; - /* fallthrough */ + MOZ_FALLTHROUGH; default: /* Save any other nodes we encounter on the way up. */ @@ -727,8 +727,8 @@ class NameResolver // Import/export spec lists contain import/export specs containing // only pairs of names. Alternatively, an export spec lists may // contain a single export batch specifier. - case PNK_IMPORT_SPEC_LIST: { case PNK_EXPORT_SPEC_LIST: + case PNK_IMPORT_SPEC_LIST: { MOZ_ASSERT(cur->isArity(PN_LIST)); #ifdef DEBUG bool isImport = cur->isKind(PNK_IMPORT_SPEC_LIST); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index dd50cc06b9..3faae013a0 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6300,7 +6300,7 @@ Parser::yieldExpression(InHandling inHandling) case TOK_MUL: kind = PNK_YIELD_STAR; tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand); - // Fall through. + MOZ_FALLTHROUGH; default: exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited); if (!exprNode) @@ -6331,7 +6331,7 @@ Parser::yieldExpression(InHandling inHandling) JSMSG_BAD_ANON_GENERATOR_RETURN); return null(); } - // Fall through. + MOZ_FALLTHROUGH; case LegacyGenerator: { @@ -6602,7 +6602,7 @@ Parser::tryStatement(YieldHandling yieldHandling) // check its validity for legacy generators. if (!checkYieldNameValidity()) return null(); - // Fall through. + MOZ_FALLTHROUGH; case TOK_NAME: { RootedPropertyName label(context, tokenStream.currentName()); @@ -9717,7 +9717,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling case TOK_YIELD: if (!checkYieldNameValidity()) return null(); - // Fall through. + MOZ_FALLTHROUGH; case TOK_NAME: return identifierName(yieldHandling); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b6875cabd6..750abd1c0c 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -236,7 +236,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */ // All inner functions in this context. Only filled in when parsing syntax. - Rooted> innerFunctions; + Rooted> innerFunctions; // In a function context, points to a Directive struct that can be updated // to reflect new directives encountered in the Directive Prologue that @@ -272,7 +272,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext parserPC(&prs->pc), oldpc(prs->pc), lexdeps(prs->context), - innerFunctions(prs->context, TraceableVector(prs->context)), + innerFunctions(prs->context, GCVector(prs->context)), newDirectives(newDirectives), inDeclDestructuring(false) { diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 6c5c5dd2cb..1e3845c6c9 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -10,7 +10,6 @@ #include "mozilla/IntegerTypeTraits.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include #include @@ -25,6 +24,7 @@ #include "frontend/BytecodeCompiler.h" #include "js/CharacterEncoding.h" +#include "js/UniquePtr.h" #include "vm/HelperThreads.h" #include "vm/Keywords.h" #include "vm/StringBuffer.h" @@ -37,7 +37,6 @@ using mozilla::Maybe; using mozilla::PodAssign; using mozilla::PodCopy; using mozilla::PodZero; -using mozilla::UniquePtr; struct KeywordInfo { const char* chars; // C string with keyword text @@ -617,7 +616,10 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne // On the main thread, report the error immediately. When compiling off // thread, save the error so that the main thread can report it later. CompileError tempErr; - CompileError& err = cx->isJSContext() ? tempErr : cx->addPendingCompileError(); + CompileError* tempErrPtr = &tempErr; + if (!cx->isJSContext() && !cx->addPendingCompileError(&tempErrPtr)) + return false; + CompileError& err = *tempErrPtr; err.report.flags = flags; err.report.errorNumber = errorNumber; @@ -854,7 +856,7 @@ bool TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated, const char* directive, int directiveLength, const char* errorMsgPragma, - UniquePtr* destination) + UniqueTwoByteChars* destination) { MOZ_ASSERT(directiveLength <= 18); char16_t peeked[18]; diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index f3712cfecd..f813adc234 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -14,7 +14,6 @@ #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include #include @@ -24,6 +23,7 @@ #include "jspubtd.h" #include "frontend/TokenKind.h" +#include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/RegExpObject.h" @@ -976,7 +976,7 @@ class MOZ_STACK_CLASS TokenStream bool getDirective(bool isMultiline, bool shouldWarnDeprecated, const char* directive, int directiveLength, const char* errorMsgPragma, - mozilla::UniquePtr* destination); + UniquePtr* destination); bool getDisplayURL(bool isMultiline, bool shouldWarnDeprecated); bool getSourceMappingURL(bool isMultiline, bool shouldWarnDeprecated); @@ -1024,9 +1024,9 @@ class MOZ_STACK_CLASS TokenStream size_t linebase; // start of current line size_t prevLinebase; // start of previous line; size_t(-1) if on the first line TokenBuf userbuf; // user input buffer - const char* filename; // input filename or null - mozilla::UniquePtr displayURL_; // the user's requested source URL or null - mozilla::UniquePtr sourceMapURL_; // source map's filename or null + const char* filename; // input filename or null + UniqueTwoByteChars displayURL_; // the user's requested source URL or null + UniqueTwoByteChars sourceMapURL_; // source map's filename or null CharBuffer tokenbuf; // current token string buffer uint8_t isExprEnding[TOK_LIMIT];// which tokens definitely terminate exprs? ExclusiveContext* const cx; diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 5c69cd1fdb..b4bfb7fe11 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -471,13 +471,14 @@ struct DefaultGCPolicy static void trace(JSTracer* trc, T** thingp, const char* name) { // If linking is failing here, it likely means that you need to define // or use a non-default GC policy for your non-gc-pointer type. - TraceManuallyBarrieredEdge(trc, thingp, name); + if (*thingp) + TraceManuallyBarrieredEdge(trc, thingp, name); } static bool needsSweep(T** thingp) { // If linking is failing here, it likely means that you need to define // or use a non-default GC policy for your non-gc-pointer type. - return gc::IsAboutToBeFinalizedUnbarriered(thingp); + return *thingp && gc::IsAboutToBeFinalizedUnbarriered(thingp); } }; diff --git a/js/src/gc/Rooting.h b/js/src/gc/Rooting.h index f6cc72c242..2aafbb807a 100644 --- a/js/src/gc/Rooting.h +++ b/js/src/gc/Rooting.h @@ -55,6 +55,11 @@ typedef JS::Rooted RootedPlainObject; typedef JS::Rooted RootedSavedFrame; typedef JS::Rooted RootedScriptSource; +typedef GCVector FunctionVector; +typedef GCVector PropertyNameVector; +typedef GCVector ShapeVector; +typedef GCVector StringVector; + } /* namespace js */ #endif /* gc_Rooting_h */ diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 82df48acfc..6339f2523f 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -9,7 +9,6 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/IntegerRange.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include #include @@ -342,7 +341,7 @@ Statistics::formatCompactSliceMessage() const slice.resetReason ? "yes - " : "no", slice.resetReason ? slice.resetReason : ""); FragmentVector fragments; - if (!fragments.append(make_string_copy(buffer)) || + if (!fragments.append(DuplicateString(buffer)) || !fragments.append(formatCompactSlicePhaseTimes(slices[index].phaseTimes))) { return UniqueChars(nullptr); @@ -356,7 +355,7 @@ Statistics::formatCompactSummaryMessage() const const double bytesPerMiB = 1024 * 1024; FragmentVector fragments; - if (!fragments.append(make_string_copy("Summary - "))) + if (!fragments.append(DuplicateString("Summary - "))) return UniqueChars(nullptr); int64_t total, longest; @@ -374,7 +373,7 @@ Statistics::formatCompactSummaryMessage() const JS_snprintf(buffer, sizeof(buffer), "Non-Incremental: %.3fms (%s); ", t(total), nonincrementalReason_); } - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); JS_snprintf(buffer, sizeof(buffer), @@ -385,7 +384,7 @@ Statistics::formatCompactSummaryMessage() const double(preBytes) / bytesPerMiB, counts[STAT_NEW_CHUNK] - counts[STAT_DESTROY_CHUNK], counts[STAT_NEW_CHUNK] + counts[STAT_DESTROY_CHUNK]); - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); MOZ_ASSERT_IF(counts[STAT_ARENA_RELOCATED], gckind == GC_SHRINK); @@ -394,7 +393,7 @@ Statistics::formatCompactSummaryMessage() const "Kind: %s; Relocated: %.3f MiB; ", ExplainInvocationKind(gckind), double(ArenaSize * counts[STAT_ARENA_RELOCATED]) / bytesPerMiB); - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); } @@ -419,13 +418,13 @@ Statistics::formatCompactSlicePhaseTimes(const PhaseTimeTable phaseTimes) const int64_t childTime = SumChildTimes(dagSlot, phase, phaseTimes); if (ownTime > MaxUnaccountedTimeUS) { JS_snprintf(buffer, sizeof(buffer), "%s: %.3fms", phases[phase].name, t(ownTime)); - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); if (childTime && (ownTime - childTime) > MaxUnaccountedTimeUS) { MOZ_ASSERT(level < 3); JS_snprintf(buffer, sizeof(buffer), "%s: %.3fms", "Other", t(ownTime - childTime)); - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); } } @@ -500,7 +499,7 @@ Statistics::formatDetailedDescription() counts[STAT_NEW_CHUNK] - counts[STAT_DESTROY_CHUNK], counts[STAT_NEW_CHUNK] + counts[STAT_DESTROY_CHUNK], double(ArenaSize * counts[STAT_ARENA_RELOCATED]) / bytesPerMiB); - return make_string_copy(buffer); + return DuplicateString(buffer); } UniqueChars @@ -524,7 +523,7 @@ Statistics::formatDetailedSliceDescription(unsigned i, const SliceData& slice) slice.resetReason ? "yes - " : "no", slice.resetReason ? slice.resetReason : "", uint64_t(slice.endFaults - slice.startFaults), t(slice.duration()), budgetDescription, t(slice.start - slices[0].start)); - return make_string_copy(buffer); + return DuplicateString(buffer); } UniqueChars @@ -547,14 +546,14 @@ Statistics::formatDetailedPhaseTimes(const PhaseTimeTable phaseTimes) if (ownTime > 0) { JS_snprintf(buffer, sizeof(buffer), " %s%s: %.3fms\n", LevelToIndent[level], phases[phase].name, t(ownTime)); - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); if (childTime && (ownTime - childTime) > MaxUnaccountedChildTimeUS) { MOZ_ASSERT(level < 3); JS_snprintf(buffer, sizeof(buffer), " %s%s: %.3fms\n", LevelToIndent[level + 1], "Other", t(ownTime - childTime)); - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); } } @@ -577,7 +576,7 @@ Statistics::formatDetailedTotals() char buffer[1024]; memset(buffer, 0, sizeof(buffer)); JS_snprintf(buffer, sizeof(buffer), format, t(total), t(longest)); - return make_string_copy(buffer); + return DuplicateString(buffer); } UniqueChars @@ -587,28 +586,28 @@ Statistics::formatJsonMessage(uint64_t timestamp) FragmentVector fragments; - if (!fragments.append(make_string_copy("{")) || + if (!fragments.append(DuplicateString("{")) || !fragments.append(formatJsonDescription(timestamp)) || - !fragments.append(make_string_copy("\"slices\":["))) + !fragments.append(DuplicateString("\"slices\":["))) { return UniqueChars(nullptr); } for (unsigned i = 0; i < slices.length(); i++) { - if (!fragments.append(make_string_copy("{")) || + if (!fragments.append(DuplicateString("{")) || !fragments.append(formatJsonSliceDescription(i, slices[i])) || - !fragments.append(make_string_copy("\"times\":{")) || + !fragments.append(DuplicateString("\"times\":{")) || !fragments.append(formatJsonPhaseTimes(slices[i].phaseTimes)) || - !fragments.append(make_string_copy("}}")) || - (i < (slices.length() - 1) && !fragments.append(make_string_copy(",")))) + !fragments.append(DuplicateString("}}")) || + (i < (slices.length() - 1) && !fragments.append(DuplicateString(",")))) { return UniqueChars(nullptr); } } - if (!fragments.append(make_string_copy("],\"totals\":{")) || + if (!fragments.append(DuplicateString("],\"totals\":{")) || !fragments.append(formatJsonPhaseTimes(phaseTimes)) || - !fragments.append(make_string_copy("}}"))) + !fragments.append(DuplicateString("}}"))) { return UniqueChars(nullptr); } @@ -664,7 +663,7 @@ Statistics::formatJsonDescription(uint64_t timestamp) unsigned(preBytes / 1024 / 1024), counts[STAT_NEW_CHUNK], counts[STAT_DESTROY_CHUNK]); - return make_string_copy(buffer); + return DuplicateString(buffer); } UniqueChars @@ -696,7 +695,7 @@ Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice) pageFaults, slices[i].start, slices[i].end); - return make_string_copy(buffer); + return DuplicateString(buffer); } UniqueChars @@ -729,7 +728,7 @@ Statistics::formatJsonPhaseTimes(const PhaseTimeTable phaseTimes) JS_snprintf(buffer, sizeof(buffer), "\"%s\":%llu.%03llu", name.get(), ownTime / 1000, ownTime % 1000); - if (!fragments.append(make_string_copy(buffer))) + if (!fragments.append(DuplicateString(buffer))) return UniqueChars(nullptr); } return Join(fragments, ","); diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index b0cb5c123d..bd9f6991ae 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -10,7 +10,6 @@ #include "mozilla/DebugOnly.h" #include "mozilla/IntegerRange.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include "jsalloc.h" #include "jsgc.h" diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp index 2a7a65ead3..ac02c047e3 100644 --- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -3815,7 +3815,7 @@ EmitAtomLetter(RegExpCompiler* compiler, } case 4: macro_assembler->CheckCharacter(chars[3], &ok); - // Fall through! + MOZ_FALLTHROUGH; case 3: macro_assembler->CheckCharacter(chars[0], &ok); macro_assembler->CheckCharacter(chars[1], &ok); diff --git a/js/src/irregexp/RegExpParser.cpp b/js/src/irregexp/RegExpParser.cpp index c3b62c3582..a36c6e789f 100644 --- a/js/src/irregexp/RegExpParser.cpp +++ b/js/src/irregexp/RegExpParser.cpp @@ -1563,7 +1563,7 @@ RegExpParser::ParseDisjunction() Advance(); break; } - // Fall through + MOZ_FALLTHROUGH; case 'd': case 's': case 'w': { widechar c = Next(); Advance(2); @@ -1605,8 +1605,8 @@ RegExpParser::ParseDisjunction() Advance(2); break; } + MOZ_FALLTHROUGH; } - // FALLTHROUGH case '0': { if (unicode_) { Advance(2); @@ -1735,7 +1735,7 @@ RegExpParser::ParseDisjunction() int dummy; if (ParseIntervalQuantifier(&dummy, &dummy)) return ReportError(JSMSG_NOTHING_TO_REPEAT); - // fallthrough + MOZ_FALLTHROUGH; } default: if (unicode_) { diff --git a/js/src/jit-test/tests/asm.js/testBasic.js b/js/src/jit-test/tests/asm.js/testBasic.js index e1656d2da4..0b67fc1084 100644 --- a/js/src/jit-test/tests/asm.js/testBasic.js +++ b/js/src/jit-test/tests/asm.js/testBasic.js @@ -97,6 +97,12 @@ var exp = asmLink(asmCompile(USE_ASM + "function f() { return 3 } return {f:f,f: assertEq(exp.f(), 3); assertEq(Object.keys(exp).join(), 'f'); +var exp = asmLink(asmCompile(USE_ASM + "function f() { return 4 } return {f1:f,f2:f}")); +assertEq(exp.f1(), 4); +assertEq(exp.f2(), 4); +assertEq(Object.keys(exp).sort().join(), 'f1,f2'); +assertEq(exp.f1, exp.f2); + assertAsmTypeFail(USE_ASM + "function f() { return 3 } return {1:f}"); assertAsmTypeFail(USE_ASM + "function f() { return 3 } return {__proto__:f}"); assertAsmTypeFail(USE_ASM + "function f() { return 3 } return {get x() {} }"); diff --git a/js/src/jit-test/tests/asm.js/testBug1236552.js b/js/src/jit-test/tests/asm.js/testBug1236552.js new file mode 100644 index 0000000000..2a47ba7ecb --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testBug1236552.js @@ -0,0 +1,3 @@ +g = newGlobal(); +Debugger(g).memory.trackingAllocationSites = true; +evaluate("function h() { 'use asm'; return {}}", { global: g }); diff --git a/js/src/jit-test/tests/basic/bug1236476.js b/js/src/jit-test/tests/basic/bug1236476.js new file mode 100644 index 0000000000..f9f2b3d5c7 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1236476.js @@ -0,0 +1,16 @@ +// |jit-test| allow-oom; allow-unhandlable-oom +// 1236476 + +if (typeof oomTest !== 'function' || + typeof offThreadCompileScript !== 'function' || + typeof runOffThreadScript !== 'function') + quit(); + +oomTest(() => { + offThreadCompileScript(` + "use asm"; + return assertEq; + `); + runOffThreadScript(); +}); + diff --git a/js/src/jit-test/tests/basic/spread-call-rest.js b/js/src/jit-test/tests/basic/spread-call-rest.js new file mode 100644 index 0000000000..6907ed34ba --- /dev/null +++ b/js/src/jit-test/tests/basic/spread-call-rest.js @@ -0,0 +1,73 @@ +// bug 1235092 +// Optimize spread call with rest parameter. + +load(libdir + "asserts.js"); + +function makeArray(...args) { + return args; +} + +// Optimizable Case. +function test(...args) { + return makeArray(...args); +} +assertDeepEq(test(1, 2, 3), [1, 2, 3]); + +// Not optimizable case 1: the array has hole. +function hole1(...args) { + args[4] = 5; + return makeArray(...args); +} +assertDeepEq(hole1(1, 2, 3), [1, 2, 3, undefined, 5]); + +function hole2(...args) { + args.length = 5; + return makeArray(...args); +} +assertDeepEq(hole2(1, 2, 3), [1, 2, 3, undefined, undefined]); + +function hole3(...args) { + delete args[1]; + return makeArray(...args); +} +assertDeepEq(hole3(1, 2, 3), [1, undefined, 3]); + +// Not optimizable case 2: array[@@iterator] is modified. +function modifiedIterator(...args) { + args[Symbol.iterator] = function*() { + for (let i = 0; i < this.length; i++) + yield this[i] * 10; + }; + return makeArray(...args); +} +assertDeepEq(modifiedIterator(1, 2, 3), [10, 20, 30]); + +// Not optimizable case 3: the array's prototype is modified. +function modifiedProto(...args) { + args.__proto__ = { + __proto__: Array.prototype, + *[Symbol.iterator]() { + for (let i = 0; i < this.length; i++) + yield this[i] * 10; + } + }; + return makeArray(...args); +} +assertDeepEq(modifiedProto(1, 2, 3), [10, 20, 30]); + +// Not optimizable case 4: Array.prototype[@@iterator] is modified. +let ArrayValues = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function*() { + for (let i = 0; i < this.length; i++) + yield this[i] * 10; +}; +assertDeepEq(test(1, 2, 3), [10, 20, 30]); +Array.prototype[Symbol.iterator] = ArrayValues; + +// Not optimizable case 5: %ArrayIteratorPrototype%.next is modified. +let ArrayIteratorPrototype = Object.getPrototypeOf(Array.prototype[Symbol.iterator]()); +let i = 1; +ArrayIteratorPrototype.next = function() { + return { done: i % 4 == 0, value: 10 * i++ }; +}; +assertDeepEq(test(1, 2, 3), [10, 20, 30]); diff --git a/js/src/jit-test/tests/basic/testBug1235874.js b/js/src/jit-test/tests/basic/testBug1235874.js new file mode 100644 index 0000000000..8e78410814 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug1235874.js @@ -0,0 +1 @@ +evaluate('evalcx("1")', { fileName: null }); diff --git a/js/src/jit-test/tests/gc/bug-1237153.js b/js/src/jit-test/tests/gc/bug-1237153.js new file mode 100644 index 0000000000..4b3f04b1fd --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1237153.js @@ -0,0 +1,2 @@ +// |jit-test| error: Error +gcparam("sliceTimeBudget", -1); diff --git a/js/src/jit-test/tests/gc/bug-1238582.js b/js/src/jit-test/tests/gc/bug-1238582.js new file mode 100644 index 0000000000..60286c8eee --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1238582.js @@ -0,0 +1,4 @@ +if (!('oomTest' in this)) + quit(); + +oomTest(() => { let a = [2147483651]; [a[0], a[undefined]].sort(); }); diff --git a/js/src/jit-test/tests/gc/gcparam.js b/js/src/jit-test/tests/gc/gcparam.js new file mode 100644 index 0000000000..5c2d4940d9 --- /dev/null +++ b/js/src/jit-test/tests/gc/gcparam.js @@ -0,0 +1,47 @@ +function testGetParam(key) { + gcparam(key); +} + +function testChangeParam(key) { + let prev = gcparam(key); + let value = prev - 1; + gcparam(key, value); + assertEq(gcparam(key), value); + gcparam(key, prev); +} + +function testLargeParamValue(key) { + let prev = gcparam(key); + const value = 1000000; + gcparam(key, value); + assertEq(gcparam(key), value); + gcparam(key, prev); +} + +testGetParam("gcBytes"); +testGetParam("gcNumber"); +testGetParam("unusedChunks"); +testGetParam("totalChunks"); + +testChangeParam("maxBytes"); +testChangeParam("maxMallocBytes"); +testChangeParam("mode"); +testChangeParam("sliceTimeBudget"); +testChangeParam("markStackLimit"); +testChangeParam("highFrequencyTimeLimit"); +testChangeParam("highFrequencyLowLimit"); +testChangeParam("highFrequencyHighLimit"); +testChangeParam("highFrequencyHeapGrowthMax"); +testChangeParam("highFrequencyHeapGrowthMin"); +testChangeParam("lowFrequencyHeapGrowth"); +testChangeParam("dynamicHeapGrowth"); +testChangeParam("dynamicMarkSlice"); +testChangeParam("allocationThreshold"); +testChangeParam("decommitThreshold"); +testChangeParam("minEmptyChunkCount"); +testChangeParam("maxEmptyChunkCount"); +testChangeParam("compactingEnabled"); + +testLargeParamValue("highFrequencyLowLimit"); +testLargeParamValue("highFrequencyHighLimit"); +testLargeParamValue("decommitThreshold"); diff --git a/js/src/jit-test/tests/ion/array-join-bug1137624-1.js b/js/src/jit-test/tests/ion/array-join-bug1137624-1.js new file mode 100644 index 0000000000..1a039dc688 --- /dev/null +++ b/js/src/jit-test/tests/ion/array-join-bug1137624-1.js @@ -0,0 +1,12 @@ + +try { + var x = ["a", {toString() { s = x.join("-"); }}]; + var a = x.join("a"); + var b = x.join("b"); + assertEq(a, b); +} catch (e) { + // Using assertion does not work. + quit(0); +} + +quit(3); diff --git a/js/src/jit-test/tests/ion/array-join-bug1137624-2.js b/js/src/jit-test/tests/ion/array-join-bug1137624-2.js new file mode 100644 index 0000000000..affceb37e7 --- /dev/null +++ b/js/src/jit-test/tests/ion/array-join-bug1137624-2.js @@ -0,0 +1,9 @@ + +function f() { + var x = 0; + var a = [{toString: () => x++}]; + for (var i=0; i<10000; i++) + a.join(""); + assertEq(x, 10000); +} +f(); diff --git a/js/src/jit-test/tests/self-test/isRelazifiableFunction-0.js b/js/src/jit-test/tests/self-test/isRelazifiableFunction-0.js new file mode 100644 index 0000000000..a75246668d --- /dev/null +++ b/js/src/jit-test/tests/self-test/isRelazifiableFunction-0.js @@ -0,0 +1,3 @@ +// |jit-test| error: Error: The first argument should be a function. + +isRelazifiableFunction(new Array()); diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index 6439079a3d..f01250136c 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -2481,6 +2481,7 @@ BacktrackingAllocator::computeSpillWeight(LiveBundle* bundle) case LUse::FIXED: fixed = true; + MOZ_FALLTHROUGH; case LUse::REGISTER: usesTotal += 2000; break; diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 040aca7dc7..fdee3acc92 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1913,7 +1913,7 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo) // Invalid assumption based on baseline code. case Bailout_OverflowInvalidate: outerScript->setHadOverflowBailout(); - // FALL THROUGH + MOZ_FALLTHROUGH; case Bailout_NonStringInputInvalidate: case Bailout_DoubleOutput: case Bailout_ObjectIdentityOrTypeGuard: diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 2d56284054..dabb863932 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -7,7 +7,6 @@ #include "jit/BaselineCompiler.h" #include "mozilla/Casting.h" -#include "mozilla/UniquePtr.h" #include "jit/BaselineIC.h" #include "jit/BaselineJIT.h" @@ -21,6 +20,8 @@ #endif #include "jit/SharedICHelpers.h" #include "jit/VMFunctions.h" +#include "js/UniquePtr.h" +#include "vm/Interpreter.h" #include "vm/ScopeObject.h" #include "vm/TraceLogging.h" @@ -195,7 +196,7 @@ BaselineCompiler::compile() // Note: There is an extra entry in the bytecode type map for the search hint, see below. size_t bytecodeTypeMapEntries = script->nTypeSets() + 1; - mozilla::UniquePtr > baselineScript( + UniquePtr baselineScript( BaselineScript::New(script, prologueOffset_.offset(), epilogueOffset_.offset(), profilerEnterFrameToggleOffset_.offset(), @@ -3184,6 +3185,27 @@ BaselineCompiler::emit_JSOP_STRICTSPREADEVAL() return emitSpreadCall(); } +typedef bool (*OptimizeSpreadCallFn)(JSContext*, HandleValue, bool*); +static const VMFunction OptimizeSpreadCallInfo = + FunctionInfo(OptimizeSpreadCall); + +bool +BaselineCompiler::emit_JSOP_OPTIMIZE_SPREADCALL() +{ + frame.syncStack(0); + masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); + + prepareVMCall(); + pushArg(R0); + + if (!callVM(OptimizeSpreadCallInfo)) + return false; + + masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R0); + frame.push(R0); + return true; +} + typedef bool (*ImplicitThisFn)(JSContext*, HandleObject, HandlePropertyName, MutableHandleValue); static const VMFunction ImplicitThisInfo = FunctionInfo(ImplicitThisOperation); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index e1938ec2ba..f356fcd273 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -167,6 +167,7 @@ namespace jit { _(JSOP_SPREADNEW) \ _(JSOP_SPREADEVAL) \ _(JSOP_STRICTSPREADEVAL) \ + _(JSOP_OPTIMIZE_SPREADCALL)\ _(JSOP_IMPLICITTHIS) \ _(JSOP_GIMPLICITTHIS) \ _(JSOP_INSTANCEOF) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 52d9e4f484..c2fc683290 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -26,7 +26,7 @@ #include "jit/SharedICHelpers.h" #include "jit/VMFunctions.h" #include "js/Conversions.h" -#include "js/TraceableVector.h" +#include "js/GCVector.h" #include "vm/Opcodes.h" #include "vm/TypedArrayCommon.h" @@ -1181,6 +1181,7 @@ RemoveExistingGetElemNativeStubs(JSContext* cx, ICGetElem_Fallback* stub, Handle case ICStub::GetElem_NativeSlotSymbol: if (indirect) continue; + MOZ_FALLTHROUGH; case ICStub::GetElem_NativePrototypeSlotName: case ICStub::GetElem_NativePrototypeSlotSymbol: case ICStub::GetElem_NativePrototypeCallNativeName: diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 4dd43349b1..173a0a96fe 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -19,7 +19,7 @@ #include "jit/BaselineJIT.h" #include "jit/SharedIC.h" #include "jit/SharedICRegisters.h" -#include "js/TraceableVector.h" +#include "js/GCVector.h" #include "vm/ArrayObject.h" #include "vm/UnboxedObject.h" diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 46a8a92d04..9701f5b853 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -55,7 +55,6 @@ using mozilla::FloatingPoint; using mozilla::Maybe; using mozilla::NegativeInfinity; using mozilla::PositiveInfinity; -using mozilla::UniquePtr; using JS::GenericNaN; namespace js { diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 46c1ecb670..b0637bb437 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -10,6 +10,7 @@ #define INLINABLE_NATIVE_LIST(_) \ _(Array) \ _(ArrayIsArray) \ + _(ArrayJoin) \ _(ArrayPop) \ _(ArrayShift) \ _(ArrayPush) \ diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 25a4444fa2..8f06f9b4ce 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -724,6 +724,7 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod case JSOP_MOD: case JSOP_NEG: type = inspector->expectedResultType(last); + break; default: break; } @@ -1556,7 +1557,7 @@ IonBuilder::traverseBytecode() MOZ_ASSERT(i == 0); if (current->peek(-1) == popped[0]) break; - // FALL THROUGH + MOZ_FALLTHROUGH; default: MOZ_ASSERT(popped[i]->isImplicitlyUsed() || @@ -1947,6 +1948,7 @@ IonBuilder::inspectOpcode(JSOp op) return pushConstant(ObjectValue(*scope)); } // Fall through to JSOP_BINDNAME + MOZ_FALLTHROUGH; case JSOP_BINDNAME: return jsop_bindname(info().getName(pc)); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 6cf3f19310..ae405b5f0e 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -853,10 +853,10 @@ class IonBuilder // SIMD intrinsics and natives. InliningStatus inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* target); - // helpers + // SIMD helpers static MIRType SimdTypeDescrToMIRType(SimdTypeDescr::Type type); - bool checkInlineSimd(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type, - unsigned numArgs, InlineTypedObject** templateObj); + bool canInlineSimd(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type, + unsigned numArgs, InlineTypedObject** templateObj); IonBuilder::InliningStatus boxSimd(CallInfo& callInfo, MInstruction* ins, InlineTypedObject* templateObj); MDefinition* convertToBooleanSimdLane(MDefinition* scalar); @@ -866,11 +866,11 @@ class IonBuilder InliningStatus inlineSimdBool32x4(CallInfo& callInfo, JSNative native); template - InliningStatus inlineBinarySimd(CallInfo& callInfo, JSNative native, + InliningStatus inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Operation op, SimdTypeDescr::Type type); - InliningStatus inlineCompSimd(CallInfo& callInfo, JSNative native, + InliningStatus inlineSimdComp(CallInfo& callInfo, JSNative native, MSimdBinaryComp::Operation op, SimdTypeDescr::Type compType); - InliningStatus inlineUnarySimd(CallInfo& callInfo, JSNative native, + InliningStatus inlineSimdUnary(CallInfo& callInfo, JSNative native, MSimdUnaryArith::Operation op, SimdTypeDescr::Type type); InliningStatus inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type); diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h index 54523c5b25..125e9f06d0 100644 --- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -320,7 +320,7 @@ class RInstructionResults { // Vector of results of recover instructions. typedef mozilla::Vector Values; - mozilla::UniquePtr > results_; + UniquePtr results_; // The frame pointer is used as a key to check if the current frame already // bailed out. diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index ed9f087eaf..34659524d8 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -10,7 +10,6 @@ #include "mozilla/MathAlgorithms.h" #include "mozilla/Maybe.h" #include "mozilla/SizePrintfMacros.h" -#include "mozilla/UniquePtr.h" #include "jsprf.h" diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h index 01c475adec..d5d974d05e 100644 --- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -923,6 +923,7 @@ class JitcodeGlobalEntry break; case IonCache: markedAny |= ionCacheEntry().mark(trc); + break; case Dummy: break; default: @@ -941,6 +942,7 @@ class JitcodeGlobalEntry break; case IonCache: ionCacheEntry().sweepChildren(rt); + break; case Dummy: break; default: diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 765ffc5241..f9a52117f5 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1815,7 +1815,7 @@ LIRGenerator::visitToDouble(MToDouble* convert) case MIRType_Boolean: MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly); - /* FALLTHROUGH */ + MOZ_FALLTHROUGH; case MIRType_Int32: { @@ -1871,7 +1871,7 @@ LIRGenerator::visitToFloat32(MToFloat32* convert) case MIRType_Boolean: MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly); - /* FALLTHROUGH */ + MOZ_FALLTHROUGH; case MIRType_Int32: { diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 9629d54d9d..54fd790f7f 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -68,6 +68,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineArray(callInfo); case InlinableNative::ArrayIsArray: return inlineArrayIsArray(callInfo); + case InlinableNative::ArrayJoin: + return inlineArrayJoin(callInfo); case InlinableNative::ArrayPop: return inlineArrayPopShift(callInfo, MArrayPopShift::Pop); case InlinableNative::ArrayShift: @@ -645,6 +647,8 @@ IonBuilder::inlineArrayJoin(CallInfo& callInfo) current->add(ins); current->push(ins); + if (!resumeAfter(ins)) + return InliningStatus_Error; return InliningStatus_Inlined; } @@ -3041,7 +3045,7 @@ IonBuilder::inlineSimdBool32x4(CallInfo& callInfo, JSNative native) { #define INLINE_SIMD_BITWISE_(OP) \ if (native == js::simd_bool32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryBitwise::OP##_, \ + return inlineSimdBinary(callInfo, native, MSimdBinaryBitwise::OP##_, \ SimdTypeDescr::Bool32x4); FOREACH_BITWISE_SIMD_BINOP(INLINE_SIMD_BITWISE_) @@ -3053,7 +3057,7 @@ IonBuilder::inlineSimdBool32x4(CallInfo& callInfo, JSNative native) return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Bool32x4); if (native == js::simd_bool32x4_not) - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Bool32x4); + return inlineSimdUnary(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Bool32x4); if (native == js::simd_bool32x4_splat) return inlineSimdSplat(callInfo, native, SimdTypeDescr::Bool32x4); @@ -3073,7 +3077,7 @@ IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native) { #define INLINE_INT32X4_SIMD_ARITH_(OP) \ if (native == js::simd_int32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryArith::Op_##OP, \ + return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_##OP, \ SimdTypeDescr::Int32x4); FOREACH_NUMERIC_SIMD_BINOP(INLINE_INT32X4_SIMD_ARITH_) @@ -3081,24 +3085,24 @@ IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native) #define INLINE_SIMD_BITWISE_(OP) \ if (native == js::simd_int32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryBitwise::OP##_, \ + return inlineSimdBinary(callInfo, native, MSimdBinaryBitwise::OP##_, \ SimdTypeDescr::Int32x4); FOREACH_BITWISE_SIMD_BINOP(INLINE_SIMD_BITWISE_) #undef INLINE_SIMD_BITWISE_ if (native == js::simd_int32x4_shiftLeftByScalar) - return inlineBinarySimd(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4); + return inlineSimdBinary(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4); if (native == js::simd_int32x4_shiftRightArithmeticByScalar || native == js::simd_int32x4_shiftRightByScalar) { - return inlineBinarySimd(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4); + return inlineSimdBinary(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4); } if (native == js::simd_int32x4_shiftRightLogicalByScalar) - return inlineBinarySimd(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4); + return inlineSimdBinary(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4); #define INLINE_SIMD_COMPARISON_(OP) \ if (native == js::simd_int32x4_##OP) \ - return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4); FOREACH_COMP_SIMD_OP(INLINE_SIMD_COMPARISON_) #undef INLINE_SIMD_COMPARISON_ @@ -3109,9 +3113,9 @@ IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native) return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Int32x4); if (native == js::simd_int32x4_not) - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4); + return inlineSimdUnary(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4); if (native == js::simd_int32x4_neg) - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::neg, SimdTypeDescr::Int32x4); + return inlineSimdUnary(callInfo, native, MSimdUnaryArith::neg, SimdTypeDescr::Int32x4); typedef bool IsCast; if (native == js::simd_int32x4_fromFloat32x4) @@ -3160,7 +3164,7 @@ IonBuilder::inlineSimdFloat32x4(CallInfo& callInfo, JSNative native) // Simd functions #define INLINE_FLOAT32X4_SIMD_ARITH_(OP) \ if (native == js::simd_float32x4_##OP) \ - return inlineBinarySimd(callInfo, native, MSimdBinaryArith::Op_##OP, \ + return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_##OP, \ SimdTypeDescr::Float32x4); FOREACH_NUMERIC_SIMD_BINOP(INLINE_FLOAT32X4_SIMD_ARITH_) @@ -3169,7 +3173,7 @@ IonBuilder::inlineSimdFloat32x4(CallInfo& callInfo, JSNative native) #define INLINE_SIMD_COMPARISON_(OP) \ if (native == js::simd_float32x4_##OP) \ - return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4); FOREACH_COMP_SIMD_OP(INLINE_SIMD_COMPARISON_) #undef INLINE_SIMD_COMPARISON_ @@ -3181,7 +3185,7 @@ IonBuilder::inlineSimdFloat32x4(CallInfo& callInfo, JSNative native) #define INLINE_SIMD_FLOAT32X4_UNARY_(OP) \ if (native == js::simd_float32x4_##OP) \ - return inlineUnarySimd(callInfo, native, MSimdUnaryArith::OP, SimdTypeDescr::Float32x4); + return inlineSimdUnary(callInfo, native, MSimdUnaryArith::OP, SimdTypeDescr::Float32x4); FOREACH_FLOAT_SIMD_UNOP(INLINE_SIMD_FLOAT32X4_UNARY_) INLINE_SIMD_FLOAT32X4_UNARY_(neg) @@ -3321,7 +3325,7 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) } bool -IonBuilder::checkInlineSimd(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type, +IonBuilder::canInlineSimd(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type, unsigned numArgs, InlineTypedObject** templateObj) { if (callInfo.argc() != numArgs) @@ -3352,11 +3356,11 @@ IonBuilder::boxSimd(CallInfo& callInfo, MInstruction* ins, InlineTypedObject* te template IonBuilder::InliningStatus -IonBuilder::inlineBinarySimd(CallInfo& callInfo, JSNative native, typename T::Operation op, +IonBuilder::inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Operation op, SimdTypeDescr::Type type) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 2, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 2, &templateObj)) return InliningStatus_NotInlined; // If the type of any of the arguments is neither a SIMD type, an Object @@ -3370,11 +3374,11 @@ IonBuilder::inlineBinarySimd(CallInfo& callInfo, JSNative native, typename T::Op } IonBuilder::InliningStatus -IonBuilder::inlineCompSimd(CallInfo& callInfo, JSNative native, MSimdBinaryComp::Operation op, +IonBuilder::inlineSimdComp(CallInfo& callInfo, JSNative native, MSimdBinaryComp::Operation op, SimdTypeDescr::Type compType) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, SimdTypeDescr::Bool32x4, 2, &templateObj)) + if (!canInlineSimd(callInfo, native, SimdTypeDescr::Bool32x4, 2, &templateObj)) return InliningStatus_NotInlined; // If the type of any of the arguments is neither a SIMD type, an Object @@ -3388,14 +3392,14 @@ IonBuilder::inlineCompSimd(CallInfo& callInfo, JSNative native, MSimdBinaryComp: } IonBuilder::InliningStatus -IonBuilder::inlineUnarySimd(CallInfo& callInfo, JSNative native, MSimdUnaryArith::Operation op, +IonBuilder::inlineSimdUnary(CallInfo& callInfo, JSNative native, MSimdUnaryArith::Operation op, SimdTypeDescr::Type type) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 1, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 1, &templateObj)) return InliningStatus_NotInlined; - // See comment in inlineBinarySimd + // See comment in inlineSimdBinary MIRType mirType = SimdTypeDescrToMIRType(type); MSimdUnaryArith* ins = MSimdUnaryArith::New(alloc(), callInfo.getArg(0), op, mirType); return boxSimd(callInfo, ins, templateObj); @@ -3405,10 +3409,10 @@ IonBuilder::InliningStatus IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 1, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 1, &templateObj)) return InliningStatus_NotInlined; - // See comment in inlineBinarySimd + // See comment in inlineSimdBinary MIRType mirType = SimdTypeDescrToMIRType(type); MDefinition* arg = callInfo.getArg(0); @@ -3424,7 +3428,7 @@ IonBuilder::InliningStatus IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 2, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 2, &templateObj)) return InliningStatus_NotInlined; MDefinition* arg = callInfo.getArg(1); @@ -3434,7 +3438,7 @@ IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdTypeD if (lane < 0 || lane >= 4) return InliningStatus_NotInlined; - // See comment in inlineBinarySimd + // See comment in inlineSimdBinary MIRType vecType = SimdTypeDescrToMIRType(type); MIRType laneType = SimdTypeToLaneType(vecType); MSimdExtractElement* ins = MSimdExtractElement::New(alloc(), callInfo.getArg(0), @@ -3449,7 +3453,7 @@ IonBuilder::InliningStatus IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 3, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 3, &templateObj)) return InliningStatus_NotInlined; MDefinition* arg = callInfo.getArg(1); @@ -3460,7 +3464,7 @@ IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdTypeD if (lane < 0 || lane >= 4) return InliningStatus_NotInlined; - // See comment in inlineBinarySimd + // See comment in inlineSimdBinary MIRType mirType = SimdTypeDescrToMIRType(type); // Convert to 0 / -1 before inserting a boolean lane. @@ -3478,10 +3482,10 @@ IonBuilder::inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast, SimdTypeDescr::Type from, SimdTypeDescr::Type to) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, to, 1, &templateObj)) + if (!canInlineSimd(callInfo, native, to, 1, &templateObj)) return InliningStatus_NotInlined; - // See comment in inlineBinarySimd + // See comment in inlineSimdBinary MInstruction* ins; MIRType fromType = SimdTypeDescrToMIRType(from); MIRType toType = SimdTypeDescrToMIRType(to); @@ -3497,10 +3501,10 @@ IonBuilder::InliningStatus IonBuilder::inlineSimdSelect(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 3, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 3, &templateObj)) return InliningStatus_NotInlined; - // See comment in inlineBinarySimd + // See comment in inlineSimdBinary MIRType mirType = SimdTypeDescrToMIRType(type); MSimdSelect* ins = MSimdSelect::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), callInfo.getArg(2), mirType); @@ -3511,7 +3515,7 @@ IonBuilder::InliningStatus IonBuilder::inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 1, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 1, &templateObj)) return InliningStatus_NotInlined; MIRType mirType = SimdTypeDescrToMIRType(type); @@ -3528,7 +3532,7 @@ IonBuilder::inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdTypeDescr unsigned numVectors, unsigned numLanes) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, numVectors + numLanes, &templateObj)) + if (!canInlineSimd(callInfo, native, type, numVectors + numLanes, &templateObj)) return InliningStatus_NotInlined; MIRType mirType = SimdTypeDescrToMIRType(type); @@ -3549,7 +3553,7 @@ IonBuilder::InliningStatus IonBuilder::inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, SimdTypeDescr::Bool32x4, 1, &templateObj)) + if (!canInlineSimd(callInfo, native, SimdTypeDescr::Bool32x4, 1, &templateObj)) return InliningStatus_NotInlined; MUnaryInstruction* ins; @@ -3637,7 +3641,7 @@ IonBuilder::inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdTypeDescr::T unsigned numElems) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 2, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 2, &templateObj)) return InliningStatus_NotInlined; Scalar::Type simdType = SimdTypeToArrayElementType(type); @@ -3660,7 +3664,7 @@ IonBuilder::inlineSimdStore(CallInfo& callInfo, JSNative native, SimdTypeDescr:: unsigned numElems) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, type, 3, &templateObj)) + if (!canInlineSimd(callInfo, native, type, 3, &templateObj)) return InliningStatus_NotInlined; Scalar::Type simdType = SimdTypeToArrayElementType(type); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index f61def2e77..6e78497b17 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -2226,6 +2226,7 @@ CanProduceNegativeZero(MDefinition* def) { case MDefinition::Op_Constant: if (def->type() == MIRType_Double && def->constantValue().toDouble() == -0.0) return true; + MOZ_FALLTHROUGH; case MDefinition::Op_BitAnd: case MDefinition::Op_BitOr: case MDefinition::Op_BitXor: @@ -2304,7 +2305,7 @@ NeedNegativeZeroCheck(MDefinition* def) if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs)) return true; - /* Fall through... */ + MOZ_FALLTHROUGH; } case MDefinition::Op_StoreElement: case MDefinition::Op_StoreElementHole: @@ -3083,7 +3084,7 @@ MTypeOf::foldsTo(TempAllocator& alloc) type = JSTYPE_OBJECT; break; } - // FALL THROUGH + MOZ_FALLTHROUGH; default: return this; } @@ -3368,9 +3369,10 @@ MToInt32::foldsTo(TempAllocator& alloc) case MIRType_Float32: case MIRType_Double: int32_t ival; - // Only the value within the range of Int32 can be substitued as constant. + // Only the value within the range of Int32 can be substituted as constant. if (mozilla::NumberEqualsInt32(val.toNumber(), &ival)) return MConstant::New(alloc, Int32Value(ival)); + break; default: break; } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 1db6e155e9..0b567a8329 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9537,7 +9537,9 @@ class MArrayJoin return true; } virtual AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::Element | AliasSet::ObjectFields); + // Array.join might coerce the elements of the Array to strings. This + // coercion might cause the evaluation of the some JavaScript code. + return AliasSet::Store(AliasSet::Any); } MDefinition* foldsTo(TempAllocator& alloc) override; }; diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 18915586a3..ae581b84a6 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -457,7 +457,7 @@ MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueReg convertInt32ToDouble(address, output.typedReg().fpu()); break; } - // Fallthrough. + MOZ_FALLTHROUGH; } case JSVAL_TYPE_BOOLEAN: diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 872e0a9fc8..4a5f89b5ab 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -258,6 +258,7 @@ RangeAnalysis::addBetaNodes() if (!compare->isNumericComparison()) continue; // Otherwise fall through to handle JSOP_STRICTEQ the same as JSOP_EQ. + MOZ_FALLTHROUGH; case JSOP_EQ: comp.setDouble(bound, bound); break; @@ -266,6 +267,7 @@ RangeAnalysis::addBetaNodes() if (!compare->isNumericComparison()) continue; // Otherwise fall through to handle JSOP_STRICTNE the same as JSOP_NE. + MOZ_FALLTHROUGH; case JSOP_NE: // Negative zero is not not-equal to zero. if (bound == 0) { @@ -3004,6 +3006,7 @@ RangeAnalysis::truncate() case MDefinition::Op_Ursh: if (!bitops.append(static_cast(*iter))) return false; + break; default:; } diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 6a6ec4423d..6fb39aff26 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -380,41 +380,15 @@ ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObje JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep) { - // The annotations in this function follow the first steps of join - // specified in ES5. - - // Step 1 - RootedObject obj(cx, array); - if (!obj) + JS::AutoValueArray<3> argv(cx); + argv[0].setUndefined(); + argv[1].setObject(*array); + argv[2].setString(sep); + if (!js::array_join(cx, 1, argv.begin())) return nullptr; - - AutoCycleDetector detector(cx, obj); - if (!detector.init()) - return nullptr; - - if (detector.foundCycle()) - return nullptr; - - // Steps 2 and 3 - uint32_t length; - if (!GetLengthProperty(cx, obj, &length)) - return nullptr; - - // Steps 4 and 5 - RootedLinearString sepstr(cx); - if (sep) { - sepstr = sep->ensureLinear(cx); - if (!sepstr) - return nullptr; - } else { - sepstr = cx->names().comma; - } - - // Step 6 to 11 - return js::ArrayJoin(cx, obj, sepstr, length); + return argv[0].toString(); } - bool CharCodeAt(JSContext* cx, HandleString str, int32_t index, uint32_t* code) { diff --git a/js/src/jit/none/MacroAssembler-none.h b/js/src/jit/none/MacroAssembler-none.h index 926b0028ec..49bc05e019 100644 --- a/js/src/jit/none/MacroAssembler-none.h +++ b/js/src/jit/none/MacroAssembler-none.h @@ -56,6 +56,16 @@ static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = { Registers::invalid_reg } static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = { Registers::invalid_reg }; static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = { Registers::invalid_reg }; + +static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = { Registers::invalid_reg }; + static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = { Registers::invalid_reg }; static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = { Registers::invalid_reg }; static MOZ_CONSTEXPR_VAR Register JSReturnReg = { Registers::invalid_reg }; diff --git a/js/src/jit/shared/CodeGenerator-shared-inl.h b/js/src/jit/shared/CodeGenerator-shared-inl.h index f3858b4594..170875fbd0 100644 --- a/js/src/jit/shared/CodeGenerator-shared-inl.h +++ b/js/src/jit/shared/CodeGenerator-shared-inl.h @@ -303,7 +303,7 @@ CodeGeneratorShared::verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, b case Scalar::Int16: if (kind == HeapAccess::Load) kind = HeapAccess::LoadSext32; - // FALL THROUGH + MOZ_FALLTHROUGH; case Scalar::Uint8: case Scalar::Uint16: case Scalar::Int32: diff --git a/js/src/jsapi-tests/testGCExactRooting.cpp b/js/src/jsapi-tests/testGCExactRooting.cpp index 18a909e850..968ae2323f 100644 --- a/js/src/jsapi-tests/testGCExactRooting.cpp +++ b/js/src/jsapi-tests/testGCExactRooting.cpp @@ -7,8 +7,8 @@ #include "ds/TraceableFifo.h" #include "js/GCHashTable.h" +#include "js/GCVector.h" #include "js/RootingAPI.h" -#include "js/TraceableVector.h" #include "jsapi-tests/tests.h" @@ -203,7 +203,7 @@ BEGIN_TEST(testGCHandleHashMap) } END_TEST(testGCHandleHashMap) -using ShapeVec = TraceableVector; +using ShapeVec = GCVector; BEGIN_TEST(testGCRootedVector) { @@ -249,7 +249,7 @@ BEGIN_TEST(testGCRootedVector) } bool -receiveConstRefToShapeVector(const JS::Rooted>& rooted) +receiveConstRefToShapeVector(const JS::Rooted>& rooted) { // Ensure range enumeration works through the reference. for (auto shape : rooted) { @@ -259,7 +259,7 @@ receiveConstRefToShapeVector(const JS::Rooted>& rooted) } bool -receiveHandleToShapeVector(JS::Handle> handle) +receiveHandleToShapeVector(JS::Handle> handle) { // Ensure range enumeration works through the handle. for (auto shape : handle) { @@ -269,7 +269,7 @@ receiveHandleToShapeVector(JS::Handle> handle) } bool -receiveMutableHandleToShapeVector(JS::MutableHandle> handle) +receiveMutableHandleToShapeVector(JS::MutableHandle> handle) { // Ensure range enumeration works through the handle. for (auto shape : handle) { @@ -318,6 +318,8 @@ BEGIN_TEST(testTraceableFifo) } END_TEST(testTraceableFifo) +using ShapeVec = GCVector; + static bool FillVector(JSContext* cx, MutableHandle shapes) { diff --git a/js/src/jsapi-tests/testGCUniqueId.cpp b/js/src/jsapi-tests/testGCUniqueId.cpp index 53d2f89c7c..40feadf101 100644 --- a/js/src/jsapi-tests/testGCUniqueId.cpp +++ b/js/src/jsapi-tests/testGCUniqueId.cpp @@ -7,6 +7,7 @@ #include "gc/GCInternals.h" #include "gc/Zone.h" +#include "js/GCVector.h" #include "jsapi-tests/tests.h" @@ -81,7 +82,7 @@ BEGIN_TEST(testGCUID) // Allocate a few arenas worth of objects to ensure we get some compaction. const static size_t N = 2049; - using ObjectVector = js::TraceableVector; + using ObjectVector = js::GCVector; JS::Rooted vec(cx, ObjectVector(cx)); for (size_t i = 0; i < N; ++i) { obj = JS_NewPlainObject(cx); diff --git a/js/src/jsapi-tests/testMutedErrors.cpp b/js/src/jsapi-tests/testMutedErrors.cpp index 44bc6154b1..7826b09feb 100644 --- a/js/src/jsapi-tests/testMutedErrors.cpp +++ b/js/src/jsapi-tests/testMutedErrors.cpp @@ -5,8 +5,6 @@ #include "jsfriendapi.h" #include "jsapi-tests/tests.h" -using mozilla::UniquePtr; - static bool sErrorReportMuted = false; BEGIN_TEST(testMutedErrors) { @@ -47,7 +45,7 @@ bool eval(const char* asciiChars, bool mutedErrors, JS::MutableHandleValue rval) { size_t len = strlen(asciiChars); - UniquePtr chars(new char16_t[len+1]); + mozilla::UniquePtr chars(new char16_t[len+1]); for (size_t i = 0; i < len; ++i) chars[i] = asciiChars[i]; chars[len] = 0; diff --git a/js/src/jsapi-tests/testPersistentRooted.cpp b/js/src/jsapi-tests/testPersistentRooted.cpp index 0856420c09..4543872b5f 100644 --- a/js/src/jsapi-tests/testPersistentRooted.cpp +++ b/js/src/jsapi-tests/testPersistentRooted.cpp @@ -6,7 +6,6 @@ #include "jsapi-tests/tests.h" using namespace JS; -using mozilla::UniquePtr; struct BarkWhenTracedClass { static int finalizeCount; @@ -82,7 +81,7 @@ BEGIN_TEST(test_PersistentRooted) { BarkWhenTracedClass::reset(); - UniquePtr kennel(Allocate(cx)); + mozilla::UniquePtr kennel(Allocate(cx)); CHECK(kennel.get()); // GC should be able to find our barker. @@ -118,13 +117,13 @@ BEGIN_TEST(test_PersistentRootedCopy) { BarkWhenTracedClass::reset(); - UniquePtr kennel(Allocate(cx)); + mozilla::UniquePtr kennel(Allocate(cx)); CHECK(kennel.get()); CHECK(GCFinalizesNBarkers(cx, 0)); // Copy construction! AMAZING! - UniquePtr newKennel(new Kennel(*kennel)); + mozilla::UniquePtr newKennel(new Kennel(*kennel)); CHECK(GCFinalizesNBarkers(cx, 0)); @@ -148,13 +147,13 @@ BEGIN_TEST(test_PersistentRootedAssign) { BarkWhenTracedClass::reset(); - UniquePtr kennel(Allocate(cx)); + mozilla::UniquePtr kennel(Allocate(cx)); CHECK(kennel.get()); CHECK(GCFinalizesNBarkers(cx, 0)); // Allocate a new, empty kennel. - UniquePtr kennel2(new Kennel(cx)); + mozilla::UniquePtr kennel2(new Kennel(cx)); // Assignment! ASTONISHING! *kennel2 = *kennel; @@ -168,7 +167,7 @@ BEGIN_TEST(test_PersistentRootedAssign) CHECK(GCFinalizesNBarkers(cx, 0)); // Allocate a second barker. - kennel2 = UniquePtr(Allocate(cx)); + kennel2 = mozilla::UniquePtr(Allocate(cx)); CHECK(kennel2.get()); *kennel = *kennel2; diff --git a/js/src/jsapi-tests/testUbiNode.cpp b/js/src/jsapi-tests/testUbiNode.cpp index eadf64f3c8..a0c74709fd 100644 --- a/js/src/jsapi-tests/testUbiNode.cpp +++ b/js/src/jsapi-tests/testUbiNode.cpp @@ -143,7 +143,7 @@ BEGIN_TEST(test_ubiNodeJSObjectConstructorName) EVAL("this.Ctor = function Ctor() {}; new Ctor", &val); CHECK(val.isObject()); - mozilla::UniquePtr ctorName; + UniqueTwoByteChars ctorName; CHECK(JS::ubi::Node(&val.toObject()).jsObjectConstructorName(cx, ctorName)); CHECK(ctorName); CHECK(js_strcmp(ctorName.get(), MOZ_UTF16("Ctor")) == 0); diff --git a/js/src/jsapi-tests/testXDR.cpp b/js/src/jsapi-tests/testXDR.cpp index e1cc401337..3fc66437eb 100644 --- a/js/src/jsapi-tests/testXDR.cpp +++ b/js/src/jsapi-tests/testXDR.cpp @@ -12,8 +12,6 @@ #include "jsscriptinlines.h" -using mozilla::UniquePtr; - static JSScript* FreezeThaw(JSContext* cx, JS::HandleScript script) { @@ -132,7 +130,7 @@ BEGIN_TEST(testXDR_sourceMap) CHECK(script); size_t len = strlen(*sm); - UniquePtr expected_wrapper(js::InflateString(cx, *sm, &len)); + UniqueTwoByteChars expected_wrapper(js::InflateString(cx, *sm, &len)); char16_t *expected = expected_wrapper.get(); CHECK(expected); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0a01d66ff7..645cf493ab 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -12,7 +12,6 @@ #include "mozilla/FloatingPoint.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include #include @@ -68,6 +67,7 @@ #include "js/Proxy.h" #include "js/SliceBudget.h" #include "js/StructuredClone.h" +#include "js/UniquePtr.h" #include "vm/DateObject.h" #include "vm/Debugger.h" #include "vm/ErrorObject.h" @@ -100,7 +100,6 @@ using namespace js::gc; using mozilla::Maybe; using mozilla::PodCopy; using mozilla::PodZero; -using mozilla::UniquePtr; using JS::AutoGCRooter; using JS::ToInt32; @@ -1483,19 +1482,6 @@ JS_GetGCParameter(JSRuntime* rt, JSGCParamKey key) return rt->gc.getParameter(key, lock); } -JS_PUBLIC_API(void) -JS_SetGCParameterForThread(JSContext* cx, JSGCParamKey key, uint32_t value) -{ - MOZ_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); -} - -JS_PUBLIC_API(uint32_t) -JS_GetGCParameterForThread(JSContext* cx, JSGCParamKey key) -{ - MOZ_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); - return 0; -} - static const size_t NumGCConfigs = 14; struct JSGCConfig { JSGCParamKey key; @@ -3888,7 +3874,7 @@ JS::OwningCompileOptions::setFileAndLine(JSContext* cx, const char* f, unsigned bool JS::OwningCompileOptions::setSourceMapURL(JSContext* cx, const char16_t* s) { - UniquePtr copy; + UniqueTwoByteChars copy; if (s) { copy = DuplicateString(cx, s); if (!copy) @@ -3973,7 +3959,7 @@ static bool Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption, const char* bytes, size_t length, MutableHandleScript script) { - mozilla::UniquePtr chars; + UniqueTwoByteChars chars; if (options.utf8) chars.reset(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get()); else @@ -4302,7 +4288,7 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& scopeChain, const char* name, unsigned nargs, const char* const* argnames, const char* bytes, size_t length, MutableHandleFunction fun) { - mozilla::UniquePtr chars; + UniqueTwoByteChars chars; if (options.utf8) chars.reset(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get()); else @@ -5947,6 +5933,8 @@ JS_PUBLIC_API(bool) DescribeScriptedCaller(JSContext* cx, UniqueChars* filename, unsigned* lineno, unsigned* column) { + if (filename) + filename->reset(); if (lineno) *lineno = 0; if (column) @@ -5961,8 +5949,8 @@ DescribeScriptedCaller(JSContext* cx, UniqueChars* filename, unsigned* lineno, if (i.activation()->scriptedCallerIsHidden()) return false; - if (filename) { - UniqueChars copy = make_string_copy(i.filename()); + if (filename && i.filename()) { + UniqueChars copy = DuplicateString(i.filename()); if (!copy) return false; *filename = Move(copy); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 3ab8cd507f..cf833f86ff 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -26,11 +26,11 @@ #include "js/CallArgs.h" #include "js/Class.h" +#include "js/GCVector.h" #include "js/HashTable.h" #include "js/Id.h" #include "js/Principals.h" #include "js/RootingAPI.h" -#include "js/TraceableVector.h" #include "js/TracingAPI.h" #include "js/Utility.h" #include "js/Value.h" @@ -218,9 +218,9 @@ typedef AutoVectorRooter AutoValueVector; typedef AutoVectorRooter AutoIdVector; typedef AutoVectorRooter AutoObjectVector; -using ValueVector = js::TraceableVector; -using IdVector = js::TraceableVector; -using ScriptVector = js::TraceableVector; +using ValueVector = js::GCVector; +using IdVector = js::GCVector; +using ScriptVector = js::GCVector; template class MOZ_RAII AutoHashMapRooter : protected AutoGCRooter @@ -1737,9 +1737,6 @@ typedef enum JSGCParamKey { /** Number of times GC has been invoked. Includes both major and minor GC. */ JSGC_NUMBER = 4, - /** Max size of the code cache in bytes. */ - JSGC_MAX_CODE_CACHE_BYTES = 5, - /** Select GC mode. */ JSGC_MODE = 6, @@ -1805,7 +1802,7 @@ typedef enum JSGCParamKey { JSGC_MAX_EMPTY_CHUNK_COUNT = 22, /** Whether compacting GC is enabled. */ - JSGC_COMPACTING_ENABLED = 23 + JSGC_COMPACTING_ENABLED = 23, } JSGCParamKey; extern JS_PUBLIC_API(void) @@ -1817,12 +1814,6 @@ JS_SetGGCMode(JSRuntime* rt, bool enabled); extern JS_PUBLIC_API(uint32_t) JS_GetGCParameter(JSRuntime* rt, JSGCParamKey key); -extern JS_PUBLIC_API(void) -JS_SetGCParameterForThread(JSContext* cx, JSGCParamKey key, uint32_t value); - -extern JS_PUBLIC_API(uint32_t) -JS_GetGCParameterForThread(JSContext* cx, JSGCParamKey key); - extern JS_PUBLIC_API(void) JS_SetGCParametersBasedOnAvailableMemory(JSRuntime* rt, uint32_t availMem); @@ -5328,8 +5319,6 @@ JS_IsIdentifier(const char16_t* chars, size_t length); namespace JS { -typedef js::UniqueChars AutoFilename; - /** * Return the current filename, line number and column number of the most * currently running frame. Returns true if a scripted frame was found, false @@ -5339,7 +5328,7 @@ typedef js::UniqueChars AutoFilename; * record, this will also return false. */ extern JS_PUBLIC_API(bool) -DescribeScriptedCaller(JSContext* cx, AutoFilename* filename = nullptr, +DescribeScriptedCaller(JSContext* cx, UniqueChars* filename = nullptr, unsigned* lineno = nullptr, unsigned* column = nullptr); extern JS_PUBLIC_API(JSObject*) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 2ac6ad11c0..5e19999796 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -54,7 +54,6 @@ using mozilla::CeilingLog2; using mozilla::CheckedInt; using mozilla::DebugOnly; using mozilla::IsNaN; -using mozilla::UniquePtr; using JS::AutoCheckCannotGC; using JS::IsArrayAnswer; @@ -1080,72 +1079,13 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len } template -JSString* -js::ArrayJoin(JSContext* cx, HandleObject obj, HandleLinearString sepstr, uint32_t length) +bool +ArrayJoin(JSContext* cx, CallArgs& args) { // This method is shared by Array.prototype.join and // Array.prototype.toLocaleString. The steps in ES5 are nearly the same, so // the annotations in this function apply to both toLocaleString and join. - // Steps 1 to 6, should be done by the caller. - - // Step 6 is implicit in the loops below. - - // An optimized version of a special case of steps 7-11: when length==1 and - // the 0th element is a string, ToString() of that element is a no-op and - // so it can be immediately returned as the result. - if (length == 1 && !Locale && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) { - Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0); - if (elem0.isString()) - return elem0.toString(); - } - - StringBuffer sb(cx); - if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars()) - return nullptr; - - // The separator will be added |length - 1| times, reserve space for that - // so that we don't have to unnecessarily grow the buffer. - size_t seplen = sepstr->length(); - CheckedInt res = CheckedInt(seplen) * (length - 1); - if (length > 0 && !res.isValid()) { - ReportAllocationOverflow(cx); - return nullptr; - } - - if (length > 0 && !sb.reserve(res.value())) - return nullptr; - - // Various optimized versions of steps 7-10. - if (seplen == 0) { - EmptySeparatorOp op; - if (!ArrayJoinKernel(cx, op, obj, length, sb)) - return nullptr; - } else if (seplen == 1) { - char16_t c = sepstr->latin1OrTwoByteChar(0); - if (c <= JSString::MAX_LATIN1_CHAR) { - CharSeparatorOp op(c); - if (!ArrayJoinKernel(cx, op, obj, length, sb)) - return nullptr; - } else { - CharSeparatorOp op(c); - if (!ArrayJoinKernel(cx, op, obj, length, sb)) - return nullptr; - } - } else { - StringSeparatorOp op(sepstr); - if (!ArrayJoinKernel(cx, op, obj, length, sb)) - return nullptr; - } - - // Step 11 - return sb.finishString(); -} - -template -bool -ArrayJoin(JSContext* cx, CallArgs& args) -{ // Step 1 RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) @@ -1168,7 +1108,7 @@ ArrayJoin(JSContext* cx, CallArgs& args) // Steps 4 and 5 RootedLinearString sepstr(cx); if (!Locale && args.hasDefined(0)) { - JSString* s = ToString(cx, args[0]); + JSString *s = ToString(cx, args[0]); if (!s) return false; sepstr = s->ensureLinear(cx); @@ -1178,12 +1118,63 @@ ArrayJoin(JSContext* cx, CallArgs& args) sepstr = cx->names().comma; } - // Step 6 to 11 - JSString* res = js::ArrayJoin(cx, obj, sepstr, length); - if (!res) + // Step 6 is implicit in the loops below. + + // An optimized version of a special case of steps 7-11: when length==1 and + // the 0th element is a string, ToString() of that element is a no-op and + // so it can be immediately returned as the result. + if (length == 1 && !Locale && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) { + Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0); + if (elem0.isString()) { + args.rval().set(elem0); + return true; + } + } + + StringBuffer sb(cx); + if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars()) return false; - args.rval().setString(res); + // The separator will be added |length - 1| times, reserve space for that + // so that we don't have to unnecessarily grow the buffer. + size_t seplen = sepstr->length(); + CheckedInt res = CheckedInt(seplen) * (length - 1); + if (length > 0 && !res.isValid()) { + ReportAllocationOverflow(cx); + return false; + } + + if (length > 0 && !sb.reserve(res.value())) + return false; + + // Various optimized versions of steps 7-10. + if (seplen == 0) { + EmptySeparatorOp op; + if (!ArrayJoinKernel(cx, op, obj, length, sb)) + return false; + } else if (seplen == 1) { + char16_t c = sepstr->latin1OrTwoByteChar(0); + if (c <= JSString::MAX_LATIN1_CHAR) { + CharSeparatorOp op(c); + if (!ArrayJoinKernel(cx, op, obj, length, sb)) + return false; + } else { + CharSeparatorOp op(c); + if (!ArrayJoinKernel(cx, op, obj, length, sb)) + return false; + } + } else { + StringSeparatorOp op(sepstr); + if (!ArrayJoinKernel(cx, op, obj, length, sb)) + return false; + } + + // Step 11 + JSString *str = sb.finishString(); + if (!str) + return false; + + args.rval().setString(str); return true; } @@ -1749,7 +1740,7 @@ SortLexicographically(JSContext* cx, AutoValueVector* vec, size_t len) Vector strElements(cx); /* MergeSort uses the upper half as scratch space. */ - if (!strElements.reserve(2 * len)) + if (!strElements.resize(2 * len)) return false; /* Convert Values to strings. */ @@ -1761,14 +1752,10 @@ SortLexicographically(JSContext* cx, AutoValueVector* vec, size_t len) if (!ValueToStringBuffer(cx, (*vec)[i], sb)) return false; - StringifiedElement el = { cursor, sb.length(), i }; - strElements.infallibleAppend(el); + strElements[i] = { cursor, sb.length(), i }; cursor = sb.length(); } - /* Resize strElements so we can perform MergeSort. */ - JS_ALWAYS_TRUE(strElements.resize(2 * len)); - /* Sort Values in vec alphabetically. */ return MergeSortByKey(strElements.begin(), len, strElements.begin() + len, SortComparatorStringifiedElements(cx, sb), vec); @@ -1788,7 +1775,7 @@ SortNumerically(JSContext* cx, AutoValueVector* vec, size_t len, ComparatorMatch Vector numElements(cx); /* MergeSort uses the upper half as scratch space. */ - if (!numElements.reserve(2 * len)) + if (!numElements.resize(2 * len)) return false; /* Convert Values to numerics. */ @@ -1800,13 +1787,9 @@ SortNumerically(JSContext* cx, AutoValueVector* vec, size_t len, ComparatorMatch if (!ToNumber(cx, (*vec)[i], &dv)) return false; - NumericElement el = { dv, i }; - numElements.infallibleAppend(el); + numElements[i] = { dv, i }; } - /* Resize strElements so we can perform MergeSort. */ - JS_ALWAYS_TRUE(numElements.resize(2 * len)); - /* Sort Values in vec numerically. */ return MergeSortByKey(numElements.begin(), len, numElements.begin() + len, SortComparatorNumerics[comp], vec); @@ -3127,7 +3110,7 @@ static const JSFunctionSpec array_methods[] = { JS_FN(js_toLocaleString_str, array_toLocaleString, 0,0), /* Perl-ish methods. */ - JS_FN("join", array_join, 1,JSFUN_GENERIC_NATIVE), + JS_INLINABLE_FN("join", array_join, 1,JSFUN_GENERIC_NATIVE, ArrayJoin), JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE), JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE), JS_INLINABLE_FN("push", array_push, 1,JSFUN_GENERIC_NATIVE, ArrayPush), @@ -3706,8 +3689,7 @@ js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp) for (unsigned i = 0; i < args.length(); i++) { HandleValue arg = args[i]; - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr); if (!bytes) return false; if (arg.isPrimitive() || diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 160be920b6..7d666fd670 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -169,10 +169,6 @@ array_splice_impl(JSContext* cx, unsigned argc, js::Value* vp, bool pop); extern bool array_concat(JSContext* cx, unsigned argc, js::Value* vp); -template -JSString* -ArrayJoin(JSContext* cx, HandleObject obj, HandleLinearString sepstr, uint32_t length); - extern bool array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2, HandleObject result); @@ -180,9 +176,6 @@ array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2, extern bool array_join(JSContext* cx, unsigned argc, js::Value* vp); -extern JSString* -array_join_impl(JSContext* cx, HandleValue array, HandleString sep); - extern void ArrayShiftMoveElements(JSObject* obj); diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 3d3c0e4d82..a6784263b4 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -427,6 +427,23 @@ js::AtomizeChars(ExclusiveContext* cx, const Latin1Char* chars, size_t length, P template JSAtom* js::AtomizeChars(ExclusiveContext* cx, const char16_t* chars, size_t length, PinningBehavior pin); +JSAtom* +js::AtomizeUTF8Chars(JSContext* cx, const char* utf8Chars, size_t utf8ByteLength) +{ + // This could be optimized to hand the char16_t's directly to the JSAtom + // instead of making a copy. UTF8CharsToNewTwoByteCharsZ should be + // refactored to take an ExclusiveContext so that this function could also. + + UTF8Chars utf8(utf8Chars, utf8ByteLength); + + size_t length; + UniquePtr chars(JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get()); + if (!chars) + return nullptr; + + return AtomizeChars(cx, chars.get(), length); +} + bool js::IndexToIdSlow(ExclusiveContext* cx, uint32_t index, MutableHandleId idp) { diff --git a/js/src/jsatom.h b/js/src/jsatom.h index ed1a6c9adf..868d780c29 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -230,6 +230,9 @@ extern JSAtom* AtomizeChars(ExclusiveContext* cx, const CharT* chars, size_t length, js::PinningBehavior pin = js::DoNotPinAtom); +extern JSAtom* +AtomizeUTF8Chars(JSContext* cx, const char* utf8Chars, size_t utf8ByteLength); + extern JSAtom* AtomizeString(ExclusiveContext* cx, JSString* str, js::PinningBehavior pin = js::DoNotPinAtom); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index a953325bc0..cfd82a4536 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -55,7 +55,6 @@ using namespace js::gc; using mozilla::DebugOnly; using mozilla::PodArrayZero; using mozilla::PointerRangeSize; -using mozilla::UniquePtr; bool js::AutoCycleDetector::init() @@ -291,7 +290,7 @@ js::ReportOutOfMemory(ExclusiveContext* cxArg) #endif if (!cxArg->isJSContext()) - return; + return cxArg->addPendingOutOfMemory(); JSContext* cx = cxArg->asJSContext(); cx->runtime()->hadOutOfMemory = true; @@ -644,7 +643,6 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, */ reportp->ucmessage = out = cx->pod_malloc(expandedLength + 1); if (!out) { - ReportOutOfMemory(cx); js_free(buffer); goto error; } @@ -843,8 +841,7 @@ js::ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v, { bool ok; - UniquePtr bytes = - DecompileValueGenerator(cx, spindex, v, fallback); + UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback); if (!bytes) return false; @@ -874,7 +871,7 @@ void js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg) { char argbuf[11]; - UniquePtr bytes; + UniqueChars bytes; RootedAtom atom(cx); JS_snprintf(argbuf, sizeof argbuf, "%u", arg); @@ -894,7 +891,7 @@ js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNum int spindex, HandleValue v, HandleString fallback, const char* arg1, const char* arg2) { - UniquePtr bytes; + UniqueChars bytes; bool ok; MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1); @@ -936,13 +933,16 @@ ExclusiveContext::ExclusiveContext(JSRuntime* rt, PerThreadData* pt, ContextKind void ExclusiveContext::recoverFromOutOfMemory() { - // If this is not a JSContext, there's nothing to do. if (JSContext* maybecx = maybeJSContext()) { if (maybecx->isExceptionPending()) { MOZ_ASSERT(maybecx->isThrowingOutOfMemory()); maybecx->clearPendingException(); } + return; } + // Keep in sync with addPendingOutOfMemory. + if (ParseTask* task = helperThread()->parseTask()) + task->outOfMemory = false; } JSContext::JSContext(JSRuntime* rt) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 2bf252bca8..2c61e45155 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -11,7 +11,7 @@ #include "mozilla/MemoryReporting.h" -#include "js/TraceableVector.h" +#include "js/GCVector.h" #include "js/Vector.h" #include "vm/Runtime.h" @@ -165,8 +165,10 @@ class ExclusiveContext : public ContextFriendFields, } void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) { - if (!isJSContext()) + if (!isJSContext()) { + addPendingOutOfMemory(); return nullptr; + } return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, asJSContext()); } @@ -280,8 +282,9 @@ class ExclusiveContext : public ContextFriendFields, } // Methods specific to any HelperThread for the context. - frontend::CompileError& addPendingCompileError(); + bool addPendingCompileError(frontend::CompileError** err); void addPendingOverRecursed(); + void addPendingOutOfMemory(); }; } /* namespace js */ @@ -669,11 +672,6 @@ CheckForInterrupt(JSContext* cx) /************************************************************************/ -typedef JS::AutoVectorRooter AutoPropertyNameVector; - -using ShapeVector = js::TraceableVector; -using StringVector = js::TraceableVector; - /* AutoArrayRooter roots an external array of Values. */ class MOZ_RAII AutoArrayRooter : private JS::AutoGCRooter { diff --git a/js/src/jsdtoa.cpp b/js/src/jsdtoa.cpp index 42ef901876..a7b7503449 100644 --- a/js/src/jsdtoa.cpp +++ b/js/src/jsdtoa.cpp @@ -136,7 +136,7 @@ js_dtostr(DtoaState* state, char* buffer, size_t bufferSize, JSDToStrMode mode, case DTOSTR_EXPONENTIAL: MOZ_ASSERT(precision > 0); minNDigits = precision; - /* Fall through */ + MOZ_FALLTHROUGH; case DTOSTR_STANDARD_EXPONENTIAL: exponentialNotation = true; break; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index a7234d63f7..f03f7f0023 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -36,7 +36,6 @@ using namespace js; using mozilla::Move; using mozilla::PodArrayZero; -using mozilla::UniquePtr; // Required by PerThreadDataFriendFields::getMainThread() JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) == @@ -52,12 +51,12 @@ PerThreadDataFriendFields::PerThreadDataFriendFields() } JS_FRIEND_API(void) -js::SetSourceHook(JSRuntime* rt, UniquePtr hook) +js::SetSourceHook(JSRuntime* rt, mozilla::UniquePtr hook) { rt->sourceHook = Move(hook); } -JS_FRIEND_API(UniquePtr) +JS_FRIEND_API(mozilla::UniquePtr) js::ForgetSourceHook(JSRuntime* rt) { return Move(rt->sourceHook); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index a3d9799c33..9bab8b5d57 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1521,13 +1521,13 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) return 0; } else { MOZ_RELEASE_ASSERT(defaultTimeBudget_ >= 0); - MOZ_RELEASE_ASSERT(defaultTimeBudget_ < UINT32_MAX); + MOZ_RELEASE_ASSERT(defaultTimeBudget_ <= UINT32_MAX); return uint32_t(defaultTimeBudget_); } case JSGC_MARK_STACK_LIMIT: return marker.maxCapacity(); case JSGC_HIGH_FREQUENCY_TIME_LIMIT: - return tunables.highFrequencyThresholdUsec(); + return tunables.highFrequencyThresholdUsec() / PRMJ_USEC_PER_MSEC; case JSGC_HIGH_FREQUENCY_LOW_LIMIT: return tunables.highFrequencyLowLimitBytes() / 1024 / 1024; case JSGC_HIGH_FREQUENCY_HIGH_LIMIT: @@ -1544,6 +1544,8 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) return tunables.isDynamicMarkSliceEnabled(); case JSGC_ALLOCATION_THRESHOLD: return tunables.gcZoneAllocThresholdBase() / 1024 / 1024; + case JSGC_DECOMMIT_THRESHOLD: + return decommitThreshold / 1024 / 1024; case JSGC_MIN_EMPTY_CHUNK_COUNT: return tunables.minEmptyChunkCount(lock); case JSGC_MAX_EMPTY_CHUNK_COUNT: @@ -6047,7 +6049,8 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea lastMarkSlice = false; incrementalState = MARK_ROOTS; - /* fall through */ + + MOZ_FALLTHROUGH; case MARK_ROOTS: if (!beginMarkPhase(reason)) { @@ -6063,7 +6066,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea if (isIncremental && zeal == ZealIncrementalRootsThenFinish) break; - /* fall through */ + MOZ_FALLTHROUGH; case MARK: AutoGCRooter::traceAllWrappers(&marker); @@ -6109,7 +6112,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea if (isIncremental && zeal == ZealIncrementalMultipleSlices) break; - /* fall through */ + MOZ_FALLTHROUGH; case SWEEP: AutoGCRooter::traceAllWrappers(&marker); @@ -6126,6 +6129,8 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea if (isCompacting && isIncremental) break; + MOZ_FALLTHROUGH; + case COMPACT: if (isCompacting) { if (!startedCompacting && beginCompactPhase() == NotFinished) @@ -7427,7 +7432,7 @@ JS::GCDescription::formatSliceMessage(JSRuntime* rt) const UniqueChars cstr = rt->gc.stats.formatCompactSliceMessage(); size_t nchars = strlen(cstr.get()); - UniquePtr out(js_pod_malloc(nchars + 1)); + UniqueTwoByteChars out(js_pod_malloc(nchars + 1)); if (!out) return nullptr; out.get()[nchars] = 0; @@ -7442,7 +7447,7 @@ JS::GCDescription::formatSummaryMessage(JSRuntime* rt) const UniqueChars cstr = rt->gc.stats.formatCompactSummaryMessage(); size_t nchars = strlen(cstr.get()); - UniquePtr out(js_pod_malloc(nchars + 1)); + UniqueTwoByteChars out(js_pod_malloc(nchars + 1)); if (!out) return nullptr; out.get()[nchars] = 0; @@ -7463,7 +7468,7 @@ JS::GCDescription::formatJSON(JSRuntime* rt, uint64_t timestamp) const UniqueChars cstr = rt->gc.stats.formatJsonMessage(timestamp); size_t nchars = strlen(cstr.get()); - UniquePtr out(js_pod_malloc(nchars + 1)); + UniqueTwoByteChars out(js_pod_malloc(nchars + 1)); if (!out) return nullptr; out.get()[nchars] = 0; diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 938432ea4d..9887409833 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -530,13 +530,13 @@ Compare(T* a, T* b, size_t c) { size_t n = (c + size_t(7)) / size_t(8); switch (c % 8) { - case 0: do { if (*a++ != *b++) return false; - case 7: if (*a++ != *b++) return false; - case 6: if (*a++ != *b++) return false; - case 5: if (*a++ != *b++) return false; - case 4: if (*a++ != *b++) return false; - case 3: if (*a++ != *b++) return false; - case 2: if (*a++ != *b++) return false; + case 0: do { if (*a++ != *b++) return false; MOZ_FALLTHROUGH; + case 7: if (*a++ != *b++) return false; MOZ_FALLTHROUGH; + case 6: if (*a++ != *b++) return false; MOZ_FALLTHROUGH; + case 5: if (*a++ != *b++) return false; MOZ_FALLTHROUGH; + case 4: if (*a++ != *b++) return false; MOZ_FALLTHROUGH; + case 3: if (*a++ != *b++) return false; MOZ_FALLTHROUGH; + case 2: if (*a++ != *b++) return false; MOZ_FALLTHROUGH; case 1: if (*a++ != *b++) return false; } while (--n > 0); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ebe3eecbe8..830013c7af 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -15,7 +15,6 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/SizePrintfMacros.h" #include "mozilla/TemplateLib.h" -#include "mozilla/UniquePtr.h" #include @@ -38,7 +37,7 @@ #include "jswin.h" #include "jswrapper.h" -#include "asmjs/AsmJS.h" +#include "asmjs/WasmModule.h" #include "builtin/Eval.h" #include "builtin/Object.h" #include "builtin/SymbolObject.h" @@ -48,6 +47,7 @@ #include "js/MemoryMetrics.h" #include "js/Proxy.h" #include "js/UbiNode.h" +#include "js/UniquePtr.h" #include "vm/ArgumentsObject.h" #include "vm/Interpreter.h" #include "vm/ProxyObject.h" @@ -74,7 +74,6 @@ using namespace js::gc; using mozilla::DebugOnly; using mozilla::Maybe; -using mozilla::UniquePtr; void js::ReportNotObject(JSContext* cx, const Value& v) @@ -82,8 +81,7 @@ js::ReportNotObject(JSContext* cx, const Value& v) MOZ_ASSERT(!v.isObject()); RootedValue value(cx, v); - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr); if (bytes) JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); } @@ -196,8 +194,7 @@ js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* me HandleValue v = args[0]; if (!v.isObject()) { - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr); if (!bytes) return false; JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, @@ -3792,9 +3789,9 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassIn ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info); } else if (is()) { SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info); - } else if (is()) { - as().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS, - &info->objectsMallocHeapMisc); + } else if (is()) { + as().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS, + &info->objectsMallocHeapMisc); #ifdef JS_HAS_CTYPES } else { // This must be the last case. diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 0d8f789054..9cb19f8885 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -22,8 +22,8 @@ #include "gc/Marking.h" #include "js/Conversions.h" #include "js/GCAPI.h" +#include "js/GCVector.h" #include "js/HeapAPI.h" -#include "js/TraceableVector.h" #include "vm/Shape.h" #include "vm/String.h" #include "vm/Xdr.h" @@ -34,7 +34,7 @@ struct ClassInfo; namespace js { -using PropertyDescriptorVector = TraceableVector; +using PropertyDescriptorVector = GCVector; class GCMarker; class Nursery; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 39a413ec39..a016130864 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1424,9 +1424,7 @@ DecompileExpressionFromStack(JSContext* cx, int spindex, int skipStackHits, Hand return ed.getOutput(res); } -typedef mozilla::UniquePtr UniquePtrChars; - -UniquePtrChars +UniqueChars js::DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v, HandleString fallbackArg, int skipStackHits) { @@ -1437,19 +1435,19 @@ js::DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v, return nullptr; if (result) { if (strcmp(result, "(intermediate value)")) - return UniquePtrChars(result); + return UniqueChars(result); js_free(result); } } if (!fallback) { if (v.isUndefined()) - return UniquePtrChars(JS_strdup(cx, js_undefined_str)); // Prevent users from seeing "(void 0)" + return UniqueChars(JS_strdup(cx, js_undefined_str)); // Prevent users from seeing "(void 0)" fallback = ValueToSource(cx, v); if (!fallback) - return UniquePtrChars(nullptr); + return UniqueChars(nullptr); } - return UniquePtrChars(JS_EncodeString(cx, fallback)); + return UniqueChars(JS_EncodeString(cx, fallback)); } static bool diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index cd80a06f5d..d1c9d3f0ea 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -11,13 +11,12 @@ * JS bytecode definitions. */ -#include "mozilla/UniquePtr.h" - #include "jsbytecode.h" #include "jstypes.h" #include "NamespaceImports.h" #include "frontend/SourceNotes.h" +#include "js/UniquePtr.h" #include "vm/Opcodes.h" #include "vm/Printer.h" @@ -558,7 +557,7 @@ GetVariableBytecodeLength(jsbytecode* pc); * * The caller must call JS_free on the result after a successful call. */ -mozilla::UniquePtr +UniqueChars DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v, HandleString fallback, int skipStackHits = 0); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 0847ef8881..7f9065d442 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -12,7 +12,6 @@ #include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include "jsatom.h" #include "jslock.h" @@ -23,6 +22,7 @@ #include "gc/Rooting.h" #include "jit/IonCode.h" #include "js/UbiNode.h" +#include "js/UniquePtr.h" #include "vm/NativeObject.h" #include "vm/Shape.h" @@ -637,10 +637,10 @@ class ScriptSource uint32_t length_; // The filename of this script. - mozilla::UniquePtr filename_; + UniqueChars filename_; - mozilla::UniquePtr displayURL_; - mozilla::UniquePtr sourceMapURL_; + UniqueTwoByteChars displayURL_; + UniqueTwoByteChars sourceMapURL_; bool mutedErrors_; // bytecode offset in caller script that generated this code. @@ -658,7 +658,7 @@ class ScriptSource // // In the case described above, this field will be non-null and will be the // original raw filename from above. Otherwise this field will be null. - mozilla::UniquePtr introducerFilename_; + UniqueChars introducerFilename_; // A string indicating how this source code was introduced into the system. // This accessor returns one of the following values: @@ -823,6 +823,9 @@ class ScriptSourceHolder { ScriptSource* ss; public: + ScriptSourceHolder() + : ss(nullptr) + {} explicit ScriptSourceHolder(ScriptSource* ss) : ss(ss) { @@ -830,7 +833,14 @@ class ScriptSourceHolder } ~ScriptSourceHolder() { - ss->decref(); + if (ss) + ss->decref(); + } + void reset(ScriptSource* newss) { + if (ss) + ss->decref(); + ss = newss; + ss->incref(); } ScriptSource* get() const { return ss; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 78c65d6906..527d26ae85 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -13,7 +13,6 @@ #include "mozilla/PodOperations.h" #include "mozilla/Range.h" #include "mozilla/TypeTraits.h" -#include "mozilla/UniquePtr.h" #include #include @@ -34,6 +33,7 @@ #include "builtin/RegExp.h" #include "jit/InlinableNatives.h" #include "js/Conversions.h" +#include "js/UniquePtr.h" #if ENABLE_INTL_API #include "unicode/unorm.h" #endif @@ -69,7 +69,6 @@ using mozilla::Move; using mozilla::PodCopy; using mozilla::PodEqual; using mozilla::RangedPtr; -using mozilla::UniquePtr; using JS::AutoCheckCannotGC; @@ -1129,13 +1128,13 @@ FirstCharMatcherUnrolled(const TextChar* text, uint32_t n, const PatChar pat) const TextChar* t = text; switch ((textend - t) & 7) { - case 0: if (*t++ == pat) return t - 1; - case 7: if (*t++ == pat) return t - 1; - case 6: if (*t++ == pat) return t - 1; - case 5: if (*t++ == pat) return t - 1; - case 4: if (*t++ == pat) return t - 1; - case 3: if (*t++ == pat) return t - 1; - case 2: if (*t++ == pat) return t - 1; + case 0: if (*t++ == pat) return t - 1; MOZ_FALLTHROUGH; + case 7: if (*t++ == pat) return t - 1; MOZ_FALLTHROUGH; + case 6: if (*t++ == pat) return t - 1; MOZ_FALLTHROUGH; + case 5: if (*t++ == pat) return t - 1; MOZ_FALLTHROUGH; + case 4: if (*t++ == pat) return t - 1; MOZ_FALLTHROUGH; + case 3: if (*t++ == pat) return t - 1; MOZ_FALLTHROUGH; + case 2: if (*t++ == pat) return t - 1; MOZ_FALLTHROUGH; case 1: if (*t++ == pat) return t - 1; } while (textend != t) { @@ -4602,7 +4601,7 @@ js_strcmp(const char16_t* lhs, const char16_t* rhs) } } -UniquePtr +UniqueChars js::DuplicateString(js::ExclusiveContext* cx, const char* s) { size_t n = strlen(s) + 1; @@ -4613,7 +4612,7 @@ js::DuplicateString(js::ExclusiveContext* cx, const char* s) return ret; } -UniquePtr +UniqueTwoByteChars js::DuplicateString(js::ExclusiveContext* cx, const char16_t* s) { size_t n = js_strlen(s) + 1; @@ -4624,11 +4623,17 @@ js::DuplicateString(js::ExclusiveContext* cx, const char16_t* s) return ret; } -UniquePtr +UniqueChars +js::DuplicateString(const char* s) +{ + return UniqueChars(js_strdup(s)); +} + +UniqueTwoByteChars js::DuplicateString(const char16_t* s) { size_t n = js_strlen(s) + 1; - UniquePtr ret(js_pod_malloc(n)); + UniqueTwoByteChars ret(js_pod_malloc(n)); if (!ret) return nullptr; PodCopy(ret.get(), s, n); diff --git a/js/src/jsstr.h b/js/src/jsstr.h index c2bab9959b..1dd125026b 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -9,7 +9,6 @@ #include "mozilla/HashFunctions.h" #include "mozilla/PodOperations.h" -#include "mozilla/UniquePtr.h" #include "mozilla/TextUtils.h" #include "jsutil.h" @@ -17,6 +16,7 @@ #include "gc/Rooting.h" #include "js/RootingAPI.h" +#include "js/UniquePtr.h" #include "vm/Printer.h" #include "vm/Unicode.h" @@ -124,15 +124,20 @@ InitStringClass(JSContext* cx, HandleObject obj); extern const char* ValueToPrintable(JSContext* cx, const Value&, JSAutoByteString* bytes, bool asSource = false); -extern mozilla::UniquePtr +extern UniqueChars DuplicateString(ExclusiveContext* cx, const char* s); -extern mozilla::UniquePtr +extern UniqueTwoByteChars DuplicateString(ExclusiveContext* cx, const char16_t* s); -// This variant does not report OOMs, you must arrange for OOMs to be reported -// yourself. -extern mozilla::UniquePtr +/* + * These variants do not report OOMs, you must arrange for OOMs to be reported + * yourself. + */ +extern UniqueChars +DuplicateString(const char* s); + +extern UniqueTwoByteChars DuplicateString(const char16_t* s); /* diff --git a/js/src/moz.build b/js/src/moz.build index 92aec2e933..4bb7c4f814 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -119,6 +119,7 @@ EXPORTS.js += [ '../public/Debug.h', '../public/GCAPI.h', '../public/GCHashTable.h', + '../public/GCVector.h', '../public/HashTable.h', '../public/HeapAPI.h', '../public/Id.h', @@ -133,7 +134,6 @@ EXPORTS.js += [ '../public/RootingAPI.h', '../public/SliceBudget.h', '../public/StructuredClone.h', - '../public/TraceableVector.h', '../public/TraceKind.h', '../public/TracingAPI.h', '../public/TrackedOptimizationInfo.h', @@ -143,6 +143,7 @@ EXPORTS.js += [ '../public/UbiNodeCensus.h', '../public/UbiNodeDominatorTree.h', '../public/UbiNodePostOrder.h', + '../public/UniquePtr.h', '../public/Utility.h', '../public/Value.h', '../public/Vector.h', @@ -269,7 +270,6 @@ UNIFIED_SOURCES += [ 'jscntxt.cpp', 'jscompartment.cpp', 'jsdate.cpp', - 'jsdtoa.cpp', 'jsexn.cpp', 'jsfriendapi.cpp', 'jsfun.cpp', @@ -357,6 +357,8 @@ UNIFIED_SOURCES += [ # without PGO # frontend/Parser.cpp cannot be built in unified mode because of explicit # template instantiations. +# jsdtoa.cpp cannot be built in unified mode because we want to suppress +# compiler warnings in third-party dtoa.c. # jsmath.cpp cannot be built in unified mode because it needs to pull rand_s # from on Windows through a preprocessor define. # jsutil.cpp cannot be built in unified mode because it is needed for @@ -370,6 +372,7 @@ SOURCES += [ 'gc/StoreBuffer.cpp', 'jsarray.cpp', 'jsatom.cpp', + 'jsdtoa.cpp', 'jsmath.cpp', 'jsutil.cpp', 'vm/Initialization.cpp', @@ -727,3 +730,7 @@ if CONFIG['JS_HAS_CTYPES']: else: # Windows needs this to be linked with a static library. DEFINES['FFI_BUILDING'] = True + +# Suppress warnings in third-party code. +if CONFIG['CLANG_CXX']: + SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough'] diff --git a/js/src/perf/jsperf.cpp b/js/src/perf/jsperf.cpp index 5d5506477e..ddb9d62564 100644 --- a/js/src/perf/jsperf.cpp +++ b/js/src/perf/jsperf.cpp @@ -11,8 +11,6 @@ using namespace js; using JS::PerfMeasurement; -using mozilla::UniquePtr; - // You cannot forward-declare a static object in C++, so instead // we have to forward-declare the helper function that refers to it. static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value, const char* fname); @@ -212,8 +210,7 @@ static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value, const char* fname) { if (!value.isObject()) { - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr); if (!bytes) return nullptr; JS_ReportErrorNumber(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); diff --git a/js/src/shell/OSObject.cpp b/js/src/shell/OSObject.cpp index e06d535271..c54dd3f240 100644 --- a/js/src/shell/OSObject.cpp +++ b/js/src/shell/OSObject.cpp @@ -42,8 +42,6 @@ # include #endif -using namespace JS; - namespace js { namespace shell { @@ -102,7 +100,7 @@ ResolvePath(JSContext* cx, HandleString filenameStr, PathResolutionMode resolveM return filenameStr; /* Get the currently executing script's name. */ - JS::AutoFilename scriptFilename; + JS::UniqueChars scriptFilename; if (!DescribeScriptedCaller(cx, &scriptFilename)) return nullptr; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index e1280ae5d7..0f44f44d0b 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -98,12 +98,10 @@ using namespace js::shell; using mozilla::ArrayLength; using mozilla::Atomic; -using mozilla::MakeUnique; using mozilla::Maybe; using mozilla::NumberEqualsInt32; using mozilla::PodCopy; using mozilla::PodEqual; -using mozilla::UniquePtr; enum JSShellExitCode { EXITCODE_RUNTIME_ERROR = 3, @@ -597,7 +595,7 @@ RunModule(JSContext* cx, const char* filename, FILE* file, bool compileOnly) RootedFunction importFun(cx); MOZ_ALWAYS_TRUE(GetImportMethod(cx, loaderObj, &importFun)); - AutoValueArray<2> args(cx); + JS::AutoValueArray<2> args(cx); args[0].setString(JS_NewStringCopyZ(cx, filename)); args[1].setUndefined(); @@ -2665,7 +2663,7 @@ EvalInContext(JSContext* cx, unsigned argc, Value* vp) return true; } - JS::AutoFilename filename; + JS::UniqueChars filename; unsigned lineno; DescribeScriptedCaller(cx, &filename, &lineno); @@ -2725,7 +2723,7 @@ WorkerMain(void* arg) return; } - mozilla::UniquePtr sr = MakeUnique(); + UniquePtr sr = MakeUnique(); if (!sr) { JS_DestroyRuntime(rt); js_delete(input); @@ -3309,7 +3307,7 @@ ParseModule(JSContext* cx, unsigned argc, Value* vp) if (!scriptContents) return false; - mozilla::UniquePtr filename; + UniqueChars filename; CompileOptions options(cx); if (args.length() > 1) { if (!args[1].isString()) { @@ -3906,7 +3904,7 @@ ThisFilename(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - JS::AutoFilename filename; + JS::UniqueChars filename; if (!DescribeScriptedCaller(cx, &filename) || !filename.get()) { args.rval().setString(cx->runtime()->emptyString); return true; @@ -4108,12 +4106,12 @@ WithSourceHook(JSContext* cx, unsigned argc, Value* vp) return false; } - UniquePtr hook = - MakeUnique(cx, args[0].toObject().as()); + mozilla::UniquePtr hook = + mozilla::MakeUnique(cx, args[0].toObject().as()); if (!hook) return false; - UniquePtr savedHook = js::ForgetSourceHook(cx->runtime()); + mozilla::UniquePtr savedHook = js::ForgetSourceHook(cx->runtime()); js::SetSourceHook(cx->runtime(), Move(hook)); RootedObject fun(cx, &args[1].toObject()); @@ -4595,7 +4593,7 @@ class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor { return; } - oom = !log.append(make_string_copy("anonymous")); + oom = !log.append(DuplicateString("anonymous")); } void Entry(JSContext* cx, JSScript* script, JS::HandleValue asyncStack, @@ -4766,6 +4764,32 @@ EntryPoints(JSContext* cx, unsigned argc, Value* vp) return false; } +static bool +SetARMHwCapFlags(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 1) { + JS_ReportError(cx, "Wrong number of arguments"); + return false; + } + + RootedString flagsListString(cx, JS::ToString(cx, args.get(0))); + if (!flagsListString) + return false; + +#if defined(JS_CODEGEN_ARM) + JSAutoByteString flagsList(cx, flagsListString); + if (!flagsList) + return false; + + jit::ParseARMHwCapFlags(flagsList.ptr()); +#endif + + args.rval().setUndefined(); + return true; +} + static const JSFunctionSpecWithHelp shell_functions[] = { JS_FN_HELP("version", Version, 0, 0, "version([number])", @@ -5226,6 +5250,11 @@ TestAssertRecoveredOnBailout, " Prints the static scope chain of an interpreted function or a module."), #endif + JS_FN_HELP("setARMHwCapFlags", SetARMHwCapFlags, 1, 0, +"setARMHwCapFlags(\"flag1,flag2 flag3\")", +" On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n" +" flags is available by calling this function with \"help\" as the flag's name"), + JS_FS_HELP_END }; @@ -5427,7 +5456,7 @@ PrintStackTrace(JSContext* cx, HandleValue exn) if (!BuildStackString(cx, stackObj, &stackStr, 2)) return false; - UniquePtr stack(JS_EncodeStringToUTF8(cx, stackStr)); + UniqueChars stack(JS_EncodeStringToUTF8(cx, stackStr)); if (!stack) return false; @@ -6840,7 +6869,7 @@ main(int argc, char** argv, char** envp) if (!rt) return 1; - mozilla::UniquePtr sr = MakeUnique(); + UniquePtr sr = MakeUnique(); if (!sr) return 1; @@ -6881,7 +6910,6 @@ main(int argc, char** argv, char** envp) return 1; JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); - JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024); JS::SetLargeAllocationFailureCallback(rt, my_LargeAllocFailCallback, (void*)cx); diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 5d039a6d93..f5343cfab2 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -53,7 +53,6 @@ using JS::ToInt32; using mozilla::DebugOnly; -using mozilla::UniquePtr; using namespace js; using namespace js::gc; diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index cb8f803b12..906c79858f 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -50,7 +50,6 @@ using mozilla::ArrayLength; using mozilla::DebugOnly; using mozilla::MakeScopeExit; using mozilla::Maybe; -using mozilla::UniquePtr; /*** Forward declarations ************************************************************************/ diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 0d02105161..9ee243c0ac 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -10,7 +10,6 @@ #include "mozilla/GuardObjects.h" #include "mozilla/LinkedList.h" #include "mozilla/Range.h" -#include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" #include "jsclist.h" diff --git a/js/src/vm/DebuggerMemory.cpp b/js/src/vm/DebuggerMemory.cpp index f763dcc098..97d4498bbe 100644 --- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -8,7 +8,6 @@ #include "mozilla/Maybe.h" #include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" #include @@ -41,7 +40,6 @@ using mozilla::Forward; using mozilla::Maybe; using mozilla::Move; using mozilla::Nothing; -using mozilla::UniquePtr; /* static */ DebuggerMemory* DebuggerMemory::create(JSContext* cx, Debugger* dbg) diff --git a/js/src/vm/ForOfIterator.cpp b/js/src/vm/ForOfIterator.cpp index 590327306a..349a68e9a8 100644 --- a/js/src/vm/ForOfIterator.cpp +++ b/js/src/vm/ForOfIterator.cpp @@ -17,8 +17,6 @@ using namespace js; using JS::ForOfIterator; -using mozilla::UniquePtr; - bool ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior) { @@ -71,8 +69,7 @@ ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavio // throw an inscrutable error message about |method| rather than this nice // one about |obj|. if (!callee.isObject() || !callee.toObject().isCallable()) { - UniquePtr bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - iterable, nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, nullptr); if (!bytes) return false; JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get()); diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index fa8cbd2f13..81ac059f4e 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -202,7 +202,8 @@ ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSC alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), exclusiveContextGlobal(initCx->runtime(), exclusiveContextGlobal), callback(callback), callbackData(callbackData), - script(initCx->runtime()), sourceObject(nullptr), errors(cx), overRecursed(false) + script(initCx->runtime()), sourceObject(initCx->runtime()), + errors(cx), overRecursed(false), outOfMemory(false) { } @@ -1055,6 +1056,12 @@ GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void if (!parseTask->finish(cx)) return nullptr; + // Report out of memory errors eagerly, or errors could be malformed. + if (parseTask->outOfMemory) { + ReportOutOfMemory(cx); + return nullptr; + } + // Report any error or warnings generated during the parse, and inform the // debugger about the compiled scripts. for (size_t i = 0; i < parseTask->errors.length(); i++) @@ -1338,15 +1345,16 @@ ExclusiveContext::setHelperThread(HelperThread* thread) perThreadData = thread->threadData.ptr(); } -frontend::CompileError& -ExclusiveContext::addPendingCompileError() +bool +ExclusiveContext::addPendingCompileError(frontend::CompileError** error) { - frontend::CompileError* error = js_new(); - if (!error) - MOZ_CRASH(); - if (!helperThread()->parseTask()->errors.append(error)) - MOZ_CRASH(); - return *error; + UniquePtr errorPtr(new_()); + if (!errorPtr) + return false; + if (!helperThread()->parseTask()->errors.append(errorPtr.get())) + return false; + *error = errorPtr.release(); + return true; } void @@ -1356,6 +1364,14 @@ ExclusiveContext::addPendingOverRecursed() helperThread()->parseTask()->overRecursed = true; } +void +ExclusiveContext::addPendingOutOfMemory() +{ + // Keep in sync with recoverFromOutOfMemory. + if (helperThread()->parseTask()) + helperThread()->parseTask()->outOfMemory = true; +} + void HelperThread::handleParseWorkload() { @@ -1389,7 +1405,7 @@ HelperThread::handleParseWorkload() task->options, srcBuf, /* source_ = */ nullptr, /* extraSct = */ nullptr, - /* sourceObjectOut = */ &(task->sourceObject)); + /* sourceObjectOut = */ task->sourceObject.address()); } // The callback is invoked while we are still off the main thread. diff --git a/js/src/vm/HelperThreads.h b/js/src/vm/HelperThreads.h index d750bf094b..5eae2d21eb 100644 --- a/js/src/vm/HelperThreads.h +++ b/js/src/vm/HelperThreads.h @@ -482,12 +482,13 @@ struct ParseTask PersistentRootedScript script; // Holds the ScriptSourceObject generated for the script compilation. - ScriptSourceObject* sourceObject; + PersistentRooted sourceObject; // Any errors or warnings produced during compilation. These are reported // when finishing the script. Vector errors; bool overRecursed; + bool outOfMemory; ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx, const char16_t* chars, size_t length, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 7999e52bce..5b0fdcf76c 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1804,7 +1804,6 @@ CASE(JSOP_NOP) CASE(JSOP_UNUSED14) CASE(JSOP_UNUSED65) CASE(JSOP_BACKPATCH) -CASE(JSOP_UNUSED178) CASE(JSOP_UNUSED179) CASE(JSOP_UNUSED180) CASE(JSOP_UNUSED181) @@ -2924,6 +2923,18 @@ CASE(JSOP_FUNCALL) ADVANCE_AND_DISPATCH(0); } +CASE(JSOP_OPTIMIZE_SPREADCALL) +{ + ReservedRooted val(&rootValue0, REGS.sp[-1]); + + bool optimized = false; + if (!OptimizeSpreadCall(cx, val, &optimized)) + goto error; + + PUSH_BOOLEAN(optimized); +} +END_CASE(JSOP_OPTIMIZE_SPREADCALL) + CASE(JSOP_THROWMSG) { JS_ALWAYS_FALSE(ThrowMsgOperation(cx, GET_UINT16(REGS.pc))); @@ -4588,14 +4599,9 @@ js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, Hand constructing ? CONSTRUCT : NO_CONSTRUCT); } -#ifdef DEBUG // The object must be an array with dense elements and no holes. Baseline's // optimized spread call stubs rely on this. - MOZ_ASSERT(aobj->getDenseInitializedLength() == length); - MOZ_ASSERT(!aobj->isIndexed()); - for (uint32_t i = 0; i < length; i++) - MOZ_ASSERT(!aobj->getDenseElement(i).isMagic()); -#endif + MOZ_ASSERT(IsPackedArray(aobj)); if (constructing) { if (!StackCheckIsConstructorCalleeNewTarget(cx, callee, newTarget)) @@ -4648,6 +4654,35 @@ js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, Hand return true; } +bool +js::OptimizeSpreadCall(JSContext* cx, HandleValue arg, bool* optimized) +{ + // Optimize spread call by skipping spread operation when following + // conditions are met: + // * the argument is an array + // * the array has no hole + // * array[@@iterator] is not modified + // * the array's prototype is Array.prototype + // * Array.prototype[@@iterator] is not modified + // * %ArrayIteratorPrototype%.next is not modified + if (!arg.isObject()) { + *optimized = false; + return true; + } + + RootedObject obj(cx, &arg.toObject()); + if (!IsPackedArray(obj)) { + *optimized = false; + return true; + } + + ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx); + if (!stubChain) + return false; + + return stubChain->tryOptimizeArray(cx, obj.as(), optimized); +} + JSObject* js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, NewObjectKind newKind /* = GenericObject */) diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 077f03e252..b244af1fbc 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -442,6 +442,9 @@ bool SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv, HandleValue callee, HandleValue arr, HandleValue newTarget, MutableHandleValue res); +bool +OptimizeSpreadCall(JSContext* cx, HandleValue arg, bool* optimized); + JSObject* NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, NewObjectKind newKind = GenericObject); diff --git a/js/src/vm/MallocProvider.h b/js/src/vm/MallocProvider.h index 1feac1557f..0fd40d0cd8 100644 --- a/js/src/vm/MallocProvider.h +++ b/js/src/vm/MallocProvider.h @@ -43,8 +43,8 @@ #include "mozilla/Attributes.h" #include "mozilla/Likely.h" -#include "mozilla/UniquePtr.h" +#include "js/UniquePtr.h" #include "js/Utility.h" namespace js { @@ -120,9 +120,9 @@ struct MallocProvider } template - mozilla::UniquePtr + UniquePtr make_pod_array(size_t numElems) { - return mozilla::UniquePtr(pod_malloc(numElems)); + return UniquePtr(pod_malloc(numElems)); } template @@ -165,10 +165,10 @@ struct MallocProvider } template - mozilla::UniquePtr + UniquePtr make_zeroed_pod_array(size_t numElems) { - return mozilla::UniquePtr(pod_calloc(numElems)); + return UniquePtr(pod_calloc(numElems)); } template diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index d6eb9432f0..66a9abbc06 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -590,6 +590,14 @@ ThrowIfNotConstructing(JSContext *cx, const CallArgs &args, const char *builtinN JSMSG_BUILTIN_CTOR_NO_NEW, builtinName); } +inline bool +IsPackedArray(JSObject* obj) +{ + return obj->is() && !obj->hasLazyGroup() && + !obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) && + obj->as().getDenseInitializedLength() == obj->as().length(); +} + } // namespace js #endif /* vm_NativeObject_inl_h */ diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index fa6989bbd1..22961e3e6e 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1818,7 +1818,16 @@ * Stack: checkVal => checkVal */ \ macro(JSOP_DEBUGCHECKSELFHOSTED, 177,"debug-checkselfhosted", NULL, 1, 1, 1, JOF_BYTE) \ - macro(JSOP_UNUSED178, 178,"unused178", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the top stack value, pushes the value and a boolean value that + * indicates whether the spread operation for the value can be optimized + * in spread call. + * Category: Statements + * Type: Function + * Operands: + * Stack: arr => arr optimized + */ \ + macro(JSOP_OPTIMIZE_SPREADCALL,178,"optimize-spreadcall", NULL, 1, 1, 2, JOF_BYTE) \ macro(JSOP_UNUSED179, 179,"unused179", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED180, 180,"unused180", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED181, 181,"unused181", NULL, 1, 0, 0, JOF_BYTE) \ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index c6d13c81cf..06562966e0 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -856,8 +856,10 @@ JSRuntime::assertCanLock(RuntimeLock which) switch (which) { case ExclusiveAccessLock: MOZ_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread()); + MOZ_FALLTHROUGH; case HelperThreadStateLock: MOZ_ASSERT(!HelperThreadState().isLocked()); + MOZ_FALLTHROUGH; case GCLock: gc.assertCanLock(); break; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 9fbcff4e5f..d628002144 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -14,7 +14,6 @@ #include "mozilla/PodOperations.h" #include "mozilla/Scoped.h" #include "mozilla/ThreadLocal.h" -#include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" #include @@ -33,11 +32,12 @@ #include "gc/Tracer.h" #include "irregexp/RegExpStack.h" #include "js/Debug.h" +#include "js/GCVector.h" #include "js/HashTable.h" #ifdef DEBUG # include "js/Proxy.h" // For AutoEnterPolicy #endif -#include "js/TraceableVector.h" +#include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/CodeCoverage.h" #include "vm/CommonPropertyNames.h" @@ -146,7 +146,7 @@ struct ScopeCoordinateNameCache { void purge(); }; -using ScriptAndCountsVector = TraceableVector; +using ScriptAndCountsVector = GCVector; struct EvalCacheEntry { @@ -1920,7 +1920,7 @@ class MOZ_RAII AutoEnterIonCompilation template class MOZ_STACK_CLASS AutoInitGCManagedObject { - typedef mozilla::UniquePtr> UniquePtrT; + typedef UniquePtr UniquePtrT; UniquePtrT ptr_; diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index bb6340511b..42bfe996a4 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -55,7 +55,6 @@ using JS::AutoCheckCannotGC; using mozilla::IsInRange; using mozilla::Maybe; using mozilla::PodMove; -using mozilla::UniquePtr; using mozilla::Maybe; static void @@ -235,8 +234,7 @@ ThrowErrorWithType(JSContext* cx, JSExnType type, const CallArgs& args) } else if (val.isString()) { errorArgs[i - 1].encodeLatin1(cx, val.toString()); } else { - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr); + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr); if (!bytes) return; errorArgs[i - 1].initBytes(bytes.release()); @@ -540,14 +538,7 @@ intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); MOZ_ASSERT(args[0].isObject()); - - JSObject* obj = &args[0].toObject(); - bool isPacked = obj->is() && !obj->hasLazyGroup() && - !obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) && - obj->as().getDenseInitializedLength() == - obj->as().length(); - - args.rval().setBoolean(isPacked); + args.rval().setBoolean(IsPackedArray(&args[0].toObject())); return true; } diff --git a/js/src/vm/String.h b/js/src/vm/String.h index b772788b0b..4433002fbd 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -1136,7 +1136,7 @@ NameToId(PropertyName* name) return NON_INTEGER_ATOM_TO_JSID(name); } -using PropertyNameVector = js::TraceableVector; +using PropertyNameVector = js::GCVector; template void diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index 6ddda04f65..834c8b3a61 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -8,7 +8,6 @@ #define TraceLogging_h #include "mozilla/GuardObjects.h" -#include "mozilla/UniquePtr.h" #include "jsalloc.h" #include "jslock.h" @@ -123,7 +122,7 @@ class TraceLoggerEvent { */ class TraceLoggerEventPayload { uint32_t textId_; - mozilla::UniquePtr string_; + UniqueChars string_; uint32_t uses_; public: @@ -170,7 +169,7 @@ class TraceLoggerThread uint32_t enabled; bool failed; - mozilla::UniquePtr graph; + UniquePtr graph; PointerHashMap pointerMap; TextIdHashMap textIdPayloads; diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp index a96dcd2a0f..3ea2110a07 100644 --- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -34,9 +34,10 @@ #include "jsobjinlines.h" #include "vm/Debugger-inl.h" +using namespace js; + using mozilla::Some; using mozilla::RangedPtr; -using mozilla::UniquePtr; using JS::DispatchTyped; using JS::HandleValue; using JS::Value; @@ -347,8 +348,7 @@ Concrete::jsObjectClassName() const } bool -Concrete::jsObjectConstructorName(JSContext* cx, - UniquePtr& outName) const +Concrete::jsObjectConstructorName(JSContext* cx, UniqueTwoByteChars& outName) const { JSAtom* name = Concrete::get().maybeConstructorDisplayAtom(); if (!name) { @@ -487,7 +487,7 @@ RootList::addRoot(Node node, const char16_t* edgeName) MOZ_ASSERT(noGC.isSome()); MOZ_ASSERT_IF(wantNames, edgeName); - UniquePtr name; + UniqueTwoByteChars name; if (edgeName) { name = js::DuplicateString(edgeName); if (!name) diff --git a/js/src/vm/UbiNodeCensus.cpp b/js/src/vm/UbiNodeCensus.cpp index 1f9b293cb7..352cb2e3a3 100644 --- a/js/src/vm/UbiNodeCensus.cpp +++ b/js/src/vm/UbiNodeCensus.cpp @@ -49,12 +49,12 @@ class SimpleCount : public CountType { { } }; - UniquePtr label; + UniqueTwoByteChars label; bool reportCount : 1; bool reportBytes : 1; public: - explicit SimpleCount(UniquePtr& label, + explicit SimpleCount(UniqueTwoByteChars& label, bool reportCount=true, bool reportBytes=true) : CountType(), @@ -929,7 +929,7 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue) if (!GetProperty(cx, breakdown, breakdown, cx->names().label, &label)) return nullptr; - UniquePtr labelUnique(nullptr); + UniqueTwoByteChars labelUnique(nullptr); if (!label.isUndefined()) { RootedString labelString(cx, ToString(cx, label)); if (!labelString) diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 7e41a8c6c3..37b5f8e69e 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -19,7 +19,6 @@ using mozilla::ArrayLength; using mozilla::DebugOnly; using mozilla::PodCopy; -using mozilla::UniquePtr; using namespace js; diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 5033c7ba1d..293e5ef751 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,7 +29,7 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 328; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 329; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index 67b27dcac0..b93590f1d0 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -600,7 +600,7 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url, nsAutoCString scheme; // Figure out who's calling us - JS::AutoFilename filename; + JS::UniqueChars filename; if (!JS::DescribeScriptedCaller(cx, &filename)) { // No scripted frame means we don't know who's calling, bail. return NS_ERROR_FAILURE; diff --git a/js/xpconnect/src/ExportHelpers.cpp b/js/xpconnect/src/ExportHelpers.cpp index 891ecec294..4b341b7d84 100644 --- a/js/xpconnect/src/ExportHelpers.cpp +++ b/js/xpconnect/src/ExportHelpers.cpp @@ -24,14 +24,13 @@ using namespace mozilla; using namespace mozilla::dom; using namespace JS; -using namespace js; namespace xpc { bool IsReflector(JSObject* obj) { - obj = CheckedUnwrap(obj, /* stopAtWindowProxy = */ false); + obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false); if (!obj) return false; return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); @@ -408,8 +407,8 @@ ExportFunction(JSContext* cx, HandleValue vfunction, HandleValue vscope, HandleV // * We must subsume the scope we are exporting to. // * We must subsume the function being exported, because the function // forwarder manually circumvents security wrapper CALL restrictions. - targetScope = CheckedUnwrap(targetScope); - funObj = CheckedUnwrap(funObj); + targetScope = js::CheckedUnwrap(targetScope); + funObj = js::CheckedUnwrap(funObj); if (!targetScope || !funObj) { JS_ReportError(cx, "Permission denied to export function into scope"); return false; diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 56c9a77f66..3a091ba55d 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -51,7 +51,6 @@ using namespace mozilla; using namespace JS; -using namespace js; using namespace xpc; using mozilla::dom::DestroyProtoAndIfaceCache; @@ -599,7 +598,7 @@ static const JSFunctionSpec SandboxFunctions[] = { bool xpc::IsSandbox(JSObject* obj) { - const Class* clasp = GetObjectClass(obj); + const js::Class* clasp = js::GetObjectClass(obj); return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass; } @@ -1018,9 +1017,9 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin compartmentOptions.behaviors().setDiscardSource(options.discardSource); - const Class* clasp = options.writeToGlobalPrototype - ? &SandboxWriteToProtoClass - : &SandboxClass; + const js::Class* clasp = options.writeToGlobalPrototype + ? &SandboxWriteToProtoClass + : &SandboxClass; RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp), principal, compartmentOptions)); diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index b335c3161d..ca0e82490f 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -492,7 +492,7 @@ XPCConvert::JSData2Native(void* d, HandleValue s, (**((nsAString**)d)).SetIsVoid(true); return true; } - // Fall through to T_DOMSTRING case. + MOZ_FALLTHROUGH; } case nsXPTType::T_DOMSTRING: { diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index 3e1746314d..8b09c15d99 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -126,7 +126,7 @@ GetLocationProperty(JSContext* cx, unsigned argc, Value* vp) //XXX: your platform should really implement this return false; #else - JS::AutoFilename filename; + JS::UniqueChars filename; if (JS::DescribeScriptedCaller(cx, &filename) && filename.get()) { nsresult rv; nsCOMPtr xpc = @@ -933,6 +933,7 @@ ProcessArgsForCompartment(JSContext* cx, char** argv, int argc) break; case 'S': RuntimeOptionsRef(cx).toggleWerror(); + MOZ_FALLTHROUGH; // because -S implies -s case 's': RuntimeOptionsRef(cx).toggleExtraWarnings(); break; diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 3534686010..faf57d3e48 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -205,7 +205,7 @@ ReportWrapperDenial(JSContext* cx, HandleId id, WrapperDenialType type, const ch return false; if (!propertyName.init(cx, str)) return false; - AutoFilename filename; + UniqueChars filename; unsigned line = 0, column = 0; DescribeScriptedCaller(cx, &filename, &line, &column); diff --git a/modules/libjar/nsJARInputStream.cpp b/modules/libjar/nsJARInputStream.cpp index 4df5d7a049..6993dd578f 100644 --- a/modules/libjar/nsJARInputStream.cpp +++ b/modules/libjar/nsJARInputStream.cpp @@ -110,7 +110,7 @@ nsJARInputStream::InitDirectory(nsJAR* aJar, case ')': case '\\': escDirName.Append('\\'); - // fall through + MOZ_FALLTHROUGH; default: escDirName.Append(*curr); } diff --git a/storage/mozStoragePrivateHelpers.cpp b/storage/mozStoragePrivateHelpers.cpp index d6819fe8c6..61fe86bbca 100644 --- a/storage/mozStoragePrivateHelpers.cpp +++ b/storage/mozStoragePrivateHelpers.cpp @@ -230,6 +230,7 @@ convertVariantToStorageVariant(nsIVariant* aVariant) // Take ownership of the data avoiding a further copy. return new AdoptedBlobVariant(v); } + MOZ_FALLTHROUGH; } case nsIDataType::VTYPE_EMPTY: case nsIDataType::VTYPE_EMPTY_ARRAY: @@ -244,7 +245,6 @@ convertVariantToStorageVariant(nsIVariant* aVariant) } return nullptr; - } namespace { @@ -273,7 +273,5 @@ newCompletionEvent(mozIStorageCompletionCallback *aCallback) return event.forget(); } - - } // namespace storage } // namespace mozilla diff --git a/toolkit/components/places/Database.cpp b/toolkit/components/places/Database.cpp index c474f9428a..4cdc79ce34 100644 --- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -177,7 +177,7 @@ SetJournalMode(nsCOMPtr& aDBConn, nsAutoCString journalMode; switch (aJournalMode) { default: - MOZ_ASSERT(false, "Trying to set an unknown journal mode."); + MOZ_FALLTHROUGH_ASSERT("Trying to set an unknown journal mode."); // Fall through to the default DELETE journal. case JOURNAL_DELETE: journalMode.AssignLiteral("delete"); diff --git a/toolkit/components/places/nsAnnotationService.cpp b/toolkit/components/places/nsAnnotationService.cpp index fc8730bc10..8865940209 100644 --- a/toolkit/components/places/nsAnnotationService.cpp +++ b/toolkit/components/places/nsAnnotationService.cpp @@ -211,6 +211,7 @@ nsAnnotationService::SetPageAnnotation(nsIURI* aURI, return NS_OK; } // Fall through int64_t case otherwise. + MOZ_FALLTHROUGH; } case nsIDataType::VTYPE_INT64: case nsIDataType::VTYPE_UINT64: { @@ -223,6 +224,7 @@ nsAnnotationService::SetPageAnnotation(nsIURI* aURI, return NS_OK; } // Fall through double case otherwise. + MOZ_FALLTHROUGH; } case nsIDataType::VTYPE_FLOAT: case nsIDataType::VTYPE_DOUBLE: { @@ -293,6 +295,7 @@ nsAnnotationService::SetItemAnnotation(int64_t aItemId, return NS_OK; } // Fall through int64_t case otherwise. + MOZ_FALLTHROUGH; } case nsIDataType::VTYPE_INT64: case nsIDataType::VTYPE_UINT64: { @@ -305,6 +308,7 @@ nsAnnotationService::SetItemAnnotation(int64_t aItemId, return NS_OK; } // Fall through double case otherwise. + MOZ_FALLTHROUGH; } case nsIDataType::VTYPE_FLOAT: case nsIDataType::VTYPE_DOUBLE: { diff --git a/toolkit/components/places/nsNavHistoryResult.cpp b/toolkit/components/places/nsNavHistoryResult.cpp index a8d30b0de4..b75c8c2fb8 100644 --- a/toolkit/components/places/nsNavHistoryResult.cpp +++ b/toolkit/components/places/nsNavHistoryResult.cpp @@ -2415,6 +2415,7 @@ nsNavHistoryQueryResultNode::OnVisit(nsIURI* aURI, int64_t aVisitId, // Fall through to check the time, if the time is not present it will // still match. + MOZ_FALLTHROUGH; } case QUERYUPDATE_TIME: { @@ -2439,8 +2440,9 @@ nsNavHistoryQueryResultNode::OnVisit(nsIURI* aURI, int64_t aVisitId, if (aTime > endTime) return NS_OK; // after our time range } - // Now we know that our visit satisfies the time range, fallback to the - // QUERYUPDATE_SIMPLE case. + // Now we know that our visit satisfies the time range, fall through to + // the QUERYUPDATE_SIMPLE case below. + MOZ_FALLTHROUGH; } case QUERYUPDATE_SIMPLE: { @@ -2874,6 +2876,7 @@ nsNavHistoryQueryResultNode::OnItemChanged(int64_t aItemId, // folders titles. if (mOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY) return NS_OK; + MOZ_FALLTHROUGH; default: (void)Refresh(); } diff --git a/toolkit/devtools/server/DeserializedNode.cpp b/toolkit/devtools/server/DeserializedNode.cpp index 3e3bd8ac20..35470a60fc 100644 --- a/toolkit/devtools/server/DeserializedNode.cpp +++ b/toolkit/devtools/server/DeserializedNode.cpp @@ -120,16 +120,15 @@ Concrete::allocationStack() const } -UniquePtr +js::UniquePtr Concrete::edges(JSRuntime* rt, bool) const { - UniquePtr> range( - js_new(get())); + js::UniquePtr range(js_new(get())); if (!range) return nullptr; - return UniquePtr(range.release()); + return js::UniquePtr(range.release()); } StackFrame diff --git a/toolkit/devtools/server/DeserializedNode.h b/toolkit/devtools/server/DeserializedNode.h index 50904de41e..068bb176fa 100644 --- a/toolkit/devtools/server/DeserializedNode.h +++ b/toolkit/devtools/server/DeserializedNode.h @@ -7,10 +7,10 @@ #define mozilla_devtools_DeserializedNode__ #include "js/UbiNode.h" +#include "js/UniquePtr.h" #include "mozilla/devtools/CoreDump.pb.h" #include "mozilla/Maybe.h" #include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" // `Deserialized{Node,Edge}` translate protobuf messages from our core dump @@ -242,7 +242,6 @@ namespace ubi { using mozilla::devtools::DeserializedNode; using mozilla::devtools::DeserializedStackFrame; -using mozilla::UniquePtr; template<> struct Concrete : public Base @@ -273,7 +272,7 @@ public: // We ignore the `bool wantNames` parameter because we can't control whether // the core dump was serialized with edge names or not. - UniquePtr edges(JSRuntime* rt, bool) const override; + js::UniquePtr edges(JSRuntime* rt, bool) const override; }; template<> diff --git a/toolkit/devtools/server/tests/gtest/DevTools.h b/toolkit/devtools/server/tests/gtest/DevTools.h index 2b01125acf..073ec13847 100644 --- a/toolkit/devtools/server/tests/gtest/DevTools.h +++ b/toolkit/devtools/server/tests/gtest/DevTools.h @@ -17,9 +17,9 @@ #include "mozilla/dom/ChromeUtils.h" #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" #include "js/Principals.h" #include "js/UbiNode.h" +#include "js/UniquePtr.h" using namespace mozilla; using namespace mozilla::devtools; @@ -173,8 +173,6 @@ public: namespace JS { namespace ubi { -using mozilla::UniquePtr; - template<> class Concrete : public Base { @@ -182,8 +180,8 @@ class Concrete : public Base return concreteTypeName; } - UniquePtr edges(JSRuntime*, bool) const override { - return UniquePtr(js_new(get().edges)); + js::UniquePtr edges(JSRuntime*, bool) const override { + return js::UniquePtr(js_new(get().edges)); } Size size(mozilla::MallocSizeOf) const override {