mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
beb68187e5
98 changed files with 13981 additions and 982 deletions
|
|
@ -224,6 +224,7 @@
|
|||
#include "mozilla/dom/PrimitiveConversions.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "nsITabChild.h"
|
||||
#include "mozilla/dom/ModuleScript.h"
|
||||
#include "mozilla/dom/MediaQueryList.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/NavigatorBinding.h"
|
||||
|
|
@ -12949,9 +12950,6 @@ nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
|
|||
RefPtr<Function> callback = handler->GetCallback();
|
||||
|
||||
if (!callback) {
|
||||
// Evaluate the timeout expression.
|
||||
const nsAString& script = handler->GetHandlerText();
|
||||
|
||||
const char* filename = nullptr;
|
||||
uint32_t lineNo = 0, dummyColumn = 0;
|
||||
handler->GetLocation(&filename, &lineNo, &dummyColumn);
|
||||
|
|
@ -12962,9 +12960,22 @@ nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
|
|||
AutoEntryScript aes(this, reason, true);
|
||||
JS::CompileOptions options(aes.cx());
|
||||
options.setFileAndLine(filename, lineNo).setVersion(JSVERSION_DEFAULT);
|
||||
options.setNoScriptRval(true);
|
||||
JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
|
||||
nsresult rv =
|
||||
nsJSUtils::EvaluateString(aes.cx(), script, global, options);
|
||||
nsresult rv;
|
||||
{
|
||||
nsJSUtils::ExecutionContext exec(aes.cx(), global);
|
||||
rv = exec.Compile(options, handler->GetHandlerText());
|
||||
|
||||
if (rv == NS_OK) {
|
||||
LoadedScript* initiatingScript = handler->GetInitiatingScript();
|
||||
if (initiatingScript) {
|
||||
initiatingScript->AssociateWithScript(exec.GetScript());
|
||||
}
|
||||
|
||||
rv = exec.ExecScript();
|
||||
}
|
||||
}
|
||||
if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
|
||||
abortIntervalHandler = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Function;
|
||||
class LoadedScript;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
@ -44,6 +45,9 @@ public:
|
|||
|
||||
// If we have a Function, get the arguments for passing to it.
|
||||
virtual const nsTArray<JS::Value>& GetArgs() = 0;
|
||||
|
||||
// If we have an expression, get the initiating script.
|
||||
virtual mozilla::dom::LoadedScript* GetInitiatingScript() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptTimeoutHandler,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
#include "mozilla/dom/ModuleScript.h"
|
||||
#include "nsAXPCNativeCallContext.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
|
@ -44,6 +45,7 @@ public:
|
|||
nsTArray<JS::Heap<JS::Value>>&& aArguments,
|
||||
ErrorResult& aError);
|
||||
nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow* aWindow,
|
||||
LoadedScript* aInitiatingScript,
|
||||
const nsAString& aExpression, bool* aAllowEval,
|
||||
ErrorResult& aError);
|
||||
nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
|
|
@ -77,6 +79,10 @@ public:
|
|||
*aColumn = mColumn;
|
||||
}
|
||||
|
||||
virtual LoadedScript* GetInitiatingScript() override {
|
||||
return mInitiatingScript;
|
||||
}
|
||||
|
||||
virtual void MarkForCC() override
|
||||
{
|
||||
if (mFunction) {
|
||||
|
|
@ -104,6 +110,9 @@ private:
|
|||
// it should be used, else use mExpr.
|
||||
nsString mExpr;
|
||||
RefPtr<Function> mFunction;
|
||||
|
||||
// Initiating script for use when evaluating mExpr on the main thread.
|
||||
RefPtr<LoadedScript> mInitiatingScript;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -112,6 +121,8 @@ private:
|
|||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFunction)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInitiatingScript)
|
||||
tmp->ReleaseJSObjects();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
|
||||
|
|
@ -151,6 +162,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
|
|||
if (tmp->mFunction) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFunction)
|
||||
}
|
||||
if (tmp->mInitiatingScript) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInitiatingScript)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
|
||||
|
|
@ -243,12 +257,14 @@ nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
|
|||
|
||||
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
|
||||
nsGlobalWindow *aWindow,
|
||||
LoadedScript* aInitiatingScript,
|
||||
const nsAString& aExpression,
|
||||
bool* aAllowEval,
|
||||
ErrorResult& aError)
|
||||
: mLineNo(0)
|
||||
, mColumn(0)
|
||||
, mExpr(aExpression)
|
||||
, mInitiatingScript(aInitiatingScript)
|
||||
{
|
||||
if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
|
||||
// This window was already closed, or never properly initialized,
|
||||
|
|
@ -352,9 +368,11 @@ already_AddRefed<nsIScriptTimeoutHandler>
|
|||
NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
|
||||
const nsAString& aExpression, ErrorResult& aError)
|
||||
{
|
||||
LoadedScript* script = ScriptLoader::GetActiveScript(aCx);
|
||||
|
||||
bool allowEval = false;
|
||||
RefPtr<nsJSScriptTimeoutHandler> handler =
|
||||
new nsJSScriptTimeoutHandler(aCx, aWindow, aExpression, &allowEval, aError);
|
||||
new nsJSScriptTimeoutHandler(aCx, aWindow, script, aExpression, &allowEval, aError);
|
||||
if (aError.Failed() || !allowEval) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,157 +123,276 @@ nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue)
|
||||
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)
|
||||
, mScript(aCx)
|
||||
, mRv(NS_OK)
|
||||
, mSkip(false)
|
||||
, mCoerceToString(false)
|
||||
, mEncodeBytecode(false)
|
||||
#ifdef DEBUG
|
||||
, mWantsReturnValue(false)
|
||||
, mExpectScopeChain(false)
|
||||
, mScriptUsed(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
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::JoinCompile(void** aOffThreadToken)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
MOZ_ASSERT(!mExpectScopeChain);
|
||||
MOZ_ASSERT(!mScript);
|
||||
mScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
if (!mScript) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::Compile(JS::CompileOptions& aCompileOptions,
|
||||
JS::SourceBufferHolder& aSrcBuf)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aSrcBuf.get());
|
||||
MOZ_ASSERT(mRetValue.isUndefined());
|
||||
#ifdef DEBUG
|
||||
mWantsReturnValue = !aCompileOptions.noScriptRval;
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(!mScript);
|
||||
bool compiled = true;
|
||||
if (mScopeChain.length() == 0) {
|
||||
compiled = JS::Compile(mCx, aCompileOptions, aSrcBuf, &mScript);
|
||||
} else {
|
||||
compiled = JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf,
|
||||
&mScript);
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(compiled, mScript);
|
||||
if (!compiled) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::Compile(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 EvaluateString(aCx, srcBuf, aEvaluationGlobal, aCompileOptions,
|
||||
aEvaluateOptions, aRetValue, nullptr);
|
||||
return Compile(aCompileOptions, srcBuf);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue,
|
||||
void **aOffThreadToken)
|
||||
nsJSUtils::ExecutionContext::Decode(JS::CompileOptions& aCompileOptions,
|
||||
mozilla::Vector<uint8_t>& aBytecodeBuf,
|
||||
size_t aBytecodeIndex)
|
||||
{
|
||||
PROFILER_LABEL("nsJSUtils", "EvaluateString",
|
||||
js::ProfileEntry::Category::JS);
|
||||
|
||||
MOZ_ASSERT_IF(aCompileOptions.versionSet,
|
||||
aCompileOptions.version != JSVERSION_UNKNOWN);
|
||||
MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, !aCompileOptions.noScriptRval);
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(aSrcBuf.get());
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
|
||||
aEvaluationGlobal);
|
||||
MOZ_ASSERT_IF(aOffThreadToken, aCompileOptions.noScriptRval);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(CycleCollectedJSContext::Get() &&
|
||||
CycleCollectedJSContext::Get()->MicroTaskLevel());
|
||||
|
||||
// Unfortunately, the JS engine actually compiles scripts with a return value
|
||||
// in a different, less efficient way. Furthermore, it can't JIT them in many
|
||||
// cases. So we need to be explicitly told whether the caller cares about the
|
||||
// return value. Callers can do this by calling the other overload of
|
||||
// EvaluateString() which calls this function with
|
||||
// aCompileOptions.noScriptRval set to true.
|
||||
aRetValue.setUndefined();
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
|
||||
|
||||
bool ok = true;
|
||||
// Scope the JSAutoCompartment so that we can later wrap the return value
|
||||
// into the caller's cx.
|
||||
{
|
||||
JSAutoCompartment ac(aCx, aEvaluationGlobal);
|
||||
|
||||
// Now make sure to wrap the scope chain into the right compartment.
|
||||
JS::AutoObjectVector scopeChain(aCx);
|
||||
if (!scopeChain.reserve(aEvaluateOptions.scopeChain.length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aEvaluateOptions.scopeChain.length(); ++i) {
|
||||
JS::ExposeObjectToActiveJS(aEvaluateOptions.scopeChain[i]);
|
||||
scopeChain.infallibleAppend(aEvaluateOptions.scopeChain[i]);
|
||||
if (!JS_WrapObject(aCx, scopeChain[i])) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok && aOffThreadToken) {
|
||||
JS::Rooted<JSScript*>
|
||||
script(aCx, JS::FinishOffThreadScript(aCx, *aOffThreadToken));
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
if (script) {
|
||||
ok = JS_ExecuteScript(aCx, scopeChain, script);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else if (ok) {
|
||||
ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf, aRetValue);
|
||||
}
|
||||
|
||||
if (ok && aEvaluateOptions.coerceToString && !aRetValue.isUndefined()) {
|
||||
JS::Rooted<JS::Value> value(aCx, aRetValue);
|
||||
JSString* str = JS::ToString(aCx, value);
|
||||
ok = !!str;
|
||||
aRetValue.set(ok ? JS::StringValue(str) : JS::UndefinedValue());
|
||||
}
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (JS_IsExceptionPending(aCx)) {
|
||||
rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
|
||||
} else {
|
||||
rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
|
||||
}
|
||||
|
||||
if (!aCompileOptions.noScriptRval) {
|
||||
aRetValue.setUndefined();
|
||||
}
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
JS::TranscodeResult tr = JS::DecodeScript(mCx, aBytecodeBuf, &mScript, 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;
|
||||
}
|
||||
|
||||
// Wrap the return value into whatever compartment aCx was in.
|
||||
if (ok && !aCompileOptions.noScriptRval) {
|
||||
if (!JS_WrapValue(aCx, aRetValue)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return mRv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::ExecutionContext::JoinDecode(void **aOffThreadToken)
|
||||
{
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
return rv;
|
||||
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
MOZ_ASSERT(!mExpectScopeChain);
|
||||
mScript.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken));
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
if (!mScript) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSScript* nsJSUtils::ExecutionContext::GetScript() {
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!mSkip);
|
||||
MOZ_ASSERT(mScript);
|
||||
mScriptUsed = true;
|
||||
#endif
|
||||
|
||||
return mScript;
|
||||
}
|
||||
|
||||
nsresult nsJSUtils::ExecutionContext::ExecScript() {
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mScript);
|
||||
|
||||
if (!JS_ExecuteScript(mCx, mScopeChain, mScript)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
|
||||
if (!aValue.isObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrap(&aValue.toObject()));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JS::IsPromiseObject(obj);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue)
|
||||
nsJSUtils::ExecutionContext::ExecScript(JS::MutableHandle<JS::Value> aRetValue)
|
||||
{
|
||||
return EvaluateString(aCx, aSrcBuf, aEvaluationGlobal, aCompileOptions,
|
||||
aEvaluateOptions, aRetValue, nullptr);
|
||||
}
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions)
|
||||
{
|
||||
EvaluateOptions options(aCx);
|
||||
aCompileOptions.setNoScriptRval(true);
|
||||
JS::RootedValue unused(aCx);
|
||||
return EvaluateString(aCx, aScript, aEvaluationGlobal, aCompileOptions,
|
||||
options, &unused);
|
||||
}
|
||||
MOZ_ASSERT(mScript);
|
||||
MOZ_ASSERT(mWantsReturnValue);
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
void **aOffThreadToken)
|
||||
{
|
||||
EvaluateOptions options(aCx);
|
||||
aCompileOptions.setNoScriptRval(true);
|
||||
JS::RootedValue unused(aCx);
|
||||
return EvaluateString(aCx, aSrcBuf, aEvaluationGlobal, aCompileOptions,
|
||||
options, &unused, aOffThreadToken);
|
||||
if (!JS_ExecuteScript(mCx, mScopeChain, mScript, aRetValue)) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
return mRv;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mWantsReturnValue = false;
|
||||
#endif
|
||||
if (mCoerceToString && IsPromiseValue(mCx, aRetValue)) {
|
||||
// We're a javascript: url and we should treat Promise return values as
|
||||
// undefined.
|
||||
//
|
||||
// Once bug 1477821 is fixed this code might be able to go away, or will
|
||||
// become enshrined in the spec, depending.
|
||||
aRetValue.setUndefined();
|
||||
}
|
||||
|
||||
if (mCoerceToString && !aRetValue.isUndefined()) {
|
||||
JSString* str = JS::ToString(mCx, aRetValue);
|
||||
if (!str) {
|
||||
// ToString can be a function call, so an exception can be raised while
|
||||
// executing the function.
|
||||
mSkip = true;
|
||||
return EvaluationExceptionToNSResult(mCx);
|
||||
}
|
||||
aRetValue.set(JS::StringValue(str));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
|||
|
|
@ -63,52 +63,129 @@ public:
|
|||
const nsAString& aBody,
|
||||
JSObject** aFunctionObject);
|
||||
|
||||
struct MOZ_STACK_CLASS EvaluateOptions {
|
||||
bool coerceToString;
|
||||
JS::AutoObjectVector scopeChain;
|
||||
// ExecutionContext is used to switch compartment.
|
||||
class MOZ_STACK_CLASS ExecutionContext {
|
||||
JSContext* mCx;
|
||||
|
||||
explicit EvaluateOptions(JSContext* cx)
|
||||
: coerceToString(false)
|
||||
, scopeChain(cx)
|
||||
{}
|
||||
// Handles switching to our global's compartment.
|
||||
JSAutoCompartment mCompartment;
|
||||
|
||||
EvaluateOptions& setCoerceToString(bool aCoerce) {
|
||||
coerceToString = aCoerce;
|
||||
// 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;
|
||||
|
||||
// The compiled script.
|
||||
JS::Rooted<JSScript*> mScript;
|
||||
|
||||
// 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;
|
||||
|
||||
// Encode the bytecode before it is being executed.
|
||||
bool mEncodeBytecode;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Should we set the return value.
|
||||
bool mWantsReturnValue;
|
||||
|
||||
bool mExpectScopeChain;
|
||||
|
||||
bool mScriptUsed;
|
||||
#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 reset when the returned value is extracted.
|
||||
MOZ_ASSERT_IF(!mSkip, !mWantsReturnValue);
|
||||
|
||||
// If encoding was started we expect the script to have been
|
||||
// used when ending the encoding.
|
||||
MOZ_ASSERT_IF(mEncodeBytecode && mScript && mRv == NS_OK, mScriptUsed);
|
||||
}
|
||||
|
||||
// The returned value would be converted to a string if the
|
||||
// |aCoerceToString| is flag set.
|
||||
ExecutionContext& SetCoerceToString(bool aCoerceToString) {
|
||||
mCoerceToString = aCoerceToString;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// When set, this flag records and encodes the bytecode as soon as it is
|
||||
// being compiled, and before it is being executed. The bytecode can then be
|
||||
// requested by using |JS::FinishIncrementalEncoding| with the mutable
|
||||
// handle |aScript| argument of |CompileAndExec| or |JoinAndExec|.
|
||||
ExecutionContext& SetEncodeBytecode(bool aEncodeBytecode) {
|
||||
mEncodeBytecode = aEncodeBytecode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Set the scope chain in which the code should be executed.
|
||||
void SetScopeChain(const JS::AutoObjectVector& aScopeChain);
|
||||
|
||||
// After getting a notification that an off-thread compilation terminated,
|
||||
// this function will take the result of the parser and move it to the main
|
||||
// thread.
|
||||
MOZ_MUST_USE nsresult JoinCompile(void** aOffThreadToken);
|
||||
|
||||
// Compile a script contained in a SourceText.
|
||||
nsresult Compile(JS::CompileOptions& aCompileOptions,
|
||||
JS::SourceBufferHolder& aSrcBuf);
|
||||
|
||||
// Compile a script contained in a string.
|
||||
nsresult Compile(JS::CompileOptions& aCompileOptions,
|
||||
const nsAString& aScript);
|
||||
|
||||
// Decode a script contained in a buffer.
|
||||
nsresult Decode(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 and move it to the main
|
||||
// thread.
|
||||
nsresult JoinDecode(void** aOffThreadToken);
|
||||
|
||||
// Get a successfully compiled script.
|
||||
JSScript* GetScript();
|
||||
|
||||
// Execute the compiled script and ignore the return value.
|
||||
MOZ_MUST_USE nsresult ExecScript();
|
||||
|
||||
// Execute the compiled script a get the return value.
|
||||
//
|
||||
// Copy the returned value into 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 is be set to the corresponding result
|
||||
// code and the mutable handle argument remains 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);
|
||||
|
||||
MOZ_MUST_USE nsresult ExecScript(JS::MutableHandle<JS::Value> aRetValue);
|
||||
};
|
||||
|
||||
// aEvaluationGlobal is the global to evaluate in. The return value
|
||||
// will then be wrapped back into the compartment aCx is in when
|
||||
// this function is called. For all the EvaluateString overloads,
|
||||
// the JSContext must come from an AutoJSAPI that has had
|
||||
// TakeOwnershipOfErrorReporting() called on it.
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue);
|
||||
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue);
|
||||
|
||||
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions);
|
||||
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions,
|
||||
void **aOffThreadToken);
|
||||
|
||||
static nsresult CompileModule(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
|
|
@ -128,16 +205,6 @@ public:
|
|||
JS::AutoObjectVector& aScopeChain);
|
||||
|
||||
static void ResetTimeZone();
|
||||
|
||||
private:
|
||||
// Implementation for our EvaluateString bits
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue,
|
||||
void **aOffThreadToken);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/EventTargetBinding.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/dom/ModuleScript.h"
|
||||
#include "mozilla/dom/ScriptLoader.h"
|
||||
#include "mozilla/dom/TouchEvent.h"
|
||||
#include "mozilla/TimelineConsumers.h"
|
||||
#include "mozilla/EventTimelineMarker.h"
|
||||
|
|
@ -1019,6 +1021,15 @@ EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
|
|||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
|
||||
|
||||
JS::Rooted<JSFunction*> func(cx, JS_GetObjectFunction(handler));
|
||||
MOZ_ASSERT(func);
|
||||
JS::Rooted<JSScript*> jsScript(cx, JS_GetFunctionScript(cx, func));
|
||||
MOZ_ASSERT(jsScript);
|
||||
RefPtr<LoadedScript> loaderScript = ScriptLoader::GetActiveScript(cx);
|
||||
if (loaderScript) {
|
||||
loaderScript->AssociateWithScript(jsScript);
|
||||
}
|
||||
|
||||
if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
|
||||
RefPtr<OnErrorEventHandlerNonNull> handlerCallback =
|
||||
new OnErrorEventHandlerNonNull(nullptr, handler, /* aIncumbentGlobal = */ nullptr);
|
||||
|
|
|
|||
|
|
@ -313,7 +313,8 @@ Request::Constructor(const GlobalObject& aGlobal,
|
|||
nsAutoString requestURL;
|
||||
nsCString fragment;
|
||||
if (NS_IsMainThread()) {
|
||||
nsIDocument* doc = GetEntryDocument();
|
||||
nsCOMPtr<nsPIDOMWindowInner> inner(do_QueryInterface(global));
|
||||
nsIDocument* doc = inner ? inner->GetExtantDoc() : nullptr;
|
||||
if (doc) {
|
||||
GetRequestURLFromDocument(doc, input, requestURL, fragment, aRv);
|
||||
} else {
|
||||
|
|
@ -362,7 +363,8 @@ Request::Constructor(const GlobalObject& aGlobal,
|
|||
} else {
|
||||
nsAutoString referrerURL;
|
||||
if (NS_IsMainThread()) {
|
||||
nsIDocument* doc = GetEntryDocument();
|
||||
nsCOMPtr<nsPIDOMWindowInner> inner(do_QueryInterface(global));
|
||||
nsIDocument* doc = inner ? inner->GetExtantDoc() : nullptr;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (doc) {
|
||||
uri = ParseURLFromDocument(doc, referrer, aRv);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
|
|||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
nsIDocument* doc = GetEntryDocument();
|
||||
nsCOMPtr<nsPIDOMWindowInner> inner(do_QueryInterface(aGlobal.GetAsSupports()));
|
||||
nsIDocument* doc = inner ? inner->GetExtantDoc() : nullptr;
|
||||
if (doc) {
|
||||
baseURI = doc->GetBaseURI();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,9 +79,6 @@ public:
|
|||
StructuredCloneData&
|
||||
operator=(const StructuredCloneData& aOther) = delete;
|
||||
|
||||
StructuredCloneData&
|
||||
operator=(StructuredCloneData&& aOther) = default;
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
|
||||
{
|
||||
return mBlobImplArray;
|
||||
|
|
|
|||
|
|
@ -275,10 +275,14 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
|
|||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(mURL.get(), 1)
|
||||
.setVersion(JSVERSION_DEFAULT);
|
||||
nsJSUtils::EvaluateOptions evalOptions(cx);
|
||||
evalOptions.setCoerceToString(true);
|
||||
rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
|
||||
globalJSObject, options, evalOptions, &v);
|
||||
{
|
||||
nsJSUtils::ExecutionContext exec(cx, globalJSObject);
|
||||
exec.SetCoerceToString(true);
|
||||
exec.Compile(options, NS_ConvertUTF8toUTF16(script));
|
||||
rv = exec.ExecScript(&v);
|
||||
}
|
||||
|
||||
js::AssertSameCompartment(cx, v);
|
||||
|
||||
if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
|
|
|
|||
|
|
@ -1388,14 +1388,23 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
|
|||
options.setFileAndLine(spec, 0)
|
||||
.setVersion(JSVERSION_DEFAULT);
|
||||
JS::Rooted<JS::Value> rval(cx);
|
||||
nsJSUtils::EvaluateOptions evalOptions(cx);
|
||||
JS::AutoObjectVector scopeChain(cx);
|
||||
if (obj != js::GetGlobalForObjectCrossCompartment(obj) &&
|
||||
!evalOptions.scopeChain.append(obj)) {
|
||||
!scopeChain.append(obj)) {
|
||||
return false;
|
||||
}
|
||||
obj = js::GetGlobalForObjectCrossCompartment(obj);
|
||||
nsresult rv = nsJSUtils::EvaluateString(cx, utf16script, obj, options,
|
||||
evalOptions, &rval);
|
||||
nsresult rv = NS_OK;
|
||||
{
|
||||
nsJSUtils::ExecutionContext exec(cx, obj);
|
||||
exec.SetScopeChain(scopeChain);
|
||||
exec.Compile(options, utf16script);
|
||||
rv = exec.ExecScript(&rval);
|
||||
}
|
||||
|
||||
if (!JS_WrapValue(cx, &rval)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(rv) &&
|
||||
(!result || JSValToNPVariant(npp, cx, rval, result));
|
||||
|
|
|
|||
|
|
@ -227,15 +227,6 @@ Promise::Then(JSContext* aCx,
|
|||
aRetval.setObject(*retval);
|
||||
}
|
||||
|
||||
// We need a dummy function to pass to JS::NewPromiseObject.
|
||||
static bool
|
||||
DoNothingPromiseExecutor(JSContext*, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
|
||||
{
|
||||
|
|
@ -246,17 +237,7 @@ Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
|
|||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JSFunction* doNothingFunc =
|
||||
JS_NewFunction(cx, DoNothingPromiseExecutor, /* nargs = */ 2,
|
||||
/* flags = */ 0, nullptr);
|
||||
if (!doNothingFunc) {
|
||||
JS_ClearPendingException(cx);
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> doNothingObj(cx, JS_GetFunctionObject(doNothingFunc));
|
||||
mPromiseObj = JS::NewPromiseObject(cx, doNothingObj, aDesiredProto);
|
||||
mPromiseObj = JS::NewPromiseObject(cx, nullptr, aDesiredProto);
|
||||
if (!mPromiseObj) {
|
||||
JS_ClearPendingException(cx);
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ModuleLoadRequest.h"
|
||||
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
|
||||
#include "ModuleScript.h"
|
||||
#include "ScriptLoader.h"
|
||||
|
||||
|
|
@ -13,58 +16,94 @@ namespace dom {
|
|||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest)
|
||||
NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest,
|
||||
mBaseURL,
|
||||
mLoader,
|
||||
mModuleScript,
|
||||
mImports)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleLoadRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleLoadRequest,
|
||||
ScriptLoadRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader, mModuleScript, mImports)
|
||||
tmp->ClearDynamicImport();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleLoadRequest,
|
||||
ScriptLoadRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader, mModuleScript, mImports)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleLoadRequest,
|
||||
ScriptLoadRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicReferencingPrivate)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicSpecifier)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
|
||||
NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
|
||||
|
||||
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI,
|
||||
nsIScriptElement* aElement,
|
||||
uint32_t aVersion,
|
||||
CORSMode aCORSMode,
|
||||
const SRIMetadata &aIntegrity,
|
||||
nsIURI* aReferrer,
|
||||
mozilla::net::ReferrerPolicy aReferrerPolicy,
|
||||
ScriptLoader* aLoader)
|
||||
: ScriptLoadRequest(ScriptKind::Module,
|
||||
aURI,
|
||||
aElement,
|
||||
aVersion,
|
||||
aCORSMode,
|
||||
aIntegrity,
|
||||
aReferrer,
|
||||
aReferrerPolicy),
|
||||
mIsTopLevel(true),
|
||||
mLoader(aLoader),
|
||||
mVisitedSet(new VisitedURLSet())
|
||||
{
|
||||
mVisitedSet->PutEntry(aURI);
|
||||
static VisitedURLSet* NewVisitedSetForTopLevelImport(nsIURI* aURI) {
|
||||
auto set = new VisitedURLSet();
|
||||
set->PutEntry(aURI);
|
||||
return set;
|
||||
}
|
||||
|
||||
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI,
|
||||
ModuleLoadRequest* aParent)
|
||||
: ScriptLoadRequest(ScriptKind::Module,
|
||||
aURI,
|
||||
aParent->mElement,
|
||||
aParent->mJSVersion,
|
||||
aParent->mCORSMode,
|
||||
SRIMetadata(),
|
||||
aParent->mURI,
|
||||
aParent->mReferrerPolicy),
|
||||
mIsTopLevel(false),
|
||||
mLoader(aParent->mLoader),
|
||||
mVisitedSet(aParent->mVisitedSet)
|
||||
{
|
||||
MOZ_ASSERT(mVisitedSet->Contains(aURI));
|
||||
|
||||
mIsInline = false;
|
||||
mScriptMode = aParent->mScriptMode;
|
||||
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateTopLevel(
|
||||
nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
|
||||
const SRIMetadata& aIntegrity, nsIURI* aReferrer, ScriptLoader* aLoader) {
|
||||
return new ModuleLoadRequest(aURI, aFetchOptions, aIntegrity, aReferrer,
|
||||
true, /* is top level */
|
||||
false, /* is dynamic import */
|
||||
aLoader, NewVisitedSetForTopLevelImport(aURI));
|
||||
}
|
||||
|
||||
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateStaticImport(
|
||||
nsIURI* aURI, ModuleLoadRequest* aParent) {
|
||||
auto request =
|
||||
new ModuleLoadRequest(aURI, aParent->mFetchOptions, SRIMetadata(),
|
||||
aParent->mURI, false, /* is top level */
|
||||
false, /* is dynamic import */
|
||||
aParent->mLoader, aParent->mVisitedSet);
|
||||
|
||||
request->mIsInline = false;
|
||||
request->mScriptMode = aParent->mScriptMode;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateDynamicImport(
|
||||
nsIURI* aURI, ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
|
||||
ScriptLoader* aLoader, JS::Handle<JS::Value> aReferencingPrivate,
|
||||
JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aSpecifier);
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
auto request = new ModuleLoadRequest(
|
||||
aURI, aFetchOptions, SRIMetadata(), aBaseURL,
|
||||
true, /* is top level */
|
||||
true, /* is dynamic import */
|
||||
aLoader, NewVisitedSetForTopLevelImport(aURI));
|
||||
|
||||
request->mIsInline = false;
|
||||
request->mScriptMode = ScriptMode::eAsync;
|
||||
request->mDynamicReferencingPrivate = aReferencingPrivate;
|
||||
request->mDynamicSpecifier = aSpecifier;
|
||||
request->mDynamicPromise = aPromise;
|
||||
|
||||
HoldJSObjects(request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
ModuleLoadRequest::ModuleLoadRequest(
|
||||
nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
|
||||
const SRIMetadata& aIntegrity, nsIURI* aReferrer, bool aIsTopLevel,
|
||||
bool aIsDynamicImport, ScriptLoader* aLoader, VisitedURLSet* aVisitedSet)
|
||||
: ScriptLoadRequest(ScriptKind::Module, aURI, aFetchOptions, aIntegrity,
|
||||
aReferrer),
|
||||
mIsTopLevel(aIsTopLevel),
|
||||
mIsDynamicImport(aIsDynamicImport),
|
||||
mLoader(aLoader),
|
||||
mVisitedSet(aVisitedSet) {}
|
||||
|
||||
void ModuleLoadRequest::Cancel()
|
||||
{
|
||||
ScriptLoadRequest::Cancel();
|
||||
|
|
@ -166,5 +205,11 @@ ModuleLoadRequest::LoadFinished()
|
|||
mLoader = nullptr;
|
||||
}
|
||||
|
||||
void ModuleLoadRequest::ClearDynamicImport() {
|
||||
mDynamicReferencingPrivate = JS::UndefinedValue();
|
||||
mDynamicSpecifier = nullptr;
|
||||
mDynamicPromise = nullptr;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
|
|
|||
|
|
@ -37,30 +37,42 @@ class ModuleLoadRequest final : public ScriptLoadRequest
|
|||
ModuleLoadRequest(const ModuleLoadRequest& aOther) = delete;
|
||||
ModuleLoadRequest(ModuleLoadRequest&& aOther) = delete;
|
||||
|
||||
ModuleLoadRequest(nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
|
||||
const SRIMetadata& aIntegrity, nsIURI* aReferrer,
|
||||
bool aIsTopLevel, bool aIsDynamicImport,
|
||||
ScriptLoader* aLoader, VisitedURLSet* aVisitedSet);
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleLoadRequest,
|
||||
ScriptLoadRequest)
|
||||
|
||||
// Create a top-level module load request.
|
||||
ModuleLoadRequest(nsIURI* aURI,
|
||||
nsIScriptElement* aElement,
|
||||
uint32_t aVersion,
|
||||
CORSMode aCORSMode,
|
||||
const SRIMetadata& aIntegrity,
|
||||
nsIURI* aReferrer,
|
||||
mozilla::net::ReferrerPolicy,
|
||||
ScriptLoader* aLoader);
|
||||
static ModuleLoadRequest* CreateTopLevel(nsIURI* aURI,
|
||||
ScriptFetchOptions* aFetchOptions,
|
||||
const SRIMetadata& aIntegrity,
|
||||
nsIURI* aReferrer,
|
||||
ScriptLoader* aLoader);
|
||||
|
||||
// Create a module load request for an imported module.
|
||||
ModuleLoadRequest(nsIURI* aURI,
|
||||
ModuleLoadRequest* aParent);
|
||||
// Create a module load request for a static module import.
|
||||
static ModuleLoadRequest* CreateStaticImport(nsIURI* aURI,
|
||||
ModuleLoadRequest* aParent);
|
||||
|
||||
// Create a module load request for dynamic module import.
|
||||
static ModuleLoadRequest* CreateDynamicImport(
|
||||
nsIURI* aURI, ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
|
||||
ScriptLoader* aLoader, JS::Handle<JS::Value> aReferencingPrivate,
|
||||
JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise);
|
||||
|
||||
bool IsTopLevel() const override {
|
||||
return mIsTopLevel;
|
||||
}
|
||||
|
||||
bool IsDynamicImport() const { return mIsDynamicImport; }
|
||||
|
||||
void SetReady() override;
|
||||
void Cancel() override;
|
||||
void ClearDynamicImport();
|
||||
|
||||
void ModuleLoaded();
|
||||
void ModuleErrored();
|
||||
|
|
@ -74,9 +86,9 @@ private:
|
|||
public:
|
||||
// Is this a request for a top level module script or an import?
|
||||
const bool mIsTopLevel;
|
||||
|
||||
// The base URL used for resolving relative module imports.
|
||||
nsCOMPtr<nsIURI> mBaseURL;
|
||||
|
||||
// Is this the top level request for a dynamic module import?
|
||||
const bool mIsDynamicImport;
|
||||
|
||||
// Pointer to the script loader, used to trigger actions when the module load
|
||||
// finishes.
|
||||
|
|
@ -97,6 +109,11 @@ public:
|
|||
// Set of module URLs visited while fetching the module graph this request is
|
||||
// part of.
|
||||
RefPtr<VisitedURLSet> mVisitedSet;
|
||||
|
||||
// For dynamic imports, the details to pass to FinishDynamicImport.
|
||||
JS::Heap<JS::Value> mDynamicReferencingPrivate;
|
||||
JS::Heap<JSString*> mDynamicSpecifier;
|
||||
JS::Heap<JSObject*> mDynamicPromise;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
|
|
|
|||
|
|
@ -7,48 +7,116 @@
|
|||
* A class that handles loading and evaluation of <script> elements.
|
||||
*/
|
||||
|
||||
#include "ScriptLoader.h"
|
||||
#include "ModuleScript.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "ScriptLoader.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// A single module script. May be used to satisfy multiple load requests.
|
||||
//////////////////////////////////////////////////////////////
|
||||
// LoadedScript
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadedScript)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(LoadedScript)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LoadedScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(LoadedScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadedScript)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadedScript)
|
||||
|
||||
LoadedScript::LoadedScript(ScriptKind aKind,
|
||||
ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
|
||||
: mKind(aKind),
|
||||
mFetchOptions(aFetchOptions),
|
||||
mBaseURL(aBaseURL)
|
||||
{
|
||||
MOZ_ASSERT(mFetchOptions);
|
||||
MOZ_ASSERT(mBaseURL);
|
||||
}
|
||||
|
||||
LoadedScript::~LoadedScript() { DropJSObjects(this); }
|
||||
|
||||
void LoadedScript::AssociateWithScript(JSScript* aScript) {
|
||||
// Set a JSScript's private value to point to this object and
|
||||
// increment our reference count. This is decremented by
|
||||
// HostFinalizeTopLevelScript() below when the JSScript dies.
|
||||
|
||||
MOZ_ASSERT(JS::GetScriptPrivate(aScript).isUndefined());
|
||||
JS::SetScriptPrivate(aScript, JS::PrivateValue(this));
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate) {
|
||||
// Decrement the reference count of a LoadedScript object that is
|
||||
// pointed to by a dying JSScript. The reference count was
|
||||
// originally incremented by AssociateWithScript() above.
|
||||
|
||||
auto script = static_cast<LoadedScript*>(aPrivate.toPrivate());
|
||||
|
||||
#ifdef DEBUG
|
||||
if (script->IsModuleScript()) {
|
||||
JSObject* module = script->AsModuleScript()->mModuleRecord.unbarrieredGet();
|
||||
MOZ_ASSERT_IF(module, JS::GetModulePrivate(module) == aPrivate);
|
||||
}
|
||||
#endif
|
||||
|
||||
script->Release();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// ClassicScript
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
ClassicScript::ClassicScript(ScriptFetchOptions* aFetchOptions,
|
||||
nsIURI* aBaseURL)
|
||||
: LoadedScript(ScriptKind::Classic, aFetchOptions, aBaseURL) {}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// ModuleScript
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleScript)
|
||||
NS_INTERFACE_MAP_END
|
||||
NS_INTERFACE_MAP_END_INHERITING(LoadedScript)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleScript, LoadedScript)
|
||||
tmp->UnlinkModuleRecord();
|
||||
tmp->mParseError.setUndefined();
|
||||
tmp->mErrorToRethrow.setUndefined();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript, LoadedScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleScript, LoadedScript)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript)
|
||||
NS_IMPL_ADDREF_INHERITED(ModuleScript, LoadedScript)
|
||||
NS_IMPL_RELEASE_INHERITED(ModuleScript, LoadedScript)
|
||||
|
||||
ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL)
|
||||
: mLoader(aLoader),
|
||||
mBaseURL(aBaseURL)
|
||||
{
|
||||
MOZ_ASSERT(mLoader);
|
||||
MOZ_ASSERT(mBaseURL);
|
||||
MOZ_ASSERT(!mModuleRecord);
|
||||
ModuleScript::ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
|
||||
: LoadedScript(ScriptKind::Module, aFetchOptions, aBaseURL) {
|
||||
MOZ_ASSERT(!ModuleRecord());
|
||||
MOZ_ASSERT(!HasParseError());
|
||||
MOZ_ASSERT(!HasErrorToRethrow());
|
||||
}
|
||||
|
|
@ -56,12 +124,15 @@ ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL)
|
|||
void
|
||||
ModuleScript::UnlinkModuleRecord()
|
||||
{
|
||||
// Remove module's back reference to this object request if present.
|
||||
// Remove the module record's pointer to this object if present and
|
||||
// decrement our reference count. The reference is added by
|
||||
// SetModuleRecord() below.
|
||||
if (mModuleRecord) {
|
||||
MOZ_ASSERT(JS::GetModuleHostDefinedField(mModuleRecord).toPrivate() ==
|
||||
MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).toPrivate() ==
|
||||
this);
|
||||
JS::SetModuleHostDefinedField(mModuleRecord, JS::UndefinedValue());
|
||||
JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue());
|
||||
mModuleRecord = nullptr;
|
||||
Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,22 +140,24 @@ ModuleScript::~ModuleScript()
|
|||
{
|
||||
// The object may be destroyed without being unlinked first.
|
||||
UnlinkModuleRecord();
|
||||
DropJSObjects(this);
|
||||
}
|
||||
|
||||
void
|
||||
ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord)
|
||||
{
|
||||
MOZ_ASSERT(!mModuleRecord);
|
||||
MOZ_ASSERT(!HasParseError());
|
||||
MOZ_ASSERT(!HasErrorToRethrow());
|
||||
MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasParseError());
|
||||
MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasErrorToRethrow());
|
||||
|
||||
mModuleRecord = aModuleRecord;
|
||||
|
||||
// Make module's host defined field point to this module script object.
|
||||
// This is cleared in the UnlinkModuleRecord().
|
||||
JS::SetModuleHostDefinedField(mModuleRecord, JS::PrivateValue(this));
|
||||
// Make module's host defined field point to this object and
|
||||
// increment our reference count. This is decremented by
|
||||
// UnlinkModuleRecord() above.
|
||||
MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).isUndefined());
|
||||
JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
|
||||
HoldJSObjects(this);
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -103,7 +176,10 @@ void
|
|||
ModuleScript::SetErrorToRethrow(const JS::Value& aError)
|
||||
{
|
||||
MOZ_ASSERT(!aError.isUndefined());
|
||||
MOZ_ASSERT(!HasErrorToRethrow());
|
||||
|
||||
// This is only called after SetModuleRecord() or SetParseError() so we don't
|
||||
// need to call HoldJSObjects() here.
|
||||
MOZ_ASSERT(ModuleRecord() || HasParseError());
|
||||
|
||||
mErrorToRethrow = aError;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "jsapi.h"
|
||||
#include "ScriptLoader.h"
|
||||
|
||||
class nsIURI;
|
||||
|
||||
|
|
@ -17,10 +18,51 @@ namespace dom {
|
|||
|
||||
class ScriptLoader;
|
||||
|
||||
class ModuleScript final : public nsISupports
|
||||
void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate);
|
||||
|
||||
class ClassicScript;
|
||||
class ModuleScript;
|
||||
|
||||
class LoadedScript : public nsISupports
|
||||
{
|
||||
ScriptKind mKind;
|
||||
RefPtr<ScriptLoader> mLoader;
|
||||
RefPtr<ScriptFetchOptions> mFetchOptions;
|
||||
nsCOMPtr<nsIURI> mBaseURL;
|
||||
|
||||
protected:
|
||||
LoadedScript(ScriptKind aKind,
|
||||
ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
|
||||
|
||||
virtual ~LoadedScript();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(LoadedScript)
|
||||
|
||||
bool IsModuleScript() const { return mKind == ScriptKind::Module; }
|
||||
|
||||
inline ClassicScript* AsClassicScript();
|
||||
inline ModuleScript* AsModuleScript();
|
||||
|
||||
ScriptFetchOptions* FetchOptions() const { return mFetchOptions; }
|
||||
nsIURI* BaseURL() const { return mBaseURL; }
|
||||
|
||||
void AssociateWithScript(JSScript* aScript);
|
||||
};
|
||||
|
||||
class ClassicScript final : public LoadedScript
|
||||
{
|
||||
~ClassicScript() = default;
|
||||
|
||||
public:
|
||||
ClassicScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
|
||||
};
|
||||
|
||||
// A single module script. May be used to satisfy multiple load requests.
|
||||
|
||||
class ModuleScript final : public LoadedScript
|
||||
{
|
||||
JS::Heap<JSObject*> mModuleRecord;
|
||||
JS::Heap<JS::Value> mParseError;
|
||||
JS::Heap<JS::Value> mErrorToRethrow;
|
||||
|
|
@ -28,19 +70,17 @@ class ModuleScript final : public nsISupports
|
|||
~ModuleScript();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript)
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleScript,
|
||||
LoadedScript)
|
||||
|
||||
ModuleScript(ScriptLoader* aLoader,
|
||||
nsIURI* aBaseURL);
|
||||
ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
|
||||
|
||||
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
|
||||
void SetParseError(const JS::Value& aError);
|
||||
void SetErrorToRethrow(const JS::Value& aError);
|
||||
|
||||
ScriptLoader* Loader() const { return mLoader; }
|
||||
JSObject* ModuleRecord() const { return mModuleRecord; }
|
||||
nsIURI* BaseURL() const { return mBaseURL; }
|
||||
|
||||
JS::Value ParseError() const { return mParseError; }
|
||||
JS::Value ErrorToRethrow() const { return mErrorToRethrow; }
|
||||
|
|
@ -48,8 +88,20 @@ public:
|
|||
bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); }
|
||||
|
||||
void UnlinkModuleRecord();
|
||||
|
||||
friend void HostFinalizeTopLevelScript(JSFreeOp*, const JS::Value&);
|
||||
};
|
||||
|
||||
ClassicScript* LoadedScript::AsClassicScript() {
|
||||
MOZ_ASSERT(!IsModuleScript());
|
||||
return static_cast<ClassicScript*>(this);
|
||||
}
|
||||
|
||||
ModuleScript* LoadedScript::AsModuleScript() {
|
||||
MOZ_ASSERT(IsModuleScript());
|
||||
return static_cast<ModuleScript*>(this);
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ ScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader,
|
|||
// request.
|
||||
nsAutoString hintCharset;
|
||||
if (!mRequest->IsPreload()) {
|
||||
mRequest->mElement->GetScriptCharset(hintCharset);
|
||||
mRequest->Element()->GetScriptCharset(hintCharset);
|
||||
} else {
|
||||
nsTArray<ScriptLoader::PreloadInfo>::index_type i =
|
||||
mScriptLoader->mPreloads.IndexOf(mRequest, 0,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -34,6 +34,7 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
|
||||
class AutoJSAPI;
|
||||
class LoadedScript;
|
||||
class ModuleLoadRequest;
|
||||
class ModuleScript;
|
||||
class ScriptLoadRequestList;
|
||||
|
|
@ -47,6 +48,34 @@ enum class ScriptKind {
|
|||
Module
|
||||
};
|
||||
|
||||
/*
|
||||
* Some options used when fetching script resources. This only loosely
|
||||
* corresponds to HTML's "script fetch options".
|
||||
*
|
||||
* These are common to all modules in a module graph, and hence a single
|
||||
* instance is shared by all ModuleLoadRequest objects in a graph.
|
||||
*/
|
||||
|
||||
class ScriptFetchOptions
|
||||
{
|
||||
~ScriptFetchOptions();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ScriptFetchOptions)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ScriptFetchOptions)
|
||||
|
||||
ScriptFetchOptions(mozilla::CORSMode aCORSMode,
|
||||
mozilla::net::ReferrerPolicy aReferrerPolicy,
|
||||
nsIScriptElement* aElement,
|
||||
nsIPrincipal* aTriggeringPrincipal);
|
||||
|
||||
const mozilla::CORSMode mCORSMode;
|
||||
const mozilla::net::ReferrerPolicy mReferrerPolicy;
|
||||
bool mIsPreload;
|
||||
nsCOMPtr<nsIScriptElement> mElement;
|
||||
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
|
||||
};
|
||||
|
||||
class ScriptLoadRequest : public nsISupports,
|
||||
private mozilla::LinkedListElement<ScriptLoadRequest>
|
||||
{
|
||||
|
|
@ -62,36 +91,9 @@ protected:
|
|||
public:
|
||||
ScriptLoadRequest(ScriptKind aKind,
|
||||
nsIURI* aURI,
|
||||
nsIScriptElement* aElement,
|
||||
uint32_t aVersion,
|
||||
mozilla::CORSMode aCORSMode,
|
||||
ScriptFetchOptions* aFetchOptions,
|
||||
const SRIMetadata& aIntegrity,
|
||||
nsIURI* aReferrer,
|
||||
mozilla::net::ReferrerPolicy aReferrerPolicy)
|
||||
: mKind(aKind),
|
||||
mElement(aElement),
|
||||
mProgress(Progress::Loading),
|
||||
mScriptMode(ScriptMode::eBlocking),
|
||||
mIsInline(true),
|
||||
mHasSourceMapURL(false),
|
||||
mInDeferList(false),
|
||||
mInAsyncList(false),
|
||||
mIsNonAsyncScriptInserted(false),
|
||||
mIsXSLT(false),
|
||||
mIsCanceled(false),
|
||||
mWasCompiledOMT(false),
|
||||
mOffThreadToken(nullptr),
|
||||
mScriptTextBuf(nullptr),
|
||||
mScriptTextLength(0),
|
||||
mJSVersion(aVersion),
|
||||
mURI(aURI),
|
||||
mLineNo(1),
|
||||
mCORSMode(aCORSMode),
|
||||
mIntegrity(aIntegrity),
|
||||
mReferrer(aReferrer),
|
||||
mReferrerPolicy(aReferrerPolicy)
|
||||
{
|
||||
}
|
||||
nsIURI* aReferrer);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
|
||||
|
|
@ -106,16 +108,16 @@ public:
|
|||
void FireScriptAvailable(nsresult aResult)
|
||||
{
|
||||
bool isInlineClassicScript = mIsInline && !IsModuleRequest();
|
||||
mElement->ScriptAvailable(aResult, mElement, isInlineClassicScript, mURI, mLineNo);
|
||||
Element()->ScriptAvailable(aResult, Element(), isInlineClassicScript, mURI, mLineNo);
|
||||
}
|
||||
void FireScriptEvaluated(nsresult aResult)
|
||||
{
|
||||
mElement->ScriptEvaluated(aResult, mElement, mIsInline);
|
||||
Element()->ScriptEvaluated(aResult, Element(), mIsInline);
|
||||
}
|
||||
|
||||
bool IsPreload()
|
||||
{
|
||||
return mElement == nullptr;
|
||||
bool IsPreload() const {
|
||||
MOZ_ASSERT_IF(mFetchOptions->mIsPreload, !Element());
|
||||
return mFetchOptions->mIsPreload;
|
||||
}
|
||||
|
||||
virtual void Cancel();
|
||||
|
|
@ -178,15 +180,61 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
mozilla::CORSMode CORSMode() const
|
||||
{
|
||||
return mFetchOptions->mCORSMode;
|
||||
}
|
||||
mozilla::net::ReferrerPolicy ReferrerPolicy() const
|
||||
{
|
||||
return mFetchOptions->mReferrerPolicy;
|
||||
}
|
||||
nsIScriptElement* Element() const
|
||||
{
|
||||
return mFetchOptions->mElement;
|
||||
}
|
||||
nsIPrincipal* TriggeringPrincipal() const
|
||||
{
|
||||
return mFetchOptions->mTriggeringPrincipal;
|
||||
}
|
||||
|
||||
// Make this request a preload (speculative) request.
|
||||
void SetIsPreloadRequest()
|
||||
{
|
||||
MOZ_ASSERT(!Element());
|
||||
MOZ_ASSERT(!IsPreload());
|
||||
mFetchOptions->mIsPreload = true;
|
||||
}
|
||||
|
||||
// Make a preload request into an actual load request for the given element.
|
||||
void SetIsLoadRequest(nsIScriptElement* aElement)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
MOZ_ASSERT(!Element());
|
||||
MOZ_ASSERT(IsPreload());
|
||||
mFetchOptions->mElement = aElement;
|
||||
mFetchOptions->mIsPreload = false;
|
||||
}
|
||||
|
||||
FromParser GetParserCreated() const
|
||||
{
|
||||
nsIScriptElement* element = Element();
|
||||
if (!element) {
|
||||
return NOT_FROM_PARSER;
|
||||
}
|
||||
return element->GetParserCreated();
|
||||
}
|
||||
|
||||
void SetScript(JSScript* aScript);
|
||||
|
||||
void MaybeCancelOffThreadScript();
|
||||
|
||||
using super::getNext;
|
||||
using super::isInList;
|
||||
|
||||
const ScriptKind mKind;
|
||||
nsCOMPtr<nsIScriptElement> mElement;
|
||||
const ScriptKind mKind; // Whether this is a classic script or a module script.
|
||||
ScriptMode mScriptMode; // Whether this is a blocking, defer or async script.
|
||||
Progress mProgress; // Are we still waiting for a load to complete?
|
||||
ScriptMode mScriptMode; // Whether this script is blocking, deferred or async.
|
||||
bool mScriptFromHead; // Synchronous head script block loading of other non js/css content.
|
||||
bool mIsInline; // Is the script inline or loaded?
|
||||
bool mHasSourceMapURL; // Does the HTTP header have a source map url?
|
||||
bool mInDeferList; // True if we live in mDeferRequests.
|
||||
|
|
@ -199,15 +247,21 @@ public:
|
|||
nsString mSourceMapURL; // Holds source map url for loaded scripts
|
||||
char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
|
||||
size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
|
||||
uint32_t mJSVersion;
|
||||
const nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIPrincipal> mOriginPrincipal;
|
||||
nsAutoCString mURL; // Keep the URI's filename alive during off thread parsing.
|
||||
int32_t mLineNo;
|
||||
const mozilla::CORSMode mCORSMode;
|
||||
const SRIMetadata mIntegrity;
|
||||
const nsCOMPtr<nsIURI> mReferrer;
|
||||
const mozilla::net::ReferrerPolicy mReferrerPolicy;
|
||||
|
||||
RefPtr<ScriptFetchOptions> mFetchOptions;
|
||||
|
||||
// Holds the top-level JSScript that corresponds to the current source, once
|
||||
// it is parsed, and planned to be saved in the bytecode cache.
|
||||
JS::Heap<JSScript*> mScript;
|
||||
|
||||
// The base URL used for resolving relative module imports.
|
||||
nsCOMPtr<nsIURI> mBaseURL;
|
||||
};
|
||||
|
||||
class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest>
|
||||
|
|
@ -511,13 +565,45 @@ public:
|
|||
*/
|
||||
void ClearModuleMap();
|
||||
|
||||
/**
|
||||
* Implement the HostResolveImportedModule abstract operation.
|
||||
*
|
||||
* Resolve a module specifier string and look this up in the module
|
||||
* map, returning the result. This is only called for previously
|
||||
* loaded modules and always succeeds.
|
||||
*
|
||||
* @param aReferencingPrivate A JS::Value which is either undefined
|
||||
* or contains a LoadedScript private pointer.
|
||||
* @param aSpecifier The module specifier.
|
||||
* @param aModuleOut This is set to the module found.
|
||||
*/
|
||||
static void ResolveImportedModule(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aReferencingPrivate,
|
||||
JS::Handle<JSString*> aSpecifier,
|
||||
JS::MutableHandle<JSObject*> aModuleOut);
|
||||
|
||||
void StartDynamicImport(ModuleLoadRequest* aRequest);
|
||||
void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
|
||||
void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
|
||||
nsresult aResult);
|
||||
|
||||
/*
|
||||
* Get the currently active script. This is used as the initiating script when
|
||||
* executing timeout handler scripts.
|
||||
*/
|
||||
static LoadedScript* GetActiveScript(JSContext* aCx);
|
||||
|
||||
nsIDocument* GetDocument() const { return mDocument; }
|
||||
|
||||
private:
|
||||
virtual ~ScriptLoader();
|
||||
|
||||
void EnsureModuleHooksInitialized();
|
||||
|
||||
ScriptLoadRequest* CreateLoadRequest(ScriptKind aKind,
|
||||
nsIURI* aURI,
|
||||
nsIScriptElement* aElement,
|
||||
uint32_t aVersion,
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
mozilla::CORSMode aCORSMode,
|
||||
const SRIMetadata& aIntegrity,
|
||||
mozilla::net::ReferrerPolicy aReferrerPolicy);
|
||||
|
|
@ -591,6 +677,7 @@ private:
|
|||
|
||||
nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest);
|
||||
nsresult ProcessRequest(ScriptLoadRequest* aRequest);
|
||||
void ProcessDynamicImport(ModuleLoadRequest* aRequest);
|
||||
nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
|
||||
void FireScriptAvailable(nsresult aResult,
|
||||
ScriptLoadRequest* aRequest);
|
||||
|
|
@ -630,8 +717,9 @@ private:
|
|||
ModuleScript* GetFetchedModule(nsIURI* aURL) const;
|
||||
|
||||
friend JSObject*
|
||||
HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule,
|
||||
JS::Handle<JSString*> aSpecifier);
|
||||
HostResolveImportedModule(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aReferencingPrivate,
|
||||
JS::Handle<JSString*> aSpecifier);
|
||||
|
||||
nsresult CreateModuleScript(ModuleLoadRequest* aRequest);
|
||||
nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest);
|
||||
|
|
@ -644,6 +732,8 @@ private:
|
|||
RefPtr<mozilla::GenericPromise>
|
||||
StartFetchingModuleAndDependencies(ModuleLoadRequest* aParent, nsIURI* aURI);
|
||||
|
||||
void RunScriptWhenSafe(ScriptLoadRequest* aRequest);
|
||||
|
||||
nsIDocument* mDocument; // [WEAK]
|
||||
nsCOMArray<nsIScriptLoaderObserver> mObservers;
|
||||
ScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests;
|
||||
|
|
@ -653,6 +743,7 @@ private:
|
|||
ScriptLoadRequestList mLoadedAsyncRequests;
|
||||
ScriptLoadRequestList mDeferRequests;
|
||||
ScriptLoadRequestList mXSLTRequests;
|
||||
ScriptLoadRequestList mDynamicImportRequests;
|
||||
RefPtr<ScriptLoadRequest> mParserBlockingRequest;
|
||||
|
||||
// In mRequests, the additional information here is stored by the element.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ XPIDL_MODULE = 'dom'
|
|||
EXPORTS += ['nsIScriptElement.h']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'ModuleScript.h',
|
||||
'ScriptElement.h',
|
||||
'ScriptLoader.h',
|
||||
'ScriptSettings.h',
|
||||
|
|
|
|||
|
|
@ -202,8 +202,6 @@ public:
|
|||
compileOptions.setVersion(JSVERSION_DEFAULT);
|
||||
compileOptions.setIsRunOnce(true);
|
||||
|
||||
// We only need the setNoScriptRval bit when compiling off-thread here,
|
||||
// since otherwise nsJSUtils::EvaluateString will set it up for us.
|
||||
compileOptions.setNoScriptRval(true);
|
||||
|
||||
JS::Rooted<JS::Value> unused(cx);
|
||||
|
|
|
|||
|
|
@ -434,14 +434,18 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
|
|||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(uriSpec.get(), mLineNumber)
|
||||
.setVersion(JSVERSION_LATEST);
|
||||
nsJSUtils::EvaluateOptions evalOptions(cx);
|
||||
if (!nsJSUtils::GetScopeChainForElement(cx, boundElement,
|
||||
evalOptions.scopeChain)) {
|
||||
JS::AutoObjectVector scopeChain(cx);
|
||||
if (!nsJSUtils::GetScopeChainForElement(cx, boundElement, scopeChain)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
rv = nsJSUtils::EvaluateString(cx, nsDependentString(mFieldText,
|
||||
mFieldTextLength),
|
||||
scopeObject, options, evalOptions, &result);
|
||||
rv = NS_OK;
|
||||
{
|
||||
nsJSUtils::ExecutionContext exec(cx, scopeObject);
|
||||
exec.SetScopeChain(scopeChain);
|
||||
exec.Compile(options, nsDependentString(mFieldText, mFieldTextLength));
|
||||
rv = exec.ExecScript(&result);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,4 @@ EXPORTS.mozilla += [
|
|||
'RemoteSpellCheckEngineParent.h',
|
||||
]
|
||||
|
||||
# This variable is referenced in configure.in. Make sure to change that file
|
||||
# too if you need to change this variable.
|
||||
DEFINES['HUNSPELL_STATIC'] = True
|
||||
DEFINES['BUILDING_LIBHUNSPELL'] = True
|
||||
|
|
|
|||
|
|
@ -90,8 +90,8 @@
|
|||
#define dupSFX (1 << 0)
|
||||
#define dupPFX (1 << 1)
|
||||
|
||||
class PfxEntry;
|
||||
class SfxEntry;
|
||||
class LIBHUNSPELL_DLL_EXPORTED PfxEntry;
|
||||
class LIBHUNSPELL_DLL_EXPORTED SfxEntry;
|
||||
|
||||
class LIBHUNSPELL_DLL_EXPORTED AffixMgr {
|
||||
PfxEntry* pStart[SETSIZE];
|
||||
|
|
|
|||
|
|
@ -81,13 +81,6 @@
|
|||
#include "atypes.hxx"
|
||||
#include "langnum.hxx"
|
||||
|
||||
// Unicode character encoding information
|
||||
struct unicode_info {
|
||||
unsigned short c;
|
||||
unsigned short cupper;
|
||||
unsigned short clower;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <wchar.h>
|
||||
|
|
|
|||
|
|
@ -74,6 +74,9 @@
|
|||
#ifndef __CSUTILHXX__
|
||||
#define __CSUTILHXX__
|
||||
|
||||
// Quick hack to make building hunspell as a shared library work.
|
||||
#undef MOZILLA_CLIENT
|
||||
|
||||
#include "hunvisapi.h"
|
||||
|
||||
// First some base level utility routines
|
||||
|
|
@ -190,6 +193,13 @@ struct cs_info {
|
|||
unsigned char cupper;
|
||||
};
|
||||
|
||||
// Unicode character encoding information
|
||||
struct unicode_info {
|
||||
unsigned short c;
|
||||
unsigned short cupper;
|
||||
unsigned short clower;
|
||||
};
|
||||
|
||||
LIBHUNSPELL_DLL_EXPORTED int initialize_utf_tbl();
|
||||
LIBHUNSPELL_DLL_EXPORTED void free_utf_tbl();
|
||||
LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetoupper(unsigned short c,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
SOURCES += [
|
||||
'affentry.cxx',
|
||||
'affixmgr.cxx',
|
||||
'csutil.cxx',
|
||||
|
|
@ -14,13 +14,16 @@ UNIFIED_SOURCES += [
|
|||
'phonet.cxx',
|
||||
'replist.cxx',
|
||||
'suggestmgr.cxx',
|
||||
'utf_info.cxx',
|
||||
]
|
||||
|
||||
# This variable is referenced in configure.in. Make sure to change that file
|
||||
# too if you need to change this variable.
|
||||
DEFINES['HUNSPELL_STATIC'] = True
|
||||
DEFINES['BUILDING_LIBHUNSPELL'] = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
SharedLibrary('hunspell')
|
||||
|
||||
USE_LIBS += [
|
||||
'mozglue'
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../glue',
|
||||
|
|
|
|||
9879
extensions/spellcheck/hunspell/src/utf_info.cxx
Normal file
9879
extensions/spellcheck/hunspell/src/utf_info.cxx
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -251,12 +251,14 @@ public:
|
|||
mSurfaces.Put(aSurface->GetSurfaceKey(), aSurface);
|
||||
}
|
||||
|
||||
void Remove(NotNull<CachedSurface*> aSurface)
|
||||
already_AddRefed<CachedSurface> Remove(NotNull<CachedSurface*> aSurface)
|
||||
{
|
||||
MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()),
|
||||
"Should not be removing a surface we don't have");
|
||||
|
||||
mSurfaces.Remove(aSurface->GetSurfaceKey());
|
||||
RefPtr<CachedSurface> surface;
|
||||
mSurfaces.Remove(aSurface->GetSurfaceKey(), getter_AddRefs(surface));
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey)
|
||||
|
|
@ -507,10 +509,14 @@ public:
|
|||
}
|
||||
|
||||
StopTracking(aSurface, aAutoLock);
|
||||
cache->Remove(aSurface);
|
||||
|
||||
// Remove the per-image cache if it's unneeded now. (Keep it if the image is
|
||||
// locked, since the per-image cache is where we store that state.)
|
||||
// Individual surfaces must be freed outside the lock.
|
||||
mCachedSurfacesDiscard.AppendElement(cache->Remove(aSurface));
|
||||
|
||||
// Remove the per-image cache if it's unneeded now. Keep it if the image is
|
||||
// locked, since the per-image cache is where we store that state. Note that
|
||||
// we don't push it into mImageCachesDiscard because all of its surfaces
|
||||
// have been removed, so it is safe to free while holding the lock.
|
||||
if (cache->IsEmpty() && !cache->IsLocked()) {
|
||||
mImageCaches.Remove(imageKey);
|
||||
}
|
||||
|
|
@ -719,11 +725,12 @@ public:
|
|||
DoUnlockSurfaces(WrapNotNull(cache), aAutoLock);
|
||||
}
|
||||
|
||||
void RemoveImage(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock)
|
||||
already_AddRefed<ImageSurfaceCache>
|
||||
RemoveImage(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock)
|
||||
{
|
||||
RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
|
||||
if (!cache) {
|
||||
return; // No cached surfaces for this image, so nothing to do.
|
||||
return nullptr; // No cached surfaces for this image, so nothing to do.
|
||||
}
|
||||
|
||||
// Discard all of the cached surfaces for this image.
|
||||
|
|
@ -738,6 +745,10 @@ public:
|
|||
// The per-image cache isn't needed anymore, so remove it as well.
|
||||
// This implicitly unlocks the image if it was locked.
|
||||
mImageCaches.Remove(aImageKey);
|
||||
|
||||
// Since we did not actually remove any of the surfaces from the cache
|
||||
// itself, only stopped tracking them, we should free it outside the lock.
|
||||
return cache.forget();
|
||||
}
|
||||
|
||||
void DiscardAll(const StaticMutexAutoLock& aAutoLock)
|
||||
|
|
@ -776,6 +787,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void TakeDiscard(nsTArray<RefPtr<CachedSurface>>& aDiscard,
|
||||
const StaticMutexAutoLock& aAutoLock)
|
||||
{
|
||||
MOZ_ASSERT(aDiscard.IsEmpty());
|
||||
aDiscard = Move(mCachedSurfacesDiscard);
|
||||
}
|
||||
|
||||
void LockSurface(NotNull<CachedSurface*> aSurface,
|
||||
const StaticMutexAutoLock& aAutoLock)
|
||||
{
|
||||
|
|
@ -922,10 +940,12 @@ private:
|
|||
Remove(WrapNotNull(surface), aAutoLock);
|
||||
}
|
||||
|
||||
struct SurfaceTracker : public ExpirationTrackerImpl<CachedSurface, 2,
|
||||
StaticMutex,
|
||||
StaticMutexAutoLock>
|
||||
class SurfaceTracker final :
|
||||
public ExpirationTrackerImpl<CachedSurface, 2,
|
||||
StaticMutex,
|
||||
StaticMutexAutoLock>
|
||||
{
|
||||
public:
|
||||
explicit SurfaceTracker(uint32_t aSurfaceCacheExpirationTimeMS)
|
||||
: ExpirationTrackerImpl<CachedSurface, 2,
|
||||
StaticMutex, StaticMutexAutoLock>(
|
||||
|
|
@ -939,23 +959,40 @@ private:
|
|||
sInstance->Remove(WrapNotNull(aSurface), aAutoLock);
|
||||
}
|
||||
|
||||
void NotifyHandlerEndLocked(const StaticMutexAutoLock& aAutoLock) override
|
||||
{
|
||||
sInstance->TakeDiscard(mDiscard, aAutoLock);
|
||||
}
|
||||
|
||||
void NotifyHandlerEnd() override
|
||||
{
|
||||
nsTArray<RefPtr<CachedSurface>> discard(Move(mDiscard));
|
||||
}
|
||||
|
||||
StaticMutex& GetMutex() override
|
||||
{
|
||||
return sInstanceMutex;
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<CachedSurface>> mDiscard;
|
||||
};
|
||||
|
||||
struct MemoryPressureObserver : public nsIObserver
|
||||
class MemoryPressureObserver final : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Observe(nsISupports*,
|
||||
const char* aTopic,
|
||||
const char16_t*) override
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
|
||||
sInstance->DiscardForMemoryPressure(lock);
|
||||
nsTArray<RefPtr<CachedSurface>> discard;
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
|
||||
sInstance->DiscardForMemoryPressure(lock);
|
||||
sInstance->TakeDiscard(discard, lock);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -967,6 +1004,7 @@ private:
|
|||
nsTArray<CostEntry> mCosts;
|
||||
nsRefPtrHashtable<nsPtrHashKey<Image>,
|
||||
ImageSurfaceCache> mImageCaches;
|
||||
nsTArray<RefPtr<CachedSurface>> mCachedSurfacesDiscard;
|
||||
SurfaceTracker mExpirationTracker;
|
||||
RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
|
||||
nsTArray<RefPtr<image::Image>> mReleasingImagesOnMainThread;
|
||||
|
|
@ -1043,45 +1081,72 @@ SurfaceCache::Initialize()
|
|||
/* static */ void
|
||||
SurfaceCache::Shutdown()
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
|
||||
sInstance = nullptr;
|
||||
RefPtr<SurfaceCacheImpl> cache;
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
|
||||
cache = sInstance.forget();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ LookupResult
|
||||
SurfaceCache::Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return LookupResult(MatchType::NOT_FOUND);
|
||||
nsTArray<RefPtr<CachedSurface>> discard;
|
||||
LookupResult rv(MatchType::NOT_FOUND);
|
||||
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = sInstance->Lookup(aImageKey, aSurfaceKey, lock);
|
||||
sInstance->TakeDiscard(discard, lock);
|
||||
}
|
||||
|
||||
return sInstance->Lookup(aImageKey, aSurfaceKey, lock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */ LookupResult
|
||||
SurfaceCache::LookupBestMatch(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return LookupResult(MatchType::NOT_FOUND);
|
||||
nsTArray<RefPtr<CachedSurface>> discard;
|
||||
LookupResult rv(MatchType::NOT_FOUND);
|
||||
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = sInstance->LookupBestMatch(aImageKey, aSurfaceKey, lock);
|
||||
sInstance->TakeDiscard(discard, lock);
|
||||
}
|
||||
|
||||
return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, lock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */ InsertOutcome
|
||||
SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider)
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return InsertOutcome::FAILURE;
|
||||
nsTArray<RefPtr<CachedSurface>> discard;
|
||||
InsertOutcome rv(InsertOutcome::FAILURE);
|
||||
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = sInstance->Insert(aProvider, /* aSetAvailable = */ false, lock);
|
||||
sInstance->TakeDiscard(discard, lock);
|
||||
}
|
||||
|
||||
return sInstance->Insert(aProvider, /* aSetAvailable = */ false, lock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
|
@ -1148,18 +1213,25 @@ SurfaceCache::UnlockEntries(const ImageKey aImageKey)
|
|||
/* static */ void
|
||||
SurfaceCache::RemoveImage(const ImageKey aImageKey)
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance) {
|
||||
sInstance->RemoveImage(aImageKey, lock);
|
||||
RefPtr<ImageSurfaceCache> discard;
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance) {
|
||||
discard = sInstance->RemoveImage(aImageKey, lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SurfaceCache::DiscardAll()
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance) {
|
||||
sInstance->DiscardAll(lock);
|
||||
nsTArray<RefPtr<CachedSurface>> discard;
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance) {
|
||||
sInstance->DiscardAll(lock);
|
||||
sInstance->TakeDiscard(discard, lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1191,15 +1263,15 @@ SurfaceCache::MaximumCapacity()
|
|||
void SurfaceCache::ReleaseImageOnMainThread(
|
||||
already_AddRefed<image::Image> aImage, bool aAlwaysProxy) {
|
||||
if (NS_IsMainThread() && !aAlwaysProxy) {
|
||||
RefPtr<image::Image> image = std::move(aImage);
|
||||
RefPtr<image::Image> image = Move(aImage);
|
||||
return;
|
||||
}
|
||||
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance) {
|
||||
sInstance->ReleaseImageOnMainThread(std::move(aImage), lock);
|
||||
sInstance->ReleaseImageOnMainThread(Move(aImage), lock);
|
||||
} else {
|
||||
NS_ReleaseOnMainThread(std::move(aImage), /* aAlwaysProxy */ true);
|
||||
NS_ReleaseOnMainThread(Move(aImage), /* aAlwaysProxy */ true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "builtin/ModuleObject.h"
|
||||
|
||||
#include "builtin/Promise.h"
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
|
|
@ -725,6 +726,12 @@ ModuleObject::namespace_()
|
|||
return &value.toObject().as<ModuleNamespaceObject>();
|
||||
}
|
||||
|
||||
ScriptSourceObject*
|
||||
ModuleObject::scriptSourceObject() const
|
||||
{
|
||||
return &getReservedSlot(ScriptSourceObjectSlot).toObject().as<ScriptSourceObject>();
|
||||
}
|
||||
|
||||
FunctionDeclarationVector*
|
||||
ModuleObject::functionDeclarations()
|
||||
{
|
||||
|
|
@ -738,8 +745,10 @@ ModuleObject::functionDeclarations()
|
|||
void
|
||||
ModuleObject::init(HandleScript script)
|
||||
{
|
||||
MOZ_ASSERT(script);
|
||||
initReservedSlot(ScriptSlot, PrivateValue(script));
|
||||
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
|
||||
initReservedSlot(ScriptSourceObjectSlot, ObjectValue(script->scriptSourceUnwrap()));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -826,18 +835,22 @@ ModuleObject::fixEnvironmentsAfterCompartmentMerge()
|
|||
AssertModuleScopesMatch(this);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleObject::hasScript() const
|
||||
JSScript*
|
||||
ModuleObject::maybeScript() const
|
||||
{
|
||||
// When modules are parsed via the Reflect.parse() API, the module object
|
||||
// doesn't have a script.
|
||||
return !getReservedSlot(ScriptSlot).isUndefined();
|
||||
Value value = getReservedSlot(ScriptSlot);
|
||||
if (value.isUndefined())
|
||||
return nullptr;
|
||||
|
||||
return static_cast<JSScript*>(value.toPrivate());
|
||||
}
|
||||
|
||||
JSScript*
|
||||
ModuleObject::script() const
|
||||
{
|
||||
return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
|
||||
JSScript* ptr = maybeScript();
|
||||
MOZ_RELEASE_ASSERT(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -868,16 +881,23 @@ ModuleObject::evaluationError() const
|
|||
return getReservedSlot(EvaluationErrorSlot);
|
||||
}
|
||||
|
||||
Value
|
||||
ModuleObject::hostDefinedField() const
|
||||
JSObject*
|
||||
ModuleObject::metaObject() const
|
||||
{
|
||||
return getReservedSlot(HostDefinedSlot);
|
||||
Value value = getReservedSlot(MetaObjectSlot);
|
||||
if (value.isObject())
|
||||
return &value.toObject();
|
||||
|
||||
MOZ_ASSERT(value.isUndefined());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ModuleObject::setHostDefinedField(const JS::Value& value)
|
||||
ModuleObject::setMetaObject(JSObject* obj)
|
||||
{
|
||||
setReservedSlot(HostDefinedSlot, value);
|
||||
MOZ_ASSERT(obj);
|
||||
MOZ_ASSERT(!metaObject());
|
||||
setReservedSlot(MetaObjectSlot, ObjectValue(*obj));
|
||||
}
|
||||
|
||||
Scope*
|
||||
|
|
@ -890,8 +910,8 @@ ModuleObject::enclosingScope() const
|
|||
ModuleObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
ModuleObject& module = obj->as<ModuleObject>();
|
||||
if (module.hasScript()) {
|
||||
JSScript* script = module.script();
|
||||
JSScript* script = module.maybeScript();
|
||||
if (script) {
|
||||
TraceManuallyBarrieredEdge(trc, &script, "Module script");
|
||||
module.setReservedSlot(ScriptSlot, PrivateValue(script));
|
||||
}
|
||||
|
|
@ -960,6 +980,11 @@ ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue
|
|||
#endif
|
||||
|
||||
RootedScript script(cx, self->script());
|
||||
|
||||
// The top-level script if a module is only ever executed once. Clear the
|
||||
// reference to prevent us keeping this alive unnecessarily.
|
||||
self->setReservedSlot(ScriptSlot, UndefinedValue());
|
||||
|
||||
RootedModuleEnvironmentObject scope(cx, self->environment());
|
||||
if (!scope) {
|
||||
JS_ReportErrorASCII(cx, "Module declarations have not yet been instantiated");
|
||||
|
|
@ -1015,6 +1040,22 @@ ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self)
|
|||
return InvokeSelfHostedMethod(cx, self, cx->names().ModuleEvaluate);
|
||||
}
|
||||
|
||||
/* static */ ModuleNamespaceObject*
|
||||
ModuleObject::GetOrCreateModuleNamespace(JSContext* cx, HandleModuleObject self)
|
||||
{
|
||||
FixedInvokeArgs<1> args(cx);
|
||||
args[0].setObject(*self);
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!CallSelfHostedFunction(cx, cx->names().GetModuleNamespace, UndefinedHandleValue, args,
|
||||
&result))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &result.toObject().as<ModuleNamespaceObject>();
|
||||
}
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, EvaluationErrorSlot)
|
||||
|
|
@ -1483,3 +1524,109 @@ ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
|
|||
|
||||
return array;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::GetOrCreateModuleMetaObject(JSContext* cx, HandleObject moduleArg)
|
||||
{
|
||||
HandleModuleObject module = moduleArg.as<ModuleObject>();
|
||||
if (JSObject* obj = module->metaObject())
|
||||
return obj;
|
||||
|
||||
RootedObject metaObject(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
|
||||
if (!metaObject)
|
||||
return nullptr;
|
||||
|
||||
JS::ModuleMetadataHook func = cx->runtime()->moduleMetadataHook;
|
||||
MOZ_ASSERT(func);
|
||||
if (!func(cx, module, metaObject))
|
||||
return nullptr;
|
||||
|
||||
module->setMetaObject(metaObject);
|
||||
|
||||
return metaObject;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier)
|
||||
{
|
||||
JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
|
||||
if (!moduleResolveHook) {
|
||||
JS_ReportErrorASCII(cx, "Module resolve hook not set");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedObject result(cx, moduleResolveHook(cx, referencingPrivate, specifier));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!result->is<ModuleObject>()) {
|
||||
JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::StartDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleValue specifierArg)
|
||||
{
|
||||
RootedObject promiseConstructor(cx, JS::GetPromiseConstructor(cx));
|
||||
if (!promiseConstructor) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedObject promiseObject(cx, JS::NewPromiseObject(cx, nullptr));
|
||||
if (!promiseObject) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Handle<PromiseObject*> promise = promiseObject.as<PromiseObject>();
|
||||
|
||||
RootedString specifier(cx, ToString(cx, specifierArg));
|
||||
if (!specifier) {
|
||||
if (!RejectPromiseWithPendingError(cx, promise))
|
||||
return nullptr;
|
||||
return promise;
|
||||
}
|
||||
|
||||
JS::ModuleDynamicImportHook importHook = cx->runtime()->moduleDynamicImportHook;
|
||||
MOZ_ASSERT(importHook);
|
||||
if (!importHook(cx, referencingPrivate, specifier, promise)) {
|
||||
if (!RejectPromiseWithPendingError(cx, promise))
|
||||
return nullptr;
|
||||
return promise;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
bool
|
||||
js::FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
|
||||
HandleObject promiseArg)
|
||||
{
|
||||
Handle<PromiseObject*> promise = promiseArg.as<PromiseObject>();
|
||||
|
||||
if (cx->isExceptionPending()) {
|
||||
return RejectPromiseWithPendingError(cx, promise);
|
||||
}
|
||||
|
||||
RootedObject result(cx, CallModuleResolveHook(cx, referencingPrivate, specifier));
|
||||
if (!result) {
|
||||
return RejectPromiseWithPendingError(cx, promise);
|
||||
}
|
||||
|
||||
RootedModuleObject module(cx, &result->as<ModuleObject>());
|
||||
if (module->status() != MODULE_STATUS_EVALUATED) {
|
||||
JS_ReportErrorASCII(cx, "Unevaluated or errored module returned by module resolve hook");
|
||||
return RejectPromiseWithPendingError(cx, promise);
|
||||
}
|
||||
|
||||
RootedObject ns(cx, ModuleObject::GetOrCreateModuleNamespace(cx, module));
|
||||
if (!ns) {
|
||||
return RejectPromiseWithPendingError(cx, promise);
|
||||
}
|
||||
|
||||
RootedValue value(cx, ObjectValue(*ns));
|
||||
return PromiseObject::resolve(cx, promise, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,8 @@ class ModuleObject : public NativeObject
|
|||
NamespaceSlot,
|
||||
StatusSlot,
|
||||
EvaluationErrorSlot,
|
||||
HostDefinedSlot,
|
||||
ScriptSourceObjectSlot,
|
||||
MetaObjectSlot,
|
||||
RequestedModulesSlot,
|
||||
ImportEntriesSlot,
|
||||
LocalExportEntriesSlot,
|
||||
|
|
@ -265,6 +266,7 @@ class ModuleObject : public NativeObject
|
|||
#endif
|
||||
void fixEnvironmentsAfterCompartmentMerge();
|
||||
|
||||
JSScript* maybeScript() const;
|
||||
JSScript* script() const;
|
||||
Scope* enclosingScope() const;
|
||||
ModuleEnvironmentObject& initialEnvironment() const;
|
||||
|
|
@ -273,7 +275,8 @@ class ModuleObject : public NativeObject
|
|||
ModuleStatus status() const;
|
||||
bool hadEvaluationError() const;
|
||||
Value evaluationError() const;
|
||||
Value hostDefinedField() const;
|
||||
ScriptSourceObject* scriptSourceObject() const;
|
||||
JSObject* metaObject() const;
|
||||
ArrayObject& requestedModules() const;
|
||||
ArrayObject& importEntries() const;
|
||||
ArrayObject& localExportEntries() const;
|
||||
|
|
@ -286,7 +289,10 @@ class ModuleObject : public NativeObject
|
|||
static bool Instantiate(JSContext* cx, HandleModuleObject self);
|
||||
static bool Evaluate(JSContext* cx, HandleModuleObject self);
|
||||
|
||||
void setHostDefinedField(const JS::Value& value);
|
||||
static ModuleNamespaceObject* GetOrCreateModuleNamespace(JSContext* cx,
|
||||
HandleModuleObject self);
|
||||
|
||||
void setMetaObject(JSObject* obj);
|
||||
|
||||
// For BytecodeEmitter.
|
||||
bool noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun);
|
||||
|
|
@ -307,7 +313,6 @@ class ModuleObject : public NativeObject
|
|||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(js::FreeOp* fop, JSObject* obj);
|
||||
|
||||
bool hasScript() const;
|
||||
bool hasImportBindings() const;
|
||||
FunctionDeclarationVector* functionDeclarations();
|
||||
};
|
||||
|
|
@ -369,6 +374,19 @@ class MOZ_STACK_CLASS ModuleBuilder
|
|||
ArrayObject* createArray(const GCVector<T>& vector);
|
||||
};
|
||||
|
||||
JSObject*
|
||||
GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module);
|
||||
|
||||
JSObject*
|
||||
CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier);
|
||||
|
||||
JSObject*
|
||||
StartDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleValue specifier);
|
||||
|
||||
bool
|
||||
FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
|
||||
HandleObject promise);
|
||||
|
||||
} // namespace js
|
||||
|
||||
template<>
|
||||
|
|
|
|||
|
|
@ -2478,8 +2478,11 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res
|
|||
{
|
||||
// The absence of a resolve/reject function can mean that, as an
|
||||
// optimization, those weren't created. In that case, a flag is set on
|
||||
// the Promise object. There are also reactions where the Promise
|
||||
// itself is missing. For those, there's nothing left to do here.
|
||||
// the Promise object. (It's also possible to not have a resolution
|
||||
// function without that flag being set. This can occur if a Promise
|
||||
// subclass constructor passes null/undefined to `super()`.)
|
||||
// There are also reactions where the Promise itself is missing. For
|
||||
// those, there's nothing left to do here.
|
||||
assertSameCompartment(cx, resolutionFun);
|
||||
assertSameCompartment(cx, result);
|
||||
assertSameCompartment(cx, promiseObj);
|
||||
|
|
@ -3863,6 +3866,16 @@ OriginalPromiseThenBuiltin(JSContext* cx, HandleValue promiseVal, HandleValue on
|
|||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
js::RejectPromiseWithPendingError(JSContext* cx, Handle<PromiseObject*> promise)
|
||||
{
|
||||
// Not much we can do about uncatchable exceptions, just bail.
|
||||
RootedValue exn(cx);
|
||||
if (!GetAndClearException(cx, &exn))
|
||||
return false;
|
||||
return PromiseObject::reject(cx, promise, exn);
|
||||
}
|
||||
|
||||
static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx,
|
||||
Handle<PromiseObject*> promise,
|
||||
Handle<PromiseReactionRecord*> reaction);
|
||||
|
|
@ -4816,7 +4829,7 @@ PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, HandleValue
|
|||
return true;
|
||||
|
||||
if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS))
|
||||
return RejectMaybeWrappedPromise(cx, promise, rejectionValue);
|
||||
return ResolvePromise(cx, promise, rejectionValue, JS::PromiseState::Rejected);
|
||||
|
||||
RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction));
|
||||
MOZ_ASSERT(IsCallable(funVal));
|
||||
|
|
|
|||
|
|
@ -146,6 +146,9 @@ OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
|
|||
MOZ_MUST_USE JSObject*
|
||||
PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
RejectPromiseWithPendingError(JSContext* cx, Handle<PromiseObject*> promise);
|
||||
|
||||
MOZ_MUST_USE PromiseObject*
|
||||
CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,12 +34,6 @@ using mozilla::ArrayLength;
|
|||
using mozilla::DebugOnly;
|
||||
using mozilla::Forward;
|
||||
|
||||
enum class ParseTarget
|
||||
{
|
||||
Script,
|
||||
Module
|
||||
};
|
||||
|
||||
enum ASTType {
|
||||
AST_ERROR = -1,
|
||||
#define ASTDEF(ast, str, method) ast,
|
||||
|
|
@ -623,6 +617,9 @@ class NodeBuilder
|
|||
MOZ_MUST_USE bool metaProperty(HandleValue meta, HandleValue property, TokenPos* pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
MOZ_MUST_USE bool callImportExpression(HandleValue ident, HandleValue arg, TokenPos* pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
MOZ_MUST_USE bool super(TokenPos* pos, MutableHandleValue dst);
|
||||
|
||||
/*
|
||||
|
|
@ -1758,6 +1755,20 @@ NodeBuilder::metaProperty(HandleValue meta, HandleValue property, TokenPos* pos,
|
|||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::callImportExpression(HandleValue ident, HandleValue arg, TokenPos* pos,
|
||||
MutableHandleValue dst)
|
||||
{
|
||||
RootedValue cb(cx, callbacks[AST_CALL_IMPORT]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, arg, pos, dst);
|
||||
|
||||
return newNode(AST_CALL_IMPORT, pos,
|
||||
"ident", ident,
|
||||
"arg", arg,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::super(TokenPos* pos, MutableHandleValue dst)
|
||||
{
|
||||
|
|
@ -3360,6 +3371,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
return classDefinition(&pn->as<ClassNode>(), true, dst);
|
||||
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_IMPORT_META:
|
||||
{
|
||||
BinaryNode* node = &pn->as<BinaryNode>();
|
||||
ParseNode* firstNode = node->left();
|
||||
|
|
@ -3370,15 +3382,41 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
MOZ_ASSERT(secondNode->isKind(PNK_POSHOLDER));
|
||||
MOZ_ASSERT(node->pn_pos.encloses(secondNode->pn_pos));
|
||||
|
||||
RootedValue newIdent(cx);
|
||||
RootedValue targetIdent(cx);
|
||||
RootedValue firstIdent(cx);
|
||||
RootedValue secondIdent(cx);
|
||||
|
||||
RootedAtom newStr(cx, cx->names().new_);
|
||||
RootedAtom targetStr(cx, cx->names().target);
|
||||
RootedAtom firstStr(cx);
|
||||
RootedAtom secondStr(cx);
|
||||
|
||||
return identifier(newStr, &firstNode->pn_pos, &newIdent) &&
|
||||
identifier(targetStr, &secondNode->pn_pos, &targetIdent) &&
|
||||
builder.metaProperty(newIdent, targetIdent, &node->pn_pos, dst);
|
||||
if (pn->getKind() == PNK_NEWTARGET) {
|
||||
firstStr = cx->names().new_;
|
||||
secondStr = cx->names().target;
|
||||
} else {
|
||||
firstStr = cx->names().import;
|
||||
secondStr = cx->names().meta;
|
||||
}
|
||||
|
||||
return identifier(firstStr, &firstNode->pn_pos, &firstIdent) &&
|
||||
identifier(secondStr, &secondNode->pn_pos, &secondIdent) &&
|
||||
builder.metaProperty(firstIdent, secondIdent, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_CALL_IMPORT:
|
||||
{
|
||||
BinaryNode* node = &pn->as<BinaryNode>();
|
||||
ParseNode* firstNode = node->left();
|
||||
MOZ_ASSERT(firstNode->isKind(PNK_POSHOLDER));
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(firstNode->pn_pos));
|
||||
ParseNode* secondNode = node->right();
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(secondNode->pn_pos));
|
||||
|
||||
RootedValue ident(cx);
|
||||
RootedValue arg(cx);
|
||||
|
||||
HandlePropertyName name = cx->names().import;
|
||||
return identifier(name, &firstNode->pn_pos, &ident) &&
|
||||
expression(secondNode, &arg) &&
|
||||
builder.callImportExpression(ident, arg, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_SETTHIS: {
|
||||
|
|
@ -3793,7 +3831,7 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
|
|||
uint32_t lineno = 1;
|
||||
bool loc = true;
|
||||
RootedObject builder(cx);
|
||||
ParseTarget target = ParseTarget::Script;
|
||||
ParseGoal target = ParseGoal::Script;
|
||||
|
||||
RootedValue arg(cx, args.get(1));
|
||||
|
||||
|
|
@ -3881,9 +3919,9 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
|
|||
return false;
|
||||
|
||||
if (isScript) {
|
||||
target = ParseTarget::Script;
|
||||
target = ParseGoal::Script;
|
||||
} else if (isModule) {
|
||||
target = ParseTarget::Module;
|
||||
target = ParseGoal::Module;
|
||||
} else {
|
||||
JS_ReportErrorASCII(cx, "Bad target value, expected 'script' or 'module'");
|
||||
return false;
|
||||
|
|
@ -3912,14 +3950,14 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
|
|||
return false;
|
||||
Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars.begin().get(),
|
||||
chars.length(), /* foldConstants = */ false, usedNames,
|
||||
nullptr, nullptr);
|
||||
nullptr, nullptr, target);
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
serialize.setParser(&parser);
|
||||
|
||||
ParseNode* pn;
|
||||
if (target == ParseTarget::Script) {
|
||||
if (target == ParseGoal::Script) {
|
||||
pn = parser.parse();
|
||||
if (!pn)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@
|
|||
#define MODULE_OBJECT_ENVIRONMENT_SLOT 1
|
||||
#define MODULE_OBJECT_STATUS_SLOT 3
|
||||
#define MODULE_OBJECT_EVALUATION_ERROR_SLOT 4
|
||||
#define MODULE_OBJECT_DFS_INDEX_SLOT 15
|
||||
#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 16
|
||||
#define MODULE_OBJECT_DFS_INDEX_SLOT 16
|
||||
#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 17
|
||||
|
||||
#define MODULE_STATUS_UNINSTANTIATED 0
|
||||
#define MODULE_STATUS_INSTANTIATING 1
|
||||
|
|
|
|||
|
|
@ -74,8 +74,9 @@ class MOZ_STACK_CLASS BytecodeCompiler
|
|||
bool createScriptSource(Maybe<uint32_t> parameterListEnd);
|
||||
bool maybeCompressSource();
|
||||
bool canLazilyParse();
|
||||
bool createParser();
|
||||
bool createSourceAndParser(Maybe<uint32_t> parameterListEnd = Nothing());
|
||||
bool createParser(ParseGoal goal);
|
||||
bool createSourceAndParser(ParseGoal goal,
|
||||
Maybe<uint32_t> parameterListEnd = Nothing());
|
||||
|
||||
// If toString{Start,End} are not explicitly passed, assume the script's
|
||||
// offsets in the source used to parse it are the same as what should be
|
||||
|
|
@ -212,7 +213,7 @@ BytecodeCompiler::canLazilyParse()
|
|||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::createParser()
|
||||
BytecodeCompiler::createParser(ParseGoal goal)
|
||||
{
|
||||
usedNames.emplace(cx);
|
||||
if (!usedNames->init())
|
||||
|
|
@ -221,14 +222,14 @@ BytecodeCompiler::createParser()
|
|||
if (canLazilyParse()) {
|
||||
syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
|
||||
/* foldConstants = */ false, *usedNames,
|
||||
(Parser<SyntaxParseHandler>*) nullptr, (LazyScript*) nullptr);
|
||||
(Parser<SyntaxParseHandler>*) nullptr, (LazyScript*) nullptr, goal);
|
||||
|
||||
if (!syntaxParser->checkOptions())
|
||||
return false;
|
||||
}
|
||||
|
||||
parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
|
||||
/* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr);
|
||||
/* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr, goal);
|
||||
parser->sct = sourceCompressor;
|
||||
parser->ss = scriptSource;
|
||||
if (!parser->checkOptions())
|
||||
|
|
@ -239,11 +240,12 @@ BytecodeCompiler::createParser()
|
|||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = Nothing() */)
|
||||
BytecodeCompiler::createSourceAndParser(ParseGoal goal,
|
||||
Maybe<uint32_t> parameterListEnd /* = Nothing() */)
|
||||
{
|
||||
return createScriptSource(parameterListEnd) &&
|
||||
maybeCompressSource() &&
|
||||
createParser();
|
||||
createParser(goal);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -322,7 +324,7 @@ BytecodeCompiler::maybeCompleteCompressSource()
|
|||
JSScript*
|
||||
BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
|
||||
{
|
||||
if (!createSourceAndParser())
|
||||
if (!createSourceAndParser(ParseGoal::Script))
|
||||
return nullptr;
|
||||
|
||||
if (!createScript())
|
||||
|
|
@ -392,7 +394,7 @@ BytecodeCompiler::compileEvalScript(HandleObject environment, HandleScope enclos
|
|||
ModuleObject*
|
||||
BytecodeCompiler::compileModule()
|
||||
{
|
||||
if (!createSourceAndParser())
|
||||
if (!createSourceAndParser(ParseGoal::Module))
|
||||
return nullptr;
|
||||
|
||||
Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
|
||||
|
|
@ -449,7 +451,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
|
|||
MOZ_ASSERT(fun);
|
||||
MOZ_ASSERT(fun->isTenured());
|
||||
|
||||
if (!createSourceAndParser(parameterListEnd))
|
||||
if (!createSourceAndParser(ParseGoal::Script, parameterListEnd))
|
||||
return false;
|
||||
|
||||
// Speculatively parse using the default directives implied by the context.
|
||||
|
|
@ -649,7 +651,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
|||
if (!usedNames.init())
|
||||
return false;
|
||||
Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
|
||||
/* foldConstants = */ true, usedNames, nullptr, lazy);
|
||||
/* foldConstants = */ true, usedNames, nullptr, lazy, lazy->parseGoal());
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
|
|
@ -685,6 +687,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
|||
if (!NameFunctions(cx, pn))
|
||||
return false;
|
||||
|
||||
// XDR the newly delazified function.
|
||||
if (script->scriptSource()->hasEncoder() &&
|
||||
!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1090,6 +1090,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
|
||||
// Trivial binary nodes with more token pos holders.
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_IMPORT_META:
|
||||
MOZ_ASSERT(pn->as<BinaryNode>().left()->isKind(PNK_POSHOLDER));
|
||||
MOZ_ASSERT(pn->as<BinaryNode>().right()->isKind(PNK_POSHOLDER));
|
||||
*answer = false;
|
||||
|
|
@ -1319,6 +1320,11 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_CALL_IMPORT:
|
||||
MOZ_ASSERT(pn->is<BinaryNode>());
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Every part of a loop might be effect-free, but looping infinitely *is*
|
||||
// an effect. (Language lawyer trivia: C++ says threads can be assumed
|
||||
// to exit or have side effects, C++14 [intro.multithread]p27, so a C++
|
||||
|
|
@ -9076,6 +9082,21 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
|||
return false;
|
||||
break;
|
||||
|
||||
case PNK_IMPORT_META:
|
||||
if (!emit1(JSOP_IMPORTMETA))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_CALL_IMPORT:
|
||||
if (!cx->compartment()->runtimeFromAnyThread()->moduleDynamicImportHook) {
|
||||
reportError(nullptr, JSMSG_NO_DYNAMIC_IMPORT);
|
||||
return false;
|
||||
}
|
||||
if (!emitTree(pn->as<BinaryNode>().right()) || !emit1(JSOP_DYNAMIC_IMPORT)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case PNK_SETTHIS:
|
||||
if (!emitSetThis(&pn->as<BinaryNode>()))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_EXPORT_SPEC:
|
||||
case PNK_EXPORT:
|
||||
case PNK_EXPORT_BATCH_SPEC:
|
||||
case PNK_CALL_IMPORT:
|
||||
*result = false;
|
||||
return true;
|
||||
|
||||
|
|
@ -403,6 +404,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSNAMES:
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_IMPORT_META:
|
||||
case PNK_POSHOLDER:
|
||||
case PNK_SUPERCALL:
|
||||
case PNK_SUPERBASE:
|
||||
|
|
@ -1900,7 +1902,8 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
Fold(cx, node->unsafeRightReference(), parser, inGenexpLambda);
|
||||
}
|
||||
|
||||
case PNK_NEWTARGET:{
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_IMPORT_META:{
|
||||
#ifdef DEBUG
|
||||
BinaryNode* node = &pn->as<BinaryNode>();
|
||||
MOZ_ASSERT(node->left()->isKind(PNK_POSHOLDER));
|
||||
|
|
@ -1909,6 +1912,12 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
return true;
|
||||
}
|
||||
|
||||
case PNK_CALL_IMPORT: {
|
||||
BinaryNode* node = &pn->as<BinaryNode>();
|
||||
MOZ_ASSERT(node->left()->isKind(PNK_POSHOLDER));
|
||||
return Fold(cx, node->unsafeRightReference(), parser, inGenexpLambda);
|
||||
}
|
||||
|
||||
case PNK_CLASSNAMES: {
|
||||
ClassNames* names = &pn->as<ClassNames>();
|
||||
if (names->outerBinding()) {
|
||||
|
|
|
|||
|
|
@ -590,6 +590,14 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
return new_<BinaryNode>(PNK_EXPORT_DEFAULT, JSOP_NOP, pos, kid, maybeBinding);
|
||||
}
|
||||
|
||||
BinaryNodeType newImportMeta(Node importHolder, Node metaHolder) {
|
||||
return new_<BinaryNode>(PNK_IMPORT_META, JSOP_NOP, importHolder, metaHolder);
|
||||
}
|
||||
|
||||
BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
|
||||
return new_<BinaryNode>(PNK_CALL_IMPORT, JSOP_DYNAMIC_IMPORT, importHolder, singleArg);
|
||||
}
|
||||
|
||||
UnaryNodeType newExprStatement(Node expr, uint32_t end) {
|
||||
MOZ_ASSERT(expr->pn_pos.end <= end);
|
||||
return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, TokenPos(expr->pn_pos.begin, end), expr);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ class EnvironmentCoordinate
|
|||
|
||||
namespace frontend {
|
||||
|
||||
enum class ParseGoal : uint8_t
|
||||
{
|
||||
Script,
|
||||
Module
|
||||
};
|
||||
|
||||
// A detailed kind used for tracking declarations in the Parser. Used for
|
||||
// specific early error semantics and better error messages.
|
||||
enum class DeclarationKind : uint8_t
|
||||
|
|
|
|||
|
|
@ -424,7 +424,8 @@ class NameResolver
|
|||
MOZ_ASSERT(!cur->as<UnaryNode>().kid()->as<NameNode>().initializer());
|
||||
break;
|
||||
|
||||
case PNK_NEWTARGET: {
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_IMPORT_META: {
|
||||
MOZ_ASSERT(cur->as<BinaryNode>().left()->isKind(PNK_POSHOLDER));
|
||||
MOZ_ASSERT(cur->as<BinaryNode>().right()->isKind(PNK_POSHOLDER));
|
||||
break;
|
||||
|
|
@ -834,6 +835,13 @@ class NameResolver
|
|||
break;
|
||||
}
|
||||
|
||||
case PNK_CALL_IMPORT: {
|
||||
BinaryNode* node = &cur->as<BinaryNode>();
|
||||
if (!resolve(node->right(), prefix))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_DOT: {
|
||||
// Super prop nodes do not have a meaningful LHS
|
||||
PropertyAccess* prop = &cur->as<PropertyAccess>();
|
||||
|
|
|
|||
|
|
@ -293,6 +293,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_SETTHIS:
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
case PNK_IMPORT_META:
|
||||
case PNK_CALL_IMPORT:
|
||||
case PNK_WITH: {
|
||||
BinaryNode* bn = &pn->as<BinaryNode>();
|
||||
stack->push(bn->left());
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@ class ObjectBox;
|
|||
F(SUPERBASE) \
|
||||
F(SUPERCALL) \
|
||||
F(SETTHIS) \
|
||||
F(IMPORT_META) \
|
||||
F(CALL_IMPORT) \
|
||||
\
|
||||
/* Unary operators. */ \
|
||||
F(TYPEOFNAME) \
|
||||
|
|
@ -539,6 +541,8 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP
|
||||
* pn_kid: array comprehension expression
|
||||
* PNK_NOP (NullaryNode)
|
||||
* PNK_IMPORT_META (BinaryNode)
|
||||
* PNK_CALL_IMPORT (BinaryNode)
|
||||
*/
|
||||
enum ParseNodeArity
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,46 +65,6 @@ using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
|
|||
using BindingIter = ParseContext::Scope::BindingIter;
|
||||
using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
|
||||
|
||||
// Read a token. Report an error and return null() if that token doesn't match
|
||||
// to the condition. Do not use MUST_MATCH_TOKEN_INTERNAL directly.
|
||||
#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport, failureValue) \
|
||||
JS_BEGIN_MACRO \
|
||||
TokenKind token; \
|
||||
if (!tokenStream.getToken(&token, modifier)) \
|
||||
return failureValue; \
|
||||
if (!(cond)) { \
|
||||
errorReport; \
|
||||
return failureValue; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define MUST_MATCH_TOKEN_MOD_OR(tt, modifier, errorNumber, failureValue) \
|
||||
MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber), failureValue)
|
||||
|
||||
#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \
|
||||
MUST_MATCH_TOKEN_MOD_OR(tt, modifier, errorNumber, null())
|
||||
|
||||
#define MUST_MATCH_TOKEN_OR(tt, errorNumber, failureValue) \
|
||||
MUST_MATCH_TOKEN_MOD_OR(tt, TokenStream::None, errorNumber, failureValue)
|
||||
|
||||
#define MUST_MATCH_TOKEN(tt, errorNumber) \
|
||||
MUST_MATCH_TOKEN_OR(tt, errorNumber, null())
|
||||
|
||||
#define MUST_MATCH_TOKEN_FUNC_MOD_OR(func, modifier, errorNumber, failureValue) \
|
||||
MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber), failureValue)
|
||||
|
||||
#define MUST_MATCH_TOKEN_FUNC_OR(func, errorNumber, failureValue) \
|
||||
MUST_MATCH_TOKEN_FUNC_MOD_OR(func, TokenStream::None, errorNumber, failureValue)
|
||||
|
||||
#define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \
|
||||
MUST_MATCH_TOKEN_FUNC_OR(func, errorNumber, null())
|
||||
|
||||
#define MUST_MATCH_TOKEN_MOD_WITH_REPORT_OR(tt, modifier, errorReport, failureValue) \
|
||||
MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport, failureValue)
|
||||
|
||||
#define MUST_MATCH_TOKEN_MOD_WITH_REPORT(tt, modifier, errorReport) \
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT_OR(tt, modifier, errorReport, null())
|
||||
|
||||
template <class T, class U>
|
||||
static inline void
|
||||
PropagateTransitiveParseFlags(const T* inner, U* outer)
|
||||
|
|
@ -611,6 +571,23 @@ FunctionBox::initWithEnclosingScope(Scope* enclosingScope)
|
|||
computeInWith(enclosingScope);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
template <typename ConditionT, typename ErrorReportT>
|
||||
bool
|
||||
Parser<ParseHandler>::mustMatchTokenInternal(ConditionT condition, Modifier modifier,
|
||||
ErrorReportT errorReport)
|
||||
{
|
||||
TokenKind actual;
|
||||
if (!tokenStream.getToken(&actual, modifier)) {
|
||||
return false;
|
||||
}
|
||||
if (!condition(actual)) {
|
||||
errorReport(actual);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ParserBase::error(unsigned errorNumber, ...)
|
||||
{
|
||||
|
|
@ -791,7 +768,8 @@ ParserBase::ParserBase(ExclusiveContext* cx, LifoAlloc& alloc,
|
|||
bool foldConstants,
|
||||
UsedNameTracker& usedNames,
|
||||
Parser<SyntaxParseHandler>* syntaxParser,
|
||||
LazyScript* lazyOuterFunction)
|
||||
LazyScript* lazyOuterFunction,
|
||||
ParseGoal parseGoal)
|
||||
: context(cx),
|
||||
alloc(alloc),
|
||||
tokenStream(cx, options, chars, length, thisForCtor()),
|
||||
|
|
@ -807,7 +785,8 @@ ParserBase::ParserBase(ExclusiveContext* cx, LifoAlloc& alloc,
|
|||
#endif
|
||||
abortedSyntaxParse(false),
|
||||
isUnexpectedEOF_(false),
|
||||
awaitIsKeyword_(false)
|
||||
awaitIsKeyword_(false),
|
||||
parseGoal_(uint8_t(parseGoal))
|
||||
{
|
||||
cx->perThreadData->frontendCollectionPool.addActiveCompilation();
|
||||
tempPoolMark = alloc.mark();
|
||||
|
|
@ -834,9 +813,10 @@ Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc,
|
|||
bool foldConstants,
|
||||
UsedNameTracker& usedNames,
|
||||
Parser<SyntaxParseHandler>* syntaxParser,
|
||||
LazyScript* lazyOuterFunction)
|
||||
LazyScript* lazyOuterFunction,
|
||||
ParseGoal parseGoal)
|
||||
: ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames, syntaxParser,
|
||||
lazyOuterFunction),
|
||||
lazyOuterFunction, parseGoal),
|
||||
AutoGCRooter(cx, PARSER),
|
||||
handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction)
|
||||
{
|
||||
|
|
@ -2470,7 +2450,8 @@ Parser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /* = false
|
|||
pc->innerFunctionsForLazy, versionNumber(),
|
||||
funbox->bufStart, funbox->bufEnd,
|
||||
funbox->toStringStart,
|
||||
funbox->startLine, funbox->startColumn);
|
||||
funbox->startLine, funbox->startColumn,
|
||||
parseGoal());
|
||||
if (!lazy)
|
||||
return false;
|
||||
|
||||
|
|
@ -3767,10 +3748,14 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
|
|||
}
|
||||
|
||||
if (bodyType == StatementListBody) {
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT_OR(TOK_RC, TokenStream::Operand,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
|
||||
JSMSG_CURLY_OPENED, openedPos),
|
||||
false);
|
||||
if (!mustMatchToken(TOK_RC, TokenStream::Operand,
|
||||
[this, openedPos](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
|
||||
JSMSG_CURLY_OPENED, openedPos);
|
||||
}))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
funbox->setEnd(pos().end);
|
||||
} else {
|
||||
#if !JS_HAS_EXPR_CLOSURES
|
||||
|
|
@ -4138,11 +4123,16 @@ template <typename ParseHandler>
|
|||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::condition(InHandling inHandling, YieldHandling yieldHandling)
|
||||
{
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
|
||||
if (!mustMatchToken(TOK_LP, JSMSG_PAREN_BEFORE_COND)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
|
||||
if (!pn)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_COND)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
/* Check for (a = b) and warn about possible (a == b) mistype. */
|
||||
if (handler.isUnparenthesizedAssignment(pn)) {
|
||||
|
|
@ -4503,9 +4493,14 @@ Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling y
|
|||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
|
||||
JSMSG_CURLY_OPENED, begin));
|
||||
if (!mustMatchToken(TOK_RC, TokenStream::None,
|
||||
[this, begin](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
|
||||
JSMSG_CURLY_OPENED, begin);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
|
|
@ -4591,9 +4586,14 @@ Parser<ParseHandler>::arrayBindingPattern(DeclarationKind kind, YieldHandling yi
|
|||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
|
||||
reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
|
||||
JSMSG_BRACKET_OPENED, begin));
|
||||
if (!mustMatchToken(TOK_RB, modifier,
|
||||
[this, begin](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
|
||||
JSMSG_BRACKET_OPENED, begin);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
|
|
@ -4650,9 +4650,13 @@ Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned error
|
|||
if (!list)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
|
||||
reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
|
||||
openedPos));
|
||||
if (!mustMatchToken(TOK_RC, TokenStream::Operand,
|
||||
[this, errorNumber, openedPos](TokenKind actual) {
|
||||
this->reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED, openedPos);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
|
||||
return finishLexicalScope(scope, list);
|
||||
}
|
||||
|
|
@ -4708,7 +4712,9 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To
|
|||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
|
||||
if (!mustMatchToken(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
|
||||
yieldHandling, TripledotProhibited);
|
||||
|
|
@ -5076,9 +5082,13 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, ListNodeTy
|
|||
} else {
|
||||
MOZ_ASSERT(tt == TOK_MUL);
|
||||
|
||||
MUST_MATCH_TOKEN_OR(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR, false);
|
||||
if (!mustMatchToken(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_FUNC_OR(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME, false);
|
||||
if (!mustMatchToken(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NameNodeType importName = newName(context->names().star);
|
||||
if (!importName)
|
||||
|
|
@ -5186,9 +5196,13 @@ Parser<FullParseHandler>::importDeclaration()
|
|||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE);
|
||||
if (!mustMatchToken(TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
|
||||
if (!mustMatchToken(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
NameNodeType moduleSpec = stringLiteral();
|
||||
|
|
@ -5214,6 +5228,22 @@ Parser<SyntaxParseHandler>::importDeclaration()
|
|||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <class ParseHandler>
|
||||
inline typename ParseHandler::Node
|
||||
Parser<ParseHandler>::importDeclarationOrImportExpr(YieldHandling yieldHandling)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IMPORT));
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt))
|
||||
return null();
|
||||
|
||||
if (tt == TOK_DOT || tt == TOK_LP)
|
||||
return expressionStatement(yieldHandling);
|
||||
|
||||
return importDeclaration();
|
||||
}
|
||||
|
||||
template<>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkExportedName(JSAtom* exportName)
|
||||
|
|
@ -5477,7 +5507,9 @@ Parser<ParseHandler>::exportFrom(uint32_t begin, Node specList)
|
|||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
|
||||
if (!mustMatchToken(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
NameNodeType moduleSpec = stringLiteral();
|
||||
if (!moduleSpec)
|
||||
|
|
@ -5517,7 +5549,9 @@ Parser<ParseHandler>::exportBatch(uint32_t begin)
|
|||
|
||||
handler.addList(kid, exportSpec);
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
|
||||
if (!mustMatchToken(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
return exportFrom(begin, kid);
|
||||
}
|
||||
|
|
@ -5582,8 +5616,11 @@ Parser<ParseHandler>::exportClause(uint32_t begin)
|
|||
bool foundAs;
|
||||
if (!tokenStream.matchToken(&foundAs, TOK_AS))
|
||||
return null();
|
||||
if (foundAs)
|
||||
MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
|
||||
if (foundAs) {
|
||||
if (!mustMatchToken(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
NameNodeType exportName = newName(tokenStream.currentName());
|
||||
if (!exportName)
|
||||
|
|
@ -6087,7 +6124,9 @@ Parser<ParseHandler>::doWhileStatement(YieldHandling yieldHandling)
|
|||
Node body = statement(yieldHandling);
|
||||
if (!body)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN_MOD(TOK_WHILE, TokenStream::Operand, JSMSG_WHILE_AFTER_DO);
|
||||
if (!mustMatchToken(TOK_WHILE, TokenStream::Operand, JSMSG_WHILE_AFTER_DO)) {
|
||||
return null();
|
||||
}
|
||||
Node cond = condition(InAllowed, yieldHandling);
|
||||
if (!cond)
|
||||
return null();
|
||||
|
|
@ -6334,7 +6373,9 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
if (!mustMatchToken(TOK_LP, JSMSG_PAREN_AFTER_FOR)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
// PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type.
|
||||
ParseNodeKind headKind;
|
||||
|
|
@ -6390,7 +6431,9 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
|
||||
// Look for an operand: |for (;| means we might have already examined
|
||||
// this semicolon with that modifier.
|
||||
MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
|
||||
if (!mustMatchToken(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
|
|
@ -6408,7 +6451,9 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
mod = TokenStream::None;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_SEMI, mod, JSMSG_SEMI_AFTER_FOR_COND);
|
||||
if (!mustMatchToken(TOK_SEMI, mod, JSMSG_SEMI_AFTER_FOR_COND)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
|
|
@ -6424,7 +6469,9 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
mod = TokenStream::None;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, mod, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
if (!mustMatchToken(TOK_RP, mod, JSMSG_PAREN_AFTER_FOR_CTRL)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
forHead = handler.newForHead(init, test, update, headPos);
|
||||
|
|
@ -6454,7 +6501,9 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
// Parser::declaration consumed everything up to the closing ')'. That
|
||||
// token follows an {Assignment,}Expression, so the next token must be
|
||||
// consumed as if an operator continued the expression, i.e. as None.
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::None, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
if (!mustMatchToken(TOK_RP, TokenStream::None, JSMSG_PAREN_AFTER_FOR_CTRL)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
forHead = handler.newForInOrOfHead(headKind, target, iteratedExpr, headPos);
|
||||
|
|
@ -6483,14 +6532,20 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
|||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
|
||||
if (!mustMatchToken(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!discriminant)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_SWITCH)) {
|
||||
return null();
|
||||
}
|
||||
if (!mustMatchToken(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
ParseContext::Statement stmt(pc, StatementKind::Switch);
|
||||
ParseContext::Scope scope(this);
|
||||
|
|
@ -6532,7 +6587,9 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
|||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
|
||||
if (!mustMatchToken(TOK_COLON, JSMSG_COLON_AFTER_CASE)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
ListNodeType body = handler.newStatementList(pos());
|
||||
if (!body)
|
||||
|
|
@ -6886,11 +6943,15 @@ Parser<ParseHandler>::withStatement(YieldHandling yieldHandling)
|
|||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
|
||||
if (!mustMatchToken(TOK_LP, JSMSG_PAREN_BEFORE_WITH)) {
|
||||
return null();
|
||||
}
|
||||
Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!objectExpr)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_WITH)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
Node innerBlock;
|
||||
{
|
||||
|
|
@ -7027,7 +7088,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
|
||||
LexicalScopeNodeType innerBlock;
|
||||
{
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
|
||||
if (!mustMatchToken(TOK_LC, JSMSG_CURLY_BEFORE_TRY)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
uint32_t openedPos = pos().begin;
|
||||
|
||||
|
|
@ -7044,9 +7107,14 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
if (!innerBlock)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
|
||||
JSMSG_CURLY_OPENED, openedPos));
|
||||
if (!mustMatchToken(TOK_RC, TokenStream::Operand,
|
||||
[this, openedPos](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
|
||||
JSMSG_CURLY_OPENED, openedPos);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasUnconditionalCatch = false;
|
||||
|
|
@ -7093,7 +7161,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
if (omittedBinding) {
|
||||
catchName = null();
|
||||
} else {
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
|
||||
if (!mustMatchToken(TOK_LP, JSMSG_PAREN_BEFORE_CATCH)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
|
|
@ -7135,9 +7205,13 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
return null();
|
||||
}
|
||||
#endif
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_CATCH)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
|
||||
if (!mustMatchToken(TOK_LC, JSMSG_CURLY_BEFORE_CATCH)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
LexicalScopeNodeType catchBody = catchBlockStatement(yieldHandling, scope);
|
||||
|
|
@ -7164,7 +7238,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
LexicalScopeNodeType finallyBlock = null();
|
||||
|
||||
if (tt == TOK_FINALLY) {
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
|
||||
if (!mustMatchToken(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
uint32_t openedPos = pos().begin;
|
||||
|
||||
|
|
@ -7181,9 +7257,15 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
if (!finallyBlock)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
|
||||
JSMSG_CURLY_OPENED, openedPos));
|
||||
if (!mustMatchToken(TOK_RC, TokenStream::Operand,
|
||||
[this, openedPos](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
|
||||
JSMSG_CURLY_OPENED, openedPos);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
|
||||
} else {
|
||||
tokenStream.ungetToken();
|
||||
}
|
||||
|
|
@ -7221,9 +7303,14 @@ Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
|
|||
if (!list)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
|
||||
JSMSG_CURLY_OPENED, openedPos));
|
||||
if (!mustMatchToken(TOK_RC, TokenStream::Operand,
|
||||
[this, openedPos](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
|
||||
JSMSG_CURLY_OPENED, openedPos);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
|
||||
// The catch parameter names are not bound in the body scope, so remove
|
||||
// them before generating bindings.
|
||||
|
|
@ -7338,7 +7425,9 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
|
|||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
|
||||
if (!mustMatchToken(TOK_LC, JSMSG_CURLY_BEFORE_CLASS)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
ListNodeType classMethods = handler.newClassMethodList(pos().begin);
|
||||
if (!classMethods)
|
||||
|
|
@ -7739,7 +7828,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
|
|||
|
||||
// ImportDeclaration (only inside modules)
|
||||
case TOK_IMPORT:
|
||||
return importDeclaration();
|
||||
return importDeclarationOrImportExpr(yieldHandling);
|
||||
|
||||
// ExportDeclaration (only inside modules)
|
||||
case TOK_EXPORT:
|
||||
|
|
@ -7930,7 +8019,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
|
|||
|
||||
// ImportDeclaration (only inside modules)
|
||||
case TOK_IMPORT:
|
||||
return importDeclaration();
|
||||
return importDeclarationOrImportExpr(yieldHandling);
|
||||
|
||||
// ExportDeclaration (only inside modules)
|
||||
case TOK_EXPORT:
|
||||
|
|
@ -8252,7 +8341,9 @@ Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandli
|
|||
if (!thenExpr)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
|
||||
if (!mustMatchToken(TOK_COLON, JSMSG_COLON_IN_COND)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
|
||||
if (!elseExpr)
|
||||
|
|
@ -8836,7 +8927,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
|||
ParseContext* outerpc = pc;
|
||||
|
||||
// If we are off the main thread, the generator meta-objects have
|
||||
// already been created by js::StartOffThreadParseScript, so cx will not
|
||||
// already been created by js::StartOffThreadParseTask, so cx will not
|
||||
// be necessary.
|
||||
RootedObject proto(context);
|
||||
JSContext* cx = context->maybeJSContext();
|
||||
|
|
@ -8882,7 +8973,9 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
|||
if (!comp)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_IN_PAREN)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
uint32_t end = pos().end;
|
||||
handler.setBeginPosition(comp, begin);
|
||||
|
|
@ -8923,11 +9016,15 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
|
|||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
if (!mustMatchToken(TOK_LP, JSMSG_PAREN_AFTER_FOR)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
// FIXME: Destructuring binding (bug 980828).
|
||||
|
||||
MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME);
|
||||
if (!mustMatchToken(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME)) {
|
||||
return null();
|
||||
}
|
||||
RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword));
|
||||
if (!name)
|
||||
return null();
|
||||
|
|
@ -8951,7 +9048,9 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
|
|||
if (!rhs)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
|
||||
|
|
@ -8996,11 +9095,15 @@ Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
|
|||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
|
||||
if (!mustMatchToken(TOK_LP, JSMSG_PAREN_BEFORE_COND)) {
|
||||
return null();
|
||||
}
|
||||
Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
|
||||
if (!cond)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_COND)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
/* Check for (a = b) and warn about possible (a == b) mistype. */
|
||||
if (handler.isUnparenthesizedAssignment(cond)) {
|
||||
|
|
@ -9080,7 +9183,9 @@ Parser<ParseHandler>::arrayComprehension(uint32_t begin)
|
|||
if (!inner)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
|
||||
if (!mustMatchToken(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
ListNodeType comp = handler.newList(PNK_ARRAYCOMP, inner);
|
||||
if (!comp)
|
||||
|
|
@ -9198,7 +9303,9 @@ Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, bool* isSpread,
|
|||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_ARGS)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
handler.setEndPosition(argsList, pos().end);
|
||||
return argsList;
|
||||
|
|
@ -9286,6 +9393,10 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
|||
lhs = handler.newSuperBase(thisName, pos());
|
||||
if (!lhs)
|
||||
return null();
|
||||
} else if (tt == TOK_IMPORT) {
|
||||
lhs = importExpr(yieldHandling, allowCallSyntax);
|
||||
if (!lhs)
|
||||
return null();
|
||||
} else {
|
||||
lhs = primaryExpr(yieldHandling, tripledotHandling, tt, possibleError, invoked);
|
||||
if (!lhs)
|
||||
|
|
@ -9427,7 +9538,9 @@ Parser<ParseHandler>::memberElemAccess(
|
|||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
|
||||
if (!mustMatchToken(TOK_RB, JSMSG_BRACKET_IN_INDEX)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
|
||||
error(JSMSG_BAD_SUPERPROP, "member");
|
||||
|
|
@ -9901,9 +10014,14 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro
|
|||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
|
||||
reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
|
||||
JSMSG_BRACKET_OPENED, begin));
|
||||
if (!mustMatchToken(TOK_RB, modifier,
|
||||
[this, begin](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
|
||||
JSMSG_BRACKET_OPENED, begin);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
}
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
|
|
@ -10128,7 +10246,9 @@ Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling,
|
|||
if (!assignNode)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
|
||||
if (!mustMatchToken(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR)) {
|
||||
return null();
|
||||
}
|
||||
return handler.newComputedName(assignNode, begin, pos().end);
|
||||
}
|
||||
|
||||
|
|
@ -10340,9 +10460,14 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
|
|||
possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
|
||||
JSMSG_CURLY_OPENED, openedPos));
|
||||
if (!mustMatchToken(TOK_RC, TokenStream::None,
|
||||
[this, openedPos](TokenKind actual) {
|
||||
this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
|
||||
JSMSG_CURLY_OPENED, openedPos);
|
||||
}))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
|
|
@ -10456,6 +10581,58 @@ Parser<ParseHandler>::tryNewTarget(BinaryNodeType* newTarget)
|
|||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::importExpr(YieldHandling yieldHandling, bool allowCallSyntax)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IMPORT));
|
||||
|
||||
Node importHolder = handler.newPosHolder(pos());
|
||||
if (!importHolder)
|
||||
return null();
|
||||
|
||||
TokenKind next;
|
||||
if (!tokenStream.getToken(&next))
|
||||
return null();
|
||||
|
||||
if (next == TOK_DOT) {
|
||||
if (!tokenStream.getToken(&next))
|
||||
return null();
|
||||
if (next != TOK_META) {
|
||||
error(JSMSG_UNEXPECTED_TOKEN, "meta", TokenKindToDesc(next));
|
||||
return null();
|
||||
}
|
||||
|
||||
if (parseGoal() != ParseGoal::Module) {
|
||||
errorAt(pos().begin, JSMSG_IMPORT_META_OUTSIDE_MODULE);
|
||||
return null();
|
||||
}
|
||||
|
||||
Node metaHolder = handler.newPosHolder(pos());
|
||||
if (!metaHolder)
|
||||
return null();
|
||||
|
||||
return handler.newImportMeta(importHolder, metaHolder);
|
||||
} else if (next == TOK_LP && allowCallSyntax) {
|
||||
Node arg = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!arg)
|
||||
return null();
|
||||
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_AFTER_ARGS)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
if (!context->compartment()->runtimeFromAnyThread()->moduleDynamicImportHook && !abortIfSyntaxParser()) {
|
||||
return null();
|
||||
}
|
||||
|
||||
return handler.newCallImport(importHolder, arg);
|
||||
} else {
|
||||
error(JSMSG_UNEXPECTED_TOKEN, TokenKindToDesc(next));
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
|
||||
TokenKind tt, PossibleError* possibleError,
|
||||
InvokedPrediction invoked /* = PredictUninvoked */)
|
||||
|
|
@ -10509,7 +10686,9 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
|
|||
Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
|
||||
if (!expr)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
|
||||
if (!mustMatchToken(TOK_RP, JSMSG_PAREN_IN_PAREN)) {
|
||||
return null();
|
||||
}
|
||||
return handler.parenthesize(expr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "frontend/NameCollections.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
#include "frontend/SyntaxParseHandler.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
|
@ -801,15 +802,21 @@ class ParserBase : public StrictModeGetter
|
|||
|
||||
bool awaitIsKeyword_:1;
|
||||
|
||||
uint8_t parseGoal_:1;
|
||||
|
||||
public:
|
||||
bool awaitIsKeyword() const {
|
||||
return awaitIsKeyword_;
|
||||
}
|
||||
|
||||
ParseGoal parseGoal() const {
|
||||
return ParseGoal(parseGoal_);
|
||||
}
|
||||
|
||||
ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length, bool foldConstants,
|
||||
UsedNameTracker& usedNames, Parser<SyntaxParseHandler>* syntaxParser,
|
||||
LazyScript* lazyOuterFunction);
|
||||
LazyScript* lazyOuterFunction, ParseGoal parseGoal);
|
||||
~ParserBase();
|
||||
|
||||
const char* getFilename() const { return tokenStream.getFilename(); }
|
||||
|
|
@ -900,6 +907,8 @@ class ParserBase : public StrictModeGetter
|
|||
template <typename ParseHandler>
|
||||
class Parser final : public ParserBase, private JS::AutoGCRooter
|
||||
{
|
||||
protected:
|
||||
using Modifier = TokenStream::Modifier;
|
||||
private:
|
||||
using Node = typename ParseHandler::Node;
|
||||
|
||||
|
|
@ -1045,7 +1054,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
public:
|
||||
Parser(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
|
||||
Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction);
|
||||
Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction, ParseGoal parseGoal);
|
||||
~Parser();
|
||||
|
||||
friend class AutoAwaitIsKeyword<ParseHandler>;
|
||||
|
|
@ -1080,6 +1089,71 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
*/
|
||||
ListNodeType parse();
|
||||
|
||||
private:
|
||||
/*
|
||||
* Gets the next token and checks if it matches to the given `condition`.
|
||||
* If it matches, returns true.
|
||||
* If it doesn't match, calls `errorReport` to report the error, and
|
||||
* returns false.
|
||||
* If other error happens, it returns false but `errorReport` may not be
|
||||
* called and other error will be thrown in that case.
|
||||
*
|
||||
* In any case, the already gotten token is not ungotten.
|
||||
*
|
||||
* The signature of `condition` is [...](TokenKind actual) -> bool, and
|
||||
* the signature of `errorReport` is [...](TokenKind actual).
|
||||
*/
|
||||
template<typename ConditionT, typename ErrorReportT>
|
||||
MOZ_MUST_USE bool mustMatchTokenInternal(ConditionT condition, Modifier modifier,
|
||||
ErrorReportT errorReport);
|
||||
|
||||
public:
|
||||
/*
|
||||
* The following mustMatchToken variants follow the behavior and parameter
|
||||
* types of mustMatchTokenInternal above.
|
||||
*
|
||||
* If modifier is omitted, `None` is used.
|
||||
* If TokenKind is passed instead of `condition`, it checks if the next
|
||||
* token is the passed token.
|
||||
* If error number is passed instead of `errorReport`, it reports an
|
||||
* error with the passed errorNumber.
|
||||
*/
|
||||
MOZ_MUST_USE bool mustMatchToken(TokenKind expected, Modifier modifier, JSErrNum errorNumber) {
|
||||
return mustMatchTokenInternal([expected](TokenKind actual) {
|
||||
return actual == expected;
|
||||
},
|
||||
modifier,
|
||||
[this, errorNumber](TokenKind) {
|
||||
this->error(errorNumber);
|
||||
});
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool mustMatchToken(TokenKind excpected, JSErrNum errorNumber) {
|
||||
return mustMatchToken(excpected, TokenStream::None, errorNumber);
|
||||
}
|
||||
|
||||
template<typename ConditionT>
|
||||
MOZ_MUST_USE bool mustMatchToken(ConditionT condition, JSErrNum errorNumber) {
|
||||
return mustMatchTokenInternal(condition, TokenStream::None,
|
||||
[this, errorNumber](TokenKind) {
|
||||
this->error(errorNumber);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ErrorReportT>
|
||||
MOZ_MUST_USE bool mustMatchToken(TokenKind expected, Modifier modifier,
|
||||
ErrorReportT errorReport) {
|
||||
return mustMatchTokenInternal([expected](TokenKind actual) {
|
||||
return actual == expected;
|
||||
},
|
||||
modifier, errorReport);
|
||||
}
|
||||
|
||||
template<typename ErrorReportT>
|
||||
MOZ_MUST_USE bool mustMatchToken(TokenKind expected, ErrorReportT errorReport) {
|
||||
return mustMatchToken(expected, TokenStream::None, errorReport);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new parsed object or function container from
|
||||
* cx->tempLifoAlloc.
|
||||
|
|
@ -1236,6 +1310,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
ListNodeType lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
|
||||
|
||||
inline BinaryNodeType importDeclaration();
|
||||
Node importDeclarationOrImportExpr(YieldHandling yieldHandling);
|
||||
|
||||
bool processExport(Node node);
|
||||
bool processExportFrom(BinaryNodeType node);
|
||||
|
|
@ -1346,6 +1421,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
|
|||
bool tryNewTarget(BinaryNodeType* newTarget);
|
||||
bool checkAndMarkSuperScope();
|
||||
|
||||
Node importExpr(YieldHandling yieldHandling, bool allowCallSyntax);
|
||||
|
||||
FunctionNodeType methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
macro(from, from, TOK_FROM) \
|
||||
macro(get, get, TOK_GET) \
|
||||
macro(let, let, TOK_LET) \
|
||||
macro(meta, meta, TOK_META) \
|
||||
macro(of, of, TOK_OF) \
|
||||
macro(set, set, TOK_SET) \
|
||||
macro(static, static_, TOK_STATIC) \
|
||||
|
|
|
|||
|
|
@ -354,6 +354,12 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
|||
BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
Node newImportMeta(Node importHolder, Node metaHolder) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
Node newCallImport(Node importHolder, Node singleArg) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
BinaryNodeType newSetThis(Node thisName, Node value) { return value; }
|
||||
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@
|
|||
macro(FROM, "'from'") \
|
||||
macro(GET, "'get'") \
|
||||
macro(LET, "'let'") \
|
||||
macro(META, "'meta'") \
|
||||
macro(OF, "'of'") \
|
||||
macro(SET, "'set'") \
|
||||
macro(STATIC, "'static'") \
|
||||
|
|
|
|||
|
|
@ -4694,3 +4694,52 @@ BaselineCompiler::emit_JSOP_JUMPTARGET()
|
|||
masm.inc64(AbsoluteAddress(counterAddr));
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef JSObject* (*GetOrCreateModuleMetaObjectFn)(JSContext*, HandleObject);
|
||||
static const VMFunction GetOrCreateModuleMetaObjectInfo =
|
||||
FunctionInfo<GetOrCreateModuleMetaObjectFn>(js::GetOrCreateModuleMetaObject,
|
||||
"GetOrCreateModuleMetaObject");
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_IMPORTMETA()
|
||||
{
|
||||
RootedModuleObject module(cx, GetModuleObjectForScript(script));
|
||||
MOZ_ASSERT(module);
|
||||
|
||||
frame.syncStack(0);
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(ImmGCPtr(module));
|
||||
if (!callVM(GetOrCreateModuleMetaObjectInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef JSObject* (*StartDynamicModuleImportFn)(JSContext*, HandleValue, HandleValue);
|
||||
static const VMFunction StartDynamicModuleImportInfo =
|
||||
FunctionInfo<StartDynamicModuleImportFn>(js::StartDynamicModuleImport,
|
||||
"StartDynamicModuleImport");
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_DYNAMIC_IMPORT()
|
||||
{
|
||||
RootedValue referencingPrivate(cx, FindScriptOrModulePrivateForScript(script));
|
||||
|
||||
// Put specifier value in R0.
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(R0);
|
||||
pushArg(referencingPrivate);
|
||||
if (!callVM(StartDynamicModuleImportInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,7 +240,9 @@ namespace jit {
|
|||
_(JSOP_DEBUGCHECKSELFHOSTED) \
|
||||
_(JSOP_JUMPTARGET) \
|
||||
_(JSOP_IS_CONSTRUCTING) \
|
||||
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
|
||||
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \
|
||||
_(JSOP_IMPORTMETA) \
|
||||
_(JSOP_DYNAMIC_IMPORT)
|
||||
|
||||
class BaselineCompiler : public BaselineCompilerSpecific
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2402,6 +2402,31 @@ CodeGenerator::visitNullarySharedStub(LNullarySharedStub* lir)
|
|||
}
|
||||
}
|
||||
|
||||
typedef JSObject* (*GetOrCreateModuleMetaObjectFn)(JSContext*, HandleObject);
|
||||
static const VMFunction GetOrCreateModuleMetaObjectInfo =
|
||||
FunctionInfo<GetOrCreateModuleMetaObjectFn>(js::GetOrCreateModuleMetaObject,
|
||||
"GetOrCreateModuleMetaObject");
|
||||
|
||||
void
|
||||
CodeGenerator::visitModuleMetadata(LModuleMetadata* lir)
|
||||
{
|
||||
pushArg(ImmPtr(lir->mir()->module()));
|
||||
callVM(GetOrCreateModuleMetaObjectInfo, lir);
|
||||
}
|
||||
|
||||
typedef JSObject* (*StartDynamicModuleImportFn)(JSContext*, HandleValue, HandleValue);
|
||||
static const VMFunction StartDynamicModuleImportInfo =
|
||||
FunctionInfo<StartDynamicModuleImportFn>(js::StartDynamicModuleImport,
|
||||
"StartDynamicModuleImport");
|
||||
|
||||
void
|
||||
CodeGenerator::visitDynamicImport(LDynamicImport* lir)
|
||||
{
|
||||
pushArg(ToValue(lir, LDynamicImport::SpecifierIndex));
|
||||
pushArg(ToValue(lir, LDynamicImport::ReferencingPrivateIndex));
|
||||
callVM(StartDynamicModuleImportInfo, lir);
|
||||
}
|
||||
|
||||
typedef JSObject* (*LambdaFn)(JSContext*, HandleFunction, HandleObject);
|
||||
static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda, "Lambda");
|
||||
|
||||
|
|
|
|||
|
|
@ -442,6 +442,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
|
||||
void visitRandom(LRandom* ins);
|
||||
void visitSignExtend(LSignExtend* ins);
|
||||
void visitModuleMetadata(LModuleMetadata* lir);
|
||||
void visitDynamicImport(LDynamicImport* lir);
|
||||
|
||||
#ifdef DEBUG
|
||||
void emitDebugForceBailing(LInstruction* lir);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "jit/Lowering.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/EnvironmentObject.h"
|
||||
#include "vm/Opcodes.h"
|
||||
#include "vm/RegExpStatics.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
|
@ -2205,6 +2206,12 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
case JSOP_CHECKOBJCOERCIBLE:
|
||||
return jsop_checkobjcoercible();
|
||||
|
||||
case JSOP_IMPORTMETA:
|
||||
return jsop_importmeta();
|
||||
|
||||
case JSOP_DYNAMIC_IMPORT:
|
||||
return jsop_dynamic_import();
|
||||
|
||||
case JSOP_DEBUGCHECKSELFHOSTED:
|
||||
{
|
||||
#ifdef DEBUG
|
||||
|
|
@ -14246,6 +14253,32 @@ IonBuilder::jsop_debugger()
|
|||
return resumeAt(debugger, pc);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_importmeta()
|
||||
{
|
||||
ModuleObject* module = GetModuleObjectForScript(script());
|
||||
MOZ_ASSERT(module);
|
||||
|
||||
MModuleMetadata* meta = MModuleMetadata::New(alloc(), module);
|
||||
current->add(meta);
|
||||
current->push(meta);
|
||||
return resumeAfter(meta);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_dynamic_import()
|
||||
{
|
||||
Value referencingPrivate = FindScriptOrModulePrivateForScript(script());
|
||||
MConstant* ref = constant(referencingPrivate);
|
||||
|
||||
MDefinition* specifier = current->pop();
|
||||
|
||||
MDynamicImport* ins = MDynamicImport::New(alloc(), ref, specifier);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
MInstruction*
|
||||
IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -788,6 +788,8 @@ class IonBuilder
|
|||
MOZ_MUST_USE bool jsop_checkiscallable(uint8_t kind);
|
||||
MOZ_MUST_USE bool jsop_checkobjcoercible();
|
||||
MOZ_MUST_USE bool jsop_pushcallobj();
|
||||
MOZ_MUST_USE bool jsop_importmeta();
|
||||
MOZ_MUST_USE bool jsop_dynamic_import();
|
||||
|
||||
/* Inlining. */
|
||||
|
||||
|
|
|
|||
|
|
@ -2427,6 +2427,23 @@ LIRGenerator::visitNullarySharedStub(MNullarySharedStub* ins)
|
|||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitModuleMetadata(MModuleMetadata* ins)
|
||||
{
|
||||
LModuleMetadata* lir = new(alloc()) LModuleMetadata();
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitDynamicImport(MDynamicImport* ins)
|
||||
{
|
||||
LDynamicImport* lir = new(alloc()) LDynamicImport(useBoxAtStart(ins->referencingPrivate()),
|
||||
useBoxAtStart(ins->specifier()));
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitLambda(MLambda* ins)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -334,6 +334,8 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitCheckIsCallable(MCheckIsCallable* ins);
|
||||
void visitCheckObjCoercible(MCheckObjCoercible* ins);
|
||||
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
|
||||
void visitModuleMetadata(MModuleMetadata* ins);
|
||||
void visitDynamicImport(MDynamicImport* ins);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
|||
|
|
@ -8379,6 +8379,49 @@ class MSubstr
|
|||
}
|
||||
};
|
||||
|
||||
class MModuleMetadata : public MNullaryInstruction
|
||||
{
|
||||
CompilerObject module_;
|
||||
|
||||
explicit MModuleMetadata(JSObject* module)
|
||||
: module_(module)
|
||||
{
|
||||
setResultType(MIRType::Object);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ModuleMetadata)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
JSObject* module() const {
|
||||
return module_;
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
bool appendRoots(MRootList& roots) const override {
|
||||
return roots.append(module_);
|
||||
}
|
||||
};
|
||||
|
||||
class MDynamicImport : public MBinaryInstruction,
|
||||
public BoxInputsPolicy::Data
|
||||
{
|
||||
explicit MDynamicImport(MDefinition* referencingPrivate, MDefinition* specifier)
|
||||
: MBinaryInstruction(referencingPrivate, specifier)
|
||||
{
|
||||
setResultType(MIRType::Object);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(DynamicImport)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, referencingPrivate))
|
||||
NAMED_OPERANDS((1, specifier))
|
||||
};
|
||||
|
||||
struct LambdaFunctionInfo
|
||||
{
|
||||
// The functions used in lambdas are the canonical original function in
|
||||
|
|
|
|||
|
|
@ -284,6 +284,8 @@ namespace jit {
|
|||
_(GlobalNameConflictsCheck) \
|
||||
_(Debugger) \
|
||||
_(NewTarget) \
|
||||
_(ModuleMetadata) \
|
||||
_(DynamicImport) \
|
||||
_(ArrowNewTarget) \
|
||||
_(CheckReturn) \
|
||||
_(CheckIsObj) \
|
||||
|
|
|
|||
|
|
@ -4939,6 +4939,39 @@ class LNullarySharedStub : public LCallInstructionHelper<BOX_PIECES, 0, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LModuleMetadata : public LCallInstructionHelper<1, 0, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(ModuleMetadata)
|
||||
|
||||
const MModuleMetadata* mir() const {
|
||||
return mir_->toModuleMetadata();
|
||||
}
|
||||
|
||||
LModuleMetadata()
|
||||
{}
|
||||
};
|
||||
|
||||
class LDynamicImport : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(DynamicImport)
|
||||
|
||||
static const size_t ReferencingPrivateIndex = 0;
|
||||
static const size_t SpecifierIndex = BOX_PIECES;
|
||||
|
||||
explicit LDynamicImport(const LBoxAllocation& referencingPrivate,
|
||||
const LBoxAllocation& specifier)
|
||||
{
|
||||
setBoxOperand(ReferencingPrivateIndex, referencingPrivate);
|
||||
setBoxOperand(SpecifierIndex, specifier);
|
||||
}
|
||||
|
||||
const MDynamicImport* mir() const {
|
||||
return mir_->toDynamicImport();
|
||||
}
|
||||
};
|
||||
|
||||
class LLambdaForSingleton : public LCallInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -401,6 +401,8 @@
|
|||
_(GlobalNameConflictsCheck) \
|
||||
_(Debugger) \
|
||||
_(NewTarget) \
|
||||
_(ModuleMetadata) \
|
||||
_(DynamicImport) \
|
||||
_(ArrowNewTarget) \
|
||||
_(CheckReturn) \
|
||||
_(CheckIsObj) \
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'fro
|
|||
MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
|
||||
MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
|
||||
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character")
|
||||
MSG_DEF(JSMSG_IMPORT_META_OUTSIDE_MODULE, 0, JSEXN_SYNTAXERR, "import.meta may only appear in a module")
|
||||
MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
|
||||
MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL, 0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer")
|
||||
MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer")
|
||||
|
|
@ -590,6 +591,9 @@ MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 0, JSEXN_SYNTAXERR, "ambiguous import")
|
|||
MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace")
|
||||
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
|
||||
MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
|
||||
MSG_DEF(JSMSG_NO_DYNAMIC_IMPORT, 0, JSEXN_SYNTAXERR, "dynamic module import is not implemented")
|
||||
MSG_DEF(JSMSG_DYNAMIC_IMPORT_FAILED, 0, JSEXN_TYPEERR, "error loading dynamically imported module")
|
||||
MSG_DEF(JSMSG_BAD_MODULE_SPECIFIER, 1, JSEXN_TYPEERR, "error resolving module specifier '{0}'")
|
||||
|
||||
// Promise
|
||||
MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")
|
||||
|
|
|
|||
138
js/src/jsapi.cpp
138
js/src/jsapi.cpp
|
|
@ -4202,6 +4202,31 @@ JS::CancelOffThreadModule(JSContext* cx, void* token)
|
|||
HelperThreadState().cancelParseTask(cx, ParseTaskKind::Module, token);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
|
||||
OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
MOZ_ASSERT(CanCompileOffThread(cx, options, buffer.length() - cursor));
|
||||
return StartOffThreadDecodeScript(cx, options, buffer, cursor, callback, callbackData);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSScript*)
|
||||
JS::FinishOffThreadScriptDecoder(JSContext* cx, void* token)
|
||||
{
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
|
||||
return HelperThreadState().finishScriptDecodeTask(cx, token);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::CancelOffThreadScriptDecoder(JSContext* cx, void* token)
|
||||
{
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
|
||||
HelperThreadState().cancelParseTask(cx, ParseTaskKind::ScriptDecode, token);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
|
||||
const JS::CompileOptions& options, MutableHandleScript script)
|
||||
|
|
@ -4240,7 +4265,8 @@ JS_BufferIsCompilableUnit(JSContext* cx, HandleObject obj, const char* utf8, siz
|
|||
frontend::Parser<frontend::FullParseHandler> parser(cx, cx->tempLifoAlloc(),
|
||||
options, chars, length,
|
||||
/* foldConstants = */ true,
|
||||
usedNames, nullptr, nullptr);
|
||||
usedNames, nullptr, nullptr,
|
||||
frontend::ParseGoal::Script);
|
||||
JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
|
||||
if (!parser.checkOptions() || !parser.parse()) {
|
||||
// We ran into an error. If it was because we ran out of source, we
|
||||
|
|
@ -4697,6 +4723,43 @@ JS::SetModuleResolveHook(JSRuntime* rt, JS::ModuleResolveHook func)
|
|||
rt->moduleResolveHook = func;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JS::ModuleMetadataHook)
|
||||
JS::GetModuleMetadataHook(JSContext* cx)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
return cx->runtime()->moduleMetadataHook;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetModuleMetadataHook(JSContext* cx, JS::ModuleMetadataHook func)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
cx->runtime()->moduleMetadataHook = func;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JS::ModuleDynamicImportHook)
|
||||
JS::GetModuleDynamicImportHook(JSContext* cx)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
return cx->runtime()->moduleDynamicImportHook;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetModuleDynamicImportHook(JSContext* cx, JS::ModuleDynamicImportHook func)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
cx->runtime()->moduleDynamicImportHook = func;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
|
||||
HandleObject promise)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
|
||||
return js::FinishDynamicModuleImport(cx, referencingPrivate, specifier, promise);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
SourceBufferHolder& srcBuf, JS::MutableHandleObject module)
|
||||
|
|
@ -4710,15 +4773,55 @@ JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetModuleHostDefinedField(JSObject* module, const JS::Value& value)
|
||||
JS::SetModulePrivate(JSObject* module, const JS::Value& value)
|
||||
{
|
||||
module->as<ModuleObject>().setHostDefinedField(value);
|
||||
module->as<ModuleObject>().scriptSourceObject()->setPrivate(value);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JS::Value)
|
||||
JS::GetModuleHostDefinedField(JSObject* module)
|
||||
JS::GetModulePrivate(JSObject* module)
|
||||
{
|
||||
return module->as<ModuleObject>().hostDefinedField();
|
||||
return module->as<ModuleObject>().scriptSourceObject()->canonicalPrivate();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetScriptPrivate(JSScript* script, const JS::Value& value)
|
||||
{
|
||||
script->scriptSourceUnwrap().setPrivate(value);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JS::Value)
|
||||
JS::GetScriptPrivate(JSScript* script)
|
||||
{
|
||||
return script->scriptSourceUnwrap().canonicalPrivate();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JS::Value)
|
||||
JS::GetScriptedCallerPrivate(JSContext* cx)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
|
||||
if (iter.done() || !iter.hasScript()) {
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
return FindScriptOrModulePrivateForScript(iter.script());
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JS::ScriptPrivateFinalizeHook)
|
||||
JS::GetScriptPrivateFinalizeHook(JSContext* cx)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
return cx->runtime()->scriptPrivateFinalizeHook;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetScriptPrivateFinalizeHook(JSContext* cx, JS::ScriptPrivateFinalizeHook func)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
cx->runtime()->scriptPrivateFinalizeHook = func;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
|
|
@ -4838,11 +4941,14 @@ JS_PUBLIC_API(JSObject*)
|
|||
JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
|
||||
{
|
||||
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
||||
MOZ_ASSERT(IsCallable(executor));
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, executor, proto);
|
||||
|
||||
if (!executor)
|
||||
return PromiseObject::createSkippingExecutor(cx);
|
||||
|
||||
MOZ_ASSERT(IsCallable(executor));
|
||||
return PromiseObject::create(cx, executor, proto);
|
||||
}
|
||||
|
||||
|
|
@ -7020,6 +7126,26 @@ JS::DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer,
|
|||
return decoder.resultCode();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::StartIncrementalEncoding(JSContext* cx, JS::HandleScript script)
|
||||
{
|
||||
if (!script)
|
||||
return false;
|
||||
if (!script->scriptSource()->xdrEncodeTopLevel(cx, script))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::FinishIncrementalEncoding(JSContext* cx, JS::HandleScript script, TranscodeBuffer& buffer)
|
||||
{
|
||||
if (!script)
|
||||
return false;
|
||||
if (!script->scriptSource()->xdrFinalizeEncoder(buffer))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetBuildIdOp(JSContext* cx, JS::BuildIdOp buildIdOp)
|
||||
{
|
||||
|
|
|
|||
141
js/src/jsapi.h
141
js/src/jsapi.h
|
|
@ -3748,7 +3748,7 @@ namespace JS {
|
|||
* addrefs/copies/tracing/etc.
|
||||
*
|
||||
* Furthermore, in some cases compile options are propagated from one entity to
|
||||
* another (e.g. from a scriipt to a function defined in that script). This
|
||||
* another (e.g. from a script to a function defined in that script). This
|
||||
* involves copying over some, but not all, of the options.
|
||||
*
|
||||
* So, we have a class hierarchy that reflects these four use cases:
|
||||
|
|
@ -4186,6 +4186,17 @@ FinishOffThreadModule(JSContext* cx, void* token);
|
|||
extern JS_PUBLIC_API(void)
|
||||
CancelOffThreadModule(JSContext* cx, void* token);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
|
||||
OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
extern JS_PUBLIC_API(JSScript*)
|
||||
FinishOffThreadScriptDecoder(JSContext* cx, void* token);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
CancelOffThreadScriptDecoder(JSContext* cx, void* token);
|
||||
|
||||
/**
|
||||
* Compile a function with envChain plus the global as its scope chain.
|
||||
* envChain must contain objects in the current compartment of cx. The actual
|
||||
|
|
@ -4324,20 +4335,58 @@ extern JS_PUBLIC_API(bool)
|
|||
Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char* filename, JS::MutableHandleValue rval);
|
||||
|
||||
using ModuleResolveHook = JSObject* (*)(JSContext*, HandleObject, HandleString);
|
||||
using ModuleResolveHook = JSObject* (*)(JSContext*, HandleValue, HandleString);
|
||||
|
||||
/**
|
||||
* Get the HostResolveImportedModule hook for the runtime.
|
||||
* Get the HostImportModuleDynamically hook for the runtime.
|
||||
*/
|
||||
extern JS_PUBLIC_API(ModuleResolveHook)
|
||||
GetModuleResolveHook(JSRuntime* rt);
|
||||
|
||||
/**
|
||||
* Set the HostResolveImportedModule hook for the runtime to the given function.
|
||||
* Set the HostImportModuleDynamically hook for the runtime to the given
|
||||
* function.
|
||||
*
|
||||
* If this hook is not set (or set to nullptr) then the JS engine will throw an
|
||||
* exception if dynamic module import is attempted.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetModuleResolveHook(JSRuntime* rt, ModuleResolveHook func);
|
||||
|
||||
using ModuleMetadataHook = bool (*)(JSContext*, HandleObject, HandleObject);
|
||||
|
||||
/**
|
||||
* Get the hook for populating the import.meta metadata object.
|
||||
*/
|
||||
extern JS_PUBLIC_API(ModuleMetadataHook)
|
||||
GetModuleMetadataHook(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Set the hook for populating the import.meta metadata object to the given
|
||||
* function.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetModuleMetadataHook(JSContext* cx, ModuleMetadataHook func);
|
||||
|
||||
using ModuleDynamicImportHook = bool (*)(JSContext* cx, HandleValue referencingPrivate,
|
||||
HandleString specifier, HandleObject promise);
|
||||
|
||||
/**
|
||||
* Get the HostResolveImportedModule hook for the runtime.
|
||||
*/
|
||||
extern JS_PUBLIC_API(ModuleDynamicImportHook)
|
||||
GetModuleDynamicImportHook(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Set the HostResolveImportedModule hook for the runtime to the given function.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetModuleDynamicImportHook(JSContext* cx, ModuleDynamicImportHook func);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
|
||||
HandleObject promise);
|
||||
|
||||
/**
|
||||
* Parse the given source buffer as a module in the scope of the current global
|
||||
* of cx and return a source text module record.
|
||||
|
|
@ -4347,17 +4396,57 @@ CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
SourceBufferHolder& srcBuf, JS::MutableHandleObject moduleRecord);
|
||||
|
||||
/**
|
||||
* Set the [[HostDefined]] field of a source text module record to the given
|
||||
* value.
|
||||
* Set a private value associated with a source text module record.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetModuleHostDefinedField(JSObject* module, const JS::Value& value);
|
||||
SetModulePrivate(JSObject* module, const JS::Value& value);
|
||||
|
||||
/**
|
||||
* Get the [[HostDefined]] field of a source text module record.
|
||||
* Get the private value associated with a source text module record.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JS::Value)
|
||||
GetModuleHostDefinedField(JSObject* module);
|
||||
GetModulePrivate(JSObject* module);
|
||||
|
||||
/**
|
||||
* Set a private value associated with a script. Note that this value is shared
|
||||
* by all nested scripts compiled from a single source file.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetScriptPrivate(JSScript* script, const JS::Value& value);
|
||||
|
||||
/**
|
||||
* Get the private value associated with a script. Note that this value is
|
||||
* shared by all nested scripts compiled from a single source file.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JS::Value)
|
||||
GetScriptPrivate(JSScript* script);
|
||||
|
||||
/*
|
||||
* Return the private value associated with currently executing script or
|
||||
* module, or undefined if there is no such script.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JS::Value)
|
||||
GetScriptedCallerPrivate(JSContext* cx);
|
||||
|
||||
/**
|
||||
* A hook that's called whenever a script or module which has a private value
|
||||
* set with SetScriptPrivate() or SetModulePrivate() is finalized. This can be
|
||||
* used to clean up the private state. The private value is passed as an
|
||||
* argument.
|
||||
*/
|
||||
using ScriptPrivateFinalizeHook = void (*)(JSFreeOp*, const JS::Value&);
|
||||
|
||||
/**
|
||||
* Get the script private finalize hook for the runtime.
|
||||
*/
|
||||
extern JS_PUBLIC_API(ScriptPrivateFinalizeHook)
|
||||
GetScriptPrivateFinalizeHook(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Set the script private finalize hook for the runtime to the given function.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetScriptPrivateFinalizeHook(JSContext* cx, ScriptPrivateFinalizeHook func);
|
||||
|
||||
/*
|
||||
* Perform the ModuleInstantiate operation on the given source text module
|
||||
|
|
@ -4465,9 +4554,14 @@ SetPromiseRejectionTrackerCallback(JSContext* cx, JSPromiseRejectionTrackerCallb
|
|||
|
||||
/**
|
||||
* Returns a new instance of the Promise builtin class in the current
|
||||
* compartment, with the right slot layout. If a `proto` is passed, that gets
|
||||
* set as the instance's [[Prototype]] instead of the original value of
|
||||
* `Promise.prototype`.
|
||||
* compartment, with the right slot layout.
|
||||
*
|
||||
* The `executor` can be a `nullptr`. In that case, the only way to resolve or
|
||||
* reject the returned promise is via the `JS::ResolvePromise` and
|
||||
* `JS::RejectPromise` JSAPI functions.
|
||||
*
|
||||
* If a `proto` is passed, that gets set as the instance's [[Prototype]]
|
||||
* instead of the original value of `Promise.prototype`.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
NewPromiseObject(JSContext* cx, JS::HandleObject executor, JS::HandleObject proto = nullptr);
|
||||
|
|
@ -6085,7 +6179,10 @@ enum TranscodeResult
|
|||
TranscodeResult_Failure_AsmJSNotSupported = TranscodeResult_Failure | 0x3,
|
||||
TranscodeResult_Failure_BadDecode = TranscodeResult_Failure | 0x4,
|
||||
|
||||
// A error, the JSContext has a pending exception.
|
||||
TranscodeResult_Failure_WrongCompileOption = TranscodeResult_Failure | 0x5,
|
||||
TranscodeResult_Failure_NotInterpretedFun = TranscodeResult_Failure | 0x6,
|
||||
|
||||
// There is a pending exception on the context.
|
||||
TranscodeResult_Throw = 0x200
|
||||
};
|
||||
|
||||
|
|
@ -6103,6 +6200,24 @@ extern JS_PUBLIC_API(TranscodeResult)
|
|||
DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
|
||||
size_t cursorIndex = 0);
|
||||
|
||||
// Register an encoder on the given script source, such that all functions can
|
||||
// be encoded as they are parsed. This strategy is used to avoid blocking the
|
||||
// main thread in a non-interruptible way.
|
||||
//
|
||||
// The |script| argument of |StartIncrementalEncoding| and
|
||||
// |FinishIncrementalEncoding| should be the top-level script returned either as
|
||||
// an out-param of any of the |Compile| functions, or the result of
|
||||
// |FinishOffThreadScript|.
|
||||
//
|
||||
// The |buffer| argument of |FinishIncrementalEncoding| is used for appending
|
||||
// the encoded bytecode into the buffer. If any of these functions failed, the
|
||||
// content of |buffer| would be undefined.
|
||||
extern JS_PUBLIC_API(bool)
|
||||
StartIncrementalEncoding(JSContext* cx, JS::HandleScript script);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
FinishIncrementalEncoding(JSContext* cx, JS::HandleScript script, TranscodeBuffer& buffer);
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ ASTDEF(AST_YIELD_EXPR, "YieldExpression", "yieldExpres
|
|||
ASTDEF(AST_CLASS_EXPR, "ClassExpression", "classExpression")
|
||||
ASTDEF(AST_METAPROPERTY, "MetaProperty", "metaProperty")
|
||||
ASTDEF(AST_SUPER, "Super", "super")
|
||||
ASTDEF(AST_CALL_IMPORT, "CallImport", "callImport")
|
||||
|
||||
ASTDEF(AST_EMPTY_STMT, "EmptyStatement", "emptyStatement")
|
||||
ASTDEF(AST_BLOCK_STMT, "BlockStatement", "blockStatement")
|
||||
|
|
|
|||
|
|
@ -538,7 +538,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
|
|||
uint32_t length = lengthAndEncoding >> 1;
|
||||
bool latin1 = lengthAndEncoding & 0x1;
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
JSAtom* atom;
|
||||
if (latin1) {
|
||||
const Latin1Char* chars = nullptr;
|
||||
|
|
@ -567,7 +567,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
|
|||
* most allocations here will be bigger than tempLifoAlloc's default
|
||||
* chunk size.
|
||||
*/
|
||||
chars = cx->runtime()->pod_malloc<char16_t>(length);
|
||||
chars = cx->pod_malloc<char16_t>(length);
|
||||
if (!chars)
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -366,6 +366,12 @@ js::AssertSameCompartment(JSContext* cx, JSObject* obj)
|
|||
assertSameCompartment(cx, obj);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::AssertSameCompartment(JSContext* cx, JS::HandleValue v)
|
||||
{
|
||||
assertSameCompartment(cx, v);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FRIEND_API(void)
|
||||
js::AssertSameCompartment(JSObject* objA, JSObject* objB)
|
||||
|
|
@ -1321,6 +1327,15 @@ js::GetAllocationMetadata(JSObject* obj)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JS::Value)
|
||||
js::MaybeGetScriptPrivate(JSObject* object) {
|
||||
if (!object->is<ScriptSourceObject>()) {
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
return object->as<ScriptSourceObject>().canonicalPrivate();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::ReportIsNotFunction(JSContext* cx, HandleValue v)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -95,6 +95,22 @@ JS_PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp = nullptr)
|
|||
extern JS_FRIEND_API(bool)
|
||||
JS_IsDeadWrapper(JSObject* obj);
|
||||
|
||||
namespace js {
|
||||
|
||||
/**
|
||||
* Get the script private value associated with an object, if any.
|
||||
*
|
||||
* The private value is set with SetScriptPrivate() or SetModulePrivate() and is
|
||||
* internally stored on the relevant ScriptSourceObject.
|
||||
*
|
||||
* This is used by the cycle collector to trace through
|
||||
* ScriptSourceObjects. This allows private values to contain an nsISupports
|
||||
* pointer and hence support references to cycle collected C++ objects.
|
||||
*/
|
||||
JS_FRIEND_API(JS::Value) MaybeGetScriptPrivate(JSObject* object);
|
||||
|
||||
} // namespace js
|
||||
|
||||
/*
|
||||
* Used by the cycle collector to trace through a shape or object group and
|
||||
* all cycle-participating data it reaches, using bounded stack space.
|
||||
|
|
@ -643,6 +659,9 @@ GetPrototypeNoProxy(JSObject* obj);
|
|||
JS_FRIEND_API(void)
|
||||
AssertSameCompartment(JSContext* cx, JSObject* obj);
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
AssertSameCompartment(JSContext* cx, JS::HandleValue v);
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
JS_FRIEND_API(void)
|
||||
AssertSameCompartment(JSObject* objA, JSObject* objB);
|
||||
|
|
|
|||
|
|
@ -530,7 +530,7 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
|||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript, MutableHandleFunction objp)
|
||||
HandleScriptSource sourceObject, MutableHandleFunction objp)
|
||||
{
|
||||
enum FirstWordFlag {
|
||||
HasAtom = 0x1,
|
||||
|
|
@ -544,21 +544,15 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
uint32_t firstword = 0; /* bitmask of FirstWordFlag */
|
||||
uint32_t flagsword = 0; /* word for argument count and fun->flags */
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
RootedFunction fun(cx);
|
||||
RootedScript script(cx);
|
||||
Rooted<LazyScript*> lazy(cx);
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
fun = objp;
|
||||
if (!fun->isInterpreted()) {
|
||||
JSAutoByteString funNameBytes;
|
||||
if (const char* name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
|
||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_NOT_SCRIPTED_FUNCTION, name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!fun->isInterpreted())
|
||||
return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
|
||||
|
||||
if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
|
||||
firstword |= HasAtom;
|
||||
|
|
@ -590,6 +584,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
fun->environment() == nullptr);
|
||||
}
|
||||
|
||||
// Everything added below can substituted by the non-lazy-script version of
|
||||
// this function later.
|
||||
js::AutoXDRTree funTree(xdr, xdr->getTreeKey(fun));
|
||||
|
||||
if (!xdr->codeUint32(&firstword))
|
||||
return false;
|
||||
|
||||
|
|
@ -601,7 +599,11 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
if (mode == XDR_DECODE) {
|
||||
RootedObject proto(cx);
|
||||
if (firstword & HasStarGeneratorProto) {
|
||||
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
|
||||
// If we are off the main thread, the generator meta-objects have
|
||||
// already been created by js::StartOffThreadParseTask, so
|
||||
// JSContext* will not be necessary.
|
||||
JSContext* context = cx->maybeJSContext();
|
||||
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(context, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
|
@ -618,10 +620,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
}
|
||||
|
||||
if (firstword & IsLazy) {
|
||||
if (!XDRLazyScript(xdr, enclosingScope, enclosingScript, fun, &lazy))
|
||||
if (!XDRLazyScript(xdr, enclosingScope, sourceObject, fun, &lazy))
|
||||
return false;
|
||||
} else {
|
||||
if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script))
|
||||
if (!XDRScript(xdr, enclosingScope, sourceObject, fun, &script))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -650,10 +652,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
}
|
||||
|
||||
template bool
|
||||
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, MutableHandleFunction);
|
||||
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource, MutableHandleFunction);
|
||||
|
||||
template bool
|
||||
js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleScope, HandleScript, MutableHandleFunction);
|
||||
js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource, MutableHandleFunction);
|
||||
|
||||
/* ES6 (04-25-16) 19.2.3.6 Function.prototype [ @@hasInstance ] */
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -879,7 +879,7 @@ JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource);
|
|||
template<XDRMode mode>
|
||||
bool
|
||||
XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript, MutableHandleFunction objp);
|
||||
HandleScriptSource sourceObject, MutableHandleFunction objp);
|
||||
|
||||
/*
|
||||
* Report an error that call.thisv is not compatible with the specified class,
|
||||
|
|
|
|||
|
|
@ -1137,7 +1137,7 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
|
|||
}
|
||||
|
||||
static bool
|
||||
GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
|
||||
GetScriptArrayObjectElements(ExclusiveContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
MOZ_ASSERT(!obj->isSingleton());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
|
|
@ -1155,7 +1155,7 @@ GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVe
|
|||
}
|
||||
|
||||
static bool
|
||||
GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj,
|
||||
GetScriptPlainObjectProperties(ExclusiveContext* cx, HandleObject obj,
|
||||
MutableHandle<IdValueVector> properties)
|
||||
{
|
||||
if (obj->is<PlainObject>()) {
|
||||
|
|
@ -1330,9 +1330,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
|
|||
{
|
||||
/* NB: Keep this in sync with DeepCloneObjectLiteral. */
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(),
|
||||
cx->compartment()->behaviors().getSingletonsAsTemplates());
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
assertSameCompartment(cx, obj);
|
||||
|
||||
// Distinguish between objects and array classes.
|
||||
uint32_t isArray = 0;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ template<XDRMode mode>
|
|||
bool
|
||||
js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
enum ConstTag {
|
||||
SCRIPT_INT,
|
||||
|
|
@ -195,7 +195,7 @@ template<XDRMode mode>
|
|||
static bool
|
||||
XDRLazyClosedOverBindings(XDRState<mode>* xdr, MutableHandle<LazyScript*> lazy)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
RootedAtom atom(cx);
|
||||
for (size_t i = 0; i < lazy->numClosedOverBindings(); i++) {
|
||||
uint8_t endOfScopeSentinel;
|
||||
|
|
@ -228,7 +228,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
|||
MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript());
|
||||
MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
uint64_t packedFields;
|
||||
{
|
||||
|
|
@ -257,7 +257,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
|
|||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
|
||||
RootedScriptSource sourceObject(cx, &script->scriptSourceUnwrap());
|
||||
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, sourceObject,
|
||||
packedFields, begin, end, toStringStart, lineno, column));
|
||||
|
||||
if (!lazy)
|
||||
|
|
@ -304,8 +305,9 @@ enum XDRClassKind {
|
|||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScript enclosingScript,
|
||||
HandleFunction fun, MutableHandleScript scriptp)
|
||||
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
||||
HandleScriptSource sourceObjectArg, HandleFunction fun,
|
||||
MutableHandleScript scriptp)
|
||||
{
|
||||
/* NB: Keep this in sync with CopyScript. */
|
||||
|
||||
|
|
@ -348,17 +350,16 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
uint32_t scriptBits = 0;
|
||||
uint32_t bodyScopeIndex = 0;
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
RootedScript script(cx);
|
||||
natoms = nsrcnotes = 0;
|
||||
nconsts = nobjects = nscopes = nregexps = ntrynotes = nscopenotes = nyieldoffsets = 0;
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
script = scriptp.get();
|
||||
MOZ_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
|
||||
MOZ_ASSERT(script->functionNonDelazifying() == fun);
|
||||
|
||||
if (!fun && script->treatAsRunOnce()) {
|
||||
if (!fun && script->treatAsRunOnce() && script->hasRunOnce()) {
|
||||
// This is a toplevel or eval script that's runOnce. We want to
|
||||
// make sure that we're not XDR-saving an object we emitted for
|
||||
// JSOP_OBJECT that then got modified. So throw if we're not
|
||||
|
|
@ -431,7 +432,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
scriptBits |= (1 << FunctionHasThisBinding);
|
||||
if (script->functionHasExtraBodyVarScope())
|
||||
scriptBits |= (1 << FunctionHasExtraBodyVarScope);
|
||||
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
|
||||
MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
|
||||
if (!sourceObjectArg)
|
||||
scriptBits |= (1 << OwnSource);
|
||||
if (script->isGeneratorExp())
|
||||
scriptBits |= (1 << IsGeneratorExp);
|
||||
|
|
@ -492,15 +494,31 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
if (!xdr->codeUint32(&scriptBits))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(!!(scriptBits & (1 << OwnSource)) == !sourceObjectArg);
|
||||
RootedScriptSource sourceObject(cx, sourceObjectArg);
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
JSVersion version_ = JSVersion(version);
|
||||
MOZ_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
|
||||
|
||||
CompileOptions options(cx);
|
||||
options.setVersion(version_)
|
||||
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
|
||||
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
|
||||
RootedScriptSource sourceObject(cx);
|
||||
// When loading from the bytecode cache, we get the CompileOption from
|
||||
// the document, which specify the version to use. If the version does
|
||||
// not match, then we should fail.
|
||||
mozilla::Maybe<CompileOptions> options;
|
||||
if (xdr->hasOptions()) {
|
||||
options.emplace(xdr->cx(), xdr->options());
|
||||
if (options->version != version_ ||
|
||||
options->noScriptRval != !!(scriptBits & (1 << NoScriptRval)) ||
|
||||
options->selfHostingMode != !!(scriptBits & (1 << SelfHosted)))
|
||||
{
|
||||
return xdr->fail(JS::TranscodeResult_Failure_WrongCompileOption);
|
||||
}
|
||||
} else {
|
||||
options.emplace(xdr->cx()->asJSContext());
|
||||
(*options).setVersion(version_)
|
||||
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
|
||||
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
|
||||
}
|
||||
if (scriptBits & (1 << OwnSource)) {
|
||||
ScriptSource* ss = cx->new_<ScriptSource>();
|
||||
if (!ss)
|
||||
|
|
@ -513,22 +531,22 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
* ScriptSourceObject, and those that are (element; elementAttributeName)
|
||||
* aren't preserved by XDR. So this can be simple.
|
||||
*/
|
||||
CompileOptions options(cx);
|
||||
ss->initFromOptions(cx, options);
|
||||
ss->initFromOptions(cx, *options);
|
||||
sourceObject = ScriptSourceObject::create(cx, ss);
|
||||
if (!sourceObject ||
|
||||
!ScriptSourceObject::initFromOptions(cx, sourceObject, options))
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(enclosingScript);
|
||||
// When decoding, all the scripts and the script source object
|
||||
// are in the same compartment, so the script's source object
|
||||
// should never be a cross-compartment wrapper.
|
||||
MOZ_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
|
||||
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
|
||||
if (xdr->hasScriptSourceObjectOut()) {
|
||||
// When the ScriptSourceObjectOut is provided by ParseTask, it
|
||||
// is stored in a location which is traced by the GC.
|
||||
*xdr->scriptSourceObjectOut() = sourceObject;
|
||||
} else {
|
||||
if (!sourceObject ||
|
||||
!ScriptSourceObject::initFromOptions(cx->asJSContext(), sourceObject, *options))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script = JSScript::Create(cx, options, sourceObject, 0, 0, 0, 0);
|
||||
script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0, 0);
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
|
|
@ -536,6 +554,10 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
// decoded may iterate the static scope chain.
|
||||
if (fun)
|
||||
fun->initScript(script);
|
||||
} else {
|
||||
// When encoding, we do not mutate any of the JSScript or LazyScript, so
|
||||
// we can safely unwrap it here.
|
||||
sourceObject = &script->scriptSourceUnwrap();
|
||||
}
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
|
|
@ -606,7 +628,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
|
||||
|
||||
if (scriptBits & (1 << OwnSource)) {
|
||||
if (!script->scriptSource()->performXDR<mode>(xdr))
|
||||
if (!sourceObject->source()->performXDR<mode>(xdr))
|
||||
return false;
|
||||
}
|
||||
if (!xdr->codeUint32(&script->sourceStart_))
|
||||
|
|
@ -846,7 +868,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
RootedFunction tmp(cx);
|
||||
if (mode == XDR_ENCODE)
|
||||
tmp = &(*objp)->as<JSFunction>();
|
||||
if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
|
||||
if (!XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp))
|
||||
return false;
|
||||
*objp = tmp;
|
||||
break;
|
||||
|
|
@ -922,27 +944,28 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
|
|||
scriptp.set(script);
|
||||
|
||||
/* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
|
||||
if (!fun)
|
||||
Debugger::onNewScript(cx, script);
|
||||
if (!fun && cx->isJSContext())
|
||||
Debugger::onNewScript(cx->asJSContext(), script);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool
|
||||
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, HandleFunction,
|
||||
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource, HandleFunction,
|
||||
MutableHandleScript);
|
||||
|
||||
template bool
|
||||
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript, HandleFunction,
|
||||
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource, HandleFunction,
|
||||
MutableHandleScript);
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
||||
HandleFunction fun, MutableHandle<LazyScript*> lazy)
|
||||
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
||||
HandleScriptSource sourceObject, HandleFunction fun,
|
||||
MutableHandle<LazyScript*> lazy)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
{
|
||||
uint32_t begin;
|
||||
|
|
@ -979,7 +1002,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
|
|||
}
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
|
||||
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, sourceObject,
|
||||
packedFields, begin, end, toStringStart, lineno, column));
|
||||
if (!lazy)
|
||||
return false;
|
||||
|
|
@ -1013,11 +1036,11 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
|
|||
}
|
||||
|
||||
template bool
|
||||
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript,
|
||||
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource,
|
||||
HandleFunction, MutableHandle<LazyScript*>);
|
||||
|
||||
template bool
|
||||
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript,
|
||||
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource,
|
||||
HandleFunction, MutableHandle<LazyScript*>);
|
||||
|
||||
void
|
||||
|
|
@ -1352,6 +1375,16 @@ ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
|
||||
sso->source()->decref();
|
||||
sso->setReservedSlot(SOURCE_SLOT, PrivateValue(nullptr));
|
||||
|
||||
Value value = sso->canonicalPrivate();
|
||||
if (!value.isUndefined()) {
|
||||
// The embedding may need to dispose of its private data.
|
||||
JS::AutoSuppressGCAnalysis suppressGC;
|
||||
if (JS::ScriptPrivateFinalizeHook hook =
|
||||
fop->runtime()->scriptPrivateFinalizeHook) {
|
||||
hook(fop, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const ClassOps ScriptSourceObjectClassOps = {
|
||||
|
|
@ -1378,7 +1411,7 @@ const Class ScriptSourceObject::class_ = {
|
|||
};
|
||||
|
||||
ScriptSourceObject*
|
||||
ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
|
||||
ScriptSourceObject::createInternal(ExclusiveContext* cx, ScriptSource* source, HandleObject canonical)
|
||||
{
|
||||
RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr));
|
||||
if (!object)
|
||||
|
|
@ -1388,6 +1421,12 @@ ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
|
|||
source->incref(); // The matching decref is in ScriptSourceObject::finalize.
|
||||
sourceObject->initReservedSlot(SOURCE_SLOT, PrivateValue(source));
|
||||
|
||||
if (canonical) {
|
||||
sourceObject->initReservedSlot(CANONICAL_SLOT, ObjectValue(*canonical));
|
||||
} else {
|
||||
sourceObject->initReservedSlot(CANONICAL_SLOT, ObjectValue(*sourceObject));
|
||||
}
|
||||
|
||||
// The remaining slots should eventually be populated by a call to
|
||||
// initFromOptions. Poison them until that point.
|
||||
sourceObject->initReservedSlot(ELEMENT_SLOT, MagicValue(JS_GENERIC_MAGIC));
|
||||
|
|
@ -1397,6 +1436,20 @@ ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
|
|||
return sourceObject;
|
||||
}
|
||||
|
||||
ScriptSourceObject*
|
||||
ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
|
||||
{
|
||||
return createInternal(cx, source, nullptr);
|
||||
}
|
||||
|
||||
ScriptSourceObject* ScriptSourceObject::unwrappedCanonical() const
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromAnyThread()));
|
||||
|
||||
JSObject* obj = &getReservedSlot(CANONICAL_SLOT).toObject();
|
||||
return &UncheckedUnwrap(obj)->as<ScriptSourceObject>();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ScriptSourceObject::initFromOptions(JSContext* cx, HandleScriptSource source,
|
||||
const ReadOnlyCompileOptions& options)
|
||||
|
|
@ -1934,6 +1987,63 @@ ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
|||
info->numScripts++;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptSource::xdrEncodeTopLevel(ExclusiveContext* cx, HandleScript script)
|
||||
{
|
||||
xdrEncoder_ = js::MakeUnique<XDRIncrementalEncoder>(cx);
|
||||
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(JS::TranscodeBuffer& buffer)
|
||||
{
|
||||
MOZ_ASSERT(hasEncoder());
|
||||
auto cleanup = mozilla::MakeScopeExit([&] {
|
||||
xdrEncoder_.reset(nullptr);
|
||||
});
|
||||
|
||||
if (!xdrEncoder_->linearize(buffer))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
ScriptSource::performXDR(XDRState<mode>* xdr)
|
||||
|
|
@ -2073,7 +2183,10 @@ ScriptSource::performXDR(XDRState<mode>* xdr)
|
|||
const char* fn = filename();
|
||||
if (!xdr->codeCString(&fn))
|
||||
return false;
|
||||
if (mode == XDR_DECODE && !setFilename(xdr->cx(), fn))
|
||||
// Note: If the decoder has an option, then the filename is defined by
|
||||
// the CompileOption from the document.
|
||||
MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
|
||||
if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -4089,7 +4202,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
|||
Handle<GCVector<JSFunction*, 8>> innerFunctions,
|
||||
JSVersion version,
|
||||
uint32_t begin, uint32_t end,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column)
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column,
|
||||
frontend::ParseGoal parseGoal)
|
||||
{
|
||||
union {
|
||||
PackedView p;
|
||||
|
|
@ -4112,6 +4226,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
|||
p.isLikelyConstructorWrapper = false;
|
||||
p.isDerivedClassConstructor = false;
|
||||
p.needsHomeObject = false;
|
||||
p.parseGoal = uint32_t(parseGoal);
|
||||
|
||||
LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, toStringStart,
|
||||
lineno, column);
|
||||
|
|
@ -4133,7 +4248,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
|||
/* static */ LazyScript*
|
||||
LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
||||
HandleScript script, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript,
|
||||
HandleScriptSource sourceObject,
|
||||
uint64_t packedFields, uint32_t begin, uint32_t end,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column)
|
||||
{
|
||||
|
|
@ -4164,11 +4279,11 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
|
|||
// values should only be non-null if we have a non-lazy enclosing script.
|
||||
// AddLazyFunctionsForCompartment relies on the source object being null
|
||||
// if we're nested inside another lazy function.
|
||||
MOZ_ASSERT(!!enclosingScript == !!enclosingScope);
|
||||
MOZ_ASSERT(!!sourceObject == !!enclosingScope);
|
||||
MOZ_ASSERT(!res->sourceObject());
|
||||
MOZ_ASSERT(!res->enclosingScope());
|
||||
if (enclosingScript)
|
||||
res->setEnclosingScopeAndSource(enclosingScope, &enclosingScript->scriptSourceUnwrap());
|
||||
if (sourceObject)
|
||||
res->setEnclosingScopeAndSource(enclosingScope, sourceObject);
|
||||
|
||||
MOZ_ASSERT(!res->hasScript());
|
||||
if (script)
|
||||
|
|
|
|||
|
|
@ -431,6 +431,11 @@ class ScriptSource
|
|||
// memory management.
|
||||
const char* introductionType_;
|
||||
|
||||
// The bytecode cache encoder is used to encode only the content of function
|
||||
// which are delazified. If this value is not nullptr, then each delazified
|
||||
// function should be recorded before their first execution.
|
||||
UniquePtr<XDRIncrementalEncoder> xdrEncoder_;
|
||||
|
||||
// True if we can call JSRuntime::sourceHook to load the source on
|
||||
// demand. If sourceRetrievable_ and hasSourceData() are false, it is not
|
||||
// possible to get source at all.
|
||||
|
|
@ -452,6 +457,7 @@ class ScriptSource
|
|||
parameterListEnd_(0),
|
||||
introducerFilename_(nullptr),
|
||||
introductionType_(nullptr),
|
||||
xdrEncoder_(nullptr),
|
||||
sourceRetrievable_(false),
|
||||
hasIntroductionOffset_(false)
|
||||
{
|
||||
|
|
@ -574,6 +580,29 @@ class ScriptSource
|
|||
introductionOffset_ = offset;
|
||||
hasIntroductionOffset_ = true;
|
||||
}
|
||||
|
||||
// Return wether an XDR encoder is present or not.
|
||||
bool hasEncoder() const { return bool(xdrEncoder_); }
|
||||
|
||||
// Create a new XDR encoder, and encode the top-level JSScript. The result
|
||||
// of the encoding would be available in the |buffer| provided as argument,
|
||||
// as soon as |xdrFinalize| is called and all xdr function calls returned
|
||||
// successfully.
|
||||
bool xdrEncodeTopLevel(ExclusiveContext* cx, 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(JS::TranscodeBuffer& buffer);
|
||||
};
|
||||
|
||||
class ScriptSourceHolder
|
||||
|
|
@ -608,6 +637,14 @@ class ScriptSourceObject : public NativeObject
|
|||
{
|
||||
static const ClassOps classOps_;
|
||||
|
||||
static ScriptSourceObject* createInternal(ExclusiveContext* cx, ScriptSource* source,
|
||||
HandleObject canonical);
|
||||
|
||||
bool isCanonical() const {
|
||||
return &getReservedSlot(CANONICAL_SLOT).toObject() == this;
|
||||
}
|
||||
ScriptSourceObject* unwrappedCanonical() const;
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
|
|
@ -638,12 +675,30 @@ class ScriptSourceObject : public NativeObject
|
|||
return static_cast<JSScript*>(untyped);
|
||||
}
|
||||
|
||||
void setPrivate(const Value& value) {
|
||||
setReservedSlot(PRIVATE_SLOT, value);
|
||||
}
|
||||
|
||||
Value getPrivate() const {
|
||||
return getReservedSlot(PRIVATE_SLOT);
|
||||
}
|
||||
|
||||
Value canonicalPrivate() const {
|
||||
Value value = getReservedSlot(PRIVATE_SLOT);
|
||||
MOZ_ASSERT_IF(!isCanonical(), value.isUndefined());
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint32_t SOURCE_SLOT = 0;
|
||||
static const uint32_t ELEMENT_SLOT = 1;
|
||||
static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
|
||||
static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
|
||||
static const uint32_t RESERVED_SLOTS = 4;
|
||||
enum {
|
||||
SOURCE_SLOT = 0,
|
||||
CANONICAL_SLOT,
|
||||
ELEMENT_SLOT,
|
||||
ELEMENT_PROPERTY_SLOT,
|
||||
INTRODUCTION_SCRIPT_SLOT,
|
||||
PRIVATE_SLOT,
|
||||
RESERVED_SLOTS
|
||||
};
|
||||
};
|
||||
|
||||
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
|
||||
|
|
@ -678,12 +733,12 @@ AsyncKindFromBits(unsigned val) {
|
|||
*/
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
||||
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
|
||||
HandleFunction fun, MutableHandleScript scriptp);
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
|
||||
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
|
||||
HandleFunction fun, MutableHandle<LazyScript*> lazy);
|
||||
|
||||
/*
|
||||
|
|
@ -791,7 +846,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
friend
|
||||
bool
|
||||
js::XDRScript(js::XDRState<mode>* xdr, js::HandleScope enclosingScope,
|
||||
js::HandleScript enclosingScript, js::HandleFunction fun,
|
||||
js::HandleScriptSource sourceObject, js::HandleFunction fun,
|
||||
js::MutableHandleScript scriptp);
|
||||
|
||||
friend bool
|
||||
|
|
@ -1194,11 +1249,11 @@ class JSScript : public js::gc::TenuredCell
|
|||
return funLength_;
|
||||
}
|
||||
|
||||
size_t sourceStart() const {
|
||||
uint32_t sourceStart() const {
|
||||
return sourceStart_;
|
||||
}
|
||||
|
||||
size_t sourceEnd() const {
|
||||
uint32_t sourceEnd() const {
|
||||
return sourceEnd_;
|
||||
}
|
||||
|
||||
|
|
@ -2009,6 +2064,7 @@ class LazyScript : public gc::TenuredCell
|
|||
uint32_t isDerivedClassConstructor : 1;
|
||||
uint32_t needsHomeObject : 1;
|
||||
uint32_t hasRest : 1;
|
||||
uint32_t parseGoal : 1;
|
||||
};
|
||||
|
||||
union {
|
||||
|
|
@ -2048,7 +2104,8 @@ class LazyScript : public gc::TenuredCell
|
|||
const frontend::AtomVector& closedOverBindings,
|
||||
Handle<GCVector<JSFunction*, 8>> innerFunctions,
|
||||
JSVersion version, uint32_t begin, uint32_t end,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column);
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column,
|
||||
frontend::ParseGoal parseGoal);
|
||||
|
||||
// Create a LazyScript and initialize the closedOverBindings and the
|
||||
// innerFunctions with dummy values to be replaced in a later initialization
|
||||
|
|
@ -2057,11 +2114,11 @@ class LazyScript : public gc::TenuredCell
|
|||
// The "script" argument to this function can be null. If it's non-null,
|
||||
// then this LazyScript should be associated with the given JSScript.
|
||||
//
|
||||
// The enclosingScript and enclosingScope arguments may be null if the
|
||||
// The sourceObject and enclosingScope arguments may be null if the
|
||||
// enclosing function is also lazy.
|
||||
static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
|
||||
HandleScript script, HandleScope enclosingScope,
|
||||
HandleScript enclosingScript,
|
||||
HandleScriptSource sourceObject,
|
||||
uint64_t packedData, uint32_t begin, uint32_t end,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column);
|
||||
|
||||
|
|
@ -2159,6 +2216,10 @@ class LazyScript : public gc::TenuredCell
|
|||
p_.isExprBody = true;
|
||||
}
|
||||
|
||||
frontend::ParseGoal parseGoal() const {
|
||||
return frontend::ParseGoal(p_.parseGoal);
|
||||
}
|
||||
|
||||
bool strict() const {
|
||||
return p_.strict;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,11 +33,40 @@ Reflect.Loader = new class {
|
|||
return module;
|
||||
}
|
||||
|
||||
["import"](name, referrer) {
|
||||
["import"](name, referencingInfo) {
|
||||
let module = this.loadAndParse(name);
|
||||
module.declarationInstantiation();
|
||||
return module.evaluation();
|
||||
}
|
||||
|
||||
populateImportMeta(module, metaObject) {
|
||||
// For the shell, use the script's filename as the base URL.
|
||||
|
||||
let path;
|
||||
if (ReflectApply(MapPrototypeHas, this.modulePaths, [module])) {
|
||||
path = ReflectApply(MapPrototypeGet, this.modulePaths, [module]);
|
||||
} else {
|
||||
path = "(unknown)";
|
||||
}
|
||||
metaObject.url = path;
|
||||
}
|
||||
};
|
||||
|
||||
setModuleResolveHook((module, requestName) => Reflect.Loader.loadAndParse(requestName));
|
||||
setModuleResolveHook((referencingInfo, requestName) => {
|
||||
let path = ReflectLoader.resolve(requestName, referencingInfo);
|
||||
return ReflectLoader.loadAndParse(path);
|
||||
});
|
||||
|
||||
setModuleMetadataHook((module, metaObject) => {
|
||||
ReflectLoader.populateImportMeta(module, metaObject);
|
||||
});
|
||||
|
||||
setModuleDynamicImportHook((referencingInfo, specifier, promise) => {
|
||||
try {
|
||||
let path = ReflectLoader.resolve(specifier, referencingInfo);
|
||||
ReflectLoader.loadAndExecute(path);
|
||||
finishDynamicModuleImport(referencingInfo, specifier, promise);
|
||||
} catch (err) {
|
||||
abortDynamicModuleImport(referencingInfo, specifier, promise, err);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -603,6 +603,32 @@ EnvironmentPreparer::invoke(HandleObject scope, Closure& closure)
|
|||
return;
|
||||
}
|
||||
|
||||
static bool
|
||||
RegisterScriptPathWithModuleLoader(JSContext* cx, HandleScript script, const char* filename)
|
||||
{
|
||||
// Set the private value associated with a script to a object containing the
|
||||
// script's filename so that the module loader can use it to resolve
|
||||
// relative imports.
|
||||
|
||||
RootedString path(cx, JS_NewStringCopyZ(cx, filename));
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject infoObject(cx, JS_NewPlainObject(cx));
|
||||
if (!infoObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue pathValue(cx, StringValue(path));
|
||||
if (!JS_DefineProperty(cx, infoObject, "path", pathValue, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::SetScriptPrivate(script, ObjectValue(*infoObject));
|
||||
return true;
|
||||
}
|
||||
|
||||
static MOZ_MUST_USE bool
|
||||
RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
|
||||
{
|
||||
|
|
@ -635,6 +661,10 @@ RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
|
|||
MOZ_ASSERT(script);
|
||||
}
|
||||
|
||||
if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (dumpEntrainedVariables)
|
||||
AnalyzeEntrainedVariables(cx, script);
|
||||
|
|
@ -1575,6 +1605,7 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
|||
bool catchTermination = false;
|
||||
bool loadBytecode = false;
|
||||
bool saveBytecode = false;
|
||||
bool saveIncrementalBytecode = false;
|
||||
bool assertEqBytecode = false;
|
||||
RootedObject callerGlobal(cx, cx->global());
|
||||
|
||||
|
|
@ -1638,6 +1669,11 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!v.isUndefined())
|
||||
saveBytecode = ToBoolean(v);
|
||||
|
||||
if (!JS_GetProperty(cx, opts, "saveIncrementalBytecode", &v))
|
||||
return false;
|
||||
if (!v.isUndefined())
|
||||
saveIncrementalBytecode = ToBoolean(v);
|
||||
|
||||
if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v))
|
||||
return false;
|
||||
if (!v.isUndefined())
|
||||
|
|
@ -1645,12 +1681,17 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// We cannot load or save the bytecode if we have no object where the
|
||||
// bytecode cache is stored.
|
||||
if (loadBytecode || saveBytecode) {
|
||||
if (loadBytecode || saveBytecode || saveIncrementalBytecode) {
|
||||
if (!cacheEntry) {
|
||||
JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
|
||||
"evaluate");
|
||||
return false;
|
||||
}
|
||||
if (saveIncrementalBytecode && saveBytecode) {
|
||||
JS_ReportErrorASCII(cx, "saveIncrementalBytecode and saveBytecode cannot be used"
|
||||
" at the same time.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1728,6 +1769,15 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!script->scriptSource()->setSourceMapURL(cx, smurl))
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we want to save the bytecode incrementally, then we should
|
||||
// register ahead the fact that every JSFunction which is being
|
||||
// delazified should be encoded at the end of the delazification.
|
||||
if (saveIncrementalBytecode) {
|
||||
if (!StartIncrementalEncoding(cx, script))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_ExecuteScript(cx, script, args.rval())) {
|
||||
if (catchTermination && !JS_IsExceptionPending(cx)) {
|
||||
JSAutoCompartment ac1(cx, callerGlobal);
|
||||
|
|
@ -1740,14 +1790,21 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Encode the bytecode after the execution of the script.
|
||||
if (saveBytecode) {
|
||||
JS::TranscodeResult rv = JS::EncodeScript(cx, saveBuffer, script);
|
||||
if (!ConvertTranscodeResultToJSException(cx, rv))
|
||||
return false;
|
||||
}
|
||||
// Serialize the encoded bytecode, recorded before the execution, into a
|
||||
// buffer which can be deserialized linearly.
|
||||
if (saveIncrementalBytecode) {
|
||||
if (!FinishIncrementalEncoding(cx, script, saveBuffer))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (saveBytecode) {
|
||||
if (saveBytecode || saveIncrementalBytecode) {
|
||||
// If we are both loading and saving, we assert that we are going to
|
||||
// replace the current bytecode by the same stream of bytes.
|
||||
if (loadBytecode && assertEqBytecode) {
|
||||
|
|
@ -2800,7 +2857,7 @@ DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp, Sprinter* sprinte
|
|||
RootedScript script(cx);
|
||||
RootedValue value(cx, p.argv[i]);
|
||||
if (value.isObject() && value.toObject().is<ModuleObject>())
|
||||
script = value.toObject().as<ModuleObject>().script();
|
||||
script = value.toObject().as<ModuleObject>().maybeScript();
|
||||
else
|
||||
script = ValueToScript(cx, value, fun.address());
|
||||
if (!script)
|
||||
|
|
@ -4035,12 +4092,12 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
|
||||
static JSObject*
|
||||
CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier)
|
||||
ShellModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier)
|
||||
{
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
|
||||
JS::AutoValueArray<2> args(cx);
|
||||
args[0].setObject(*module);
|
||||
args[0].set(referencingPrivate);
|
||||
args[1].setString(specifier);
|
||||
|
||||
RootedValue result(cx);
|
||||
|
|
@ -4055,6 +4112,192 @@ CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier
|
|||
return &result.toObject();
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportArgumentTypeError(JSContext* cx, HandleValue value, const char* expected)
|
||||
{
|
||||
const char* typeName = InformalValueTypeName(value);
|
||||
JS_ReportErrorASCII(cx, "Expected %s, got %s", expected, typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ShellSetModulePrivate(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() != 2) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"setModulePrivate", "0", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[0], "module object");
|
||||
}
|
||||
|
||||
JS::SetModulePrivate(&args[0].toObject(), args[1]);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ShellGetModulePrivate(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() != 1) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"getModulePrivate", "0", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[0], "module object");
|
||||
}
|
||||
|
||||
args.rval().set(JS::GetModulePrivate(&args[0].toObject()));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetModuleMetadataHook(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 1) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"setModuleMetadataHook", "0", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
|
||||
const char* typeName = InformalValueTypeName(args[0]);
|
||||
JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
global->setReservedSlot(GlobalAppSlotModuleMetadataHook, args[0]);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CallModuleMetadataHook(JSContext* cx, HandleObject module, HandleObject metaObject)
|
||||
{
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
RootedValue hookValue(cx, global->getReservedSlot(GlobalAppSlotModuleMetadataHook));
|
||||
if (hookValue.isUndefined()) {
|
||||
JS_ReportErrorASCII(cx, "Module metadata hook not set");
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
|
||||
|
||||
JS::AutoValueArray<2> args(cx);
|
||||
args[0].setObject(*module);
|
||||
args[1].setObject(*metaObject);
|
||||
|
||||
RootedValue dummy(cx);
|
||||
return JS_CallFunctionValue(cx, nullptr, hookValue, args, &dummy);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetModuleDynamicImportHook(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 1) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"setModuleDynamicImportHook", "0", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
|
||||
const char* typeName = InformalValueTypeName(args[0]);
|
||||
JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
global->setReservedSlot(GlobalAppSlotModuleDynamicImportHook, args[0]);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
FinishDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 3) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"finishDynamicModuleImport", "0", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[1].isString()) {
|
||||
return ReportArgumentTypeError(cx, args[1], "String");
|
||||
}
|
||||
|
||||
if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[2], "PromiseObject");
|
||||
}
|
||||
|
||||
RootedString specifier(cx, args[1].toString());
|
||||
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
|
||||
|
||||
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
|
||||
}
|
||||
|
||||
static bool
|
||||
AbortDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 4) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"abortDynamicModuleImport", "0", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[1].isString()) {
|
||||
return ReportArgumentTypeError(cx, args[1], "String");
|
||||
}
|
||||
|
||||
if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
|
||||
return ReportArgumentTypeError(cx, args[2], "PromiseObject");
|
||||
}
|
||||
|
||||
RootedString specifier(cx, args[1].toString());
|
||||
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
|
||||
|
||||
cx->setPendingException(args[3]);
|
||||
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
|
||||
}
|
||||
|
||||
static bool
|
||||
ShellModuleDynamicImportHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
|
||||
HandleObject promise)
|
||||
{
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
RootedValue hookValue(cx, global->getReservedSlot(GlobalAppSlotModuleDynamicImportHook));
|
||||
if (hookValue.isUndefined()) {
|
||||
JS_ReportErrorASCII(cx, "Module resolve hook not set");
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
|
||||
|
||||
JS::AutoValueArray<3> args(cx);
|
||||
args[0].set(referencingPrivate);
|
||||
args[1].setString(specifier);
|
||||
args[2].setObject(*promise);
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!JS_CallFunctionValue(cx, nullptr, hookValue, args, &result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
|
@ -4099,7 +4342,8 @@ Parse(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!usedNames.init())
|
||||
return false;
|
||||
Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
|
||||
/* foldConstants = */ true, usedNames, nullptr, nullptr);
|
||||
/* foldConstants = */ true, usedNames, nullptr, nullptr,
|
||||
ParseGoal::Script);
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
|
|
@ -4150,7 +4394,8 @@ SyntaxParse(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
Parser<frontend::SyntaxParseHandler> parser(cx, cx->tempLifoAlloc(),
|
||||
options, chars, length, false,
|
||||
usedNames, nullptr, nullptr);
|
||||
usedNames, nullptr, nullptr,
|
||||
ParseGoal::Script);
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
|
|
@ -5417,7 +5662,11 @@ DumpScopeChain(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
script = JSFunction::getOrCreateScript(cx, fun);
|
||||
} else {
|
||||
script = obj->as<ModuleObject>().script();
|
||||
script = obj->as<ModuleObject>().maybeScript();
|
||||
if (!script) {
|
||||
JS_ReportErrorASCII(cx, "module does not have an associated script");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
script->bodyScope()->dump();
|
||||
|
|
@ -5918,11 +6167,41 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
|||
" Parses source text as a module and returns a Module object."),
|
||||
|
||||
JS_FN_HELP("setModuleResolveHook", SetModuleResolveHook, 1, 0,
|
||||
"setModuleResolveHook(function(module, specifier) {})",
|
||||
"setModuleResolveHook(function(referrer, specifier))",
|
||||
" Set the HostResolveImportedModule hook to |function|.\n"
|
||||
" This hook is used to look up a previously loaded module object. It should\n"
|
||||
" be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("setModulePrivate", ShellSetModulePrivate, 2, 0,
|
||||
"setModulePrivate(scriptObject, privateValue)",
|
||||
" Associate a private value with a module object.\n"),
|
||||
|
||||
JS_FN_HELP("getModulePrivate", ShellGetModulePrivate, 2, 0,
|
||||
"getModulePrivate(scriptObject)",
|
||||
" Get the private value associated with a module object.\n"),
|
||||
|
||||
JS_FN_HELP("setModuleMetadataHook", SetModuleMetadataHook, 1, 0,
|
||||
"setModuleMetadataHook(function(module) {})",
|
||||
" Set the HostPopulateImportMeta hook to |function|.\n"
|
||||
" This hook is used to create the metadata object returned by import.meta for\n"
|
||||
" a module. It should be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("setModuleDynamicImportHook", SetModuleDynamicImportHook, 1, 0,
|
||||
"setModuleDynamicImportHook(function(referrer, specifier, promise))",
|
||||
" Set the HostImportModuleDynamically hook to |function|.\n"
|
||||
" This hook is used to dynamically import a module. It should\n"
|
||||
" be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("finishDynamicModuleImport", FinishDynamicModuleImport, 3, 0,
|
||||
"finishDynamicModuleImport(referrer, specifier, promise)",
|
||||
" The module loader's dynamic import hook should call this when the module has"
|
||||
" been loaded successfully."),
|
||||
|
||||
JS_FN_HELP("abortDynamicModuleImport", AbortDynamicModuleImport, 4, 0,
|
||||
"abortDynamicModuleImport(referrer, specifier, promise, error)",
|
||||
" The module loader's dynamic import hook should call this when the module "
|
||||
" import has failed."),
|
||||
|
||||
JS_FN_HELP("getModuleLoadPath", GetModuleLoadPath, 0, 0,
|
||||
"getModuleLoadPath()",
|
||||
" Return any --module-load-path argument passed to the shell. Used by the\n"
|
||||
|
|
@ -7222,7 +7501,7 @@ ProcessArgs(JSContext* cx, OptionParser* op)
|
|||
if (const char* path = op->getStringOption("module-load-path"))
|
||||
moduleLoadPath = path;
|
||||
|
||||
if (!modulePaths.empty() && !InitModuleLoader(cx))
|
||||
if (!InitModuleLoader(cx))
|
||||
return false;
|
||||
|
||||
while (!filePaths.empty() || !codeChunks.empty() || !modulePaths.empty()) {
|
||||
|
|
@ -7980,7 +8259,9 @@ main(int argc, char** argv, char** envp)
|
|||
|
||||
js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
|
||||
|
||||
JS::SetModuleResolveHook(cx->runtime(), CallModuleResolveHook);
|
||||
JS::SetModuleResolveHook(cx->runtime(), ShellModuleResolveHook);
|
||||
JS::SetModuleDynamicImportHook(cx, ShellModuleDynamicImportHook);
|
||||
JS::SetModuleMetadataHook(cx, ShellModuleMetadataHook);
|
||||
|
||||
result = Shell(cx, &op, envp);
|
||||
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@
|
|||
macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \
|
||||
macro(get, get, "get") \
|
||||
macro(getInternals, getInternals, "getInternals") \
|
||||
macro(GetModuleNamespace, GetModuleNamespace, "GetModuleNamespace") \
|
||||
macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
|
||||
macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
|
||||
macro(getPrefix, getPrefix, "get ") \
|
||||
|
|
@ -226,6 +227,7 @@
|
|||
macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \
|
||||
macro(maximumSignificantDigits, maximumSignificantDigits, "maximumSignificantDigits") \
|
||||
macro(message, message, "message") \
|
||||
macro(meta, meta, "meta") \
|
||||
macro(minDays, minDays, "minDays") \
|
||||
macro(minimumFractionDigits, minimumFractionDigits, "minimumFractionDigits") \
|
||||
macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \
|
||||
|
|
|
|||
|
|
@ -5084,7 +5084,8 @@ Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
|
|||
frontend::Parser<frontend::FullParseHandler> parser(cx, cx->tempLifoAlloc(),
|
||||
options, chars.twoByteChars(),
|
||||
length, /* foldConstants = */ true,
|
||||
usedNames, nullptr, nullptr);
|
||||
usedNames, nullptr, nullptr,
|
||||
frontend::ParseGoal::Script);
|
||||
JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
|
||||
if (!parser.checkOptions() || !parser.parse()) {
|
||||
// We ran into an error. If it was because we ran out of memory we report
|
||||
|
|
|
|||
|
|
@ -3065,14 +3065,41 @@ WithEnvironmentObject::scope() const
|
|||
|
||||
ModuleEnvironmentObject*
|
||||
js::GetModuleEnvironmentForScript(JSScript* script)
|
||||
{
|
||||
ModuleObject* module = GetModuleObjectForScript(script);
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
return module->environment();
|
||||
}
|
||||
|
||||
ModuleObject*
|
||||
js::GetModuleObjectForScript(JSScript* script)
|
||||
{
|
||||
for (ScopeIter si(script); si; si++) {
|
||||
if (si.kind() == ScopeKind::Module)
|
||||
return si.scope()->as<ModuleScope>().module()->environment();
|
||||
return si.scope()->as<ModuleScope>().module();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value
|
||||
js::FindScriptOrModulePrivateForScript(JSScript* script)
|
||||
{
|
||||
while (script) {
|
||||
ScriptSourceObject* sso = &script->scriptSourceUnwrap();
|
||||
Value value = sso->canonicalPrivate();
|
||||
if (!value.isUndefined()) {
|
||||
return value;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(sso->introductionScript() != script);
|
||||
script = sso->introductionScript();
|
||||
}
|
||||
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
|
||||
MutableHandleValue res)
|
||||
|
|
|
|||
|
|
@ -1075,6 +1075,12 @@ CreateObjectsForEnvironmentChain(JSContext* cx, AutoObjectVector& chain,
|
|||
HandleObject terminatingEnv,
|
||||
MutableHandleObject envObj);
|
||||
|
||||
ModuleObject*
|
||||
GetModuleObjectForScript(JSScript* script);
|
||||
|
||||
Value
|
||||
FindScriptOrModulePrivateForScript(JSScript* script);
|
||||
|
||||
ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
|
|
|
|||
|
|
@ -304,6 +304,18 @@ ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusi
|
|||
{
|
||||
}
|
||||
|
||||
ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: kind(kind), cx(cx), options(initCx), buffer(&buffer), cursor(cursor),
|
||||
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
exclusiveContextGlobal(exclusiveContextGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
script(nullptr), sourceObject(nullptr),
|
||||
errors(cx), overRecursed(false), outOfMemory(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
|
||||
{
|
||||
|
|
@ -389,6 +401,29 @@ ModuleParseTask::parse()
|
|||
script = module->script();
|
||||
}
|
||||
|
||||
ScriptDecodeTask::ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::ScriptDecode, cx, exclusiveContextGlobal, initCx,
|
||||
buffer, cursor, callback, callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ScriptDecodeTask::parse()
|
||||
{
|
||||
RootedScript resultScript(cx);
|
||||
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
|
||||
*buffer, cursor);
|
||||
decoder.codeScript(&resultScript);
|
||||
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
|
||||
if (decoder.resultCode() == JS::TranscodeResult_Ok) {
|
||||
script = resultScript.get();
|
||||
} else {
|
||||
sourceObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
js::CancelOffThreadParses(JSRuntime* rt)
|
||||
{
|
||||
|
|
@ -557,10 +592,10 @@ QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename TaskFunctor>
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
ParseTaskKind kind, TaskFunctor& taskFunctor)
|
||||
{
|
||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||
// which could require barriers on the atoms compartment.
|
||||
|
|
@ -568,7 +603,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
|||
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
||||
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
||||
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, kind, nogc);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
|
|
@ -578,9 +613,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
|||
if (!helpercx)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ParseTask> task(
|
||||
cx->new_<ScriptParseTask>(helpercx.get(), global, cx, chars, length,
|
||||
callback, callbackData));
|
||||
ScopedJSDeletePtr<ParseTask> task(taskFunctor(helpercx.get(), global));
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
|
|
@ -594,41 +627,40 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptParseTask* {
|
||||
return cx->new_<ScriptParseTask>(helpercx, global, cx, chars, length,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::Script, functor);
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||
// which could require barriers on the atoms compartment.
|
||||
gc::AutoSuppressGC nogc(cx);
|
||||
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
||||
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
||||
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ModuleParseTask* {
|
||||
return cx->new_<ModuleParseTask>(helpercx, global, cx, chars, length,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
|
||||
}
|
||||
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Module, nogc);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ExclusiveContext> helpercx(
|
||||
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
|
||||
ExclusiveContext::Context_Exclusive, cx->options()));
|
||||
if (!helpercx)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ParseTask> task(
|
||||
cx->new_<ModuleParseTask>(helpercx.get(), global, cx, chars, length,
|
||||
callback, callbackData));
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
helpercx.forget();
|
||||
|
||||
if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
|
||||
return false;
|
||||
|
||||
task.forget();
|
||||
|
||||
return true;
|
||||
bool
|
||||
js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptDecodeTask* {
|
||||
return cx->new_<ScriptDecodeTask>(helpercx, global, cx, buffer, cursor,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1280,6 +1312,14 @@ GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
|
|||
return script;
|
||||
}
|
||||
|
||||
JSScript*
|
||||
GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, void* token)
|
||||
{
|
||||
JSScript* script = finishParseTask(cx, ParseTaskKind::ScriptDecode, token);
|
||||
MOZ_ASSERT_IF(script, script->isGlobalCode());
|
||||
return script;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ namespace wasm {
|
|||
enum class ParseTaskKind
|
||||
{
|
||||
Script,
|
||||
Module
|
||||
Module,
|
||||
ScriptDecode
|
||||
};
|
||||
|
||||
// Per-process state for off thread work items.
|
||||
|
|
@ -244,6 +245,7 @@ class GlobalHelperThreadState
|
|||
|
||||
public:
|
||||
JSScript* finishScriptParseTask(JSContext* cx, void* token);
|
||||
JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
|
||||
JSObject* finishModuleParseTask(JSContext* cx, void* token);
|
||||
bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
|
||||
SourceCompressionTask* compressionTaskForSource(ScriptSource* ss, const AutoLockHelperThreadState& lock);
|
||||
|
|
@ -482,6 +484,11 @@ StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
bool
|
||||
StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
/*
|
||||
* Called at the end of GC to enqueue any Parse tasks that were waiting on an
|
||||
* atoms-zone GC to finish.
|
||||
|
|
@ -534,8 +541,21 @@ struct ParseTask
|
|||
ParseTaskKind kind;
|
||||
ExclusiveContext* cx;
|
||||
OwningCompileOptions options;
|
||||
const char16_t* chars;
|
||||
size_t length;
|
||||
// Anonymous union, the only correct interpretation is provided by the
|
||||
// ParseTaskKind value, or from the virtual parse function.
|
||||
union {
|
||||
struct {
|
||||
const char16_t* chars;
|
||||
size_t length;
|
||||
};
|
||||
struct {
|
||||
// This should be a reference, but C++ prevents us from using union
|
||||
// with references as it assumes the reference constness might be
|
||||
// violated.
|
||||
JS::TranscodeBuffer* const buffer;
|
||||
size_t cursor;
|
||||
};
|
||||
};
|
||||
LifoAlloc alloc;
|
||||
|
||||
// Rooted pointer to the global object used by 'cx'.
|
||||
|
|
@ -562,6 +582,9 @@ struct ParseTask
|
|||
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
|
||||
|
||||
void activate(JSRuntime* rt);
|
||||
|
|
@ -593,6 +616,14 @@ struct ModuleParseTask : public ParseTask
|
|||
void parse() override;
|
||||
};
|
||||
|
||||
struct ScriptDecodeTask : public ParseTask
|
||||
{
|
||||
ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse() override;
|
||||
};
|
||||
|
||||
// Return whether, if a new parse task was started, it would need to wait for
|
||||
// an in-progress GC to complete before starting.
|
||||
extern bool
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "jsstr.h"
|
||||
|
||||
#include "builtin/Eval.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/Ion.h"
|
||||
|
|
@ -4187,6 +4188,35 @@ CASE(JSOP_NEWTARGET)
|
|||
MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
|
||||
END_CASE(JSOP_NEWTARGET)
|
||||
|
||||
CASE(JSOP_IMPORTMETA)
|
||||
{
|
||||
ReservedRooted<JSObject*> module(&rootObject0, GetModuleObjectForScript(script));
|
||||
MOZ_ASSERT(module);
|
||||
|
||||
JSObject* metaObject = GetOrCreateModuleMetaObject(cx, module);
|
||||
if (!metaObject)
|
||||
goto error;
|
||||
|
||||
PUSH_OBJECT(*metaObject);
|
||||
}
|
||||
END_CASE(JSOP_IMPORTMETA)
|
||||
|
||||
CASE(JSOP_DYNAMIC_IMPORT)
|
||||
{
|
||||
ReservedRooted<Value> referencingPrivate(&rootValue0);
|
||||
referencingPrivate = FindScriptOrModulePrivateForScript(script);
|
||||
|
||||
ReservedRooted<Value> specifier(&rootValue1);
|
||||
POP_COPY_TO(specifier);
|
||||
|
||||
JSObject* promise = StartDynamicModuleImport(cx, referencingPrivate, specifier);
|
||||
if (!promise)
|
||||
goto error;
|
||||
|
||||
PUSH_OBJECT(*promise);
|
||||
}
|
||||
END_CASE(JSOP_DYNAMIC_IMPORT)
|
||||
|
||||
CASE(JSOP_SUPERFUN)
|
||||
{
|
||||
ReservedRooted<JSObject*> superEnvFunc(&rootObject0, &GetSuperEnvFunction(cx, REGS));
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
* Super
|
||||
* Arguments
|
||||
* Var Scope
|
||||
* Modules
|
||||
* [Operators]
|
||||
* Comparison Operators
|
||||
* Arithmetic Operators
|
||||
|
|
@ -2335,14 +2336,32 @@
|
|||
* Operands: int32_t offset
|
||||
* Stack: cond => cond
|
||||
*/ \
|
||||
macro(JSOP_COALESCE, 232, "coalesce", NULL, 5, 1, 1, JOF_JUMP|JOF_DETECTING)
|
||||
macro(JSOP_COALESCE, 232, "coalesce", NULL, 5, 1, 1, JOF_JUMP|JOF_DETECTING) \
|
||||
/*
|
||||
* Push "import.meta"
|
||||
*
|
||||
* Category: Variables and Scopes
|
||||
* Type: Modules
|
||||
* Operands:
|
||||
* Stack: => import.meta
|
||||
*/ \
|
||||
macro(JSOP_IMPORTMETA, 233, "importmeta", NULL, 1, 0, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Dynamic import of the module specified by the string value on the top of
|
||||
* the stack.
|
||||
*
|
||||
* Category: Variables and Scopes
|
||||
* Type: Modules
|
||||
* Operands:
|
||||
* Stack: arg => rval
|
||||
*/ \
|
||||
macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* In certain circumstances it may be useful to "pad out" the opcode space to
|
||||
* a power of two. Use this macro to do so.
|
||||
*/
|
||||
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
|
||||
macro(233) \
|
||||
macro(234) \
|
||||
macro(235) \
|
||||
macro(236) \
|
||||
macro(237) \
|
||||
|
|
|
|||
|
|
@ -1552,7 +1552,7 @@ js::XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp
|
|||
if (mode == XDR_DECODE) {
|
||||
RegExpFlag flags = RegExpFlag(flagsword);
|
||||
RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags, nullptr,
|
||||
xdr->cx()->tempLifoAlloc());
|
||||
xdr->lifoAlloc());
|
||||
if (!reobj)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -243,7 +243,10 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
|||
stackFormat_(parentRuntime ?
|
||||
js::StackFormat::Default :
|
||||
js::StackFormat::SpiderMonkey),
|
||||
moduleResolveHook()
|
||||
moduleResolveHook(),
|
||||
moduleMetadataHook(),
|
||||
moduleDynamicImportHook(),
|
||||
scriptPrivateFinalizeHook()
|
||||
{
|
||||
setGCStoreBufferPtr(&gc.storeBuffer);
|
||||
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,17 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
|
||||
// The implementation-defined abstract operation HostResolveImportedModule.
|
||||
JS::ModuleResolveHook moduleResolveHook;
|
||||
|
||||
// A hook that implements the abstract operations
|
||||
// HostGetImportMetaProperties and HostFinalizeImportMeta.
|
||||
JS::ModuleMetadataHook moduleMetadataHook;
|
||||
|
||||
// A hook that implements the abstract operation
|
||||
// HostImportModuleDynamically.
|
||||
JS::ModuleDynamicImportHook moduleDynamicImportHook;
|
||||
|
||||
// A hook called on script finalization.
|
||||
JS::ScriptPrivateFinalizeHook scriptPrivateFinalizeHook;
|
||||
};
|
||||
|
||||
namespace js {
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0)
|
|||
static bool
|
||||
XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
RootedAtom atom(cx, bindingName->name());
|
||||
bool hasAtom = !!atom;
|
||||
|
|
@ -220,7 +220,7 @@ XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
|||
static bool
|
||||
XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
uint8_t u8;
|
||||
if (!xdr->codeUint8(&u8))
|
||||
|
|
@ -254,7 +254,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
|||
{
|
||||
MOZ_ASSERT(!data);
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
uint32_t length;
|
||||
if (mode == XDR_ENCODE)
|
||||
|
|
@ -542,7 +542,7 @@ template <XDRMode mode>
|
|||
LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
|
||||
|
|
@ -725,7 +725,7 @@ template <XDRMode mode>
|
|||
FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
|
||||
return false;
|
||||
|
|
@ -861,7 +861,7 @@ template <XDRMode mode>
|
|||
VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
|
||||
return false;
|
||||
|
|
@ -962,7 +962,7 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
|||
{
|
||||
MOZ_ASSERT((mode == XDR_DECODE) == !scope);
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
|
||||
return false;
|
||||
|
|
@ -1084,7 +1084,7 @@ template <XDRMode mode>
|
|||
EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||
MutableHandleScope scope)
|
||||
{
|
||||
JSContext* cx = xdr->cx();
|
||||
ExclusiveContext* cx = xdr->cx();
|
||||
Rooted<Data*> data(cx);
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1999,14 +1999,9 @@ intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp)
|
|||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
RootedString specifier(cx, args[1].toString());
|
||||
|
||||
JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
|
||||
if (!moduleResolveHook) {
|
||||
JS_ReportErrorASCII(cx, "Module resolve hook not set");
|
||||
return false;
|
||||
}
|
||||
RootedValue referencingPrivate(cx, JS::GetModulePrivate(module));
|
||||
RootedObject result(cx, CallModuleResolveHook(cx, referencingPrivate, specifier));
|
||||
|
||||
RootedObject result(cx);
|
||||
result = moduleResolveHook(cx, module, specifier);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -151,8 +151,8 @@ AssertScopeMatchesEnvironment(Scope* scope, JSObject* originalEnv)
|
|||
break;
|
||||
|
||||
case ScopeKind::Module:
|
||||
MOZ_ASSERT(env->as<ModuleEnvironmentObject>().module().script() ==
|
||||
si.scope()->as<ModuleScope>().script());
|
||||
MOZ_ASSERT(&env->as<ModuleEnvironmentObject>().module() ==
|
||||
si.scope()->as<ModuleScope>().module());
|
||||
env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsscript.h"
|
||||
|
||||
#include "vm/Debugger.h"
|
||||
|
|
@ -18,11 +19,17 @@
|
|||
using namespace js;
|
||||
using mozilla::PodEqual;
|
||||
|
||||
template<XDRMode mode>
|
||||
LifoAlloc&
|
||||
XDRState<mode>::lifoAlloc() const {
|
||||
return buf.cx()->asJSContext()->tempLifoAlloc();
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
void
|
||||
XDRState<mode>::postProcessContextErrors(JSContext* cx)
|
||||
XDRState<mode>::postProcessContextErrors(ExclusiveContext* cx)
|
||||
{
|
||||
if (cx->isExceptionPending()) {
|
||||
if (cx->isJSContext() && cx->asJSContext()->isExceptionPending()) {
|
||||
MOZ_ASSERT(resultCode_ == JS::TranscodeResult_Ok);
|
||||
resultCode_ = JS::TranscodeResult_Throw;
|
||||
}
|
||||
|
|
@ -40,7 +47,7 @@ XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
|
|||
return true;
|
||||
uint8_t* ptr = buf.write(nchars);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
|
||||
mozilla::PodCopy(ptr, chars, nchars);
|
||||
return true;
|
||||
|
|
@ -56,7 +63,7 @@ XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
|
|||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(nbytes);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(nbytes);
|
||||
|
|
@ -70,11 +77,8 @@ static bool
|
|||
VersionCheck(XDRState<mode>* xdr)
|
||||
{
|
||||
JS::BuildIdCharVector buildId;
|
||||
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId)) {
|
||||
JS_ReportErrorNumberASCII(xdr->cx(), GetErrorMessage, nullptr,
|
||||
JSMSG_BUILD_ID_NOT_AVAILABLE);
|
||||
return false;
|
||||
}
|
||||
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId))
|
||||
return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
|
||||
MOZ_ASSERT(!buildId.empty());
|
||||
|
||||
uint32_t buildIdLength;
|
||||
|
|
@ -97,7 +101,7 @@ VersionCheck(XDRState<mode>* xdr)
|
|||
// buildId.
|
||||
if (!decodedBuildId.resize(buildIdLength)) {
|
||||
ReportOutOfMemory(xdr->cx());
|
||||
return false;
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
|
||||
|
|
@ -113,20 +117,26 @@ VersionCheck(XDRState<mode>* xdr)
|
|||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRState<mode>::codeFunction(MutableHandleFunction funp)
|
||||
XDRState<mode>::codeFunction(MutableHandleFunction funp, HandleScriptSource sourceObject)
|
||||
{
|
||||
if (mode == XDR_DECODE)
|
||||
RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
|
||||
if (mode == XDR_DECODE) {
|
||||
MOZ_ASSERT(!sourceObject);
|
||||
funp.set(nullptr);
|
||||
else
|
||||
} else if (getTreeKey(funp) != AutoXDRTree::noKey) {
|
||||
MOZ_ASSERT(sourceObject);
|
||||
scope = funp->nonLazyScript()->enclosingScope();
|
||||
} else {
|
||||
MOZ_ASSERT(!sourceObject);
|
||||
MOZ_ASSERT(funp->nonLazyScript()->enclosingScope()->is<GlobalScope>());
|
||||
}
|
||||
|
||||
if (!VersionCheck(this)) {
|
||||
postProcessContextErrors(cx());
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
|
||||
if (!XDRInterpretedFunction(this, scope, nullptr, funp)) {
|
||||
if (!XDRInterpretedFunction(this, scope, sourceObject, funp)) {
|
||||
postProcessContextErrors(cx());
|
||||
funp.set(nullptr);
|
||||
return false;
|
||||
|
|
@ -144,6 +154,8 @@ XDRState<mode>::codeScript(MutableHandleScript scriptp)
|
|||
else
|
||||
MOZ_ASSERT(!scriptp->enclosingScope());
|
||||
|
||||
AutoXDRTree scriptTree(this, getTopLevelTreeKey());
|
||||
|
||||
if (!VersionCheck(this)) {
|
||||
postProcessContextErrors(cx());
|
||||
return false;
|
||||
|
|
@ -167,3 +179,189 @@ XDRState<mode>::codeConstValue(MutableHandleValue vp)
|
|||
|
||||
template class js::XDRState<XDR_ENCODE>;
|
||||
template class js::XDRState<XDR_DECODE>;
|
||||
|
||||
AutoXDRTree::AutoXDRTree(XDRCoderBase* xdr, AutoXDRTree::Key key)
|
||||
: key_(key),
|
||||
parent_(this),
|
||||
xdr_(xdr)
|
||||
{
|
||||
if (key_ != AutoXDRTree::noKey)
|
||||
xdr->createOrReplaceSubTree(this);
|
||||
}
|
||||
|
||||
AutoXDRTree::~AutoXDRTree()
|
||||
{
|
||||
if (key_ != AutoXDRTree::noKey)
|
||||
xdr_->endSubTree();
|
||||
}
|
||||
|
||||
constexpr AutoXDRTree::Key AutoXDRTree::noKey;
|
||||
constexpr AutoXDRTree::Key AutoXDRTree::noSubTree;
|
||||
constexpr AutoXDRTree::Key AutoXDRTree::topLevel;
|
||||
|
||||
AutoXDRTree::Key
|
||||
XDRIncrementalEncoder::getTopLevelTreeKey() const
|
||||
{
|
||||
return AutoXDRTree::topLevel;
|
||||
}
|
||||
|
||||
AutoXDRTree::Key
|
||||
XDRIncrementalEncoder::getTreeKey(JSFunction* fun) const
|
||||
{
|
||||
if (fun->isInterpretedLazy()) {
|
||||
static_assert(sizeof(fun->lazyScript()->begin()) == 4 ||
|
||||
sizeof(fun->lazyScript()->end()) == 4,
|
||||
"AutoXDRTree key requires LazyScripts positions to be uint32");
|
||||
return uint64_t(fun->lazyScript()->begin()) << 32 | fun->lazyScript()->end();
|
||||
}
|
||||
|
||||
if (fun->isInterpreted()) {
|
||||
static_assert(sizeof(fun->nonLazyScript()->sourceStart()) == 4 ||
|
||||
sizeof(fun->nonLazyScript()->sourceEnd()) == 4,
|
||||
"AutoXDRTree key requires JSScripts positions to be uint32");
|
||||
return uint64_t(fun->nonLazyScript()->sourceStart()) << 32 | fun->nonLazyScript()->sourceEnd();
|
||||
}
|
||||
|
||||
return AutoXDRTree::noKey;
|
||||
}
|
||||
|
||||
bool
|
||||
XDRIncrementalEncoder::init()
|
||||
{
|
||||
if (!tree_.init())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
XDRIncrementalEncoder::createOrReplaceSubTree(AutoXDRTree* child)
|
||||
{
|
||||
AutoXDRTree* parent = scope_;
|
||||
child->parent_ = parent;
|
||||
scope_ = child;
|
||||
if (oom_)
|
||||
return;
|
||||
|
||||
size_t cursor = buf.cursor();
|
||||
|
||||
// End the parent slice here, set the key to the child.
|
||||
if (parent) {
|
||||
Slice& last = node_->back();
|
||||
last.sliceLength = cursor - last.sliceBegin;
|
||||
last.child = child->key_;
|
||||
MOZ_ASSERT_IF(uint32_t(parent->key_) != 0,
|
||||
uint32_t(parent->key_ >> 32) <= uint32_t(child->key_ >> 32) &&
|
||||
uint32_t(child->key_) <= uint32_t(parent->key_));
|
||||
}
|
||||
|
||||
// Create or replace the part with what is going to be encoded next.
|
||||
SlicesTree::AddPtr p = tree_.lookupForAdd(child->key_);
|
||||
SlicesNode tmp;
|
||||
if (!p) {
|
||||
// Create a new sub-tree node.
|
||||
if (!tree_.add(p, child->key_, mozilla::Move(tmp))) {
|
||||
oom_ = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Replace an exisiting sub-tree.
|
||||
p->value() = mozilla::Move(tmp);
|
||||
}
|
||||
node_ = &p->value();
|
||||
|
||||
// Add content to the root of the new sub-tree,
|
||||
// i-e an empty slice with no children.
|
||||
if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree }))
|
||||
MOZ_CRASH("SlicesNode have a reserved space of 1.");
|
||||
}
|
||||
|
||||
void
|
||||
XDRIncrementalEncoder::endSubTree()
|
||||
{
|
||||
AutoXDRTree* child = scope_;
|
||||
AutoXDRTree* parent = child->parent_;
|
||||
scope_ = parent;
|
||||
if (oom_)
|
||||
return;
|
||||
|
||||
size_t cursor = buf.cursor();
|
||||
|
||||
// End the child sub-tree.
|
||||
Slice& last = node_->back();
|
||||
last.sliceLength = cursor - last.sliceBegin;
|
||||
MOZ_ASSERT(last.child == AutoXDRTree::noSubTree);
|
||||
|
||||
// Stop at the top-level.
|
||||
if (!parent) {
|
||||
node_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore the parent node.
|
||||
SlicesTree::Ptr p = tree_.lookup(parent->key_);
|
||||
node_ = &p->value();
|
||||
|
||||
// Append the new slice in the parent node.
|
||||
if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree })) {
|
||||
oom_ = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
XDRIncrementalEncoder::linearize(JS::TranscodeBuffer& buffer)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
242
js/src/vm/Xdr.h
242
js/src/vm/Xdr.h
|
|
@ -16,10 +16,10 @@ namespace js {
|
|||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
XDRBuffer(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: context_(cx), buffer_(buffer), cursor_(cursor) { }
|
||||
|
||||
JSContext* cx() const {
|
||||
ExclusiveContext* cx() const {
|
||||
return context_;
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ class XDRBuffer {
|
|||
uint8_t* write(size_t n) {
|
||||
MOZ_ASSERT(n != 0);
|
||||
if (!buffer_.growByUninitialized(n)) {
|
||||
JS_ReportOutOfMemory(cx());
|
||||
ReportOutOfMemory(cx());
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t* ptr = &buffer_[cursor_];
|
||||
|
|
@ -50,30 +50,108 @@ class XDRBuffer {
|
|||
return ptr;
|
||||
}
|
||||
|
||||
size_t cursor() const {
|
||||
return cursor_;
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* const context_;
|
||||
ExclusiveContext* const context_;
|
||||
JS::TranscodeBuffer& buffer_;
|
||||
size_t cursor_;
|
||||
};
|
||||
|
||||
class XDRCoderBase;
|
||||
class XDRIncrementalEncoder;
|
||||
|
||||
// An AutoXDRTree is used to identify section encoded by an XDRIncrementalEncoder.
|
||||
//
|
||||
// Its primary goal is to identify functions, such that we can first encode them
|
||||
// as LazyScript, and later replaced by them by their corresponding bytecode
|
||||
// once delazified.
|
||||
//
|
||||
// As a convenience, this is also used to identify the top-level of the content
|
||||
// encoded by an XDRIncrementalEncoder.
|
||||
//
|
||||
// Sections can be encoded any number of times in an XDRIncrementalEncoder, and
|
||||
// the latest encoded version would replace all the previous one.
|
||||
class MOZ_RAII AutoXDRTree
|
||||
{
|
||||
public:
|
||||
// For a JSFunction, a tree key is defined as being:
|
||||
// script()->begin << 32 | script()->end
|
||||
//
|
||||
// Based on the invariant that |begin <= end|, we can make special
|
||||
// keys, such as the top-level script.
|
||||
using Key = uint64_t;
|
||||
|
||||
AutoXDRTree(XDRCoderBase* xdr, Key key);
|
||||
~AutoXDRTree();
|
||||
|
||||
// Indicate the lack of a key for the current tree.
|
||||
static constexpr Key noKey = 0;
|
||||
|
||||
// Used to end the slices when there is no children.
|
||||
static constexpr Key noSubTree = Key(1) << 32;
|
||||
|
||||
// Used as the root key of the tree in the hash map.
|
||||
static constexpr Key topLevel = Key(2) << 32;
|
||||
|
||||
private:
|
||||
friend class XDRIncrementalEncoder;
|
||||
|
||||
Key key_;
|
||||
AutoXDRTree* parent_;
|
||||
XDRCoderBase* xdr_;
|
||||
};
|
||||
|
||||
class XDRCoderBase
|
||||
{
|
||||
protected:
|
||||
XDRCoderBase() {}
|
||||
|
||||
public:
|
||||
virtual AutoXDRTree::Key getTopLevelTreeKey() const { return AutoXDRTree::noKey; }
|
||||
virtual AutoXDRTree::Key getTreeKey(JSFunction* fun) const { return AutoXDRTree::noKey; }
|
||||
virtual void createOrReplaceSubTree(AutoXDRTree* child) {};
|
||||
virtual void endSubTree() {};
|
||||
};
|
||||
|
||||
/*
|
||||
* XDR serialization state. All data is encoded in little endian.
|
||||
*/
|
||||
template <XDRMode mode>
|
||||
class XDRState {
|
||||
class XDRState : public XDRCoderBase
|
||||
{
|
||||
public:
|
||||
XDRBuffer buf;
|
||||
private:
|
||||
JS::TranscodeResult resultCode_;
|
||||
|
||||
XDRState(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: buf(cx, buffer, cursor), resultCode_(JS::TranscodeResult_Ok) { }
|
||||
public:
|
||||
XDRState(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: buf(cx, buffer, cursor),
|
||||
resultCode_(JS::TranscodeResult_Ok)
|
||||
{
|
||||
}
|
||||
|
||||
JSContext* cx() const {
|
||||
virtual ~XDRState() {};
|
||||
|
||||
ExclusiveContext* cx() const {
|
||||
return buf.cx();
|
||||
}
|
||||
virtual LifoAlloc& lifoAlloc() const;
|
||||
|
||||
virtual bool hasOptions() const { return false; }
|
||||
virtual const ReadOnlyCompileOptions& options() {
|
||||
MOZ_CRASH("does not have options");
|
||||
}
|
||||
virtual bool hasScriptSourceObjectOut() const { return false; }
|
||||
virtual ScriptSourceObject** scriptSourceObjectOut() {
|
||||
MOZ_CRASH("does not have scriptSourceObjectOut.");
|
||||
}
|
||||
|
||||
// Record logical failures of XDR.
|
||||
void postProcessContextErrors(JSContext* cx);
|
||||
void postProcessContextErrors(ExclusiveContext* cx);
|
||||
JS::TranscodeResult resultCode() const {
|
||||
return resultCode_;
|
||||
}
|
||||
|
|
@ -87,7 +165,7 @@ class XDRState {
|
|||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
*ptr = *n;
|
||||
} else {
|
||||
*n = *buf.read(sizeof(*n));
|
||||
|
|
@ -99,7 +177,7 @@ class XDRState {
|
|||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::LittleEndian::writeUint16(ptr, *n);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||
|
|
@ -112,7 +190,7 @@ class XDRState {
|
|||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::LittleEndian::writeUint32(ptr, *n);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||
|
|
@ -125,7 +203,7 @@ class XDRState {
|
|||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(sizeof(*n));
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
mozilla::LittleEndian::writeUint64(ptr, *n);
|
||||
} else {
|
||||
const uint8_t* ptr = buf.read(sizeof(*n));
|
||||
|
|
@ -188,7 +266,7 @@ class XDRState {
|
|||
if (mode == XDR_ENCODE) {
|
||||
uint8_t* ptr = buf.write(len);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
memcpy(ptr, bytes, len);
|
||||
} else {
|
||||
memcpy(bytes, buf.read(len), len);
|
||||
|
|
@ -207,7 +285,7 @@ class XDRState {
|
|||
size_t n = strlen(*sp) + 1;
|
||||
uint8_t* ptr = buf.write(n);
|
||||
if (!ptr)
|
||||
return false;
|
||||
return fail(JS::TranscodeResult_Throw);
|
||||
memcpy(ptr, *sp, n);
|
||||
} else {
|
||||
*sp = buf.readCString();
|
||||
|
|
@ -218,7 +296,7 @@ class XDRState {
|
|||
bool codeChars(const JS::Latin1Char* chars, size_t nchars);
|
||||
bool codeChars(char16_t* chars, size_t nchars);
|
||||
|
||||
bool codeFunction(JS::MutableHandleFunction objp);
|
||||
bool codeFunction(JS::MutableHandleFunction objp, HandleScriptSource sourceObject = nullptr);
|
||||
bool codeScript(MutableHandleScript scriptp);
|
||||
bool codeConstValue(MutableHandleValue vp);
|
||||
};
|
||||
|
|
@ -226,6 +304,138 @@ class XDRState {
|
|||
using XDREncoder = XDRState<XDR_ENCODE>;
|
||||
using XDRDecoder = XDRState<XDR_DECODE>;
|
||||
|
||||
class XDROffThreadDecoder : public XDRDecoder
|
||||
{
|
||||
const ReadOnlyCompileOptions* options_;
|
||||
ScriptSourceObject** sourceObjectOut_;
|
||||
LifoAlloc& alloc_;
|
||||
|
||||
public:
|
||||
// Note, when providing an ExclusiveContext, where isJSContext is false,
|
||||
// then the initialization of the ScriptSourceObject would remain
|
||||
// incomplete. Thus, the sourceObjectOut must be used to finish the
|
||||
// initialization with ScriptSourceObject::initFromOptions after the
|
||||
// decoding.
|
||||
//
|
||||
// When providing a sourceObjectOut pointer, you have to ensure that it is
|
||||
// marked by the GC to avoid dangling pointers.
|
||||
XDROffThreadDecoder(ExclusiveContext* cx, LifoAlloc& alloc,
|
||||
const ReadOnlyCompileOptions* options,
|
||||
ScriptSourceObject** sourceObjectOut,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor = 0)
|
||||
: XDRDecoder(cx, buffer, cursor),
|
||||
options_(options),
|
||||
sourceObjectOut_(sourceObjectOut),
|
||||
alloc_(alloc)
|
||||
{
|
||||
MOZ_ASSERT(options);
|
||||
MOZ_ASSERT(sourceObjectOut);
|
||||
MOZ_ASSERT(*sourceObjectOut == nullptr);
|
||||
}
|
||||
|
||||
LifoAlloc& lifoAlloc() const override {
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
bool hasOptions() const override { return true; }
|
||||
const ReadOnlyCompileOptions& options() override {
|
||||
return *options_;
|
||||
}
|
||||
bool hasScriptSourceObjectOut() const override { return true; }
|
||||
ScriptSourceObject** scriptSourceObjectOut() override {
|
||||
return sourceObjectOut_;
|
||||
}
|
||||
};
|
||||
|
||||
class XDRIncrementalEncoder : public XDREncoder
|
||||
{
|
||||
// The incremental encoder encodes the content of scripts and functions in
|
||||
// the XDRBuffer. It can be used to encode multiple times the same AutoXDRTree,
|
||||
// and uses its key to identify which part to replace.
|
||||
//
|
||||
// Internally, this encoder keeps a tree representation of the scopes. Each
|
||||
// node is composed of a vector of slices which are interleaved by child
|
||||
// nodes.
|
||||
//
|
||||
// A slice corresponds to an index and a length within the content of the
|
||||
// slices_ buffer. The index is updated when a slice is created, and the
|
||||
// length is updated when the slice is ended, either by creating a new scope
|
||||
// child, or by closing the scope and going back to the parent.
|
||||
//
|
||||
// +---+---+---+
|
||||
// begin | | | |
|
||||
// length | | | |
|
||||
// child | . | . | . |
|
||||
// +-|-+-|-+---+
|
||||
// | |
|
||||
// +---------+ +---------+
|
||||
// | |
|
||||
// v v
|
||||
// +---+---+ +---+
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// | . | . | | . |
|
||||
// +-|-+---+ +---+
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// v
|
||||
// +---+
|
||||
// | |
|
||||
// | |
|
||||
// | . |
|
||||
// +---+
|
||||
//
|
||||
//
|
||||
// The tree key is used to identify the child nodes, and to make them
|
||||
// easily replaceable.
|
||||
//
|
||||
// The tree is rooted at the |topLevel| key.
|
||||
//
|
||||
|
||||
struct Slice {
|
||||
size_t sliceBegin;
|
||||
size_t sliceLength;
|
||||
AutoXDRTree::Key child;
|
||||
};
|
||||
|
||||
using SlicesNode = Vector<Slice, 1, SystemAllocPolicy>;
|
||||
using SlicesTree = HashMap<AutoXDRTree::Key, SlicesNode, DefaultHasher<AutoXDRTree::Key>,
|
||||
SystemAllocPolicy>;
|
||||
|
||||
// Last opened XDR-tree on the stack.
|
||||
AutoXDRTree* scope_;
|
||||
// Node corresponding to the opened scope.
|
||||
SlicesNode* node_;
|
||||
// Tree of slices.
|
||||
SlicesTree tree_;
|
||||
JS::TranscodeBuffer slices_;
|
||||
bool oom_;
|
||||
|
||||
public:
|
||||
XDRIncrementalEncoder(ExclusiveContext* cx)
|
||||
: XDREncoder(cx, slices_, 0),
|
||||
scope_(nullptr),
|
||||
node_(nullptr),
|
||||
oom_(false)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Append the content collected during the incremental encoding into the
|
||||
// buffer given as argument.
|
||||
MOZ_MUST_USE bool linearize(JS::TranscodeBuffer& buffer);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* vm_Xdr_h */
|
||||
|
|
|
|||
|
|
@ -1325,6 +1325,8 @@ pref("javascript.options.main_thread_stack_quota_cap", 6291456);
|
|||
pref("javascript.options.main_thread_stack_quota_cap", 2097152);
|
||||
#endif
|
||||
|
||||
// Dynamic module import.
|
||||
pref("javascript.options.dynamicImport", false);
|
||||
|
||||
// advanced prefs
|
||||
pref("advanced.mailftp", false);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "MDNSResponderReply.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "private/pprio.h"
|
||||
#include "nsSocketTransportService2.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "nsICancelable.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "private/pprio.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
|
|
|||
|
|
@ -196,6 +196,8 @@ if CONFIG['MOZ_SYSTEM_JPEG']:
|
|||
|
||||
if CONFIG['MOZ_SYSTEM_HUNSPELL']:
|
||||
OS_LIBS += CONFIG['MOZ_HUNSPELL_LIBS']
|
||||
else:
|
||||
USE_LIBS += [ 'hunspell' ]
|
||||
|
||||
if not CONFIG['MOZ_TREE_PIXMAN']:
|
||||
OS_LIBS += CONFIG['MOZ_PIXMAN_LIBS']
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "jsprf.h"
|
||||
#include "js/Debug.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollectionNoteRootCallback.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
|
@ -642,25 +643,33 @@ CycleCollectedJSContext::NoteGCThingXPCOMChildren(const js::Class* aClasp,
|
|||
}
|
||||
// XXX This test does seem fragile, we should probably whitelist classes
|
||||
// that do hold a strong reference, but that might not be possible.
|
||||
else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
|
||||
aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
|
||||
if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
|
||||
aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
|
||||
aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
|
||||
} else {
|
||||
const DOMJSClass* domClass = GetDOMClass(aObj);
|
||||
if (domClass) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
|
||||
// It's possible that our object is an unforgeable holder object, in
|
||||
// which case it doesn't actually have a C++ DOM object associated with
|
||||
// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
|
||||
// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
|
||||
if (domClass->mDOMObjectIsISupports) {
|
||||
aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
|
||||
} else if (domClass->mParticipant) {
|
||||
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
|
||||
domClass->mParticipant);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const DOMJSClass* domClass = GetDOMClass(aObj);
|
||||
if (domClass) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
|
||||
// It's possible that our object is an unforgeable holder object, in
|
||||
// which case it doesn't actually have a C++ DOM object associated with
|
||||
// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
|
||||
// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
|
||||
if (domClass->mDOMObjectIsISupports) {
|
||||
aCb.NoteXPCOMChild(
|
||||
UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
|
||||
} else if (domClass->mParticipant) {
|
||||
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
|
||||
domClass->mParticipant);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Value value = js::MaybeGetScriptPrivate(aObj);
|
||||
if (!value.isUndefined()) {
|
||||
aCb.NoteXPCOMChild(static_cast<nsISupports*>(value.toPrivate()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -568,6 +568,13 @@
|
|||
ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC, FAILURE(1027)),
|
||||
ERROR(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC, FAILURE(1028)),
|
||||
|
||||
|
||||
/* When manipulating the bytecode cache with the JS API, some transcoding
|
||||
* errors, such as a different bytecode format can cause failures of the
|
||||
* decoding process.
|
||||
*/
|
||||
ERROR(NS_ERROR_DOM_JS_DECODING_ERROR, FAILURE(1030)),
|
||||
|
||||
/* May be used to indicate when e.g. setting a property value didn't
|
||||
* actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
|
||||
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ public:
|
|||
// Base types
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct PtrInfo;
|
||||
class PtrInfo;
|
||||
|
||||
class EdgePool
|
||||
{
|
||||
|
|
@ -533,13 +533,15 @@ enum NodeColor { black, white, grey };
|
|||
// hundreds of thousands of them to be allocated and touched
|
||||
// repeatedly during each cycle collection.
|
||||
|
||||
struct PtrInfo
|
||||
class PtrInfo final
|
||||
{
|
||||
public:
|
||||
void* mPointer;
|
||||
nsCycleCollectionParticipant* mParticipant;
|
||||
uint32_t mColor : 2;
|
||||
uint32_t mInternalRefs : 30;
|
||||
uint32_t mRefCount;
|
||||
|
||||
private:
|
||||
EdgePool::Iterator mFirstChild;
|
||||
|
||||
|
|
@ -609,8 +611,29 @@ public:
|
|||
CC_GRAPH_ASSERT(aLastChild.Initialized());
|
||||
(this + 1)->mFirstChild = aLastChild;
|
||||
}
|
||||
|
||||
void AnnotatedReleaseAssert(bool aCondition, const char* aMessage);
|
||||
};
|
||||
|
||||
void
|
||||
PtrInfo::AnnotatedReleaseAssert(bool aCondition, const char* aMessage)
|
||||
{
|
||||
if (aCondition) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
const char* piName = "Unknown";
|
||||
if (mParticipant) {
|
||||
piName = mParticipant->ClassName();
|
||||
}
|
||||
nsPrintfCString msg("%s, for class %s", aMessage, piName);
|
||||
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CycleCollector"), msg);
|
||||
#endif
|
||||
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
/**
|
||||
* A structure designed to be used like a linked list of PtrInfo, except
|
||||
* it allocates many PtrInfos at a time.
|
||||
|
|
@ -2297,8 +2320,10 @@ CCGraphBuilder::NoteNativeRoot(void* aRoot,
|
|||
NS_IMETHODIMP_(void)
|
||||
CCGraphBuilder::DescribeRefCountedNode(nsrefcnt aRefCount, const char* aObjName)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aRefCount != 0, "CCed refcounted object has zero refcount");
|
||||
MOZ_RELEASE_ASSERT(aRefCount != UINT32_MAX, "CCed refcounted object has overflowing refcount");
|
||||
mCurrPi->AnnotatedReleaseAssert(aRefCount != 0,
|
||||
"CCed refcounted object has zero refcount");
|
||||
mCurrPi->AnnotatedReleaseAssert(aRefCount != UINT32_MAX,
|
||||
"CCed refcounted object has overflowing refcount");
|
||||
|
||||
mResults.mVisitedRefCounted++;
|
||||
|
||||
|
|
@ -3112,9 +3137,8 @@ nsCycleCollector::ScanWhiteNodes(bool aFullySynchGraphBuild)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (pi->mInternalRefs > pi->mRefCount) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
pi->AnnotatedReleaseAssert(pi->mInternalRefs <= pi->mRefCount,
|
||||
"More references to an object than its refcount");
|
||||
|
||||
// This node will get marked black in the next pass.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,6 +322,20 @@ protected:
|
|||
*/
|
||||
virtual void NotifyExpiredLocked(T*, const AutoLock&) = 0;
|
||||
|
||||
/**
|
||||
* This may be overridden to perform any post-aging work that needs to be
|
||||
* done while still holding the lock. It will be called once after each timer
|
||||
* event, and each low memory event has been handled.
|
||||
*/
|
||||
virtual void NotifyHandlerEndLocked(const AutoLock&) { };
|
||||
|
||||
/**
|
||||
* This may be overridden to perform any post-aging work that needs to be
|
||||
* done outside the lock. It will be called once after each
|
||||
* NotifyEndTransactionLocked call.
|
||||
*/
|
||||
virtual void NotifyHandlerEnd() { };
|
||||
|
||||
virtual Mutex& GetMutex() = 0;
|
||||
|
||||
private:
|
||||
|
|
@ -364,18 +378,26 @@ private:
|
|||
};
|
||||
|
||||
void HandleLowMemory() {
|
||||
AutoLock lock(GetMutex());
|
||||
AgeAllGenerationsLocked(lock);
|
||||
{
|
||||
AutoLock lock(GetMutex());
|
||||
AgeAllGenerationsLocked(lock);
|
||||
NotifyHandlerEndLocked(lock);
|
||||
}
|
||||
NotifyHandlerEnd();
|
||||
}
|
||||
|
||||
void HandleTimeout() {
|
||||
AutoLock lock(GetMutex());
|
||||
AgeOneGenerationLocked(lock);
|
||||
// Cancel the timer if we have no objects to track
|
||||
if (IsEmptyLocked(lock)) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
{
|
||||
AutoLock lock(GetMutex());
|
||||
AgeOneGenerationLocked(lock);
|
||||
// Cancel the timer if we have no objects to track
|
||||
if (IsEmptyLocked(lock)) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
NotifyHandlerEndLocked(lock);
|
||||
}
|
||||
NotifyHandlerEnd();
|
||||
}
|
||||
|
||||
static void TimerCallback(nsITimer* aTimer, void* aThis)
|
||||
|
|
@ -449,6 +471,14 @@ class nsExpirationTracker : protected ::detail::SingleThreadedExpirationTracker<
|
|||
NotifyExpired(aObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since there are no users of these callbacks in the single threaded case,
|
||||
* we mark them as final with the hope that the compiler can optimize the
|
||||
* method calls out entirely.
|
||||
*/
|
||||
void NotifyHandlerEndLocked(const AutoLock&) final override { }
|
||||
void NotifyHandlerEnd() final override { }
|
||||
|
||||
protected:
|
||||
virtual void NotifyExpired(T* aObj) = 0;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue