Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-04-30 21:26:08 +08:00
commit beb68187e5
98 changed files with 13981 additions and 982 deletions

View file

@ -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;
}

View file

@ -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,

View file

@ -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;
}

View file

@ -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

View file

@ -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>

View file

@ -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);

View file

@ -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);

View file

@ -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();
}

View file

@ -79,9 +79,6 @@ public:
StructuredCloneData&
operator=(const StructuredCloneData& aOther) = delete;
StructuredCloneData&
operator=(StructuredCloneData&& aOther) = default;
const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
{
return mBlobImplArray;

View file

@ -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;

View file

@ -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));

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -12,6 +12,7 @@ XPIDL_MODULE = 'dom'
EXPORTS += ['nsIScriptElement.h']
EXPORTS.mozilla.dom += [
'ModuleScript.h',
'ScriptElement.h',
'ScriptLoader.h',
'ScriptSettings.h',

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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];

View file

@ -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>

View file

@ -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,

View file

@ -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',

File diff suppressed because it is too large Load diff

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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<>

View file

@ -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));

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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()) {

View file

@ -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);

View file

@ -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

View file

@ -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>();

View file

@ -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());

View file

@ -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
{

View file

@ -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);
}

View file

@ -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);
/*

View file

@ -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) \

View file

@ -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; }

View file

@ -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'") \

View file

@ -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;
}

View file

@ -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
{

View file

@ -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");

View file

@ -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);

View file

@ -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)
{

View file

@ -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. */

View file

@ -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)
{

View file

@ -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

View file

@ -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

View file

@ -284,6 +284,8 @@ namespace jit {
_(GlobalNameConflictsCheck) \
_(Debugger) \
_(NewTarget) \
_(ModuleMetadata) \
_(DynamicImport) \
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \

View file

@ -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:

View file

@ -401,6 +401,8 @@
_(GlobalNameConflictsCheck) \
_(Debugger) \
_(NewTarget) \
_(ModuleMetadata) \
_(DynamicImport) \
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \

View file

@ -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.")

View file

@ -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)
{

View file

@ -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 {

View file

@ -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")

View file

@ -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;
}

View file

@ -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)
{

View file

@ -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);

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

View file

@ -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);
}
});

View file

@ -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);

View file

@ -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") \

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)
{

View file

@ -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

View file

@ -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));

View file

@ -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) \

View file

@ -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;

View file

@ -243,7 +243,10 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
stackFormat_(parentRuntime ?
js::StackFormat::Default :
js::StackFormat::SpiderMonkey),
moduleResolveHook()
moduleResolveHook(),
moduleMetadataHook(),
moduleDynamicImportHook(),
scriptPrivateFinalizeHook()
{
setGCStoreBufferPtr(&gc.storeBuffer);

View file

@ -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 {

View file

@ -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);
{

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 */

View file

@ -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);

View file

@ -6,6 +6,7 @@
#include "MDNSResponderReply.h"
#include "mozilla/EndianUtils.h"
#include "private/pprio.h"
#include "nsSocketTransportService2.h"
namespace mozilla {
namespace net {

View file

@ -8,6 +8,7 @@
#include "nsICancelable.h"
#include "nsXULAppAPI.h"
#include "private/pprio.h"
#include "MainThreadUtils.h"
namespace mozilla {
namespace net {

View file

@ -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']

View file

@ -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()));
}
}

View file

@ -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.

View file

@ -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.
}

View file

@ -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;