--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4198,18 +4198,29 @@ JS::CancelOffThreadModule(JSContext* cx,
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Module, token);
}
JS_PUBLIC_API(bool)
JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
OffThreadCompileCallback callback, void* callbackData)
{
- MOZ_ASSERT(CanCompileOffThread(cx, options, buffer.length() - cursor));
- return StartOffThreadDecodeScript(cx, options, buffer, cursor, callback, callbackData);
+ JS::TranscodeRange range(buffer.begin() + cursor, buffer.length() - cursor);
+
+ MOZ_ASSERT(CanCompileOffThread(cx, options, range.length()));
+ return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
+}
+
+JS_PUBLIC_API(bool)
+JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+ const mozilla::Range<uint8_t>& range /* TranscodeRange& */,
+ OffThreadCompileCallback callback, void* callbackData)
+{
+ MOZ_ASSERT(CanCompileOffThread(cx, options, range.length()));
+ return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
}
JS_PUBLIC_API(JSScript*)
JS::FinishOffThreadScriptDecoder(JSContext* cx, void* token)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
return HelperThreadState().finishScriptDecodeTask(cx, token);
@@ -6959,16 +6970,25 @@ JS::DecodeScript(JSContext* cx, Transcod
{
XDRDecoder decoder(cx, buffer, cursorIndex);
decoder.codeScript(scriptp);
MOZ_ASSERT(bool(scriptp) == (decoder.resultCode() == TranscodeResult_Ok));
return decoder.resultCode();
}
JS_PUBLIC_API(JS::TranscodeResult)
+JS::DecodeScript(JSContext* cx, const TranscodeRange& range, JS::MutableHandleScript scriptp)
+{
+ XDRDecoder decoder(cx, range);
+ decoder.codeScript(scriptp);
+ MOZ_ASSERT(bool(scriptp) == (decoder.resultCode() == TranscodeResult_Ok));
+ return decoder.resultCode();
+}
+
+JS_PUBLIC_API(JS::TranscodeResult)
JS::DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer,
JS::MutableHandleFunction funp,
size_t cursorIndex)
{
XDRDecoder decoder(cx, buffer, cursorIndex);
decoder.codeFunction(funp);
MOZ_ASSERT(bool(funp) == (decoder.resultCode() == TranscodeResult_Ok));
return decoder.resultCode();
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4265,16 +4265,21 @@ FinishOffThreadModule(JSContext* cx, voi
extern JS_PUBLIC_API(void)
CancelOffThreadModule(JSContext* cx, void* token);
extern JS_PUBLIC_API(bool)
DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
OffThreadCompileCallback callback, void* callbackData);
+extern JS_PUBLIC_API(bool)
+DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+ const mozilla::Range<uint8_t>& range /* TranscodeRange& */,
+ OffThreadCompileCallback callback, void* callbackData);
+
extern JS_PUBLIC_API(JSScript*)
FinishOffThreadScriptDecoder(JSContext* cx, void* token);
extern JS_PUBLIC_API(void)
CancelOffThreadScriptDecoder(JSContext* cx, void* token);
/**
* Compile a function with envChain plus the global as its scope chain.
@@ -6138,16 +6143,17 @@ class MOZ_RAII AutoHideScriptedCaller
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
/*
* Encode/Decode interpreted scripts and functions to/from memory.
*/
typedef mozilla::Vector<uint8_t> TranscodeBuffer;
+typedef mozilla::Range<uint8_t> TranscodeRange;
enum TranscodeResult
{
// Successful encoding / decoding.
TranscodeResult_Ok = 0,
// A warning message, is set to the message out-param.
TranscodeResult_Failure = 0x100,
@@ -6168,16 +6174,19 @@ EncodeScript(JSContext* cx, TranscodeBuf
extern JS_PUBLIC_API(TranscodeResult)
EncodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::HandleObject funobj);
extern JS_PUBLIC_API(TranscodeResult)
DecodeScript(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleScript scriptp,
size_t cursorIndex = 0);
extern JS_PUBLIC_API(TranscodeResult)
+DecodeScript(JSContext* cx, const TranscodeRange& range, JS::MutableHandleScript scriptp);
+
+extern JS_PUBLIC_API(TranscodeResult)
DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
size_t cursorIndex = 0);
// Register an encoder on the given script source, such that all functions can
// be encoded as they are parsed. This strategy is used to avoid blocking the
// active thread in a non-interruptible way.
//
// The |script| argument of |StartIncrementalEncoding| and
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -292,35 +292,37 @@ static const JSClassOps parseTaskGlobalC
static const JSClass parseTaskGlobalClass = {
"internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
&parseTaskGlobalClassOps
};
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
- : kind(kind), options(cx), chars(chars), length(length),
+ : kind(kind), options(cx),
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
overRecursed(false), outOfMemory(false)
{
+ data.construct<TwoByteChars>(chars, length);
}
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
- JS::TranscodeBuffer& buffer, size_t cursor,
+ const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData)
- : kind(kind), options(cx), buffer(&buffer), cursor(cursor),
+ : kind(kind), options(cx),
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
overRecursed(false), outOfMemory(false)
{
+ data.construct<const JS::TranscodeRange>(range);
}
bool
ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
{
if (!this->options.copy(cx, options))
return false;
@@ -377,53 +379,55 @@ ScriptParseTask::ScriptParseTask(JSConte
: ParseTask(ParseTaskKind::Script, cx, parseGlobal, chars, length, callback,
callbackData)
{
}
void
ScriptParseTask::parse(JSContext* cx)
{
- SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+ auto& range = data.ref<TwoByteChars>();
+ SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
options, srcBuf,
/* sourceObjectOut = */ &sourceObject);
}
ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
: ParseTask(ParseTaskKind::Module, cx, parseGlobal, chars, length, callback,
callbackData)
{
}
void
ModuleParseTask::parse(JSContext* cx)
{
- SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+ auto& range = data.ref<TwoByteChars>();
+ SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
if (module)
script = module->script();
}
ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
- JS::TranscodeBuffer& buffer, size_t cursor,
+ const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData)
: ParseTask(ParseTaskKind::ScriptDecode, cx, parseGlobal,
- buffer, cursor, callback, callbackData)
+ range, callback, callbackData)
{
}
void
ScriptDecodeTask::parse(JSContext* cx)
{
RootedScript resultScript(cx);
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
- *buffer, cursor);
+ data.ref<const JS::TranscodeRange>());
decoder.codeScript(&resultScript);
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
if (decoder.resultCode() == JS::TranscodeResult_Ok) {
script = resultScript.get();
} else {
sourceObject = nullptr;
}
}
@@ -645,22 +649,21 @@ js::StartOffThreadParseModule(JSContext*
return cx->new_<ModuleParseTask>(cx, global, chars, length,
callback, callbackData);
};
return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
}
bool
js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
- JS::TranscodeBuffer& buffer, size_t cursor,
+ const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData)
{
auto functor = [&](JSObject* global) -> ScriptDecodeTask* {
- return cx->new_<ScriptDecodeTask>(cx, global, buffer, cursor,
- callback, callbackData);
+ return cx->new_<ScriptDecodeTask>(cx, global, range, callback, callbackData);
};
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
}
void
js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
{
MOZ_ASSERT(!OffThreadParsingMustWaitForGC(rt));
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -10,16 +10,17 @@
* and are distinct from e.g. web workers.
*/
#ifndef vm_HelperThreads_h
#define vm_HelperThreads_h
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
+#include "mozilla/MaybeOneOf.h"
#include "mozilla/PodOperations.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Variant.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "frontend/TokenStream.h"
@@ -530,17 +531,17 @@ StartOffThreadParseScript(JSContext* cx,
bool
StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData);
bool
StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
- JS::TranscodeBuffer& buffer, size_t cursor,
+ const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
/*
* Called at the end of GC to enqueue any Parse tasks that were waiting on an
* atoms-zone GC to finish.
*/
void
EnqueuePendingParseTasksAfterGC(JSRuntime* rt);
@@ -589,31 +590,19 @@ class MOZ_RAII AutoUnlockHelperThreadSta
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
};
struct ParseTask
{
ParseTaskKind kind;
OwningCompileOptions options;
- // Anonymous union, the only correct interpretation is provided by the
- // ParseTaskKind value, or from the virtual parse function.
- union {
- struct {
- const char16_t* chars;
- size_t length;
- };
- struct {
- // This should be a reference, but C++ prevents us from using union
- // with references as it assumes the reference constness might be
- // violated.
- JS::TranscodeBuffer* const buffer;
- size_t cursor;
- };
- };
+
+ mozilla::MaybeOneOf<const JS::TranscodeRange, JS::TwoByteChars> data;
+
LifoAlloc alloc;
// Rooted pointer to the global object to use while parsing.
JSObject* parseGlobal;
// Callback invoked off thread when the parse finishes.
JS::OffThreadCompileCallback callback;
void* callbackData;
@@ -631,17 +620,17 @@ struct ParseTask
Vector<CompileError*, 0, SystemAllocPolicy> errors;
bool overRecursed;
bool outOfMemory;
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData);
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
- JS::TranscodeBuffer& buffer, size_t cursor,
+ const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
void activate(JSRuntime* rt);
virtual void parse(JSContext* cx) = 0;
bool finish(JSContext* cx);
bool runtimeMatches(JSRuntime* rt) {
@@ -667,17 +656,17 @@ struct ModuleParseTask : public ParseTas
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData);
void parse(JSContext* cx) override;
};
struct ScriptDecodeTask : public ParseTask
{
ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
- JS::TranscodeBuffer& buffer, size_t cursor,
+ const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
void parse(JSContext* cx) override;
};
// Return whether, if a new parse task was started, it would need to wait for
// an in-progress GC to complete before starting.
extern bool
OffThreadParsingMustWaitForGC(JSRuntime* rt);
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -10,60 +10,106 @@
#include "mozilla/EndianUtils.h"
#include "mozilla/TypeTraits.h"
#include "jsatom.h"
#include "jsfriendapi.h"
namespace js {
-class XDRBuffer {
+class XDRBufferBase
+{
public:
- XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
- : context_(cx), buffer_(buffer), cursor_(cursor) { }
+ explicit XDRBufferBase(JSContext* cx, size_t cursor = 0)
+ : context_(cx), cursor_(cursor) { }
JSContext* cx() const {
return context_;
}
- const uint8_t* read(size_t n) {
- MOZ_ASSERT(cursor_ < buffer_.length());
- uint8_t* ptr = &buffer_[cursor_];
- cursor_ += n;
- return ptr;
+ size_t cursor() const {
+ return cursor_;
}
- const char* readCString() {
- char* ptr = reinterpret_cast<char*>(&buffer_[cursor_]);
- uint8_t* end = reinterpret_cast<uint8_t*>(strchr(ptr, '\0')) + 1;
- MOZ_ASSERT(buffer_.begin() < end);
- MOZ_ASSERT(end <= buffer_.end());
- cursor_ = end - buffer_.begin();
- return ptr;
- }
+ protected:
+ JSContext* const context_;
+ size_t cursor_;
+};
+
+template <XDRMode mode>
+class XDRBuffer;
+
+template <>
+class XDRBuffer<XDR_ENCODE> : public XDRBufferBase
+{
+ public:
+ XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
+ : XDRBufferBase(cx, cursor),
+ buffer_(buffer) { }
uint8_t* write(size_t n) {
MOZ_ASSERT(n != 0);
if (!buffer_.growByUninitialized(n)) {
ReportOutOfMemory(cx());
return nullptr;
}
uint8_t* ptr = &buffer_[cursor_];
cursor_ += n;
return ptr;
}
- size_t cursor() const {
- return cursor_;
+ const char* readCString() {
+ MOZ_CRASH("Should never read in encode mode");
+ return nullptr;
+ }
+
+ const uint8_t* read(size_t n) {
+ MOZ_CRASH("Should never read in encode mode");
+ return nullptr;
}
private:
- JSContext* const context_;
JS::TranscodeBuffer& buffer_;
- size_t cursor_;
+};
+
+template <>
+class XDRBuffer<XDR_DECODE> : public XDRBufferBase
+{
+ public:
+ XDRBuffer(JSContext* cx, const JS::TranscodeRange& range)
+ : XDRBufferBase(cx),
+ buffer_(range) { }
+
+ XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
+ : XDRBufferBase(cx, cursor),
+ buffer_(buffer.begin(), buffer.length()) { }
+
+ const char* readCString() {
+ char* ptr = reinterpret_cast<char*>(&buffer_[cursor_]);
+ uint8_t* end = reinterpret_cast<uint8_t*>(strchr(ptr, '\0')) + 1;
+ MOZ_ASSERT(buffer_.begin().get() < end);
+ MOZ_ASSERT(end <= buffer_.end().get());
+ cursor_ = end - buffer_.begin().get();
+ return ptr;
+ }
+
+ const uint8_t* read(size_t n) {
+ MOZ_ASSERT(cursor_ < buffer_.length());
+ uint8_t* ptr = &buffer_[cursor_];
+ cursor_ += n;
+ return ptr;
+ }
+
+ uint8_t* write(size_t n) {
+ MOZ_CRASH("Should never write in decode mode");
+ return nullptr;
+ }
+
+ private:
+ const JS::TranscodeRange buffer_;
};
class XDRCoderBase;
class XDRIncrementalEncoder;
// An AutoXDRTree is used to identify section encoded by an XDRIncrementalEncoder.
//
// Its primary goal is to identify functions, such that we can first encode them
@@ -119,27 +165,34 @@ class XDRCoderBase
/*
* XDR serialization state. All data is encoded in little endian.
*/
template <XDRMode mode>
class XDRState : public XDRCoderBase
{
public:
- XDRBuffer buf;
+ XDRBuffer<mode> buf;
private:
JS::TranscodeResult resultCode_;
public:
XDRState(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
: buf(cx, buffer, cursor),
resultCode_(JS::TranscodeResult_Ok)
{
}
+ template <typename RangeType>
+ XDRState(JSContext* cx, const RangeType& range)
+ : buf(cx, range),
+ resultCode_(JS::TranscodeResult_Ok)
+ {
+ }
+
virtual ~XDRState() {};
JSContext* cx() const {
return buf.cx();
}
virtual LifoAlloc& lifoAlloc() const;
virtual bool hasOptions() const { return false; }
@@ -302,18 +355,18 @@ class XDROffThreadDecoder : public XDRDe
// initialization with ScriptSourceObject::initFromOptions after the
// decoding.
//
// When providing a sourceObjectOut pointer, you have to ensure that it is
// marked by the GC to avoid dangling pointers.
XDROffThreadDecoder(JSContext* cx, LifoAlloc& alloc,
const ReadOnlyCompileOptions* options,
ScriptSourceObject** sourceObjectOut,
- JS::TranscodeBuffer& buffer, size_t cursor = 0)
- : XDRDecoder(cx, buffer, cursor),
+ const JS::TranscodeRange& range)
+ : XDRDecoder(cx, range),
options_(options),
sourceObjectOut_(sourceObjectOut),
alloc_(alloc)
{
MOZ_ASSERT(options);
MOZ_ASSERT(sourceObjectOut);
MOZ_ASSERT(*sourceObjectOut == nullptr);
}