mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
Issue #1691 - Part 7e: Dependencies for required to finish part 7d. https://bugzilla.mozilla.org/show_bug.cgi?id=1331662 Reimplement EvaluateString using the ExecutionContext class. https://bugzilla.mozilla.org/show_bug.cgi?id=1316078 Extract redudant code into StartOffThreadParseTask. Use an ExclusiveContext instead of a JSContext in XDR functions. Add a script decoder as a valid off-main-thread parse-task. https://bugzilla.mozilla.org/show_bug.cgi?id=900784 Add nsJSUtils functions for encoding and decoding the bytecode. https://bugzilla.mozilla.org/show_bug.cgi?id=1316081 Add XDRIncrementalEncoder to replace delazified LazyScript in the encoded XDR buffer. Add an XDRIncrementalEncoder instance on the ScriptSource. Expose a new JSAPI to incrementally encode bytecode when it is generated. https://bugzilla.mozilla.org/show_bug.cgi?id=1334091 XDR function use the sourceObject instead of the enclosingScript as argument.
(cherry picked from commit d6de9a669f4b2f5115670bd771cd53d7cfb3956a)
This commit is contained in:
@@ -123,6 +123,244 @@ nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
EvaluationExceptionToNSResult(JSContext* aCx)
|
||||
{
|
||||
if (JS_IsExceptionPending(aCx)) {
|
||||
return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
|
||||
}
|
||||
return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
|
||||
}
|
||||
|
||||
nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGlobal)
|
||||
: mCx(aCx)
|
||||
, mCompartment(aCx, aGlobal)
|
||||
, mRetValue(aCx)
|
||||
, mScopeChain(aCx)
|
||||
, mRv(NS_OK)
|
||||
, mSkip(false)
|
||||
, mCoerceToString(false)
|
||||
#ifdef DEBUG
|
||||
, mWantsReturnValue(false)
|
||||
, mExpectScopeChain(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
|
||||
MOZ_ASSERT(mRetValue.isUndefined());
|
||||
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aGlobal) == aGlobal);
|
||||
if (MOZ_UNLIKELY(!xpc::Scriptability::Get(aGlobal).Allowed())) {
|
||||
mSkip = true;
|
||||
mRv = NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsJSUtils::ExecutionContext::SetScopeChain(
|
||||
const JS::AutoObjectVector& aScopeChain)
|
||||
{
|
||||
if (mSkip) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mExpectScopeChain = true;
|
||||
#endif
|
||||
// Now make sure to wrap the scope chain into the right compartment.
|
||||
if (!mScopeChain.reserve(aScopeChain.length())) {
|
||||
mSkip = true;
|
||||
mRv = NS_ERROR_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aScopeChain.length(); ++i) {
|
||||
JS::ExposeObjectToActiveJS(aScopeChain[i]);
|
||||
mScopeChain.infallibleAppend(aScopeChain[i]);
|
||||
if (!JS_WrapObject(mCx, mScopeChain[i])) {
|
||||
mSkip = true;
|
||||
mRv = NS_ERROR_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::JoinAndExec(void **aOffThreadToken,
|
||||
JS::MutableHandle<JSScript*> aScript)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
MOZ_ASSERT(!mExpectScopeChain);
|
||||
aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
if (!aScript || !JS_ExecuteScript(mCx, mScopeChain, aScript)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
|
||||
JS::SourceBufferHolder& aSrcBuf)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(aCompileOptions.versionSet,
|
||||
aCompileOptions.version != JSVERSION_UNKNOWN);
|
||||
MOZ_ASSERT(aSrcBuf.get());
|
||||
MOZ_ASSERT(mRetValue.isUndefined());
|
||||
#ifdef DEBUG
|
||||
mWantsReturnValue = !aCompileOptions.noScriptRval;
|
||||
#endif
|
||||
MOZ_ASSERT(!mCoerceToString || mWantsReturnValue);
|
||||
if (!JS::Evaluate(mCx, mScopeChain, aCompileOptions, aSrcBuf, &mRetValue)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
|
||||
const nsAString& aScript)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
|
||||
JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(),
|
||||
JS::SourceBufferHolder::NoOwnership);
|
||||
return CompileAndExec(aCompileOptions, srcBuf);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::DecodeAndExec(JS::CompileOptions& aCompileOptions,
|
||||
mozilla::Vector<uint8_t>& aBytecodeBuf,
|
||||
size_t aBytecodeIndex)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
JS::Rooted<JSScript*> script(mCx);
|
||||
JS::TranscodeResult tr = JS::DecodeScript(mCx, aBytecodeBuf, &script, aBytecodeIndex);
|
||||
// These errors are external parameters which should be handled before the
|
||||
// decoding phase, and which are the only reasons why you might want to
|
||||
// fallback on decoding failures.
|
||||
MOZ_ASSERT(tr != JS::TranscodeResult_Failure_BadBuildId &&
|
||||
tr != JS::TranscodeResult_Failure_WrongCompileOption);
|
||||
if (tr != JS::TranscodeResult_Ok) {
|
||||
mSkip = true;
|
||||
mRv = NS_ERROR_DOM_JS_DECODING_ERROR;
|
||||
return mRv;
|
||||
}
|
||||
|
||||
if (!JS_ExecuteScript(mCx, mScopeChain, script)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return mRv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::DecodeJoinAndExec(void **aOffThreadToken)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
MOZ_ASSERT(!mExpectScopeChain);
|
||||
JS::Rooted<JSScript*> script(mCx);
|
||||
script.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken));
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
if (!script || !JS_ExecuteScript(mCx, mScopeChain, script)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::JoinEncodeAndExec(void **aOffThreadToken,
|
||||
mozilla::Vector<uint8_t>& aBytecodeBuf,
|
||||
JS::MutableHandle<JSScript*> aScript)
|
||||
{
|
||||
MOZ_ASSERT_IF(aOffThreadToken, !mWantsReturnValue);
|
||||
aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
if (!aScript) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
if (!StartIncrementalEncoding(mCx, aBytecodeBuf, aScript)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return mRv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue)
|
||||
{
|
||||
MOZ_ASSERT(aRetValue.isUndefined());
|
||||
if (mSkip) {
|
||||
// Repeat earlier result, as NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW are not
|
||||
// failures cases.
|
||||
#ifdef DEBUG
|
||||
mWantsReturnValue = false;
|
||||
#endif
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mWantsReturnValue);
|
||||
#ifdef DEBUG
|
||||
mWantsReturnValue = false;
|
||||
#endif
|
||||
if (mCoerceToString && !mRetValue.isUndefined()) {
|
||||
JSString* str = JS::ToString(mCx, mRetValue);
|
||||
if (!str) {
|
||||
// ToString can be a function call, so an exception can be raised while
|
||||
// executing the function.
|
||||
mSkip = true;
|
||||
return EvaluationExceptionToNSResult(mCx);
|
||||
}
|
||||
mRetValue.set(JS::StringValue(str));
|
||||
}
|
||||
|
||||
aRetValue.set(mRetValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
|
||||
@@ -63,6 +63,107 @@ public:
|
||||
const nsAString& aBody,
|
||||
JSObject** aFunctionObject);
|
||||
|
||||
// ExecutionContext is used to switch compartment.
|
||||
class MOZ_STACK_CLASS ExecutionContext {
|
||||
JSContext* mCx;
|
||||
|
||||
// Handles switching to our global's compartment.
|
||||
JSAutoCompartment mCompartment;
|
||||
|
||||
// Set to a valid handle if a return value is expected.
|
||||
JS::Rooted<JS::Value> mRetValue;
|
||||
|
||||
// Scope chain in which the execution takes place.
|
||||
JS::AutoObjectVector mScopeChain;
|
||||
|
||||
// returned value forwarded when we have to interupt the execution eagerly
|
||||
// with mSkip.
|
||||
nsresult mRv;
|
||||
|
||||
// Used to skip upcoming phases in case of a failure. In such case the
|
||||
// result is carried by mRv.
|
||||
bool mSkip;
|
||||
|
||||
// Should the result be serialized before being returned.
|
||||
bool mCoerceToString;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Should we set the return value.
|
||||
bool mWantsReturnValue;
|
||||
|
||||
bool mExpectScopeChain;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// Enter compartment in which the code would be executed. The JSContext
|
||||
// must come from an AutoEntryScript that has had
|
||||
// TakeOwnershipOfErrorReporting() called on it.
|
||||
ExecutionContext(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
|
||||
|
||||
ExecutionContext(const ExecutionContext&) = delete;
|
||||
ExecutionContext(ExecutionContext&&) = delete;
|
||||
|
||||
~ExecutionContext() {
|
||||
// This flag is resetted, when the returned value is extracted.
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
}
|
||||
|
||||
// The returned value would be converted to a string if the
|
||||
// |aCoerceToString| is flag set.
|
||||
ExecutionContext& SetCoerceToString(bool aCoerceToString) {
|
||||
mCoerceToString = aCoerceToString;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Set the scope chain in which the code should be executed.
|
||||
void SetScopeChain(const JS::AutoObjectVector& aScopeChain);
|
||||
|
||||
// Copy the returned value in the mutable handle argument, in case of a
|
||||
// evaluation failure either during the execution or the conversion of the
|
||||
// result to a string, the nsresult would be set to the corresponding result
|
||||
// code, and the mutable handle argument would remain unchanged.
|
||||
//
|
||||
// The value returned in the mutable handle argument is part of the
|
||||
// compartment given as argument to the ExecutionContext constructor. If the
|
||||
// caller is in a different compartment, then the out-param value should be
|
||||
// wrapped by calling |JS_WrapValue|.
|
||||
MOZ_MUST_USE nsresult
|
||||
ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue);
|
||||
|
||||
// After getting a notification that an off-thread compilation terminated,
|
||||
// this function will take the result of the parser by moving it to the main
|
||||
// thread before starting the execution of the script.
|
||||
//
|
||||
// The compiled script would be returned in the |aScript| out-param.
|
||||
MOZ_MUST_USE nsresult JoinAndExec(void **aOffThreadToken,
|
||||
JS::MutableHandle<JSScript*> aScript);
|
||||
|
||||
// Compile a script contained in a SourceBuffer, and execute it.
|
||||
nsresult CompileAndExec(JS::CompileOptions& aCompileOptions,
|
||||
JS::SourceBufferHolder& aSrcBuf);
|
||||
|
||||
// Compile a script contained in a string, and execute it.
|
||||
nsresult CompileAndExec(JS::CompileOptions& aCompileOptions,
|
||||
const nsAString& aScript);
|
||||
|
||||
// Decode a script contained in a buffer, and execute it.
|
||||
MOZ_MUST_USE nsresult DecodeAndExec(JS::CompileOptions& aCompileOptions,
|
||||
mozilla::Vector<uint8_t>& aBytecodeBuf,
|
||||
size_t aBytecodeIndex);
|
||||
|
||||
// After getting a notification that an off-thread decoding terminated, this
|
||||
// function will get the result of the decoder by moving it to the main
|
||||
// thread before starting the execution of the script.
|
||||
MOZ_MUST_USE nsresult DecodeJoinAndExec(void **aOffThreadToken);
|
||||
|
||||
// Similar to JoinAndExec, except that in addition to fecthing the source,
|
||||
// we register the fact that we plan to encode its bytecode later.
|
||||
MOZ_MUST_USE nsresult JoinEncodeAndExec(void **aOffThreadToken,
|
||||
mozilla::Vector<uint8_t>& aBytecodeBuf,
|
||||
JS::MutableHandle<JSScript*> aScript);
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS EvaluateOptions {
|
||||
bool coerceToString;
|
||||
JS::AutoObjectVector scopeChain;
|
||||
|
||||
@@ -79,9 +79,6 @@ public:
|
||||
StructuredCloneData&
|
||||
operator=(const StructuredCloneData& aOther) = delete;
|
||||
|
||||
StructuredCloneData&
|
||||
operator=(StructuredCloneData&& aOther) = default;
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
|
||||
{
|
||||
return mBlobImplArray;
|
||||
|
||||
@@ -62,8 +62,6 @@ using JS::SourceBufferHolder;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static LazyLogModule gCspPRLog("CSP");
|
||||
|
||||
void
|
||||
ImplCycleCollectionUnlink(ScriptLoadRequestList& aField);
|
||||
|
||||
@@ -2343,17 +2341,21 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
|
||||
FinishDynamicImport(cx, request, rv);
|
||||
}
|
||||
} else {
|
||||
// Update our current script.
|
||||
AutoCurrentScriptUpdater scriptUpdater(this, aRequest->Element());
|
||||
|
||||
JS::CompileOptions options(cx);
|
||||
rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsAutoString inlineData;
|
||||
SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
|
||||
rv = nsJSUtils::EvaluateString(cx, srcBuf, global, options,
|
||||
aRequest->OffThreadTokenPtr());
|
||||
{
|
||||
nsJSUtils::ExecutionContext exec(cx, global);
|
||||
if (aRequest->mOffThreadToken) {
|
||||
JS::Rooted<JSScript*> script(cx);
|
||||
rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
|
||||
} else {
|
||||
nsAutoString inlineData;
|
||||
SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
|
||||
rv = exec.CompileAndExec(options, srcBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,6 +687,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
||||
if (!NameFunctions(cx, pn))
|
||||
return false;
|
||||
|
||||
// XDR the newly delazified function.
|
||||
if (script->scriptSource()->hasEncoder() &&
|
||||
!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8854,7 +8854,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
||||
ParseContext* outerpc = pc;
|
||||
|
||||
// If we are off the main thread, the generator meta-objects have
|
||||
// already been created by js::StartOffThreadParseScript, so cx will not
|
||||
// already been created by js::StartOffThreadParseTask, so cx will not
|
||||
// be necessary.
|
||||
RootedObject proto(context);
|
||||
JSContext* cx = context->maybeJSContext();
|
||||
|
||||
@@ -4202,6 +4202,31 @@ JS::CancelOffThreadModule(JSContext* cx, void* token)
|
||||
HelperThreadState().cancelParseTask(cx, 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_PUBLIC_API(JSScript*)
|
||||
JS::FinishOffThreadScriptDecoder(JSContext* cx, void* token)
|
||||
{
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
|
||||
return HelperThreadState().finishScriptDecodeTask(cx, token);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::CancelOffThreadScriptDecoder(JSContext* cx, void* token)
|
||||
{
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
|
||||
HelperThreadState().cancelParseTask(cx, ParseTaskKind::ScriptDecode, token);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
|
||||
const JS::CompileOptions& options, MutableHandleScript script)
|
||||
@@ -7101,6 +7126,26 @@ JS::DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer,
|
||||
return decoder.resultCode();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::StartIncrementalEncoding(JSContext* cx, TranscodeBuffer& buffer, JS::HandleScript script)
|
||||
{
|
||||
if (!script)
|
||||
return false;
|
||||
if (!script->scriptSource()->xdrEncodeTopLevel(cx, buffer, script))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::FinishIncrementalEncoding(JSContext* cx, JS::HandleScript script)
|
||||
{
|
||||
if (!script)
|
||||
return false;
|
||||
if (!script->scriptSource()->xdrFinalizeEncoder())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetBuildIdOp(JSContext* cx, JS::BuildIdOp buildIdOp)
|
||||
{
|
||||
|
||||
+35
-2
@@ -3748,7 +3748,7 @@ namespace JS {
|
||||
* addrefs/copies/tracing/etc.
|
||||
*
|
||||
* Furthermore, in some cases compile options are propagated from one entity to
|
||||
* another (e.g. from a scriipt to a function defined in that script). This
|
||||
* another (e.g. from a script to a function defined in that script). This
|
||||
* involves copying over some, but not all, of the options.
|
||||
*
|
||||
* So, we have a class hierarchy that reflects these four use cases:
|
||||
@@ -4186,6 +4186,17 @@ FinishOffThreadModule(JSContext* cx, void* token);
|
||||
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(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.
|
||||
* envChain must contain objects in the current compartment of cx. The actual
|
||||
@@ -6168,7 +6179,10 @@ enum TranscodeResult
|
||||
TranscodeResult_Failure_AsmJSNotSupported = TranscodeResult_Failure | 0x3,
|
||||
TranscodeResult_Failure_BadDecode = TranscodeResult_Failure | 0x4,
|
||||
|
||||
// A error, the JSContext has a pending exception.
|
||||
TranscodeResult_Failure_WrongCompileOption = TranscodeResult_Failure | 0x5,
|
||||
TranscodeResult_Failure_NotInterpretedFun = TranscodeResult_Failure | 0x6,
|
||||
|
||||
// There is a pending exception on the context.
|
||||
TranscodeResult_Throw = 0x200
|
||||
};
|
||||
|
||||
@@ -6186,6 +6200,25 @@ 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
|
||||
// main thread in a non-interruptible way.
|
||||
//
|
||||
// The |script| argument of |StartIncrementalEncoding| and
|
||||
// |FinishIncrementalEncoding| should be the top-level script returned either as
|
||||
// an out-param of any of the |Compile| functions, or the result of
|
||||
// |FinishOffThreadScript|.
|
||||
//
|
||||
// The |buffer| argument should not be used before until
|
||||
// |FinishIncrementalEncoding| is called on the same script, and returns
|
||||
// successfully. If any of these functions failed, the |buffer| content is
|
||||
// undefined.
|
||||
extern JS_PUBLIC_API(bool)
|
||||
StartIncrementalEncoding(JSContext* cx, TranscodeBuffer& buffer, JS::HandleScript script);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
FinishIncrementalEncoding(JSContext* cx, JS::HandleScript script);
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
|
||||
+2
-2
@@ -538,7 +538,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
|
||||
uint32_t length = lengthAndEncoding >> 1;
|
||||
bool latin1 = lengthAndEncoding & 0x1;
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
JSAtom* atom;
|
||||
if (latin1) {
|
||||
const Latin1Char* chars = nullptr;
|
||||
@@ -567,7 +567,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
|
||||
* most allocations here will be bigger than tempLifoAlloc's default
|
||||
* chunk size.
|
||||
*/
|
||||
chars = cx->runtime()->pod_malloc<char16_t>(length);
|
||||
chars = cx->pod_malloc<char16_t>(length);
|
||||
if (!chars)
|
||||
return false;
|
||||
}
|
||||
|
||||
+17
-15
@@ -530,7 +530,7 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript, MutableHandleFunction objp)
|
||||
HandleScriptSource sourceObject, MutableHandleFunction objp)
|
||||
{
|
||||
enum FirstWordFlag {
|
||||
HasAtom = 0x1,
|
||||
@@ -544,21 +544,15 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
uint32_t firstword = 0; /* bitmask of FirstWordFlag */
|
||||
uint32_t flagsword = 0; /* word for argument count and fun->flags */
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
RootedFunction fun(cx);
|
||||
RootedScript script(cx);
|
||||
Rooted<LazyScript*> lazy(cx);
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
fun = objp;
|
||||
if (!fun->isInterpreted()) {
|
||||
JSAutoByteString funNameBytes;
|
||||
if (const char* name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
|
||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_NOT_SCRIPTED_FUNCTION, name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!fun->isInterpreted())
|
||||
return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
|
||||
|
||||
if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
|
||||
firstword |= HasAtom;
|
||||
@@ -590,6 +584,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
fun->environment() == nullptr);
|
||||
}
|
||||
|
||||
// Everything added below can substituted by the non-lazy-script version of
|
||||
// this function later.
|
||||
js::AutoXDRTree funTree(xdr, xdr->getTreeKey(fun));
|
||||
|
||||
if (!xdr->codeUint32(&firstword))
|
||||
return false;
|
||||
|
||||
@@ -601,7 +599,11 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
if (mode == XDR_DECODE) {
|
||||
RootedObject proto(cx);
|
||||
if (firstword & HasStarGeneratorProto) {
|
||||
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
|
||||
// If we are off the main thread, the generator meta-objects have
|
||||
// already been created by js::StartOffThreadParseTask, so
|
||||
// JSContext* will not be necessary.
|
||||
JSContext* context = cx->maybeJSContext();
|
||||
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(context, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
@@ -618,10 +620,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
}
|
||||
|
||||
if (firstword & IsLazy) {
|
||||
if (!XDRLazyScript(xdr, enclosingScope, enclosingScript, fun, &lazy))
|
||||
if (!XDRLazyScript(xdr, enclosingScope, sourceObject, fun, &lazy))
|
||||
return false;
|
||||
} else {
|
||||
if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script))
|
||||
if (!XDRScript(xdr, enclosingScope, sourceObject, fun, &script))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -650,10 +652,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
}
|
||||
|
||||
template bool
|
||||
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, MutableHandleFunction);
|
||||
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource, MutableHandleFunction);
|
||||
|
||||
template bool
|
||||
js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleScope, HandleScript, MutableHandleFunction);
|
||||
js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource, MutableHandleFunction);
|
||||
|
||||
/* ES6 (04-25-16) 19.2.3.6 Function.prototype [ @@hasInstance ] */
|
||||
bool
|
||||
|
||||
+1
-1
@@ -879,7 +879,7 @@ JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource);
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript, MutableHandleFunction objp);
|
||||
HandleScriptSource sourceObject, MutableHandleFunction objp);
|
||||
|
||||
/*
|
||||
* Report an error that call.thisv is not compatible with the specified class,
|
||||
|
||||
+4
-5
@@ -1137,7 +1137,7 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
|
||||
}
|
||||
|
||||
static bool
|
||||
GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
|
||||
GetScriptArrayObjectElements(ExclusiveContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
MOZ_ASSERT(!obj->isSingleton());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
@@ -1155,7 +1155,7 @@ GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVe
|
||||
}
|
||||
|
||||
static bool
|
||||
GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj,
|
||||
GetScriptPlainObjectProperties(ExclusiveContext* cx, HandleObject obj,
|
||||
MutableHandle<IdValueVector> properties)
|
||||
{
|
||||
if (obj->is<PlainObject>()) {
|
||||
@@ -1330,9 +1330,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
|
||||
{
|
||||
/* NB: Keep this in sync with DeepCloneObjectLiteral. */
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(),
|
||||
cx->compartment()->behaviors().getSingletonsAsTemplates());
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
assertSameCompartment(cx, obj);
|
||||
|
||||
// Distinguish between objects and array classes.
|
||||
uint32_t isArray = 0;
|
||||
|
||||
+129
-45
@@ -72,7 +72,7 @@ template<XDRMode mode>
|
||||
bool
|
||||
js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
enum ConstTag {
|
||||
SCRIPT_INT,
|
||||
@@ -195,7 +195,7 @@ template<XDRMode mode>
|
||||
static bool
|
||||
XDRLazyClosedOverBindings(XDRState<mode>* xdr, MutableHandle<LazyScript*> lazy)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
RootedAtom atom(cx);
|
||||
for (size_t i = 0; i < lazy->numClosedOverBindings(); i++) {
|
||||
uint8_t endOfScopeSentinel;
|
||||
@@ -228,7 +228,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
||||
MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript());
|
||||
MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
uint64_t packedFields;
|
||||
{
|
||||
@@ -257,7 +257,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
|
||||
RootedScriptSource sourceObject(cx, &script->scriptSourceUnwrap());
|
||||
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, sourceObject,
|
||||
packedFields, begin, end, toStringStart, lineno, column));
|
||||
|
||||
if (!lazy)
|
||||
@@ -304,8 +305,9 @@ enum XDRClassKind {
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScript enclosingScript,
|
||||
HandleFunction fun, MutableHandleScript scriptp)
|
||||
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
||||
HandleScriptSource sourceObjectArg, HandleFunction fun,
|
||||
MutableHandleScript scriptp)
|
||||
{
|
||||
/* NB: Keep this in sync with CopyScript. */
|
||||
|
||||
@@ -348,17 +350,16 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
uint32_t scriptBits = 0;
|
||||
uint32_t bodyScopeIndex = 0;
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
RootedScript script(cx);
|
||||
natoms = nsrcnotes = 0;
|
||||
nconsts = nobjects = nscopes = nregexps = ntrynotes = nscopenotes = nyieldoffsets = 0;
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
script = scriptp.get();
|
||||
MOZ_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
|
||||
MOZ_ASSERT(script->functionNonDelazifying() == fun);
|
||||
|
||||
if (!fun && script->treatAsRunOnce()) {
|
||||
if (!fun && script->treatAsRunOnce() && script->hasRunOnce()) {
|
||||
// This is a toplevel or eval script that's runOnce. We want to
|
||||
// make sure that we're not XDR-saving an object we emitted for
|
||||
// JSOP_OBJECT that then got modified. So throw if we're not
|
||||
@@ -431,7 +432,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
scriptBits |= (1 << FunctionHasThisBinding);
|
||||
if (script->functionHasExtraBodyVarScope())
|
||||
scriptBits |= (1 << FunctionHasExtraBodyVarScope);
|
||||
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
|
||||
MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
|
||||
if (!sourceObjectArg)
|
||||
scriptBits |= (1 << OwnSource);
|
||||
if (script->isGeneratorExp())
|
||||
scriptBits |= (1 << IsGeneratorExp);
|
||||
@@ -492,15 +494,31 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
if (!xdr->codeUint32(&scriptBits))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(!!(scriptBits & (1 << OwnSource)) == !sourceObjectArg);
|
||||
RootedScriptSource sourceObject(cx, sourceObjectArg);
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
JSVersion version_ = JSVersion(version);
|
||||
MOZ_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
|
||||
|
||||
CompileOptions options(cx);
|
||||
options.setVersion(version_)
|
||||
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
|
||||
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
|
||||
RootedScriptSource sourceObject(cx);
|
||||
// When loading from the bytecode cache, we get the CompileOption from
|
||||
// the document, which specify the version to use. If the version does
|
||||
// not match, then we should fail.
|
||||
mozilla::Maybe<CompileOptions> options;
|
||||
if (xdr->hasOptions()) {
|
||||
options.emplace(xdr->cx(), xdr->options());
|
||||
if (options->version != version_ ||
|
||||
options->noScriptRval != !!(scriptBits & (1 << NoScriptRval)) ||
|
||||
options->selfHostingMode != !!(scriptBits & (1 << SelfHosted)))
|
||||
{
|
||||
return xdr->fail(JS::TranscodeResult_Failure_WrongCompileOption);
|
||||
}
|
||||
} else {
|
||||
options.emplace(xdr->cx()->asJSContext());
|
||||
(*options).setVersion(version_)
|
||||
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
|
||||
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
|
||||
}
|
||||
if (scriptBits & (1 << OwnSource)) {
|
||||
ScriptSource* ss = cx->new_<ScriptSource>();
|
||||
if (!ss)
|
||||
@@ -513,22 +531,22 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
* ScriptSourceObject, and those that are (element; elementAttributeName)
|
||||
* aren't preserved by XDR. So this can be simple.
|
||||
*/
|
||||
CompileOptions options(cx);
|
||||
ss->initFromOptions(cx, options);
|
||||
ss->initFromOptions(cx, *options);
|
||||
sourceObject = ScriptSourceObject::create(cx, ss);
|
||||
if (!sourceObject ||
|
||||
!ScriptSourceObject::initFromOptions(cx, sourceObject, options))
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(enclosingScript);
|
||||
// When decoding, all the scripts and the script source object
|
||||
// are in the same compartment, so the script's source object
|
||||
// should never be a cross-compartment wrapper.
|
||||
MOZ_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
|
||||
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
|
||||
if (xdr->hasScriptSourceObjectOut()) {
|
||||
// When the ScriptSourceObjectOut is provided by ParseTask, it
|
||||
// is stored in a location which is traced by the GC.
|
||||
*xdr->scriptSourceObjectOut() = sourceObject;
|
||||
} else {
|
||||
if (!sourceObject ||
|
||||
!ScriptSourceObject::initFromOptions(cx->asJSContext(), sourceObject, *options))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script = JSScript::Create(cx, options, sourceObject, 0, 0, 0, 0);
|
||||
script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0, 0);
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
@@ -536,6 +554,10 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
// decoded may iterate the static scope chain.
|
||||
if (fun)
|
||||
fun->initScript(script);
|
||||
} else {
|
||||
// When encoding, we do not mutate any of the JSScript or LazyScript, so
|
||||
// we can safely unwrap it here.
|
||||
sourceObject = &script->scriptSourceUnwrap();
|
||||
}
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
@@ -606,7 +628,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
|
||||
|
||||
if (scriptBits & (1 << OwnSource)) {
|
||||
if (!script->scriptSource()->performXDR<mode>(xdr))
|
||||
if (!sourceObject->source()->performXDR<mode>(xdr))
|
||||
return false;
|
||||
}
|
||||
if (!xdr->codeUint32(&script->sourceStart_))
|
||||
@@ -846,7 +868,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
RootedFunction tmp(cx);
|
||||
if (mode == XDR_ENCODE)
|
||||
tmp = &(*objp)->as<JSFunction>();
|
||||
if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
|
||||
if (!XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp))
|
||||
return false;
|
||||
*objp = tmp;
|
||||
break;
|
||||
@@ -922,27 +944,28 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||
scriptp.set(script);
|
||||
|
||||
/* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
|
||||
if (!fun)
|
||||
Debugger::onNewScript(cx, script);
|
||||
if (!fun && cx->isJSContext())
|
||||
Debugger::onNewScript(cx->asJSContext(), script);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool
|
||||
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, HandleFunction,
|
||||
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource, HandleFunction,
|
||||
MutableHandleScript);
|
||||
|
||||
template bool
|
||||
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript, HandleFunction,
|
||||
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource, HandleFunction,
|
||||
MutableHandleScript);
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
||||
HandleFunction fun, MutableHandle<LazyScript*> lazy)
|
||||
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
HandleScriptSource sourceObject, HandleFunction fun,
|
||||
MutableHandle<LazyScript*> lazy)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
{
|
||||
uint32_t begin;
|
||||
@@ -979,7 +1002,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
|
||||
}
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
|
||||
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, sourceObject,
|
||||
packedFields, begin, end, toStringStart, lineno, column));
|
||||
if (!lazy)
|
||||
return false;
|
||||
@@ -1013,11 +1036,11 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
|
||||
}
|
||||
|
||||
template bool
|
||||
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript,
|
||||
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource,
|
||||
HandleFunction, MutableHandle<LazyScript*>);
|
||||
|
||||
template bool
|
||||
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript,
|
||||
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource,
|
||||
HandleFunction, MutableHandle<LazyScript*>);
|
||||
|
||||
void
|
||||
@@ -1934,6 +1957,64 @@ ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
info->numScripts++;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptSource::xdrEncodeTopLevel(ExclusiveContext* cx, JS::TranscodeBuffer& buffer,
|
||||
HandleScript script)
|
||||
{
|
||||
xdrEncoder_ = js::MakeUnique<XDRIncrementalEncoder>(cx, buffer, buffer.length());
|
||||
if (!xdrEncoder_) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(hasEncoder());
|
||||
auto failureCase = mozilla::MakeScopeExit([&] {
|
||||
xdrEncoder_.reset(nullptr);
|
||||
});
|
||||
|
||||
if (!xdrEncoder_->init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedScript s(cx, script);
|
||||
if (!xdrEncoder_->codeScript(&s))
|
||||
return false;
|
||||
|
||||
failureCase.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptSource::xdrEncodeFunction(ExclusiveContext* cx, HandleFunction fun, HandleScriptSource sourceObject)
|
||||
{
|
||||
MOZ_ASSERT(sourceObject->source() == this);
|
||||
MOZ_ASSERT(hasEncoder());
|
||||
auto failureCase = mozilla::MakeScopeExit([&] {
|
||||
xdrEncoder_.reset(nullptr);
|
||||
});
|
||||
|
||||
RootedFunction f(cx, fun);
|
||||
if (!xdrEncoder_->codeFunction(&f, sourceObject))
|
||||
return false;
|
||||
|
||||
failureCase.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptSource::xdrFinalizeEncoder()
|
||||
{
|
||||
MOZ_ASSERT(hasEncoder());
|
||||
auto cleanup = mozilla::MakeScopeExit([&] {
|
||||
xdrEncoder_.reset(nullptr);
|
||||
});
|
||||
|
||||
if (!xdrEncoder_->linearize())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
ScriptSource::performXDR(XDRState<mode>* xdr)
|
||||
@@ -2073,7 +2154,10 @@ ScriptSource::performXDR(XDRState<mode>* xdr)
|
||||
const char* fn = filename();
|
||||
if (!xdr->codeCString(&fn))
|
||||
return false;
|
||||
if (mode == XDR_DECODE && !setFilename(xdr->cx(), fn))
|
||||
// Note: If the decoder has an option, then the filename is defined by
|
||||
// the CompileOption from the document.
|
||||
MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
|
||||
if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4135,7 +4219,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
||||
/* static */ LazyScript*
|
||||
LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
||||
HandleScript script, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript,
|
||||
HandleScriptSource sourceObject,
|
||||
uint64_t packedFields, uint32_t begin, uint32_t end,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column)
|
||||
{
|
||||
@@ -4166,11 +4250,11 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
||||
// values should only be non-null if we have a non-lazy enclosing script.
|
||||
// AddLazyFunctionsForCompartment relies on the source object being null
|
||||
// if we're nested inside another lazy function.
|
||||
MOZ_ASSERT(!!enclosingScript == !!enclosingScope);
|
||||
MOZ_ASSERT(!!sourceObject == !!enclosingScope);
|
||||
MOZ_ASSERT(!res->sourceObject());
|
||||
MOZ_ASSERT(!res->enclosingScope());
|
||||
if (enclosingScript)
|
||||
res->setEnclosingScopeAndSource(enclosingScope, &enclosingScript->scriptSourceUnwrap());
|
||||
if (sourceObject)
|
||||
res->setEnclosingScopeAndSource(enclosingScope, sourceObject);
|
||||
|
||||
MOZ_ASSERT(!res->hasScript());
|
||||
if (script)
|
||||
|
||||
+36
-7
@@ -431,6 +431,11 @@ class ScriptSource
|
||||
// memory management.
|
||||
const char* introductionType_;
|
||||
|
||||
// The bytecode cache encoder is used to encode only the content of function
|
||||
// which are delazified. If this value is not nullptr, then each delazified
|
||||
// function should be recorded before their first execution.
|
||||
UniquePtr<XDRIncrementalEncoder> xdrEncoder_;
|
||||
|
||||
// True if we can call JSRuntime::sourceHook to load the source on
|
||||
// demand. If sourceRetrievable_ and hasSourceData() are false, it is not
|
||||
// possible to get source at all.
|
||||
@@ -452,6 +457,7 @@ class ScriptSource
|
||||
parameterListEnd_(0),
|
||||
introducerFilename_(nullptr),
|
||||
introductionType_(nullptr),
|
||||
xdrEncoder_(nullptr),
|
||||
sourceRetrievable_(false),
|
||||
hasIntroductionOffset_(false)
|
||||
{
|
||||
@@ -574,6 +580,29 @@ class ScriptSource
|
||||
introductionOffset_ = offset;
|
||||
hasIntroductionOffset_ = true;
|
||||
}
|
||||
|
||||
// Return wether an XDR encoder is present or not.
|
||||
bool hasEncoder() const { return bool(xdrEncoder_); }
|
||||
|
||||
// Create a new XDR encoder, and encode the top-level JSScript. The result
|
||||
// of the encoding would be available in the |buffer| provided as argument,
|
||||
// as soon as |xdrFinalize| is called and all xdr function calls returned
|
||||
// successfully.
|
||||
bool xdrEncodeTopLevel(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, HandleScript script);
|
||||
|
||||
// Encode a delazified JSFunction. In case of errors, the XDR encoder is
|
||||
// freed and the |buffer| provided as argument to |xdrEncodeTopLevel| is
|
||||
// considered undefined.
|
||||
//
|
||||
// The |sourceObject| argument is the object holding the current
|
||||
// ScriptSource.
|
||||
bool xdrEncodeFunction(ExclusiveContext* cx, HandleFunction fun,
|
||||
HandleScriptSource sourceObject);
|
||||
|
||||
// Linearize the encoded content in the |buffer| provided as argument to
|
||||
// |xdrEncodeTopLevel|, and free the XDR encoder. In case of errors, the
|
||||
// |buffer| is considered undefined.
|
||||
bool xdrFinalizeEncoder();
|
||||
};
|
||||
|
||||
class ScriptSourceHolder
|
||||
@@ -695,12 +724,12 @@ AsyncKindFromBits(unsigned val) {
|
||||
*/
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
||||
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
|
||||
HandleFunction fun, MutableHandleScript scriptp);
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
||||
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
|
||||
HandleFunction fun, MutableHandle<LazyScript*> lazy);
|
||||
|
||||
/*
|
||||
@@ -808,7 +837,7 @@ class JSScript : public js::gc::TenuredCell
|
||||
friend
|
||||
bool
|
||||
js::XDRScript(js::XDRState<mode>* xdr, js::HandleScope enclosingScope,
|
||||
js::HandleScript enclosingScript, js::HandleFunction fun,
|
||||
js::HandleScriptSource sourceObject, js::HandleFunction fun,
|
||||
js::MutableHandleScript scriptp);
|
||||
|
||||
friend bool
|
||||
@@ -1211,11 +1240,11 @@ class JSScript : public js::gc::TenuredCell
|
||||
return funLength_;
|
||||
}
|
||||
|
||||
size_t sourceStart() const {
|
||||
uint32_t sourceStart() const {
|
||||
return sourceStart_;
|
||||
}
|
||||
|
||||
size_t sourceEnd() const {
|
||||
uint32_t sourceEnd() const {
|
||||
return sourceEnd_;
|
||||
}
|
||||
|
||||
@@ -2076,11 +2105,11 @@ class LazyScript : public gc::TenuredCell
|
||||
// The "script" argument to this function can be null. If it's non-null,
|
||||
// then this LazyScript should be associated with the given JSScript.
|
||||
//
|
||||
// The enclosingScript and enclosingScope arguments may be null if the
|
||||
// The sourceObject and enclosingScope arguments may be null if the
|
||||
// enclosing function is also lazy.
|
||||
static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
|
||||
HandleScript script, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript,
|
||||
HandleScriptSource sourceObject,
|
||||
uint64_t packedData, uint32_t begin, uint32_t end,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column);
|
||||
|
||||
|
||||
+29
-2
@@ -1605,6 +1605,7 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||
bool catchTermination = false;
|
||||
bool loadBytecode = false;
|
||||
bool saveBytecode = false;
|
||||
bool saveIncrementalBytecode = false;
|
||||
bool assertEqBytecode = false;
|
||||
RootedObject callerGlobal(cx, cx->global());
|
||||
|
||||
@@ -1668,6 +1669,11 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||
if (!v.isUndefined())
|
||||
saveBytecode = ToBoolean(v);
|
||||
|
||||
if (!JS_GetProperty(cx, opts, "saveIncrementalBytecode", &v))
|
||||
return false;
|
||||
if (!v.isUndefined())
|
||||
saveIncrementalBytecode = ToBoolean(v);
|
||||
|
||||
if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v))
|
||||
return false;
|
||||
if (!v.isUndefined())
|
||||
@@ -1675,12 +1681,17 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
// We cannot load or save the bytecode if we have no object where the
|
||||
// bytecode cache is stored.
|
||||
if (loadBytecode || saveBytecode) {
|
||||
if (loadBytecode || saveBytecode || saveIncrementalBytecode) {
|
||||
if (!cacheEntry) {
|
||||
JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
|
||||
"evaluate");
|
||||
return false;
|
||||
}
|
||||
if (saveIncrementalBytecode && saveBytecode) {
|
||||
JS_ReportErrorASCII(cx, "saveIncrementalBytecode and saveBytecode cannot be used"
|
||||
" at the same time.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1758,6 +1769,15 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||
if (!script->scriptSource()->setSourceMapURL(cx, smurl))
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we want to save the bytecode incrementally, then we should
|
||||
// register ahead the fact that every JSFunction which is being
|
||||
// delazified should be encoded at the end of the delazification.
|
||||
if (saveIncrementalBytecode) {
|
||||
if (!StartIncrementalEncoding(cx, saveBuffer, script))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_ExecuteScript(cx, script, args.rval())) {
|
||||
if (catchTermination && !JS_IsExceptionPending(cx)) {
|
||||
JSAutoCompartment ac1(cx, callerGlobal);
|
||||
@@ -1770,14 +1790,21 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encode the bytecode after the execution of the script.
|
||||
if (saveBytecode) {
|
||||
JS::TranscodeResult rv = JS::EncodeScript(cx, saveBuffer, script);
|
||||
if (!ConvertTranscodeResultToJSException(cx, rv))
|
||||
return false;
|
||||
}
|
||||
// Serialize the encoded bytecode, recorded before the execution, into a
|
||||
// buffer which can be deserialized linearly.
|
||||
if (saveIncrementalBytecode) {
|
||||
if (!FinishIncrementalEncoding(cx, script))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (saveBytecode) {
|
||||
if (saveBytecode || saveIncrementalBytecode) {
|
||||
// If we are both loading and saving, we assert that we are going to
|
||||
// replace the current bytecode by the same stream of bytes.
|
||||
if (loadBytecode && assertEqBytecode) {
|
||||
|
||||
+76
-36
@@ -304,6 +304,18 @@ ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusi
|
||||
{
|
||||
}
|
||||
|
||||
ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: kind(kind), cx(cx), options(initCx), buffer(&buffer), cursor(cursor),
|
||||
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
exclusiveContextGlobal(exclusiveContextGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
script(nullptr), sourceObject(nullptr),
|
||||
errors(cx), overRecursed(false), outOfMemory(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
|
||||
{
|
||||
@@ -389,6 +401,29 @@ ModuleParseTask::parse()
|
||||
script = module->script();
|
||||
}
|
||||
|
||||
ScriptDecodeTask::ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::ScriptDecode, cx, exclusiveContextGlobal, initCx,
|
||||
buffer, cursor, callback, callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ScriptDecodeTask::parse()
|
||||
{
|
||||
RootedScript resultScript(cx);
|
||||
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
|
||||
*buffer, cursor);
|
||||
decoder.codeScript(&resultScript);
|
||||
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
|
||||
if (decoder.resultCode() == JS::TranscodeResult_Ok) {
|
||||
script = resultScript.get();
|
||||
} else {
|
||||
sourceObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
js::CancelOffThreadParses(JSRuntime* rt)
|
||||
{
|
||||
@@ -557,10 +592,10 @@ QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TaskFunctor>
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
ParseTaskKind kind, TaskFunctor& taskFunctor)
|
||||
{
|
||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||
// which could require barriers on the atoms compartment.
|
||||
@@ -568,7 +603,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
||||
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
||||
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
||||
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, kind, nogc);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
@@ -578,9 +613,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
||||
if (!helpercx)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ParseTask> task(
|
||||
cx->new_<ScriptParseTask>(helpercx.get(), global, cx, chars, length,
|
||||
callback, callbackData));
|
||||
ScopedJSDeletePtr<ParseTask> task(taskFunctor(helpercx.get(), global));
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
@@ -594,41 +627,40 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptParseTask* {
|
||||
return cx->new_<ScriptParseTask>(helpercx, global, cx, chars, length,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::Script, functor);
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||
// which could require barriers on the atoms compartment.
|
||||
gc::AutoSuppressGC nogc(cx);
|
||||
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
||||
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
||||
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ModuleParseTask* {
|
||||
return cx->new_<ModuleParseTask>(helpercx, global, cx, chars, length,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
|
||||
}
|
||||
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Module, nogc);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ExclusiveContext> helpercx(
|
||||
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
|
||||
ExclusiveContext::Context_Exclusive, cx->options()));
|
||||
if (!helpercx)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ParseTask> task(
|
||||
cx->new_<ModuleParseTask>(helpercx.get(), global, cx, chars, length,
|
||||
callback, callbackData));
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
helpercx.forget();
|
||||
|
||||
if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
|
||||
return false;
|
||||
|
||||
task.forget();
|
||||
|
||||
return true;
|
||||
bool
|
||||
js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptDecodeTask* {
|
||||
return cx->new_<ScriptDecodeTask>(helpercx, global, cx, buffer, cursor,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1280,6 +1312,14 @@ GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
|
||||
return script;
|
||||
}
|
||||
|
||||
JSScript*
|
||||
GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, void* token)
|
||||
{
|
||||
JSScript* script = finishParseTask(cx, ParseTaskKind::ScriptDecode, token);
|
||||
MOZ_ASSERT_IF(script, script->isGlobalCode());
|
||||
return script;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,8 @@ namespace wasm {
|
||||
enum class ParseTaskKind
|
||||
{
|
||||
Script,
|
||||
Module
|
||||
Module,
|
||||
ScriptDecode
|
||||
};
|
||||
|
||||
// Per-process state for off thread work items.
|
||||
@@ -244,6 +245,7 @@ class GlobalHelperThreadState
|
||||
|
||||
public:
|
||||
JSScript* finishScriptParseTask(JSContext* cx, void* token);
|
||||
JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
|
||||
JSObject* finishModuleParseTask(JSContext* cx, void* token);
|
||||
bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
|
||||
SourceCompressionTask* compressionTaskForSource(ScriptSource* ss, const AutoLockHelperThreadState& lock);
|
||||
@@ -482,6 +484,11 @@ 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,
|
||||
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.
|
||||
@@ -534,8 +541,21 @@ struct ParseTask
|
||||
ParseTaskKind kind;
|
||||
ExclusiveContext* cx;
|
||||
OwningCompileOptions options;
|
||||
const char16_t* chars;
|
||||
size_t length;
|
||||
// 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;
|
||||
};
|
||||
};
|
||||
LifoAlloc alloc;
|
||||
|
||||
// Rooted pointer to the global object used by 'cx'.
|
||||
@@ -562,6 +582,9 @@ struct ParseTask
|
||||
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
|
||||
|
||||
void activate(JSRuntime* rt);
|
||||
@@ -593,6 +616,14 @@ struct ModuleParseTask : public ParseTask
|
||||
void parse() override;
|
||||
};
|
||||
|
||||
struct ScriptDecodeTask : public ParseTask
|
||||
{
|
||||
ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse() 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
|
||||
|
||||
@@ -1552,7 +1552,7 @@ js::XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp
|
||||
if (mode == XDR_DECODE) {
|
||||
RegExpFlag flags = RegExpFlag(flagsword);
|
||||
RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags, nullptr,
|
||||
xdr->cx()->tempLifoAlloc());
|
||||
xdr->lifoAlloc());
|
||||
if (!reobj)
|
||||
return false;
|
||||
|
||||
|
||||
+8
-8
@@ -202,7 +202,7 @@ NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0)
|
||||
static bool
|
||||
XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
RootedAtom atom(cx, bindingName->name());
|
||||
bool hasAtom = !!atom;
|
||||
@@ -220,7 +220,7 @@ XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
||||
static bool
|
||||
XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
uint8_t u8;
|
||||
if (!xdr->codeUint8(&u8))
|
||||
@@ -254,7 +254,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
||||
{
|
||||
MOZ_ASSERT(!data);
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
uint32_t length;
|
||||
if (mode == XDR_ENCODE)
|
||||
@@ -542,7 +542,7 @@ template <XDRMode mode>
|
||||
LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
|
||||
@@ -725,7 +725,7 @@ template <XDRMode mode>
|
||||
FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
|
||||
return false;
|
||||
@@ -861,7 +861,7 @@ template <XDRMode mode>
|
||||
VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
|
||||
return false;
|
||||
@@ -962,7 +962,7 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
||||
{
|
||||
MOZ_ASSERT((mode == XDR_DECODE) == !scope);
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
|
||||
return false;
|
||||
@@ -1084,7 +1084,7 @@ template <XDRMode mode>
|
||||
EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
|
||||
{
|
||||
|
||||
+213
-15
@@ -10,6 +10,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsscript.h"
|
||||
|
||||
#include "vm/Debugger.h"
|
||||
@@ -18,11 +19,17 @@
|
||||
using namespace js;
|
||||
using mozilla::PodEqual;
|
||||
|
||||
template<XDRMode mode>
|
||||
LifoAlloc&
|
||||
XDRState<mode>::lifoAlloc() const {
|
||||
return buf.cx()->asJSContext()->tempLifoAlloc();
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
void
|
||||
XDRState<mode>::postProcessContextErrors(JSContext* cx)
|
||||
XDRState<mode>::postProcessContextErrors(ExclusiveContext* cx)
|
||||
{
|
||||
if (cx->isExceptionPending()) {
|
||||
if (cx->isJSContext() && cx->asJSContext()->isExceptionPending()) {
|
||||
MOZ_ASSERT(resultCode_ == JS::TranscodeResult_Ok);
|
||||
resultCode_ = JS::TranscodeResult_Throw;
|
||||
}
|
||||
@@ -40,7 +47,7 @@ XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
|
||||
return true;
|
||||
uint8_t* ptr = buf.write(nchars);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
|
||||
mozilla::PodCopy(ptr, chars, nchars);
|
||||
return true;
|
||||
@@ -56,7 +63,7 @@ XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
|
||||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(nbytes);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(nbytes);
|
||||
@@ -70,11 +77,8 @@ static bool
|
||||
VersionCheck(XDRState<mode>* xdr)
|
||||
{
|
||||
JS::BuildIdCharVector buildId;
|
||||
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId)) {
|
||||
JS_ReportErrorNumberASCII(xdr->cx(), GetErrorMessage, nullptr,
|
||||
JSMSG_BUILD_ID_NOT_AVAILABLE);
|
||||
return false;
|
||||
}
|
||||
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId))
|
||||
return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
|
||||
MOZ_ASSERT(!buildId.empty());
|
||||
|
||||
uint32_t buildIdLength;
|
||||
@@ -97,7 +101,7 @@ VersionCheck(XDRState<mode>* xdr)
|
||||
// buildId.
|
||||
if (!decodedBuildId.resize(buildIdLength)) {
|
||||
ReportOutOfMemory(xdr->cx());
|
||||
return false;
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
|
||||
@@ -113,20 +117,26 @@ VersionCheck(XDRState<mode>* xdr)
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRState<mode>::codeFunction(MutableHandleFunction funp)
|
||||
XDRState<mode>::codeFunction(MutableHandleFunction funp, HandleScriptSource sourceObject)
|
||||
{
|
||||
if (mode == XDR_DECODE)
|
||||
RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
|
||||
if (mode == XDR_DECODE) {
|
||||
MOZ_ASSERT(!sourceObject);
|
||||
funp.set(nullptr);
|
||||
else
|
||||
} else if (getTreeKey(funp) != AutoXDRTree::noKey) {
|
||||
MOZ_ASSERT(sourceObject);
|
||||
scope = funp->nonLazyScript()->enclosingScope();
|
||||
} else {
|
||||
MOZ_ASSERT(!sourceObject);
|
||||
MOZ_ASSERT(funp->nonLazyScript()->enclosingScope()->is<GlobalScope>());
|
||||
}
|
||||
|
||||
if (!VersionCheck(this)) {
|
||||
postProcessContextErrors(cx());
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
|
||||
if (!XDRInterpretedFunction(this, scope, nullptr, funp)) {
|
||||
if (!XDRInterpretedFunction(this, scope, sourceObject, funp)) {
|
||||
postProcessContextErrors(cx());
|
||||
funp.set(nullptr);
|
||||
return false;
|
||||
@@ -144,6 +154,8 @@ XDRState<mode>::codeScript(MutableHandleScript scriptp)
|
||||
else
|
||||
MOZ_ASSERT(!scriptp->enclosingScope());
|
||||
|
||||
AutoXDRTree scriptTree(this, getTopLevelTreeKey());
|
||||
|
||||
if (!VersionCheck(this)) {
|
||||
postProcessContextErrors(cx());
|
||||
return false;
|
||||
@@ -167,3 +179,189 @@ XDRState<mode>::codeConstValue(MutableHandleValue vp)
|
||||
|
||||
template class js::XDRState<XDR_ENCODE>;
|
||||
template class js::XDRState<XDR_DECODE>;
|
||||
|
||||
AutoXDRTree::AutoXDRTree(XDRCoderBase* xdr, AutoXDRTree::Key key)
|
||||
: key_(key),
|
||||
parent_(this),
|
||||
xdr_(xdr)
|
||||
{
|
||||
if (key_ != AutoXDRTree::noKey)
|
||||
xdr->createOrReplaceSubTree(this);
|
||||
}
|
||||
|
||||
AutoXDRTree::~AutoXDRTree()
|
||||
{
|
||||
if (key_ != AutoXDRTree::noKey)
|
||||
xdr_->endSubTree();
|
||||
}
|
||||
|
||||
constexpr AutoXDRTree::Key AutoXDRTree::noKey;
|
||||
constexpr AutoXDRTree::Key AutoXDRTree::noSubTree;
|
||||
constexpr AutoXDRTree::Key AutoXDRTree::topLevel;
|
||||
|
||||
AutoXDRTree::Key
|
||||
XDRIncrementalEncoder::getTopLevelTreeKey() const
|
||||
{
|
||||
return AutoXDRTree::topLevel;
|
||||
}
|
||||
|
||||
AutoXDRTree::Key
|
||||
XDRIncrementalEncoder::getTreeKey(JSFunction* fun) const
|
||||
{
|
||||
if (fun->isInterpretedLazy()) {
|
||||
static_assert(sizeof(fun->lazyScript()->begin()) == 4 ||
|
||||
sizeof(fun->lazyScript()->end()) == 4,
|
||||
"AutoXDRTree key requires LazyScripts positions to be uint32");
|
||||
return uint64_t(fun->lazyScript()->begin()) << 32 | fun->lazyScript()->end();
|
||||
}
|
||||
|
||||
if (fun->isInterpreted()) {
|
||||
static_assert(sizeof(fun->nonLazyScript()->sourceStart()) == 4 ||
|
||||
sizeof(fun->nonLazyScript()->sourceEnd()) == 4,
|
||||
"AutoXDRTree key requires JSScripts positions to be uint32");
|
||||
return uint64_t(fun->nonLazyScript()->sourceStart()) << 32 | fun->nonLazyScript()->sourceEnd();
|
||||
}
|
||||
|
||||
return AutoXDRTree::noKey;
|
||||
}
|
||||
|
||||
bool
|
||||
XDRIncrementalEncoder::init()
|
||||
{
|
||||
if (!tree_.init())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
XDRIncrementalEncoder::createOrReplaceSubTree(AutoXDRTree* child)
|
||||
{
|
||||
AutoXDRTree* parent = scope_;
|
||||
child->parent_ = parent;
|
||||
scope_ = child;
|
||||
if (oom_)
|
||||
return;
|
||||
|
||||
size_t cursor = buf.cursor();
|
||||
|
||||
// End the parent slice here, set the key to the child.
|
||||
if (parent) {
|
||||
Slice& last = node_->back();
|
||||
last.sliceLength = cursor - last.sliceBegin;
|
||||
last.child = child->key_;
|
||||
MOZ_ASSERT_IF(uint32_t(parent->key_) != 0,
|
||||
uint32_t(parent->key_ >> 32) <= uint32_t(child->key_ >> 32) &&
|
||||
uint32_t(child->key_) <= uint32_t(parent->key_));
|
||||
}
|
||||
|
||||
// Create or replace the part with what is going to be encoded next.
|
||||
SlicesTree::AddPtr p = tree_.lookupForAdd(child->key_);
|
||||
SlicesNode tmp;
|
||||
if (!p) {
|
||||
// Create a new sub-tree node.
|
||||
if (!tree_.add(p, child->key_, mozilla::Move(tmp))) {
|
||||
oom_ = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Replace an exisiting sub-tree.
|
||||
p->value() = mozilla::Move(tmp);
|
||||
}
|
||||
node_ = &p->value();
|
||||
|
||||
// Add content to the root of the new sub-tree,
|
||||
// i-e an empty slice with no children.
|
||||
if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree }))
|
||||
MOZ_CRASH("SlicesNode have a reserved space of 1.");
|
||||
}
|
||||
|
||||
void
|
||||
XDRIncrementalEncoder::endSubTree()
|
||||
{
|
||||
AutoXDRTree* child = scope_;
|
||||
AutoXDRTree* parent = child->parent_;
|
||||
scope_ = parent;
|
||||
if (oom_)
|
||||
return;
|
||||
|
||||
size_t cursor = buf.cursor();
|
||||
|
||||
// End the child sub-tree.
|
||||
Slice& last = node_->back();
|
||||
last.sliceLength = cursor - last.sliceBegin;
|
||||
MOZ_ASSERT(last.child == AutoXDRTree::noSubTree);
|
||||
|
||||
// Stop at the top-level.
|
||||
if (!parent) {
|
||||
node_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore the parent node.
|
||||
SlicesTree::Ptr p = tree_.lookup(parent->key_);
|
||||
node_ = &p->value();
|
||||
|
||||
// Append the new slice in the parent node.
|
||||
if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree })) {
|
||||
oom_ = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
XDRIncrementalEncoder::linearize()
|
||||
{
|
||||
if (oom_) {
|
||||
ReportOutOfMemory(cx());
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
// Do not linearize while we are currently adding bytes.
|
||||
MOZ_ASSERT(scope_ == nullptr);
|
||||
|
||||
// Visit the tree parts in a depth first order, to linearize the bits.
|
||||
Vector<SlicesNode::ConstRange> depthFirst(cx());
|
||||
|
||||
SlicesTree::Ptr p = tree_.lookup(AutoXDRTree::topLevel);
|
||||
MOZ_ASSERT(p);
|
||||
|
||||
if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
|
||||
ReportOutOfMemory(cx());
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
while (!depthFirst.empty()) {
|
||||
SlicesNode::ConstRange& iter = depthFirst.back();
|
||||
Slice slice = iter.popCopyFront();
|
||||
// These fields have different meaning, but they should be correlated if
|
||||
// the tree is well formatted.
|
||||
MOZ_ASSERT_IF(slice.child == AutoXDRTree::noSubTree, iter.empty());
|
||||
if (iter.empty())
|
||||
depthFirst.popBack();
|
||||
|
||||
// Copy the bytes associated with the current slice to the transcode
|
||||
// buffer which would be serialized.
|
||||
MOZ_ASSERT(slice.sliceBegin <= slices_.length());
|
||||
MOZ_ASSERT(slice.sliceBegin + slice.sliceLength <= slices_.length());
|
||||
if (!buffer_.append(slices_.begin() + slice.sliceBegin, slice.sliceLength)) {
|
||||
ReportOutOfMemory(cx());
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
// If we are at the end, go to back to the parent script.
|
||||
if (slice.child == AutoXDRTree::noSubTree)
|
||||
continue;
|
||||
|
||||
// Visit the sub-parts before visiting the rest of the current slice.
|
||||
SlicesTree::Ptr p = tree_.lookup(slice.child);
|
||||
MOZ_ASSERT(p);
|
||||
if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
|
||||
ReportOutOfMemory(cx());
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
}
|
||||
|
||||
tree_.finish();
|
||||
slices_.clearAndFree();
|
||||
return true;
|
||||
}
|
||||
|
||||
+229
-16
@@ -16,10 +16,10 @@ namespace js {
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
XDRBuffer(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: context_(cx), buffer_(buffer), cursor_(cursor) { }
|
||||
|
||||
JSContext* cx() const {
|
||||
ExclusiveContext* cx() const {
|
||||
return context_;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class XDRBuffer {
|
||||
uint8_t* write(size_t n) {
|
||||
MOZ_ASSERT(n != 0);
|
||||
if (!buffer_.growByUninitialized(n)) {
|
||||
JS_ReportOutOfMemory(cx());
|
||||
ReportOutOfMemory(cx());
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t* ptr = &buffer_[cursor_];
|
||||
@@ -50,30 +50,108 @@ class XDRBuffer {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
size_t cursor() const {
|
||||
return cursor_;
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* const context_;
|
||||
ExclusiveContext* const context_;
|
||||
JS::TranscodeBuffer& buffer_;
|
||||
size_t cursor_;
|
||||
};
|
||||
|
||||
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
|
||||
// as LazyScript, and later replaced by them by their corresponding bytecode
|
||||
// once delazified.
|
||||
//
|
||||
// As a convenience, this is also used to identify the top-level of the content
|
||||
// encoded by an XDRIncrementalEncoder.
|
||||
//
|
||||
// Sections can be encoded any number of times in an XDRIncrementalEncoder, and
|
||||
// the latest encoded version would replace all the previous one.
|
||||
class MOZ_RAII AutoXDRTree
|
||||
{
|
||||
public:
|
||||
// For a JSFunction, a tree key is defined as being:
|
||||
// script()->begin << 32 | script()->end
|
||||
//
|
||||
// Based on the invariant that |begin <= end|, we can make special
|
||||
// keys, such as the top-level script.
|
||||
using Key = uint64_t;
|
||||
|
||||
AutoXDRTree(XDRCoderBase* xdr, Key key);
|
||||
~AutoXDRTree();
|
||||
|
||||
// Indicate the lack of a key for the current tree.
|
||||
static constexpr Key noKey = 0;
|
||||
|
||||
// Used to end the slices when there is no children.
|
||||
static constexpr Key noSubTree = Key(1) << 32;
|
||||
|
||||
// Used as the root key of the tree in the hash map.
|
||||
static constexpr Key topLevel = Key(2) << 32;
|
||||
|
||||
private:
|
||||
friend class XDRIncrementalEncoder;
|
||||
|
||||
Key key_;
|
||||
AutoXDRTree* parent_;
|
||||
XDRCoderBase* xdr_;
|
||||
};
|
||||
|
||||
class XDRCoderBase
|
||||
{
|
||||
protected:
|
||||
XDRCoderBase() {}
|
||||
|
||||
public:
|
||||
virtual AutoXDRTree::Key getTopLevelTreeKey() const { return AutoXDRTree::noKey; }
|
||||
virtual AutoXDRTree::Key getTreeKey(JSFunction* fun) const { return AutoXDRTree::noKey; }
|
||||
virtual void createOrReplaceSubTree(AutoXDRTree* child) {};
|
||||
virtual void endSubTree() {};
|
||||
};
|
||||
|
||||
/*
|
||||
* XDR serialization state. All data is encoded in little endian.
|
||||
*/
|
||||
template <XDRMode mode>
|
||||
class XDRState {
|
||||
class XDRState : public XDRCoderBase
|
||||
{
|
||||
public:
|
||||
XDRBuffer buf;
|
||||
private:
|
||||
JS::TranscodeResult resultCode_;
|
||||
|
||||
XDRState(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: buf(cx, buffer, cursor), resultCode_(JS::TranscodeResult_Ok) { }
|
||||
public:
|
||||
XDRState(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: buf(cx, buffer, cursor),
|
||||
resultCode_(JS::TranscodeResult_Ok)
|
||||
{
|
||||
}
|
||||
|
||||
JSContext* cx() const {
|
||||
virtual ~XDRState() {};
|
||||
|
||||
ExclusiveContext* cx() const {
|
||||
return buf.cx();
|
||||
}
|
||||
virtual LifoAlloc& lifoAlloc() const;
|
||||
|
||||
virtual bool hasOptions() const { return false; }
|
||||
virtual const ReadOnlyCompileOptions& options() {
|
||||
MOZ_CRASH("does not have options");
|
||||
}
|
||||
virtual bool hasScriptSourceObjectOut() const { return false; }
|
||||
virtual ScriptSourceObject** scriptSourceObjectOut() {
|
||||
MOZ_CRASH("does not have scriptSourceObjectOut.");
|
||||
}
|
||||
|
||||
// Record logical failures of XDR.
|
||||
void postProcessContextErrors(JSContext* cx);
|
||||
void postProcessContextErrors(ExclusiveContext* cx);
|
||||
JS::TranscodeResult resultCode() const {
|
||||
return resultCode_;
|
||||
}
|
||||
@@ -87,7 +165,7 @@ class XDRState {
|
||||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
*ptr = *n;
|
||||
} else {
|
||||
*n = *buf.read(sizeof(*n));
|
||||
@@ -99,7 +177,7 @@ class XDRState {
|
||||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::LittleEndian::writeUint16(ptr, *n);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||
@@ -112,7 +190,7 @@ class XDRState {
|
||||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::LittleEndian::writeUint32(ptr, *n);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||
@@ -125,7 +203,7 @@ class XDRState {
|
||||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::LittleEndian::writeUint64(ptr, *n);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||
@@ -188,7 +266,7 @@ class XDRState {
|
||||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(len);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
memcpy(ptr, bytes, len);
|
||||
} else {
|
||||
memcpy(bytes, buf.read(len), len);
|
||||
@@ -207,7 +285,7 @@ class XDRState {
|
||||
size_t n = strlen(*sp) + 1;
|
||||
uint8_t* ptr = buf.write(n);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
memcpy(ptr, *sp, n);
|
||||
} else {
|
||||
*sp = buf.readCString();
|
||||
@@ -218,7 +296,7 @@ class XDRState {
|
||||
bool codeChars(const JS::Latin1Char* chars, size_t nchars);
|
||||
bool codeChars(char16_t* chars, size_t nchars);
|
||||
|
||||
bool codeFunction(JS::MutableHandleFunction objp);
|
||||
bool codeFunction(JS::MutableHandleFunction objp, HandleScriptSource sourceObject = nullptr);
|
||||
bool codeScript(MutableHandleScript scriptp);
|
||||
bool codeConstValue(MutableHandleValue vp);
|
||||
};
|
||||
@@ -226,6 +304,141 @@ class XDRState {
|
||||
using XDREncoder = XDRState<XDR_ENCODE>;
|
||||
using XDRDecoder = XDRState<XDR_DECODE>;
|
||||
|
||||
class XDROffThreadDecoder : public XDRDecoder
|
||||
{
|
||||
const ReadOnlyCompileOptions* options_;
|
||||
ScriptSourceObject** sourceObjectOut_;
|
||||
LifoAlloc& alloc_;
|
||||
|
||||
public:
|
||||
// Note, when providing an ExclusiveContext, where isJSContext is false,
|
||||
// then the initialization of the ScriptSourceObject would remain
|
||||
// incomplete. Thus, the sourceObjectOut must be used to finish the
|
||||
// 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(ExclusiveContext* cx, LifoAlloc& alloc,
|
||||
const ReadOnlyCompileOptions* options,
|
||||
ScriptSourceObject** sourceObjectOut,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: XDRDecoder(cx, buffer, cursor),
|
||||
options_(options),
|
||||
sourceObjectOut_(sourceObjectOut),
|
||||
alloc_(alloc)
|
||||
{
|
||||
MOZ_ASSERT(options);
|
||||
MOZ_ASSERT(sourceObjectOut);
|
||||
MOZ_ASSERT(*sourceObjectOut == nullptr);
|
||||
}
|
||||
|
||||
LifoAlloc& lifoAlloc() const override {
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
bool hasOptions() const override { return true; }
|
||||
const ReadOnlyCompileOptions& options() override {
|
||||
return *options_;
|
||||
}
|
||||
bool hasScriptSourceObjectOut() const override { return true; }
|
||||
ScriptSourceObject** scriptSourceObjectOut() override {
|
||||
return sourceObjectOut_;
|
||||
}
|
||||
};
|
||||
|
||||
class XDRIncrementalEncoder : public XDREncoder
|
||||
{
|
||||
// The incremental encoder encodes the content of scripts and functions in
|
||||
// the XDRBuffer. It can be used to encode multiple times the same AutoXDRTree,
|
||||
// and uses its key to identify which part to replace.
|
||||
//
|
||||
// Internally, this encoder keeps a tree representation of the scopes. Each
|
||||
// node is composed of a vector of slices which are interleaved by child
|
||||
// nodes.
|
||||
//
|
||||
// A slice corresponds to an index and a length within the content of the
|
||||
// slices_ buffer. The index is updated when a slice is created, and the
|
||||
// length is updated when the slice is ended, either by creating a new scope
|
||||
// child, or by closing the scope and going back to the parent.
|
||||
//
|
||||
// +---+---+---+
|
||||
// begin | | | |
|
||||
// length | | | |
|
||||
// child | . | . | . |
|
||||
// +-|-+-|-+---+
|
||||
// | |
|
||||
// +---------+ +---------+
|
||||
// | |
|
||||
// v v
|
||||
// +---+---+ +---+
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// | . | . | | . |
|
||||
// +-|-+---+ +---+
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// v
|
||||
// +---+
|
||||
// | |
|
||||
// | |
|
||||
// | . |
|
||||
// +---+
|
||||
//
|
||||
//
|
||||
// The tree key is used to identify the child nodes, and to make them
|
||||
// easily replaceable.
|
||||
//
|
||||
// The tree is rooted at the |topLevel| key.
|
||||
//
|
||||
|
||||
struct Slice {
|
||||
size_t sliceBegin;
|
||||
size_t sliceLength;
|
||||
AutoXDRTree::Key child;
|
||||
};
|
||||
|
||||
using SlicesNode = Vector<Slice, 1, SystemAllocPolicy>;
|
||||
using SlicesTree = HashMap<AutoXDRTree::Key, SlicesNode, DefaultHasher<AutoXDRTree::Key>,
|
||||
SystemAllocPolicy>;
|
||||
|
||||
// Last opened XDR-tree on the stack.
|
||||
AutoXDRTree* scope_;
|
||||
// Node corresponding to the opened scope.
|
||||
SlicesNode* node_;
|
||||
// Tree of slices.
|
||||
SlicesTree tree_;
|
||||
JS::TranscodeBuffer slices_;
|
||||
JS::TranscodeBuffer& buffer_;
|
||||
bool oom_;
|
||||
|
||||
public:
|
||||
XDRIncrementalEncoder(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor)
|
||||
: XDREncoder(cx, slices_, 0),
|
||||
scope_(nullptr),
|
||||
node_(nullptr),
|
||||
buffer_(buffer),
|
||||
oom_(false)
|
||||
{
|
||||
MOZ_ASSERT(buffer.length() == cursor, "NYI");
|
||||
}
|
||||
|
||||
virtual ~XDRIncrementalEncoder() {}
|
||||
|
||||
AutoXDRTree::Key getTopLevelTreeKey() const override;
|
||||
AutoXDRTree::Key getTreeKey(JSFunction* fun) const override;
|
||||
|
||||
MOZ_MUST_USE bool init();
|
||||
|
||||
void createOrReplaceSubTree(AutoXDRTree* child) override;
|
||||
void endSubTree() override;
|
||||
|
||||
// In the current XDRBuffer, move replaceable-parts to form a linear
|
||||
// sequence of bytes.
|
||||
MOZ_MUST_USE bool linearize();
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* vm_Xdr_h */
|
||||
|
||||
@@ -568,6 +568,13 @@
|
||||
ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC, FAILURE(1027)),
|
||||
ERROR(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC, FAILURE(1028)),
|
||||
|
||||
|
||||
/* When manipulating the bytecode cache with the JS API, some transcoding
|
||||
* errors, such as a different bytecode format can cause failures of the
|
||||
* decoding process.
|
||||
*/
|
||||
ERROR(NS_ERROR_DOM_JS_DECODING_ERROR, FAILURE(1030)),
|
||||
|
||||
/* May be used to indicate when e.g. setting a property value didn't
|
||||
* actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
|
||||
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
|
||||
|
||||
Reference in New Issue
Block a user