mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-05 06:47:31 +09: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:
parent
d5d7bb5e47
commit
993476283d
22 changed files with 1224 additions and 171 deletions
|
|
@ -123,6 +123,244 @@ nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
||||||
return NS_OK;
|
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
|
nsresult
|
||||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||||
const nsAString& aScript,
|
const nsAString& aScript,
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,107 @@ public:
|
||||||
const nsAString& aBody,
|
const nsAString& aBody,
|
||||||
JSObject** aFunctionObject);
|
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 {
|
struct MOZ_STACK_CLASS EvaluateOptions {
|
||||||
bool coerceToString;
|
bool coerceToString;
|
||||||
JS::AutoObjectVector scopeChain;
|
JS::AutoObjectVector scopeChain;
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,6 @@ public:
|
||||||
StructuredCloneData&
|
StructuredCloneData&
|
||||||
operator=(const StructuredCloneData& aOther) = delete;
|
operator=(const StructuredCloneData& aOther) = delete;
|
||||||
|
|
||||||
StructuredCloneData&
|
|
||||||
operator=(StructuredCloneData&& aOther) = default;
|
|
||||||
|
|
||||||
const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
|
const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
|
||||||
{
|
{
|
||||||
return mBlobImplArray;
|
return mBlobImplArray;
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,6 @@ using JS::SourceBufferHolder;
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
static LazyLogModule gCspPRLog("CSP");
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ImplCycleCollectionUnlink(ScriptLoadRequestList& aField);
|
ImplCycleCollectionUnlink(ScriptLoadRequestList& aField);
|
||||||
|
|
||||||
|
|
@ -2343,17 +2341,21 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
|
||||||
FinishDynamicImport(cx, request, rv);
|
FinishDynamicImport(cx, request, rv);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update our current script.
|
|
||||||
AutoCurrentScriptUpdater scriptUpdater(this, aRequest->Element());
|
|
||||||
|
|
||||||
JS::CompileOptions options(cx);
|
JS::CompileOptions options(cx);
|
||||||
rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
|
rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
{
|
||||||
|
nsJSUtils::ExecutionContext exec(cx, global);
|
||||||
|
if (aRequest->mOffThreadToken) {
|
||||||
|
JS::Rooted<JSScript*> script(cx);
|
||||||
|
rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
|
||||||
|
} else {
|
||||||
nsAutoString inlineData;
|
nsAutoString inlineData;
|
||||||
SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
|
SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
|
||||||
rv = nsJSUtils::EvaluateString(cx, srcBuf, global, options,
|
rv = exec.CompileAndExec(options, srcBuf);
|
||||||
aRequest->OffThreadTokenPtr());
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -687,6 +687,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
||||||
if (!NameFunctions(cx, pn))
|
if (!NameFunctions(cx, pn))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// XDR the newly delazified function.
|
||||||
|
if (script->scriptSource()->hasEncoder() &&
|
||||||
|
!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8854,7 +8854,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
||||||
ParseContext* outerpc = pc;
|
ParseContext* outerpc = pc;
|
||||||
|
|
||||||
// If we are off the main thread, the generator meta-objects have
|
// 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.
|
// be necessary.
|
||||||
RootedObject proto(context);
|
RootedObject proto(context);
|
||||||
JSContext* cx = context->maybeJSContext();
|
JSContext* cx = context->maybeJSContext();
|
||||||
|
|
|
||||||
|
|
@ -4202,6 +4202,31 @@ JS::CancelOffThreadModule(JSContext* cx, void* token)
|
||||||
HelperThreadState().cancelParseTask(cx, ParseTaskKind::Module, 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_PUBLIC_API(bool)
|
||||||
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
|
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
|
||||||
const JS::CompileOptions& options, MutableHandleScript script)
|
const JS::CompileOptions& options, MutableHandleScript script)
|
||||||
|
|
@ -7101,6 +7126,26 @@ JS::DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer,
|
||||||
return decoder.resultCode();
|
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_PUBLIC_API(void)
|
||||||
JS::SetBuildIdOp(JSContext* cx, JS::BuildIdOp buildIdOp)
|
JS::SetBuildIdOp(JSContext* cx, JS::BuildIdOp buildIdOp)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3748,7 +3748,7 @@ namespace JS {
|
||||||
* addrefs/copies/tracing/etc.
|
* addrefs/copies/tracing/etc.
|
||||||
*
|
*
|
||||||
* Furthermore, in some cases compile options are propagated from one entity to
|
* 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.
|
* involves copying over some, but not all, of the options.
|
||||||
*
|
*
|
||||||
* So, we have a class hierarchy that reflects these four use cases:
|
* 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)
|
extern JS_PUBLIC_API(void)
|
||||||
CancelOffThreadModule(JSContext* cx, void* token);
|
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.
|
* Compile a function with envChain plus the global as its scope chain.
|
||||||
* envChain must contain objects in the current compartment of cx. The actual
|
* 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_AsmJSNotSupported = TranscodeResult_Failure | 0x3,
|
||||||
TranscodeResult_Failure_BadDecode = TranscodeResult_Failure | 0x4,
|
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
|
TranscodeResult_Throw = 0x200
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -6186,6 +6200,25 @@ extern JS_PUBLIC_API(TranscodeResult)
|
||||||
DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
|
DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
|
||||||
size_t cursorIndex = 0);
|
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 */
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
|
||||||
|
|
@ -538,7 +538,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
|
||||||
uint32_t length = lengthAndEncoding >> 1;
|
uint32_t length = lengthAndEncoding >> 1;
|
||||||
bool latin1 = lengthAndEncoding & 0x1;
|
bool latin1 = lengthAndEncoding & 0x1;
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
JSAtom* atom;
|
JSAtom* atom;
|
||||||
if (latin1) {
|
if (latin1) {
|
||||||
const Latin1Char* chars = nullptr;
|
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
|
* most allocations here will be bigger than tempLifoAlloc's default
|
||||||
* chunk size.
|
* chunk size.
|
||||||
*/
|
*/
|
||||||
chars = cx->runtime()->pod_malloc<char16_t>(length);
|
chars = cx->pod_malloc<char16_t>(length);
|
||||||
if (!chars)
|
if (!chars)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -530,7 +530,7 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||||
HandleScript enclosingScript, MutableHandleFunction objp)
|
HandleScriptSource sourceObject, MutableHandleFunction objp)
|
||||||
{
|
{
|
||||||
enum FirstWordFlag {
|
enum FirstWordFlag {
|
||||||
HasAtom = 0x1,
|
HasAtom = 0x1,
|
||||||
|
|
@ -544,21 +544,15 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||||
uint32_t firstword = 0; /* bitmask of FirstWordFlag */
|
uint32_t firstword = 0; /* bitmask of FirstWordFlag */
|
||||||
uint32_t flagsword = 0; /* word for argument count and fun->flags */
|
uint32_t flagsword = 0; /* word for argument count and fun->flags */
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
RootedFunction fun(cx);
|
RootedFunction fun(cx);
|
||||||
RootedScript script(cx);
|
RootedScript script(cx);
|
||||||
Rooted<LazyScript*> lazy(cx);
|
Rooted<LazyScript*> lazy(cx);
|
||||||
|
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
fun = objp;
|
fun = objp;
|
||||||
if (!fun->isInterpreted()) {
|
if (!fun->isInterpreted())
|
||||||
JSAutoByteString funNameBytes;
|
return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
|
||||||
if (const char* name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
|
|
||||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
|
|
||||||
JSMSG_NOT_SCRIPTED_FUNCTION, name);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
|
if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
|
||||||
firstword |= HasAtom;
|
firstword |= HasAtom;
|
||||||
|
|
@ -590,6 +584,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||||
fun->environment() == nullptr);
|
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))
|
if (!xdr->codeUint32(&firstword))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -601,7 +599,11 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
RootedObject proto(cx);
|
RootedObject proto(cx);
|
||||||
if (firstword & HasStarGeneratorProto) {
|
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)
|
if (!proto)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -618,10 +620,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstword & IsLazy) {
|
if (firstword & IsLazy) {
|
||||||
if (!XDRLazyScript(xdr, enclosingScope, enclosingScript, fun, &lazy))
|
if (!XDRLazyScript(xdr, enclosingScope, sourceObject, fun, &lazy))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script))
|
if (!XDRScript(xdr, enclosingScope, sourceObject, fun, &script))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -650,10 +652,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
template bool
|
template bool
|
||||||
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, MutableHandleFunction);
|
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource, MutableHandleFunction);
|
||||||
|
|
||||||
template bool
|
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 ] */
|
/* ES6 (04-25-16) 19.2.3.6 Function.prototype [ @@hasInstance ] */
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -879,7 +879,7 @@ JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource);
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
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,
|
* Report an error that call.thisv is not compatible with the specified class,
|
||||||
|
|
|
||||||
|
|
@ -1137,7 +1137,7 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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->isSingleton());
|
||||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||||
|
|
@ -1155,7 +1155,7 @@ GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVe
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj,
|
GetScriptPlainObjectProperties(ExclusiveContext* cx, HandleObject obj,
|
||||||
MutableHandle<IdValueVector> properties)
|
MutableHandle<IdValueVector> properties)
|
||||||
{
|
{
|
||||||
if (obj->is<PlainObject>()) {
|
if (obj->is<PlainObject>()) {
|
||||||
|
|
@ -1330,9 +1330,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
|
||||||
{
|
{
|
||||||
/* NB: Keep this in sync with DeepCloneObjectLiteral. */
|
/* NB: Keep this in sync with DeepCloneObjectLiteral. */
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(),
|
assertSameCompartment(cx, obj);
|
||||||
cx->compartment()->behaviors().getSingletonsAsTemplates());
|
|
||||||
|
|
||||||
// Distinguish between objects and array classes.
|
// Distinguish between objects and array classes.
|
||||||
uint32_t isArray = 0;
|
uint32_t isArray = 0;
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
|
js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
|
|
||||||
enum ConstTag {
|
enum ConstTag {
|
||||||
SCRIPT_INT,
|
SCRIPT_INT,
|
||||||
|
|
@ -195,7 +195,7 @@ template<XDRMode mode>
|
||||||
static bool
|
static bool
|
||||||
XDRLazyClosedOverBindings(XDRState<mode>* xdr, MutableHandle<LazyScript*> lazy)
|
XDRLazyClosedOverBindings(XDRState<mode>* xdr, MutableHandle<LazyScript*> lazy)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
RootedAtom atom(cx);
|
RootedAtom atom(cx);
|
||||||
for (size_t i = 0; i < lazy->numClosedOverBindings(); i++) {
|
for (size_t i = 0; i < lazy->numClosedOverBindings(); i++) {
|
||||||
uint8_t endOfScopeSentinel;
|
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, script->isRelazifiable() && script->maybeLazyScript());
|
||||||
MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
|
MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
|
|
||||||
uint64_t packedFields;
|
uint64_t packedFields;
|
||||||
{
|
{
|
||||||
|
|
@ -257,7 +257,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
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));
|
packedFields, begin, end, toStringStart, lineno, column));
|
||||||
|
|
||||||
if (!lazy)
|
if (!lazy)
|
||||||
|
|
@ -304,8 +305,9 @@ enum XDRClassKind {
|
||||||
|
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScript enclosingScript,
|
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
||||||
HandleFunction fun, MutableHandleScript scriptp)
|
HandleScriptSource sourceObjectArg, HandleFunction fun,
|
||||||
|
MutableHandleScript scriptp)
|
||||||
{
|
{
|
||||||
/* NB: Keep this in sync with CopyScript. */
|
/* 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 scriptBits = 0;
|
||||||
uint32_t bodyScopeIndex = 0;
|
uint32_t bodyScopeIndex = 0;
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
RootedScript script(cx);
|
RootedScript script(cx);
|
||||||
natoms = nsrcnotes = 0;
|
natoms = nsrcnotes = 0;
|
||||||
nconsts = nobjects = nscopes = nregexps = ntrynotes = nscopenotes = nyieldoffsets = 0;
|
nconsts = nobjects = nscopes = nregexps = ntrynotes = nscopenotes = nyieldoffsets = 0;
|
||||||
|
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
script = scriptp.get();
|
script = scriptp.get();
|
||||||
MOZ_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
|
|
||||||
MOZ_ASSERT(script->functionNonDelazifying() == fun);
|
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
|
// 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
|
// 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
|
// 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);
|
scriptBits |= (1 << FunctionHasThisBinding);
|
||||||
if (script->functionHasExtraBodyVarScope())
|
if (script->functionHasExtraBodyVarScope())
|
||||||
scriptBits |= (1 << FunctionHasExtraBodyVarScope);
|
scriptBits |= (1 << FunctionHasExtraBodyVarScope);
|
||||||
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
|
MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
|
||||||
|
if (!sourceObjectArg)
|
||||||
scriptBits |= (1 << OwnSource);
|
scriptBits |= (1 << OwnSource);
|
||||||
if (script->isGeneratorExp())
|
if (script->isGeneratorExp())
|
||||||
scriptBits |= (1 << IsGeneratorExp);
|
scriptBits |= (1 << IsGeneratorExp);
|
||||||
|
|
@ -492,15 +494,31 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||||
if (!xdr->codeUint32(&scriptBits))
|
if (!xdr->codeUint32(&scriptBits))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
MOZ_ASSERT(!!(scriptBits & (1 << OwnSource)) == !sourceObjectArg);
|
||||||
|
RootedScriptSource sourceObject(cx, sourceObjectArg);
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
JSVersion version_ = JSVersion(version);
|
JSVersion version_ = JSVersion(version);
|
||||||
MOZ_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
|
MOZ_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
|
||||||
|
|
||||||
CompileOptions options(cx);
|
// When loading from the bytecode cache, we get the CompileOption from
|
||||||
options.setVersion(version_)
|
// 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)))
|
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
|
||||||
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
|
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
|
||||||
RootedScriptSource sourceObject(cx);
|
}
|
||||||
if (scriptBits & (1 << OwnSource)) {
|
if (scriptBits & (1 << OwnSource)) {
|
||||||
ScriptSource* ss = cx->new_<ScriptSource>();
|
ScriptSource* ss = cx->new_<ScriptSource>();
|
||||||
if (!ss)
|
if (!ss)
|
||||||
|
|
@ -513,22 +531,22 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||||
* ScriptSourceObject, and those that are (element; elementAttributeName)
|
* ScriptSourceObject, and those that are (element; elementAttributeName)
|
||||||
* aren't preserved by XDR. So this can be simple.
|
* 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);
|
sourceObject = ScriptSourceObject::create(cx, ss);
|
||||||
if (!sourceObject ||
|
if (xdr->hasScriptSourceObjectOut()) {
|
||||||
!ScriptSourceObject::initFromOptions(cx, sourceObject, options))
|
// When the ScriptSourceObjectOut is provided by ParseTask, it
|
||||||
return false;
|
// is stored in a location which is traced by the GC.
|
||||||
|
*xdr->scriptSourceObjectOut() = sourceObject;
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(enclosingScript);
|
if (!sourceObject ||
|
||||||
// When decoding, all the scripts and the script source object
|
!ScriptSourceObject::initFromOptions(cx->asJSContext(), sourceObject, *options))
|
||||||
// are in the same compartment, so the script's source object
|
{
|
||||||
// should never be a cross-compartment wrapper.
|
return false;
|
||||||
MOZ_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
|
}
|
||||||
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
script = JSScript::Create(cx, options, sourceObject, 0, 0, 0, 0);
|
script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0, 0);
|
||||||
if (!script)
|
if (!script)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -536,6 +554,10 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||||
// decoded may iterate the static scope chain.
|
// decoded may iterate the static scope chain.
|
||||||
if (fun)
|
if (fun)
|
||||||
fun->initScript(script);
|
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) {
|
if (mode == XDR_DECODE) {
|
||||||
|
|
@ -606,7 +628,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||||
JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
|
JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
|
||||||
|
|
||||||
if (scriptBits & (1 << OwnSource)) {
|
if (scriptBits & (1 << OwnSource)) {
|
||||||
if (!script->scriptSource()->performXDR<mode>(xdr))
|
if (!sourceObject->source()->performXDR<mode>(xdr))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!xdr->codeUint32(&script->sourceStart_))
|
if (!xdr->codeUint32(&script->sourceStart_))
|
||||||
|
|
@ -846,7 +868,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||||
RootedFunction tmp(cx);
|
RootedFunction tmp(cx);
|
||||||
if (mode == XDR_ENCODE)
|
if (mode == XDR_ENCODE)
|
||||||
tmp = &(*objp)->as<JSFunction>();
|
tmp = &(*objp)->as<JSFunction>();
|
||||||
if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
|
if (!XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp))
|
||||||
return false;
|
return false;
|
||||||
*objp = tmp;
|
*objp = tmp;
|
||||||
break;
|
break;
|
||||||
|
|
@ -922,27 +944,28 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
||||||
scriptp.set(script);
|
scriptp.set(script);
|
||||||
|
|
||||||
/* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
|
/* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
|
||||||
if (!fun)
|
if (!fun && cx->isJSContext())
|
||||||
Debugger::onNewScript(cx, script);
|
Debugger::onNewScript(cx->asJSContext(), script);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template bool
|
template bool
|
||||||
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, HandleFunction,
|
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource, HandleFunction,
|
||||||
MutableHandleScript);
|
MutableHandleScript);
|
||||||
|
|
||||||
template bool
|
template bool
|
||||||
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript, HandleFunction,
|
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource, HandleFunction,
|
||||||
MutableHandleScript);
|
MutableHandleScript);
|
||||||
|
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||||
HandleFunction fun, MutableHandle<LazyScript*> lazy)
|
HandleScriptSource sourceObject, HandleFunction fun,
|
||||||
|
MutableHandle<LazyScript*> lazy)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
|
|
||||||
{
|
{
|
||||||
uint32_t begin;
|
uint32_t begin;
|
||||||
|
|
@ -979,7 +1002,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
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));
|
packedFields, begin, end, toStringStart, lineno, column));
|
||||||
if (!lazy)
|
if (!lazy)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1013,11 +1036,11 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
|
||||||
}
|
}
|
||||||
|
|
||||||
template bool
|
template bool
|
||||||
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript,
|
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource,
|
||||||
HandleFunction, MutableHandle<LazyScript*>);
|
HandleFunction, MutableHandle<LazyScript*>);
|
||||||
|
|
||||||
template bool
|
template bool
|
||||||
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript,
|
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource,
|
||||||
HandleFunction, MutableHandle<LazyScript*>);
|
HandleFunction, MutableHandle<LazyScript*>);
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -1934,6 +1957,64 @@ ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||||
info->numScripts++;
|
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>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
ScriptSource::performXDR(XDRState<mode>* xdr)
|
ScriptSource::performXDR(XDRState<mode>* xdr)
|
||||||
|
|
@ -2073,7 +2154,10 @@ ScriptSource::performXDR(XDRState<mode>* xdr)
|
||||||
const char* fn = filename();
|
const char* fn = filename();
|
||||||
if (!xdr->codeCString(&fn))
|
if (!xdr->codeCString(&fn))
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4135,7 +4219,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
||||||
/* static */ LazyScript*
|
/* static */ LazyScript*
|
||||||
LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
||||||
HandleScript script, HandleScope enclosingScope,
|
HandleScript script, HandleScope enclosingScope,
|
||||||
HandleScript enclosingScript,
|
HandleScriptSource sourceObject,
|
||||||
uint64_t packedFields, uint32_t begin, uint32_t end,
|
uint64_t packedFields, uint32_t begin, uint32_t end,
|
||||||
uint32_t toStringStart, uint32_t lineno, uint32_t column)
|
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.
|
// values should only be non-null if we have a non-lazy enclosing script.
|
||||||
// AddLazyFunctionsForCompartment relies on the source object being null
|
// AddLazyFunctionsForCompartment relies on the source object being null
|
||||||
// if we're nested inside another lazy function.
|
// if we're nested inside another lazy function.
|
||||||
MOZ_ASSERT(!!enclosingScript == !!enclosingScope);
|
MOZ_ASSERT(!!sourceObject == !!enclosingScope);
|
||||||
MOZ_ASSERT(!res->sourceObject());
|
MOZ_ASSERT(!res->sourceObject());
|
||||||
MOZ_ASSERT(!res->enclosingScope());
|
MOZ_ASSERT(!res->enclosingScope());
|
||||||
if (enclosingScript)
|
if (sourceObject)
|
||||||
res->setEnclosingScopeAndSource(enclosingScope, &enclosingScript->scriptSourceUnwrap());
|
res->setEnclosingScopeAndSource(enclosingScope, sourceObject);
|
||||||
|
|
||||||
MOZ_ASSERT(!res->hasScript());
|
MOZ_ASSERT(!res->hasScript());
|
||||||
if (script)
|
if (script)
|
||||||
|
|
|
||||||
|
|
@ -431,6 +431,11 @@ class ScriptSource
|
||||||
// memory management.
|
// memory management.
|
||||||
const char* introductionType_;
|
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
|
// True if we can call JSRuntime::sourceHook to load the source on
|
||||||
// demand. If sourceRetrievable_ and hasSourceData() are false, it is not
|
// demand. If sourceRetrievable_ and hasSourceData() are false, it is not
|
||||||
// possible to get source at all.
|
// possible to get source at all.
|
||||||
|
|
@ -452,6 +457,7 @@ class ScriptSource
|
||||||
parameterListEnd_(0),
|
parameterListEnd_(0),
|
||||||
introducerFilename_(nullptr),
|
introducerFilename_(nullptr),
|
||||||
introductionType_(nullptr),
|
introductionType_(nullptr),
|
||||||
|
xdrEncoder_(nullptr),
|
||||||
sourceRetrievable_(false),
|
sourceRetrievable_(false),
|
||||||
hasIntroductionOffset_(false)
|
hasIntroductionOffset_(false)
|
||||||
{
|
{
|
||||||
|
|
@ -574,6 +580,29 @@ class ScriptSource
|
||||||
introductionOffset_ = offset;
|
introductionOffset_ = offset;
|
||||||
hasIntroductionOffset_ = true;
|
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
|
class ScriptSourceHolder
|
||||||
|
|
@ -695,12 +724,12 @@ AsyncKindFromBits(unsigned val) {
|
||||||
*/
|
*/
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
|
||||||
HandleFunction fun, MutableHandleScript scriptp);
|
HandleFunction fun, MutableHandleScript scriptp);
|
||||||
|
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
|
||||||
HandleFunction fun, MutableHandle<LazyScript*> lazy);
|
HandleFunction fun, MutableHandle<LazyScript*> lazy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -808,7 +837,7 @@ class JSScript : public js::gc::TenuredCell
|
||||||
friend
|
friend
|
||||||
bool
|
bool
|
||||||
js::XDRScript(js::XDRState<mode>* xdr, js::HandleScope enclosingScope,
|
js::XDRScript(js::XDRState<mode>* xdr, js::HandleScope enclosingScope,
|
||||||
js::HandleScript enclosingScript, js::HandleFunction fun,
|
js::HandleScriptSource sourceObject, js::HandleFunction fun,
|
||||||
js::MutableHandleScript scriptp);
|
js::MutableHandleScript scriptp);
|
||||||
|
|
||||||
friend bool
|
friend bool
|
||||||
|
|
@ -1211,11 +1240,11 @@ class JSScript : public js::gc::TenuredCell
|
||||||
return funLength_;
|
return funLength_;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sourceStart() const {
|
uint32_t sourceStart() const {
|
||||||
return sourceStart_;
|
return sourceStart_;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sourceEnd() const {
|
uint32_t sourceEnd() const {
|
||||||
return sourceEnd_;
|
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,
|
// The "script" argument to this function can be null. If it's non-null,
|
||||||
// then this LazyScript should be associated with the given JSScript.
|
// 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.
|
// enclosing function is also lazy.
|
||||||
static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
|
static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
|
||||||
HandleScript script, HandleScope enclosingScope,
|
HandleScript script, HandleScope enclosingScope,
|
||||||
HandleScript enclosingScript,
|
HandleScriptSource sourceObject,
|
||||||
uint64_t packedData, uint32_t begin, uint32_t end,
|
uint64_t packedData, uint32_t begin, uint32_t end,
|
||||||
uint32_t toStringStart, uint32_t lineno, uint32_t column);
|
uint32_t toStringStart, uint32_t lineno, uint32_t column);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1605,6 +1605,7 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||||
bool catchTermination = false;
|
bool catchTermination = false;
|
||||||
bool loadBytecode = false;
|
bool loadBytecode = false;
|
||||||
bool saveBytecode = false;
|
bool saveBytecode = false;
|
||||||
|
bool saveIncrementalBytecode = false;
|
||||||
bool assertEqBytecode = false;
|
bool assertEqBytecode = false;
|
||||||
RootedObject callerGlobal(cx, cx->global());
|
RootedObject callerGlobal(cx, cx->global());
|
||||||
|
|
||||||
|
|
@ -1668,6 +1669,11 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (!v.isUndefined())
|
if (!v.isUndefined())
|
||||||
saveBytecode = ToBoolean(v);
|
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))
|
if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v))
|
||||||
return false;
|
return false;
|
||||||
if (!v.isUndefined())
|
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
|
// We cannot load or save the bytecode if we have no object where the
|
||||||
// bytecode cache is stored.
|
// bytecode cache is stored.
|
||||||
if (loadBytecode || saveBytecode) {
|
if (loadBytecode || saveBytecode || saveIncrementalBytecode) {
|
||||||
if (!cacheEntry) {
|
if (!cacheEntry) {
|
||||||
JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
|
JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
|
||||||
"evaluate");
|
"evaluate");
|
||||||
return false;
|
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))
|
if (!script->scriptSource()->setSourceMapURL(cx, smurl))
|
||||||
return false;
|
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 (!JS_ExecuteScript(cx, script, args.rval())) {
|
||||||
if (catchTermination && !JS_IsExceptionPending(cx)) {
|
if (catchTermination && !JS_IsExceptionPending(cx)) {
|
||||||
JSAutoCompartment ac1(cx, callerGlobal);
|
JSAutoCompartment ac1(cx, callerGlobal);
|
||||||
|
|
@ -1770,14 +1790,21 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode the bytecode after the execution of the script.
|
||||||
if (saveBytecode) {
|
if (saveBytecode) {
|
||||||
JS::TranscodeResult rv = JS::EncodeScript(cx, saveBuffer, script);
|
JS::TranscodeResult rv = JS::EncodeScript(cx, saveBuffer, script);
|
||||||
if (!ConvertTranscodeResultToJSException(cx, rv))
|
if (!ConvertTranscodeResultToJSException(cx, rv))
|
||||||
return false;
|
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
|
// If we are both loading and saving, we assert that we are going to
|
||||||
// replace the current bytecode by the same stream of bytes.
|
// replace the current bytecode by the same stream of bytes.
|
||||||
if (loadBytecode && assertEqBytecode) {
|
if (loadBytecode && assertEqBytecode) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
bool
|
||||||
ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
|
ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
|
||||||
{
|
{
|
||||||
|
|
@ -389,6 +401,29 @@ ModuleParseTask::parse()
|
||||||
script = module->script();
|
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
|
void
|
||||||
js::CancelOffThreadParses(JSRuntime* rt)
|
js::CancelOffThreadParses(JSRuntime* rt)
|
||||||
{
|
{
|
||||||
|
|
@ -557,10 +592,10 @@ QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename TaskFunctor>
|
||||||
bool
|
bool
|
||||||
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||||
const char16_t* chars, size_t length,
|
ParseTaskKind kind, TaskFunctor& taskFunctor)
|
||||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
|
||||||
{
|
{
|
||||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||||
// which could require barriers on the atoms compartment.
|
// which could require barriers on the atoms compartment.
|
||||||
|
|
@ -568,7 +603,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
||||||
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
||||||
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
||||||
|
|
||||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
|
JSObject* global = CreateGlobalForOffThreadParse(cx, kind, nogc);
|
||||||
if (!global)
|
if (!global)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -578,9 +613,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
||||||
if (!helpercx)
|
if (!helpercx)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ScopedJSDeletePtr<ParseTask> task(
|
ScopedJSDeletePtr<ParseTask> task(taskFunctor(helpercx.get(), global));
|
||||||
cx->new_<ScriptParseTask>(helpercx.get(), global, cx, chars, length,
|
|
||||||
callback, callbackData));
|
|
||||||
if (!task)
|
if (!task)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -594,41 +627,40 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
||||||
return true;
|
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
|
bool
|
||||||
js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||||
const char16_t* chars, size_t length,
|
const char16_t* chars, size_t length,
|
||||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||||
{
|
{
|
||||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ModuleParseTask* {
|
||||||
// which could require barriers on the atoms compartment.
|
return cx->new_<ModuleParseTask>(helpercx, global, cx, chars, length,
|
||||||
gc::AutoSuppressGC nogc(cx);
|
callback, callbackData);
|
||||||
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
};
|
||||||
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
|
||||||
|
}
|
||||||
|
|
||||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Module, nogc);
|
bool
|
||||||
if (!global)
|
js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||||
return false;
|
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||||
|
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||||
ScopedJSDeletePtr<ExclusiveContext> helpercx(
|
{
|
||||||
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
|
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptDecodeTask* {
|
||||||
ExclusiveContext::Context_Exclusive, cx->options()));
|
return cx->new_<ScriptDecodeTask>(helpercx, global, cx, buffer, cursor,
|
||||||
if (!helpercx)
|
callback, callbackData);
|
||||||
return false;
|
};
|
||||||
|
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -1280,6 +1312,14 @@ GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
|
||||||
return script;
|
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*
|
JSObject*
|
||||||
GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
|
GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,8 @@ namespace wasm {
|
||||||
enum class ParseTaskKind
|
enum class ParseTaskKind
|
||||||
{
|
{
|
||||||
Script,
|
Script,
|
||||||
Module
|
Module,
|
||||||
|
ScriptDecode
|
||||||
};
|
};
|
||||||
|
|
||||||
// Per-process state for off thread work items.
|
// Per-process state for off thread work items.
|
||||||
|
|
@ -244,6 +245,7 @@ class GlobalHelperThreadState
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JSScript* finishScriptParseTask(JSContext* cx, void* token);
|
JSScript* finishScriptParseTask(JSContext* cx, void* token);
|
||||||
|
JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
|
||||||
JSObject* finishModuleParseTask(JSContext* cx, void* token);
|
JSObject* finishModuleParseTask(JSContext* cx, void* token);
|
||||||
bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
|
bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
|
||||||
SourceCompressionTask* compressionTaskForSource(ScriptSource* ss, 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,
|
const char16_t* chars, size_t length,
|
||||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
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
|
* Called at the end of GC to enqueue any Parse tasks that were waiting on an
|
||||||
* atoms-zone GC to finish.
|
* atoms-zone GC to finish.
|
||||||
|
|
@ -534,8 +541,21 @@ struct ParseTask
|
||||||
ParseTaskKind kind;
|
ParseTaskKind kind;
|
||||||
ExclusiveContext* cx;
|
ExclusiveContext* cx;
|
||||||
OwningCompileOptions options;
|
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;
|
const char16_t* chars;
|
||||||
size_t length;
|
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;
|
LifoAlloc alloc;
|
||||||
|
|
||||||
// Rooted pointer to the global object used by 'cx'.
|
// Rooted pointer to the global object used by 'cx'.
|
||||||
|
|
@ -562,6 +582,9 @@ struct ParseTask
|
||||||
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||||
JSContext* initCx, const char16_t* chars, size_t length,
|
JSContext* initCx, const char16_t* chars, size_t length,
|
||||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
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);
|
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
|
||||||
|
|
||||||
void activate(JSRuntime* rt);
|
void activate(JSRuntime* rt);
|
||||||
|
|
@ -593,6 +616,14 @@ struct ModuleParseTask : public ParseTask
|
||||||
void parse() override;
|
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
|
// Return whether, if a new parse task was started, it would need to wait for
|
||||||
// an in-progress GC to complete before starting.
|
// an in-progress GC to complete before starting.
|
||||||
extern bool
|
extern bool
|
||||||
|
|
|
||||||
|
|
@ -1552,7 +1552,7 @@ js::XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
RegExpFlag flags = RegExpFlag(flagsword);
|
RegExpFlag flags = RegExpFlag(flagsword);
|
||||||
RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags, nullptr,
|
RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags, nullptr,
|
||||||
xdr->cx()->tempLifoAlloc());
|
xdr->lifoAlloc());
|
||||||
if (!reobj)
|
if (!reobj)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0)
|
||||||
static bool
|
static bool
|
||||||
XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
|
|
||||||
RootedAtom atom(cx, bindingName->name());
|
RootedAtom atom(cx, bindingName->name());
|
||||||
bool hasAtom = !!atom;
|
bool hasAtom = !!atom;
|
||||||
|
|
@ -220,7 +220,7 @@ XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
||||||
static bool
|
static bool
|
||||||
XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
|
XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
|
|
||||||
uint8_t u8;
|
uint8_t u8;
|
||||||
if (!xdr->codeUint8(&u8))
|
if (!xdr->codeUint8(&u8))
|
||||||
|
|
@ -254,7 +254,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!data);
|
MOZ_ASSERT(!data);
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
|
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
if (mode == XDR_ENCODE)
|
if (mode == XDR_ENCODE)
|
||||||
|
|
@ -542,7 +542,7 @@ template <XDRMode mode>
|
||||||
LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
MutableHandleScope scope)
|
MutableHandleScope scope)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
|
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
|
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
|
||||||
|
|
@ -725,7 +725,7 @@ template <XDRMode mode>
|
||||||
FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
|
FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
|
||||||
MutableHandleScope scope)
|
MutableHandleScope scope)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
|
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -861,7 +861,7 @@ template <XDRMode mode>
|
||||||
VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
MutableHandleScope scope)
|
MutableHandleScope scope)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
|
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -962,7 +962,7 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT((mode == XDR_DECODE) == !scope);
|
MOZ_ASSERT((mode == XDR_DECODE) == !scope);
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
|
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1084,7 +1084,7 @@ template <XDRMode mode>
|
||||||
EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
MutableHandleScope scope)
|
MutableHandleScope scope)
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
ExclusiveContext* cx = xdr->cx();
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
|
#include "jscntxt.h"
|
||||||
#include "jsscript.h"
|
#include "jsscript.h"
|
||||||
|
|
||||||
#include "vm/Debugger.h"
|
#include "vm/Debugger.h"
|
||||||
|
|
@ -18,11 +19,17 @@
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using mozilla::PodEqual;
|
using mozilla::PodEqual;
|
||||||
|
|
||||||
|
template<XDRMode mode>
|
||||||
|
LifoAlloc&
|
||||||
|
XDRState<mode>::lifoAlloc() const {
|
||||||
|
return buf.cx()->asJSContext()->tempLifoAlloc();
|
||||||
|
}
|
||||||
|
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
void
|
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);
|
MOZ_ASSERT(resultCode_ == JS::TranscodeResult_Ok);
|
||||||
resultCode_ = JS::TranscodeResult_Throw;
|
resultCode_ = JS::TranscodeResult_Throw;
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +47,7 @@ XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
|
||||||
return true;
|
return true;
|
||||||
uint8_t* ptr = buf.write(nchars);
|
uint8_t* ptr = buf.write(nchars);
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
|
|
||||||
mozilla::PodCopy(ptr, chars, nchars);
|
mozilla::PodCopy(ptr, chars, nchars);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -56,7 +63,7 @@ XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
uint8_t* ptr = buf.write(nbytes);
|
uint8_t* ptr = buf.write(nbytes);
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
|
mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
|
||||||
} else {
|
} else {
|
||||||
const uint8_t* ptr = buf.read(nbytes);
|
const uint8_t* ptr = buf.read(nbytes);
|
||||||
|
|
@ -70,11 +77,8 @@ static bool
|
||||||
VersionCheck(XDRState<mode>* xdr)
|
VersionCheck(XDRState<mode>* xdr)
|
||||||
{
|
{
|
||||||
JS::BuildIdCharVector buildId;
|
JS::BuildIdCharVector buildId;
|
||||||
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId)) {
|
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId))
|
||||||
JS_ReportErrorNumberASCII(xdr->cx(), GetErrorMessage, nullptr,
|
return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
|
||||||
JSMSG_BUILD_ID_NOT_AVAILABLE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(!buildId.empty());
|
MOZ_ASSERT(!buildId.empty());
|
||||||
|
|
||||||
uint32_t buildIdLength;
|
uint32_t buildIdLength;
|
||||||
|
|
@ -97,7 +101,7 @@ VersionCheck(XDRState<mode>* xdr)
|
||||||
// buildId.
|
// buildId.
|
||||||
if (!decodedBuildId.resize(buildIdLength)) {
|
if (!decodedBuildId.resize(buildIdLength)) {
|
||||||
ReportOutOfMemory(xdr->cx());
|
ReportOutOfMemory(xdr->cx());
|
||||||
return false;
|
return xdr->fail(JS::TranscodeResult_Throw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
|
if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
|
||||||
|
|
@ -113,20 +117,26 @@ VersionCheck(XDRState<mode>* xdr)
|
||||||
|
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
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);
|
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>());
|
MOZ_ASSERT(funp->nonLazyScript()->enclosingScope()->is<GlobalScope>());
|
||||||
|
}
|
||||||
|
|
||||||
if (!VersionCheck(this)) {
|
if (!VersionCheck(this)) {
|
||||||
postProcessContextErrors(cx());
|
postProcessContextErrors(cx());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
|
if (!XDRInterpretedFunction(this, scope, sourceObject, funp)) {
|
||||||
if (!XDRInterpretedFunction(this, scope, nullptr, funp)) {
|
|
||||||
postProcessContextErrors(cx());
|
postProcessContextErrors(cx());
|
||||||
funp.set(nullptr);
|
funp.set(nullptr);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -144,6 +154,8 @@ XDRState<mode>::codeScript(MutableHandleScript scriptp)
|
||||||
else
|
else
|
||||||
MOZ_ASSERT(!scriptp->enclosingScope());
|
MOZ_ASSERT(!scriptp->enclosingScope());
|
||||||
|
|
||||||
|
AutoXDRTree scriptTree(this, getTopLevelTreeKey());
|
||||||
|
|
||||||
if (!VersionCheck(this)) {
|
if (!VersionCheck(this)) {
|
||||||
postProcessContextErrors(cx());
|
postProcessContextErrors(cx());
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -167,3 +179,189 @@ XDRState<mode>::codeConstValue(MutableHandleValue vp)
|
||||||
|
|
||||||
template class js::XDRState<XDR_ENCODE>;
|
template class js::XDRState<XDR_ENCODE>;
|
||||||
template class js::XDRState<XDR_DECODE>;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
245
js/src/vm/Xdr.h
245
js/src/vm/Xdr.h
|
|
@ -16,10 +16,10 @@ namespace js {
|
||||||
|
|
||||||
class XDRBuffer {
|
class XDRBuffer {
|
||||||
public:
|
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) { }
|
: context_(cx), buffer_(buffer), cursor_(cursor) { }
|
||||||
|
|
||||||
JSContext* cx() const {
|
ExclusiveContext* cx() const {
|
||||||
return context_;
|
return context_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ class XDRBuffer {
|
||||||
uint8_t* write(size_t n) {
|
uint8_t* write(size_t n) {
|
||||||
MOZ_ASSERT(n != 0);
|
MOZ_ASSERT(n != 0);
|
||||||
if (!buffer_.growByUninitialized(n)) {
|
if (!buffer_.growByUninitialized(n)) {
|
||||||
JS_ReportOutOfMemory(cx());
|
ReportOutOfMemory(cx());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
uint8_t* ptr = &buffer_[cursor_];
|
uint8_t* ptr = &buffer_[cursor_];
|
||||||
|
|
@ -50,30 +50,108 @@ class XDRBuffer {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t cursor() const {
|
||||||
|
return cursor_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSContext* const context_;
|
ExclusiveContext* const context_;
|
||||||
JS::TranscodeBuffer& buffer_;
|
JS::TranscodeBuffer& buffer_;
|
||||||
size_t cursor_;
|
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.
|
* XDR serialization state. All data is encoded in little endian.
|
||||||
*/
|
*/
|
||||||
template <XDRMode mode>
|
template <XDRMode mode>
|
||||||
class XDRState {
|
class XDRState : public XDRCoderBase
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
XDRBuffer buf;
|
XDRBuffer buf;
|
||||||
|
private:
|
||||||
JS::TranscodeResult resultCode_;
|
JS::TranscodeResult resultCode_;
|
||||||
|
|
||||||
XDRState(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
public:
|
||||||
: buf(cx, buffer, cursor), resultCode_(JS::TranscodeResult_Ok) { }
|
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();
|
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.
|
// Record logical failures of XDR.
|
||||||
void postProcessContextErrors(JSContext* cx);
|
void postProcessContextErrors(ExclusiveContext* cx);
|
||||||
JS::TranscodeResult resultCode() const {
|
JS::TranscodeResult resultCode() const {
|
||||||
return resultCode_;
|
return resultCode_;
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +165,7 @@ class XDRState {
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
uint8_t* ptr = buf.write(sizeof(*n));
|
uint8_t* ptr = buf.write(sizeof(*n));
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
*ptr = *n;
|
*ptr = *n;
|
||||||
} else {
|
} else {
|
||||||
*n = *buf.read(sizeof(*n));
|
*n = *buf.read(sizeof(*n));
|
||||||
|
|
@ -99,7 +177,7 @@ class XDRState {
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
uint8_t* ptr = buf.write(sizeof(*n));
|
uint8_t* ptr = buf.write(sizeof(*n));
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
mozilla::LittleEndian::writeUint16(ptr, *n);
|
mozilla::LittleEndian::writeUint16(ptr, *n);
|
||||||
} else {
|
} else {
|
||||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||||
|
|
@ -112,7 +190,7 @@ class XDRState {
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
uint8_t* ptr = buf.write(sizeof(*n));
|
uint8_t* ptr = buf.write(sizeof(*n));
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
mozilla::LittleEndian::writeUint32(ptr, *n);
|
mozilla::LittleEndian::writeUint32(ptr, *n);
|
||||||
} else {
|
} else {
|
||||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||||
|
|
@ -125,7 +203,7 @@ class XDRState {
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
uint8_t* ptr = buf.write(sizeof(*n));
|
uint8_t* ptr = buf.write(sizeof(*n));
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
mozilla::LittleEndian::writeUint64(ptr, *n);
|
mozilla::LittleEndian::writeUint64(ptr, *n);
|
||||||
} else {
|
} else {
|
||||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||||
|
|
@ -188,7 +266,7 @@ class XDRState {
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
uint8_t* ptr = buf.write(len);
|
uint8_t* ptr = buf.write(len);
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
memcpy(ptr, bytes, len);
|
memcpy(ptr, bytes, len);
|
||||||
} else {
|
} else {
|
||||||
memcpy(bytes, buf.read(len), len);
|
memcpy(bytes, buf.read(len), len);
|
||||||
|
|
@ -207,7 +285,7 @@ class XDRState {
|
||||||
size_t n = strlen(*sp) + 1;
|
size_t n = strlen(*sp) + 1;
|
||||||
uint8_t* ptr = buf.write(n);
|
uint8_t* ptr = buf.write(n);
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return false;
|
return fail(JS::TranscodeResult_Throw);
|
||||||
memcpy(ptr, *sp, n);
|
memcpy(ptr, *sp, n);
|
||||||
} else {
|
} else {
|
||||||
*sp = buf.readCString();
|
*sp = buf.readCString();
|
||||||
|
|
@ -218,7 +296,7 @@ class XDRState {
|
||||||
bool codeChars(const JS::Latin1Char* chars, size_t nchars);
|
bool codeChars(const JS::Latin1Char* chars, size_t nchars);
|
||||||
bool codeChars(char16_t* 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 codeScript(MutableHandleScript scriptp);
|
||||||
bool codeConstValue(MutableHandleValue vp);
|
bool codeConstValue(MutableHandleValue vp);
|
||||||
};
|
};
|
||||||
|
|
@ -226,6 +304,141 @@ class XDRState {
|
||||||
using XDREncoder = XDRState<XDR_ENCODE>;
|
using XDREncoder = XDRState<XDR_ENCODE>;
|
||||||
using XDRDecoder = XDRState<XDR_DECODE>;
|
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 */
|
} /* namespace js */
|
||||||
|
|
||||||
#endif /* vm_Xdr_h */
|
#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_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC, FAILURE(1027)),
|
||||||
ERROR(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC, FAILURE(1028)),
|
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
|
/* 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";
|
* actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
|
||||||
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
|
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue