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

This commit is contained in:
roytam1 2023-01-26 11:42:02 +08:00
commit fd45d63241
49 changed files with 816 additions and 347 deletions

View file

@ -6,6 +6,7 @@
#include "TimelineConsumers.h"
#include "mozilla/ClearOnShutdown.h"
#include "jsapi.h"
#include "nsAppRunner.h" // for XRE_IsContentProcess, XRE_IsParentProcess
#include "nsDocShell.h"
@ -125,6 +126,9 @@ TimelineConsumers::AddConsumer(nsDocShell* aDocShell)
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
MOZ_ASSERT(!observed);
if (mActiveConsumers == 0) {
JS::SetProfileTimelineRecordingEnabled(true);
}
mActiveConsumers++;
ObservedDocShell* obsDocShell = new ObservedDocShell(aDocShell);
@ -144,6 +148,9 @@ TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell)
MOZ_ASSERT(observed);
mActiveConsumers--;
if (mActiveConsumers == 0) {
JS::SetProfileTimelineRecordingEnabled(false);
}
// Clear all markers from the `mTimelineMarkers` store.
observed.get()->ClearMarkers();

View file

@ -224,16 +224,23 @@ ProcessNameForCollectorLog()
namespace xpc {
// This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC
// Exceptions.
// This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
// Exceptions, and arbitrary values that were associated with a stack by the
// JS engine when they were thrown, as specified by exceptionStack.
//
// Note that the returned object is _not_ wrapped into the compartment of
// exceptionValue.
JSObject*
FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
JS::HandleValue exceptionValue)
JS::HandleValue exceptionValue,
JS::HandleObject exceptionStack)
{
if (!exceptionValue.isObject()) {
// Use the stack provided by the JS engine, if available. This will not be
// a wrapper.
if (exceptionStack) {
return exceptionStack;
}
return nullptr;
}
@ -257,6 +264,10 @@ FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
// Not a DOM Exception, try XPC Exception.
UNWRAP_OBJECT(Exception, exceptionObject, exception);
if (!exception) {
// As above, use the stack provided by the JS engine, if available.
if (exceptionStack) {
return exceptionStack;
}
return nullptr;
}
}
@ -421,10 +432,12 @@ public:
ScriptErrorEvent(nsPIDOMWindowInner* aWindow,
JS::RootingContext* aRootingCx,
xpc::ErrorReport* aReport,
JS::Handle<JS::Value> aError)
JS::Handle<JS::Value> aError,
JS::Handle<JSObject*> aErrorStack)
: mWindow(aWindow)
, mReport(aReport)
, mError(aRootingCx, aError)
, mErrorStack(aRootingCx, aErrorStack)
{}
NS_IMETHOD Run() override
@ -471,7 +484,7 @@ public:
if (status != nsEventStatus_eConsumeNoDefault) {
JS::Rooted<JSObject*> stack(rootingCx,
xpc::FindExceptionStackForConsoleReport(win, mError));
xpc::FindExceptionStackForConsoleReport(win, mError, mErrorStack));
mReport->LogToConsoleWithStack(stack);
}
@ -481,7 +494,8 @@ public:
private:
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<xpc::ErrorReport> mReport;
JS::PersistentRootedValue mError;
JS::PersistentRootedValue mError;
JS::PersistentRootedObject mErrorStack;
static bool sHandlingScriptError;
};
@ -494,9 +508,10 @@ namespace xpc {
void
DispatchScriptErrorEvent(nsPIDOMWindowInner *win, JS::RootingContext* rootingCx,
xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception)
xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception,
JS::Handle<JSObject*> exceptionStack)
{
nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception));
nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception, exceptionStack));
}
} /* namespace xpc */

View file

@ -577,8 +577,9 @@ AutoJSAPI::ReportException()
}
JSAutoCompartment ac(cx(), errorGlobal);
JS::Rooted<JS::Value> exn(cx());
JS::Rooted<JSObject*> exnStack(cx());
js::ErrorReport jsReport(cx());
if (StealException(&exn) &&
if (StealExceptionAndStack(&exn, &exnStack) &&
jsReport.init(cx(), exn, js::ErrorReport::WithSideEffects)) {
if (mIsMainThread) {
RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
@ -595,10 +596,10 @@ AutoJSAPI::ReportException()
inner ? inner->WindowID() : 0);
if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) {
JS::RootingContext* rcx = JS::RootingContext::get(cx());
DispatchScriptErrorEvent(inner, rcx, xpcReport, exn);
DispatchScriptErrorEvent(inner, rcx, xpcReport, exn, exnStack);
} else {
JS::Rooted<JSObject*> stack(cx(),
xpc::FindExceptionStackForConsoleReport(inner, exn));
xpc::FindExceptionStackForConsoleReport(inner, exn, exnStack));
xpcReport->LogToConsoleWithStack(stack);
}
} else {
@ -638,9 +639,16 @@ AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal)
bool
AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
{
JS::Rooted<JSObject*> stack(cx());
return StealExceptionAndStack(aVal, &stack);
}
bool AutoJSAPI::StealExceptionAndStack(JS::MutableHandle<JS::Value> aVal,
JS::MutableHandle<JSObject*> aStack) {
if (!PeekException(aVal)) {
return false;
}
aStack.set(JS::GetPendingExceptionStack(cx()));
JS_ClearPendingException(cx());
return true;
}

View file

@ -274,6 +274,12 @@ public:
// into the current compartment.
MOZ_MUST_USE bool StealException(JS::MutableHandle<JS::Value> aVal);
// As for StealException(), but put the saved frames for any stack trace
// associated with the point the exception was thrown into aStack.
// aVal will be in the current compartment, but aStack might not be.
MOZ_MUST_USE bool StealExceptionAndStack(JS::MutableHandle<JS::Value> aVal,
JS::MutableHandle<JSObject*> aStack);
// Peek the current exception from the JS engine, without stealing it.
// Callers must ensure that HasException() is true, and that cx() is in a
// non-null compartment.

View file

@ -2479,7 +2479,18 @@ PromiseObject::unforgeableResolve(JSContext* cx, HandleValue value)
return CommonStaticResolveRejectImpl(cx, cVal, value, ResolveMode);
}
// ES2016, 25.4.4.6, implemented in Promise.js.
/**
* ES2016, 25.4.4.6 get Promise [ @@species ]
*/
static bool
Promise_static_species(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1: Return the this value.
args.rval().set(args.thisv());
return true;
}
// ES2016, 25.4.5.1, implemented in Promise.js.
@ -2520,6 +2531,12 @@ NewReactionRecord(JSContext* cx, HandleObject resultPromise, HandleValue onFulfi
return reaction;
}
static bool
IsPromiseSpecies(JSContext* cx, JSFunction* species)
{
return species->maybeNative() == Promise_static_species;
}
// ES2016, 25.4.5.3., steps 3-5.
MOZ_MUST_USE bool
js::OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
@ -2538,10 +2555,9 @@ js::OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
if (createDependent) {
// Step 3.
RootedValue ctorVal(cx);
if (!SpeciesConstructor(cx, promiseObj, JSProto_Promise, &ctorVal))
RootedObject C(cx, SpeciesConstructor(cx, promiseObj, JSProto_Promise, IsPromiseSpecies));
if (!C)
return false;
RootedObject C(cx, &ctorVal.toObject());
// Step 4.
if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, true))
@ -2577,6 +2593,13 @@ js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal)
return promise;
}
bool
js::IsPromiseForAsync(JSObject* promise)
{
return promise->is<PromiseObject>() &&
PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_ASYNC);
}
// ES 2018 draft 25.5.5.2 steps 3.f, 3.g.
MOZ_MUST_USE bool
js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise)
@ -2838,6 +2861,8 @@ js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenO
// Step 5.
RootedObject resultPromise(cx, request->promise());
asyncGenObj->cacheRequest(request);
// Step 6.
RootedObject resultObj(cx, CreateIterResultObject(cx, value, done));
if (!resultObj)
@ -2876,6 +2901,8 @@ js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenOb
// Step 5.
RootedObject resultPromise(cx, request->promise());
asyncGenObj->cacheRequest(request);
// Step 6.
if (!RejectMaybeWrappedPromise(cx, resultPromise, exception))
return false;
@ -3013,7 +3040,8 @@ js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
// Step 5 (reordered).
Rooted<AsyncGeneratorRequest*> request(
cx, AsyncGeneratorRequest::create(cx, completionKind, completionValue, resultPromise));
cx, AsyncGeneratorObject::createRequest(cx, asyncGenObj, completionKind, completionValue,
resultPromise));
if (!request)
return false;
@ -3186,11 +3214,10 @@ BlockOnPromise(JSContext* cx, HandleValue promiseVal, HandleObject blockedPromis
RootedObject PromiseCtor(cx);
if (!GetBuiltinConstructor(cx, JSProto_Promise, &PromiseCtor))
return false;
RootedValue PromiseCtorVal(cx, ObjectValue(*PromiseCtor));
RootedValue CVal(cx);
if (!SpeciesConstructor(cx, promiseObj, PromiseCtorVal, &CVal))
RootedObject C(cx, SpeciesConstructor(cx, PromiseCtor, JSProto_Promise, IsPromiseSpecies));
if (!C)
return false;
RootedObject C(cx, &CVal.toObject());
RootedObject resultPromise(cx, blockedPromise_);
RootedObject resolveFun(cx);
@ -3572,7 +3599,7 @@ static const JSFunctionSpec promise_static_methods[] = {
};
static const JSPropertySpec promise_static_properties[] = {
JS_SELF_HOSTED_SYM_GET(species, "Promise_static_get_species", 0),
JS_SYM_GET(species, Promise_static_species, 0),
JS_PS_END
};

View file

@ -141,6 +141,9 @@ PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value);
MOZ_MUST_USE PromiseObject*
CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal);
MOZ_MUST_USE bool
IsPromiseForAsync(JSObject* promise);
MOZ_MUST_USE bool
AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value);

View file

@ -2,13 +2,6 @@
* 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/. */
// ES6, 25.4.4.6.
function Promise_static_get_species() {
// Step 1.
return this;
}
_SetCanonicalName(Promise_static_get_species, "get [Symbol.species]");
// ES6, 25.4.5.1.
function Promise_catch(onRejected) {
// Steps 1-2.

View file

@ -1398,6 +1398,11 @@ ResolvePromise(JSContext* cx, unsigned argc, Value* vp)
return false;
}
if (IsPromiseForAsync(promise)) {
JS_ReportErrorASCII(cx, "async function's promise shouldn't be manually resolved");
return false;
}
bool result = JS::ResolvePromise(cx, promise, resolution);
if (result)
args.rval().setUndefined();
@ -1425,6 +1430,11 @@ RejectPromise(JSContext* cx, unsigned argc, Value* vp)
return false;
}
if (IsPromiseForAsync(promise)) {
JS_ReportErrorASCII(cx, "async function's promise shouldn't be manually rejected");
return false;
}
bool result = JS::RejectPromise(cx, promise, reason);
if (result)
args.rval().setUndefined();

View file

@ -550,7 +550,7 @@ bool IsSupportedBinaryProperty(UProperty property) {
case UCHAR_EMOJI_MODIFIER_BASE:
case UCHAR_EMOJI_MODIFIER:
case UCHAR_EMOJI_PRESENTATION:
// case UCHAR_EXTENDED_PICTOGRAPHIC:
case UCHAR_EXTENDED_PICTOGRAPHIC:
case UCHAR_EXTENDER:
case UCHAR_GRAPHEME_BASE:
case UCHAR_GRAPHEME_EXTEND:

View file

@ -4557,20 +4557,20 @@ BaselineCompiler::emit_JSOP_RESUME()
Register initLength = regs.takeAny();
masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
masm.load32(Address(scratch2, ObjectElements::offsetOfInitializedLength()), initLength);
masm.store32(Imm32(0), Address(scratch2, ObjectElements::offsetOfInitializedLength()));
Label loop, loopDone;
masm.bind(&loop);
masm.branchTest32(Assembler::Zero, initLength, initLength, &loopDone);
{
masm.pushValue(Address(scratch2, 0));
masm.patchableCallPreBarrier(exprStackSlot, MIRType::Value);
masm.addPtr(Imm32(sizeof(Value)), scratch2);
masm.sub32(Imm32(1), initLength);
masm.jump(&loop);
}
masm.bind(&loopDone);
masm.patchableCallPreBarrier(exprStackSlot, MIRType::Value);
masm.storeValue(NullValue(), exprStackSlot);
regs.add(initLength);
}

View file

@ -941,7 +941,7 @@ HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr, bool* mus
return jit::DebugEpilogue(cx, frame, pc, true);
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
return false;
default:

View file

@ -984,8 +984,8 @@ LookupStdName(const JSAtomState& names, JSAtom* name, const JSStdName* table)
* JSProtoKey does not correspond to a class with a meaningful constructor, we
* insert a null entry into the table.
*/
#define STD_NAME_ENTRY(name, code, init, clasp) { EAGER_ATOM(name), static_cast<JSProtoKey>(code) },
#define STD_DUMMY_ENTRY(name, code, init, dummy) { 0, JSProto_Null },
#define STD_NAME_ENTRY(name, init, clasp) { EAGER_ATOM(name), JSProto_##name },
#define STD_DUMMY_ENTRY(name, init, dummy) { 0, JSProto_Null },
static const JSStdName standard_class_names[] = {
JS_FOR_PROTOTYPES(STD_NAME_ENTRY, STD_DUMMY_ENTRY)
{ 0, JSProto_LIMIT }
@ -1276,6 +1276,20 @@ JS::detail::ComputeThis(JSContext* cx, Value* vp)
return thisv;
}
static bool gProfileTimelineRecordingEnabled = false;
JS_PUBLIC_API(void)
JS::SetProfileTimelineRecordingEnabled(bool enabled)
{
gProfileTimelineRecordingEnabled = enabled;
}
JS_PUBLIC_API(bool)
JS::IsProfileTimelineRecordingEnabled()
{
return gProfileTimelineRecordingEnabled;
}
JS_PUBLIC_API(void*)
JS_malloc(JSContext* cx, size_t nbytes)
{
@ -6205,12 +6219,16 @@ JS_GetPendingException(JSContext* cx, MutableHandleValue vp)
}
JS_PUBLIC_API(void)
JS_SetPendingException(JSContext* cx, HandleValue value)
JS_SetPendingException(JSContext* cx, HandleValue value, JS::ExceptionStackBehavior behavior)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
releaseAssertSameCompartment(cx, value);
cx->setPendingException(value);
if (behavior == JS::ExceptionStackBehavior::Capture) {
cx->setPendingExceptionAndCaptureStack(value);
} else {
cx->setPendingException(value, nullptr);
}
}
JS_PUBLIC_API(void)
@ -6220,12 +6238,20 @@ JS_ClearPendingException(JSContext* cx)
cx->clearPendingException();
}
JS_PUBLIC_API(JSObject*)
JS::GetPendingExceptionStack(JSContext* cx)
{
AssertHeapIsIdle(cx);
return cx->getPendingExceptionStack();
}
JS::AutoSaveExceptionState::AutoSaveExceptionState(JSContext* cx)
: context(cx),
wasPropagatingForcedReturn(cx->propagatingForcedReturn_),
wasOverRecursed(cx->overRecursed_),
wasThrowing(cx->throwing),
exceptionValue(cx)
exceptionValue(cx),
exceptionStack(cx)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
@ -6235,10 +6261,21 @@ JS::AutoSaveExceptionState::AutoSaveExceptionState(JSContext* cx)
cx->overRecursed_ = false;
if (wasThrowing) {
exceptionValue = cx->unwrappedException_;
exceptionStack = cx->unwrappedExceptionStack_;
cx->clearPendingException();
}
}
void
JS::AutoSaveExceptionState::drop()
{
wasPropagatingForcedReturn = false;
wasOverRecursed = false;
wasThrowing = false;
exceptionValue.setUndefined();
exceptionStack = nullptr;
}
void
JS::AutoSaveExceptionState::restore()
{
@ -6246,6 +6283,9 @@ JS::AutoSaveExceptionState::restore()
context->overRecursed_ = wasOverRecursed;
context->throwing = wasThrowing;
context->unwrappedException_ = exceptionValue;
if (exceptionStack) {
context->unwrappedExceptionStack_ = &exceptionStack->as<SavedFrame>();
}
drop();
}
@ -6258,6 +6298,9 @@ JS::AutoSaveExceptionState::~AutoSaveExceptionState()
context->overRecursed_ = wasOverRecursed;
context->throwing = true;
context->unwrappedException_ = exceptionValue;
if (exceptionStack) {
context->unwrappedExceptionStack_ = &exceptionStack->as<SavedFrame>();
}
}
}
}

View file

@ -1545,6 +1545,22 @@ JS_DefineProfilingFunctions(JSContext* cx, JS::HandleObject obj);
extern JS_PUBLIC_API(bool)
JS_DefineDebuggerObject(JSContext* cx, JS::HandleObject obj);
namespace JS {
/**
* Tell JS engine whether Profile Timeline Recording is enabled or not.
* If Profile Timeline Recording is enabled, data shown there like stack won't
* be optimized out.
* This is global state and not associated with specific runtime or context.
*/
extern JS_PUBLIC_API(void)
SetProfileTimelineRecordingEnabled(bool enabled);
extern JS_PUBLIC_API(bool)
IsProfileTimelineRecordingEnabled();
} // namespace JS
#ifdef JS_HAS_CTYPES
/**
* Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes'
@ -2054,6 +2070,9 @@ inline int CheckIsSetterOp(JSSetterOp op);
#define JS_PSGS(name, getter, setter, flags) \
JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(setter), flags, \
JSPROP_SHARED)
#define JS_SYM_GET(symbol, getter, flags) \
JS_PS_ACCESSOR_SPEC(reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(nullptr), flags, JSPROP_SHARED)
#define JS_SELF_HOSTED_GET(name, getterName, flags) \
JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName), JSNATIVE_WRAPPER(nullptr), flags, \
JSPROP_SHARED | JSPROP_GETTER)
@ -5746,8 +5765,22 @@ JS_IsExceptionPending(JSContext* cx);
extern JS_PUBLIC_API(bool)
JS_GetPendingException(JSContext* cx, JS::MutableHandleValue vp);
namespace JS {
enum class ExceptionStackBehavior: bool {
// Do not capture any stack.
DoNotCapture,
// Capture the current JS stack when setting the exception. It may be
// retrieved by JS::GetPendingExceptionStack.
Capture
};
} // namespace JS
extern JS_PUBLIC_API(void)
JS_SetPendingException(JSContext* cx, JS::HandleValue v);
JS_SetPendingException(JSContext* cx, JS::HandleValue v,
JS::ExceptionStackBehavior behavior = JS::ExceptionStackBehavior::Capture);
extern JS_PUBLIC_API(void)
JS_ClearPendingException(JSContext* cx);
@ -5774,6 +5807,7 @@ class JS_PUBLIC_API(AutoSaveExceptionState)
bool wasOverRecursed;
bool wasThrowing;
RootedValue exceptionValue;
RootedObject exceptionStack;
public:
/*
@ -5792,12 +5826,7 @@ class JS_PUBLIC_API(AutoSaveExceptionState)
* Discard any stored exception state.
* If this is called, the destructor is a no-op.
*/
void drop() {
wasPropagatingForcedReturn = false;
wasOverRecursed = false;
wasThrowing = false;
exceptionValue.setUndefined();
}
void drop();
/*
* Replace cx's exception state with the stored exception state. Then
@ -5807,6 +5836,18 @@ class JS_PUBLIC_API(AutoSaveExceptionState)
void restore();
};
/**
* Get the SavedFrame stack object captured when the pending exception was set
* on the JSContext. This fuzzily correlates with a `throw` statement in JS,
* although arbitrary JSAPI consumers or VM code may also set pending exceptions
* via `JS_SetPendingException`.
*
* This is not the same stack as `e.stack` when `e` is an `Error` object. (That
* would be JS::ExceptionStackOrNull).
*/
MOZ_MUST_USE JS_PUBLIC_API(JSObject*)
GetPendingExceptionStack(JSContext* cx);
} /* namespace JS */
/* Deprecated API. Use AutoSaveExceptionState instead. */

View file

@ -44,7 +44,7 @@ js::AtomToPrintableString(ExclusiveContext* cx, JSAtom* atom, JSAutoByteString*
return bytes->encodeLatin1(cx, str);
}
#define DEFINE_PROTO_STRING(name,code,init,clasp) const char js_##name##_str[] = #name;
#define DEFINE_PROTO_STRING(name,init,clasp) const char js_##name##_str[] = #name;
JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
#undef DEFINE_PROTO_STRING
@ -98,7 +98,7 @@ JSRuntime::initializeAtoms(JSContext* cx)
#define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
#undef COMMON_NAME_INFO
#define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
#define COMMON_NAME_INFO(name, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
#undef COMMON_NAME_INFO
#define COMMON_NAME_INFO(name) { #name, sizeof(#name) - 1 },

View file

@ -132,7 +132,7 @@ extern bool
AtomIsPinned(JSContext* cx, JSAtom* atom);
/* Well-known predefined C strings. */
#define DECLARE_PROTO_STR(name,code,init,clasp) extern const char js_##name##_str[];
#define DECLARE_PROTO_STR(name,init,clasp) extern const char js_##name##_str[];
JS_FOR_EACH_PROTOTYPE(DECLARE_PROTO_STR)
#undef DECLARE_PROTO_STR

View file

@ -241,7 +241,8 @@ js::ReportOutOfMemory(ExclusiveContext* cxArg)
if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback)
oomCallback(cx, cx->runtime()->oomCallbackData);
cx->setPendingException(StringValue(cx->names().outOfMemory));
RootedValue oomMessage(cx, StringValue(cx->names().outOfMemory));
cx->setPendingException(oomMessage, nullptr);
}
void
@ -1013,6 +1014,7 @@ JSContext::JSContext(JSRuntime* parentRuntime)
JSRuntime(parentRuntime),
throwing(false),
unwrappedException_(this),
unwrappedExceptionStack_(this),
overRecursed_(false),
propagatingForcedReturn_(false),
liveVolatileJitFrameIterators_(nullptr),
@ -1038,6 +1040,24 @@ JSContext::~JSContext()
MOZ_ASSERT(!resolvingList);
}
void
JSContext::setPendingExceptionAndCaptureStack(HandleValue value)
{
static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
RootedObject stack(this);
if (!CaptureCurrentStack(this, &stack, JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)))) {
clearPendingException();
}
RootedSavedFrame nstack(this);
if (stack) {
nstack = &stack->as<SavedFrame>();
}
setPendingException(value, nstack);
}
bool
JSContext::getPendingException(MutableHandleValue rval)
{
@ -1045,16 +1065,23 @@ JSContext::getPendingException(MutableHandleValue rval)
rval.set(unwrappedException_);
if (IsAtomsCompartment(compartment()))
return true;
RootedSavedFrame stack(this, unwrappedExceptionStack_);
bool wasOverRecursed = overRecursed_;
clearPendingException();
if (!compartment()->wrap(this, rval))
return false;
assertSameCompartment(this, rval);
setPendingException(rval);
setPendingException(rval, stack);
overRecursed_ = wasOverRecursed;
return true;
}
SavedFrame*
JSContext::getPendingExceptionStack()
{
return unwrappedExceptionStack_;
}
bool
JSContext::isThrowingOutOfMemory()
{

View file

@ -369,6 +369,7 @@ struct JSContext : public js::ExclusiveContext,
/* Exception state -- the exception member is a GC root by definition. */
bool throwing; /* is there a pending exception? */
JS::PersistentRooted<JS::Value> unwrappedException_; /* most-recently-thrown exception */
JS::PersistentRooted<js::SavedFrame*> unwrappedExceptionStack_; /* stack when the exception was thrown */
// True if the exception currently being thrown is by result of
// ReportOverRecursed. See Debugger::slowPathOnExceptionUnwind.
@ -495,17 +496,21 @@ struct JSContext : public js::ExclusiveContext,
MOZ_MUST_USE
bool getPendingException(JS::MutableHandleValue rval);
js::SavedFrame* getPendingExceptionStack();
bool isThrowingOutOfMemory();
bool isThrowingDebuggeeWouldRun();
bool isClosingGenerator();
void setPendingException(const js::Value& v);
void setPendingException(JS::HandleValue v, js::HandleSavedFrame stack);
void setPendingExceptionAndCaptureStack(JS::HandleValue v);
void clearPendingException() {
throwing = false;
overRecursed_ = false;
unwrappedException_.setUndefined();
unwrappedExceptionStack_ = nullptr;
}
bool isThrowingOverRecursed() const { return throwing && overRecursed_; }

View file

@ -368,12 +368,13 @@ ExclusiveContext::typeLifoAlloc()
} /* namespace js */
inline void
JSContext::setPendingException(const js::Value& v)
JSContext::setPendingException(JS::HandleValue v, js::HandleSavedFrame stack)
{
// overRecursed_ is set after the fact by ReportOverRecursed.
this->overRecursed_ = false;
this->throwing = true;
this->unwrappedException_ = v;
this->unwrappedExceptionStack_ = stack;
// We don't use assertSameCompartment here to allow
// js::SetPendingExceptionCrossContext to work.
MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());

View file

@ -85,6 +85,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
jitCompartment_(nullptr),
mappedArgumentsTemplate_(nullptr),
unmappedArgumentsTemplate_(nullptr),
iterResultTemplate_(nullptr),
lcovOutput()
{
runtime_->numCompartments++;
@ -846,6 +847,9 @@ JSCompartment::sweepTemplateObjects()
if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
unmappedArgumentsTemplate_.set(nullptr);
if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_))
iterResultTemplate_.set(nullptr);
}
/* static */ void

View file

@ -861,6 +861,7 @@ struct JSCompartment
js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_;
js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_;
js::ReadBarriered<js::NativeObject*> iterResultTemplate_;
public:
bool ensureJitCompartmentExists(JSContext* cx);
@ -872,6 +873,10 @@ struct JSCompartment
js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
static const size_t IterResultObjectValueSlot = 0;
static const size_t IterResultObjectDoneSlot = 1;
js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
public:
// Aggregated output used to collect JSScript hit counts when code coverage
// is enabled.

View file

@ -361,13 +361,13 @@ struct SuppressErrorsGuard
}
};
// Cut off the stack if it gets too deep (most commonly for infinite recursion
// errors).
static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
static bool
CaptureStack(JSContext* cx, MutableHandleObject stack)
{
// Cut off the stack if it gets too deep (most commonly for infinite recursion
// errors).
static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
return CaptureCurrentStack(cx, stack,
JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)));
}
@ -699,7 +699,12 @@ js::ErrorToException(JSContext* cx, JSErrorReport* reportp,
return;
// Throw it.
cx->setPendingException(ObjectValue(*errObject));
RootedValue errValue(cx, ObjectValue(*errObject));
RootedSavedFrame nstack(cx);
if (stack) {
nstack = &stack->as<SavedFrame>();
}
cx->setPendingException(errValue, nstack);
// Flag the error report passed in to indicate an exception was raised.
reportp->flags |= JSREPORT_EXCEPTION;

View file

@ -8,6 +8,7 @@
#include "jsiter.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/PodOperations.h"
@ -45,6 +46,7 @@ using namespace js::gc;
using JS::ForOfIterator;
using mozilla::ArrayLength;
using mozilla::DebugOnly;
using mozilla::Maybe;
using mozilla::PodCopy;
using mozilla::PodZero;
@ -944,25 +946,78 @@ js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
// Step 1 (implicit).
// Step 2.
RootedObject resultObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!resultObj)
RootedObject templateObject(cx, cx->compartment()->getOrCreateIterResultTemplateObject(cx));
if (!templateObject)
return nullptr;
NativeObject* resultObj = NativeObject::createWithTemplate(cx, gc::DefaultHeap, templateObject);
if (!resultObj)
return nullptr;
// Step 3.
if (!DefineProperty(cx, resultObj, cx->names().value, value))
return nullptr;
resultObj->setSlot(JSCompartment::IterResultObjectValueSlot, value);
// Step 4.
if (!DefineProperty(cx, resultObj, cx->names().done,
done ? TrueHandleValue : FalseHandleValue))
{
return nullptr;
}
resultObj->setSlot(JSCompartment::IterResultObjectDoneSlot,
done ? TrueHandleValue : FalseHandleValue);
// Step 5.
return resultObj;
}
NativeObject*
JSCompartment::getOrCreateIterResultTemplateObject(JSContext* cx)
{
if (iterResultTemplate_)
return iterResultTemplate_;
// Create template plain object
RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
if (!templateObject)
return iterResultTemplate_; // = nullptr
// Create a new group for the template.
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(),
proto));
if (!group)
return iterResultTemplate_; // = nullptr
templateObject->setGroup(group);
// Set dummy `value` property
if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
JSPROP_ENUMERATE))
{
return iterResultTemplate_; // = nullptr
}
// Set dummy `done` property
if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
JSPROP_ENUMERATE))
{
return iterResultTemplate_; // = nullptr
}
// Update `value` property typeset, since it can be any value.
HeapTypeSet* types = group->maybeGetProperty(NameToId(cx->names().value));
MOZ_ASSERT(types);
{
AutoEnterAnalysis enter(cx);
types->makeUnknown(cx);
}
// Make sure that the properties are in the right slots.
DebugOnly<Shape*> shape = templateObject->lastProperty();
MOZ_ASSERT(shape->previous()->slot() == JSCompartment::IterResultObjectValueSlot &&
shape->previous()->propidRef() == NameToId(cx->names().value));
MOZ_ASSERT(shape->slot() == JSCompartment::IterResultObjectDoneSlot &&
shape->propidRef() == NameToId(cx->names().done));
iterResultTemplate_.set(templateObject);
return iterResultTemplate_;
}
bool
js::ThrowStopIteration(JSContext* cx)
{
@ -971,8 +1026,10 @@ js::ThrowStopIteration(JSContext* cx)
// StopIteration isn't a constructor, but it's stored in GlobalObject
// as one, out of laziness. Hence the GetBuiltinConstructor call here.
RootedObject ctor(cx);
if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor))
cx->setPendingException(ObjectValue(*ctor));
if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor)) {
RootedValue ctorval(cx, ObjectValue(*ctor));
cx->setPendingExceptionAndCaptureStack(ctorval);
}
return false;
}
@ -1261,12 +1318,13 @@ js::UnwindIteratorForException(JSContext* cx, HandleObject obj)
{
RootedValue v(cx);
bool getOk = cx->getPendingException(&v);
RootedSavedFrame stack(cx, cx->getPendingExceptionStack());
cx->clearPendingException();
if (!CloseIterator(cx, obj))
return false;
if (!getOk)
return false;
cx->setPendingException(v);
cx->setPendingException(v, stack);
return true;
}

View file

@ -3196,8 +3196,8 @@ GetObjectSlotNameFunctor::operator()(JS::CallbackTracer* trc, char* buf, size_t
pattern = "CLASS_OBJECT(%s)";
if (false)
;
#define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp) \
else if ((code) == slot) { slotname = js_##name##_str; }
#define TEST_SLOT_MATCHES_PROTOTYPE(name,init,clasp) \
else if ((JSProto_##name) == slot) { slotname = js_##name##_str; }
JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
#undef TEST_SLOT_MATCHES_PROTOTYPE
} else {
@ -3864,34 +3864,77 @@ JSObject::maybeConstructorDisplayAtom() const
return displayAtomFromObjectGroup(*group());
}
bool
js::SpeciesConstructor(JSContext* cx, HandleObject obj, HandleValue defaultCtor, MutableHandleValue pctor)
// ES 2016 7.3.20.
MOZ_MUST_USE JSObject*
js::SpeciesConstructor(JSContext* cx, HandleObject obj, HandleObject defaultCtor,
bool (*isDefaultSpecies)(JSContext*, JSFunction*))
{
HandlePropertyName shName = cx->names().SpeciesConstructor;
RootedValue func(cx);
if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, shName, 2, &func))
return false;
// Step 1 (implicit).
FixedInvokeArgs<2> args(cx);
// Fast-path for steps 2 - 8. Applies if all of the following conditions
// are met:
// - obj.constructor can be retrieved without side-effects.
// - obj.constructor[[@@species]] can be retrieved without side-effects.
// - obj.constructor[[@@species]] is the builtin's original @@species
// getter.
RootedValue ctor(cx);
bool ctorGetSucceeded = GetPropertyPure(cx, obj, NameToId(cx->names().constructor),
ctor.address());
if (ctorGetSucceeded && ctor.isObject() && &ctor.toObject() == defaultCtor) {
RootedObject ctorObj(cx, &ctor.toObject());
RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
JSFunction* getter;
if (GetGetterPure(cx, ctorObj, speciesId, &getter) && getter &&
isDefaultSpecies(cx, getter))
{
return defaultCtor;
}
}
args[0].setObject(*obj);
args[1].set(defaultCtor);
// Step 2.
if (!ctorGetSucceeded && !GetProperty(cx, obj, obj, cx->names().constructor, &ctor))
return nullptr;
if (!Call(cx, func, UndefinedHandleValue, args, pctor))
return false;
// Step 3.
if (ctor.isUndefined())
return defaultCtor;
pctor.set(args.rval());
return true;
// Step 4.
if (!ctor.isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
"object's 'constructor' property");
return nullptr;
}
// Step 5.
RootedObject ctorObj(cx, &ctor.toObject());
RootedValue s(cx);
RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
if (!GetProperty(cx, ctorObj, ctor, speciesId, &s))
return nullptr;
// Step 6.
if (s.isNullOrUndefined())
return defaultCtor;
// Step 7.
if (IsConstructor(s))
return &s.toObject();
// Step 8.
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR,
"[Symbol.species] property of object's constructor");
return nullptr;
}
bool
MOZ_MUST_USE JSObject*
js::SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
MutableHandleValue pctor)
bool (*isDefaultSpecies)(JSContext*, JSFunction*))
{
if (!GlobalObject::ensureConstructor(cx, cx->global(), ctorKey))
return false;
RootedValue defaultCtor(cx, cx->global()->getConstructor(ctorKey));
return SpeciesConstructor(cx, obj, defaultCtor, pctor);
return nullptr;
RootedObject defaultCtor(cx, &cx->global()->getConstructor(ctorKey).toObject());
return SpeciesConstructor(cx, obj, defaultCtor, isDefaultSpecies);
}
bool

View file

@ -1347,11 +1347,13 @@ FreezeObject(JSContext* cx, HandleObject obj)
extern bool
TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bool* resultp);
extern bool
SpeciesConstructor(JSContext* cx, HandleObject obj, HandleValue defaultCtor, MutableHandleValue pctor);
extern MOZ_MUST_USE JSObject*
SpeciesConstructor(JSContext* cx, HandleObject obj, HandleObject defaultCtor,
bool (*isDefaultSpecies)(JSContext*, JSFunction*));
extern bool
SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey, MutableHandleValue pctor);
extern MOZ_MUST_USE JSObject*
SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
bool (*isDefaultSpecies)(JSContext*, JSFunction*));
extern bool
GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj);

View file

@ -9,9 +9,8 @@
/* A higher-order macro for enumerating all JSProtoKey values. */
/*
* Consumers define macros as follows:
* macro(name, code, init, clasp)
* macro(name, init, clasp)
* name: The canonical name of the class.
* code: The enumerator code. There are part of the XDR API, and must not change.
* init: Initialization function. These are |extern "C";|, and clients should use
* |extern "C" {}| as appropriate when using this macro.
* clasp: The JSClass for this object, or "dummy" if it doesn't exist.
@ -57,61 +56,61 @@
#endif
#define JS_FOR_PROTOTYPES(real,imaginary) \
imaginary(Null, 0, InitNullClass, dummy) \
real(Object, 1, InitViaClassSpec, OCLASP(Plain)) \
real(Function, 2, InitViaClassSpec, &JSFunction::class_) \
real(Array, 3, InitViaClassSpec, OCLASP(Array)) \
real(Boolean, 4, InitBooleanClass, OCLASP(Boolean)) \
real(JSON, 5, InitJSONClass, CLASP(JSON)) \
real(Date, 6, InitViaClassSpec, OCLASP(Date)) \
real(Math, 7, InitMathClass, CLASP(Math)) \
real(Number, 8, InitNumberClass, OCLASP(Number)) \
real(String, 9, InitStringClass, OCLASP(String)) \
real(RegExp, 10, InitViaClassSpec, OCLASP(RegExp)) \
real(Error, 11, InitViaClassSpec, ERROR_CLASP(JSEXN_ERR)) \
real(InternalError, 12, InitViaClassSpec, ERROR_CLASP(JSEXN_INTERNALERR)) \
real(EvalError, 13, InitViaClassSpec, ERROR_CLASP(JSEXN_EVALERR)) \
real(RangeError, 14, InitViaClassSpec, ERROR_CLASP(JSEXN_RANGEERR)) \
real(ReferenceError, 15, InitViaClassSpec, ERROR_CLASP(JSEXN_REFERENCEERR)) \
real(SyntaxError, 16, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR)) \
real(TypeError, 17, InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR)) \
real(URIError, 18, InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR)) \
real(DebuggeeWouldRun, 19, InitViaClassSpec, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \
real(CompileError, 20, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \
real(RuntimeError, 21, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \
real(Iterator, 22, InitLegacyIteratorClass,OCLASP(PropertyIterator)) \
real(StopIteration, 23, InitStopIterationClass, OCLASP(StopIteration)) \
real(ArrayBuffer, 24, InitViaClassSpec, OCLASP(ArrayBuffer)) \
real(Int8Array, 25, InitViaClassSpec, TYPED_ARRAY_CLASP(Int8)) \
real(Uint8Array, 26, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8)) \
real(Int16Array, 27, InitViaClassSpec, TYPED_ARRAY_CLASP(Int16)) \
real(Uint16Array, 28, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint16)) \
real(Int32Array, 29, InitViaClassSpec, TYPED_ARRAY_CLASP(Int32)) \
real(Uint32Array, 30, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint32)) \
real(Float32Array, 31, InitViaClassSpec, TYPED_ARRAY_CLASP(Float32)) \
real(Float64Array, 32, InitViaClassSpec, TYPED_ARRAY_CLASP(Float64)) \
real(Uint8ClampedArray, 33, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8Clamped)) \
real(Proxy, 34, InitProxyClass, js::ProxyClassPtr) \
real(WeakMap, 35, InitWeakMapClass, OCLASP(WeakMap)) \
real(Map, 36, InitMapClass, OCLASP(Map)) \
real(Set, 37, InitSetClass, OCLASP(Set)) \
real(DataView, 38, InitDataViewClass, OCLASP(DataView)) \
real(Symbol, 39, InitSymbolClass, OCLASP(Symbol)) \
IF_SAB(real,imaginary)(SharedArrayBuffer, 40, InitViaClassSpec, OCLASP(SharedArrayBuffer)) \
IF_INTL(real,imaginary) (Intl, 41, InitIntlClass, CLASP(Intl)) \
IF_BDATA(real,imaginary)(TypedObject, 42, InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
real(Reflect, 43, InitReflect, nullptr) \
IF_SIMD(real,imaginary)(SIMD, 44, InitSimdClass, OCLASP(Simd)) \
real(WeakSet, 45, InitWeakSetClass, OCLASP(WeakSet)) \
real(TypedArray, 46, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
IF_SAB(real,imaginary)(Atomics, 47, InitAtomicsClass, OCLASP(Atomics)) \
real(SavedFrame, 48, InitViaClassSpec, &js::SavedFrame::class_) \
real(WebAssembly, 49, InitWebAssemblyClass, CLASP(WebAssembly)) \
imaginary(WasmModule, 50, dummy, dummy) \
imaginary(WasmInstance, 51, dummy, dummy) \
imaginary(WasmMemory, 52, dummy, dummy) \
imaginary(WasmTable, 53, dummy, dummy) \
real(Promise, 54, InitViaClassSpec, OCLASP(Promise)) \
imaginary(Null, InitNullClass, dummy) \
real(Object, InitViaClassSpec, OCLASP(Plain)) \
real(Function, InitViaClassSpec, &JSFunction::class_) \
real(Array, InitViaClassSpec, OCLASP(Array)) \
real(Boolean, InitBooleanClass, OCLASP(Boolean)) \
real(JSON, InitJSONClass, CLASP(JSON)) \
real(Date, InitViaClassSpec, OCLASP(Date)) \
real(Math, InitMathClass, CLASP(Math)) \
real(Number, InitNumberClass, OCLASP(Number)) \
real(String, InitStringClass, OCLASP(String)) \
real(RegExp, InitViaClassSpec, OCLASP(RegExp)) \
real(Error, InitViaClassSpec, ERROR_CLASP(JSEXN_ERR)) \
real(InternalError, InitViaClassSpec, ERROR_CLASP(JSEXN_INTERNALERR)) \
real(EvalError, InitViaClassSpec, ERROR_CLASP(JSEXN_EVALERR)) \
real(RangeError, InitViaClassSpec, ERROR_CLASP(JSEXN_RANGEERR)) \
real(ReferenceError, InitViaClassSpec, ERROR_CLASP(JSEXN_REFERENCEERR)) \
real(SyntaxError, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR)) \
real(TypeError, InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR)) \
real(URIError, InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR)) \
real(DebuggeeWouldRun, InitViaClassSpec, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \
real(CompileError, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \
real(RuntimeError, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \
real(Iterator, InitLegacyIteratorClass,OCLASP(PropertyIterator)) \
real(StopIteration, InitStopIterationClass, OCLASP(StopIteration)) \
real(ArrayBuffer, InitViaClassSpec, OCLASP(ArrayBuffer)) \
real(Int8Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Int8)) \
real(Uint8Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8)) \
real(Int16Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Int16)) \
real(Uint16Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint16)) \
real(Int32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Int32)) \
real(Uint32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint32)) \
real(Float32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float32)) \
real(Float64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float64)) \
real(Uint8ClampedArray, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8Clamped)) \
real(Proxy, InitProxyClass, js::ProxyClassPtr) \
real(WeakMap, InitWeakMapClass, OCLASP(WeakMap)) \
real(Map, InitMapClass, OCLASP(Map)) \
real(Set, InitSetClass, OCLASP(Set)) \
real(DataView, InitDataViewClass, OCLASP(DataView)) \
real(Symbol, InitSymbolClass, OCLASP(Symbol)) \
IF_SAB(real,imaginary)(SharedArrayBuffer, InitViaClassSpec, OCLASP(SharedArrayBuffer)) \
IF_INTL(real,imaginary) (Intl, InitIntlClass, CLASP(Intl)) \
IF_BDATA(real,imaginary)(TypedObject, InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
real(Reflect, InitReflect, nullptr) \
IF_SIMD(real,imaginary)(SIMD, InitSimdClass, OCLASP(Simd)) \
real(WeakSet, InitWeakSetClass, OCLASP(WeakSet)) \
real(TypedArray, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
real(SavedFrame, InitViaClassSpec, &js::SavedFrame::class_) \
real(WebAssembly, InitWebAssemblyClass, CLASP(WebAssembly)) \
imaginary(WasmModule, dummy, dummy) \
imaginary(WasmInstance, dummy, dummy) \
imaginary(WasmMemory, dummy, dummy) \
imaginary(WasmTable, dummy, dummy) \
real(Promise, InitViaClassSpec, OCLASP(Promise)) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)

View file

@ -84,7 +84,7 @@ enum JSType {
/* Dense index into cached prototypes and class atoms for standard objects. */
enum JSProtoKey {
#define PROTOKEY_AND_INITIALIZER(name,code,init,clasp) JSProto_##name = code,
#define PROTOKEY_AND_INITIALIZER(name,init,clasp) JSProto_##name,
JS_FOR_EACH_PROTOTYPE(PROTOKEY_AND_INITIALIZER)
#undef PROTOKEY_AND_INITIALIZER
JSProto_LIMIT

View file

@ -408,12 +408,15 @@ ErrorCopier::~ErrorCopier()
{
RootedValue exc(cx);
if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
RootedSavedFrame stack(cx, cx->getPendingExceptionStack());
cx->clearPendingException();
ac.reset();
Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
JSObject* copyobj = CopyErrorObject(cx, errObj);
if (copyobj)
cx->setPendingException(ObjectValue(*copyobj));
if (copyobj) {
RootedValue rootedCopy(cx, ObjectValue(*copyobj));
cx->setPendingException(rootedCopy, stack);
}
}
}
}

View file

@ -0,0 +1,89 @@
// 22.2.4.3 TypedArray ( typedArray )
// Test [[Prototype]] of newly created typed array and its array buffer, and
// ensure they are both created in the correct global.
const thisGlobal = this;
const otherGlobal = newGlobal();
const ta_i32 = otherGlobal.eval("new Int32Array(0)");
function assertBufferPrototypeFrom(newTypedArray, prototype) {
var typedArrayName = newTypedArray.constructor.name;
assertEq(Object.getPrototypeOf(newTypedArray), thisGlobal[typedArrayName].prototype);
assertEq(Object.getPrototypeOf(newTypedArray.buffer), prototype);
}
const EMPTY = {};
// Test SpeciesConstructor() implementation selects the correct (fallback) constructor.
const testCases = [
// Create the array buffer from the species constructor.
{ constructor: EMPTY, prototype: otherGlobal.ArrayBuffer.prototype },
// Use %ArrayBuffer% from this global if constructor is undefined.
{ constructor: undefined, prototype: ArrayBuffer.prototype },
// Use %ArrayBuffer% from this global if species is undefined.
{ constructor: {[Symbol.species]: undefined}, prototype: ArrayBuffer.prototype },
// Use %ArrayBuffer% from this global if species is null.
{ constructor: {[Symbol.species]: null}, prototype: ArrayBuffer.prototype },
];
for (let { constructor, prototype } of testCases) {
if (constructor !== EMPTY) {
ta_i32.buffer.constructor = constructor;
}
// Same element type.
assertBufferPrototypeFrom(new Int32Array(ta_i32), prototype);
// Different element type.
assertBufferPrototypeFrom(new Int16Array(ta_i32), prototype);
}
// Also ensure TypeErrors are thrown from the correct global.
const errorTestCases = [
// Constructor property is neither undefined nor an object.
{ constructor: null },
{ constructor: 123 },
// Species property is neither undefined/null nor a constructor function.
{ constructor: { [Symbol.species]: 123 } },
{ constructor: { [Symbol.species]: [] } },
{ constructor: { [Symbol.species]: () => {} } },
];
for (let { constructor } of errorTestCases) {
ta_i32.buffer.constructor = constructor;
// Same element type.
assertThrowsInstanceOf(() => new Int32Array(ta_i32), TypeError);
// Different element type.
assertThrowsInstanceOf(() => new Int32Array(ta_i32), TypeError);
}
// TypedArrays using SharedArrayBuffers never call the SpeciesConstructor operation.
if (this.SharedArrayBuffer) {
const ta_i32_shared = otherGlobal.eval("new Int32Array(new SharedArrayBuffer(0))");
Object.defineProperty(ta_i32_shared.buffer, "constructor", {
get() {
throw new Error("constructor property accessed");
}
});
// Same element type.
assertBufferPrototypeFrom(new Int32Array(ta_i32_shared), ArrayBuffer.prototype);
// Different element type.
assertBufferPrototypeFrom(new Int16Array(ta_i32_shared), ArrayBuffer.prototype);
}
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -311,9 +311,24 @@ AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue
// Step 8.
asyncGenObj->clearSingleQueueRequest();
asyncGenObj->clearCachedRequest();
return asyncGenObj;
}
/* static */ AsyncGeneratorRequest*
AsyncGeneratorObject::createRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, HandleValue completionValue,
HandleObject promise)
{
if (!asyncGenObj->hasCachedRequest())
return AsyncGeneratorRequest::create(cx, completionKind, completionValue, promise);
AsyncGeneratorRequest* request = asyncGenObj->takeCachedRequest();
request->init(completionKind, completionValue, promise);
return request;
}
static MOZ_MUST_USE bool
InternalEnqueue(JSContext* cx, HandleArrayObject queue, HandleValue val)
{
@ -428,17 +443,15 @@ const Class AsyncGeneratorRequest::class_ = {
// Async Iteration proposal 11.4.3.1.
/* static */ AsyncGeneratorRequest*
AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_,
HandleValue completionValue_, HandleObject promise_)
AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind,
HandleValue completionValue, HandleObject promise)
{
RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr));
if (!obj)
return nullptr;
Handle<AsyncGeneratorRequest*> request = obj.as<AsyncGeneratorRequest>();
request->setCompletionKind(completionKind_);
request->setCompletionValue(completionValue_);
request->setPromise(promise_);
request->init(completionKind, completionValue, promise);
return request;
}

View file

@ -50,6 +50,8 @@ AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason);
class AsyncGeneratorObject;
class AsyncGeneratorRequest : public NativeObject
{
private:
@ -60,23 +62,26 @@ class AsyncGeneratorRequest : public NativeObject
Slots,
};
void setCompletionKind(CompletionKind completionKind_) {
void init(CompletionKind completionKind, HandleValue completionValue,
HandleObject promise) {
setFixedSlot(Slot_CompletionKind,
Int32Value(static_cast<int32_t>(completionKind_)));
Int32Value(static_cast<int32_t>(completionKind)));
setFixedSlot(Slot_CompletionValue, completionValue);
setFixedSlot(Slot_Promise, ObjectValue(*promise));
}
void setCompletionValue(HandleValue completionValue_) {
setFixedSlot(Slot_CompletionValue, completionValue_);
}
void setPromise(HandleObject promise_) {
setFixedSlot(Slot_Promise, ObjectValue(*promise_));
void clearData() {
setFixedSlot(Slot_CompletionValue, NullValue());
setFixedSlot(Slot_Promise, NullValue());
}
friend AsyncGeneratorObject;
public:
static const Class class_;
static AsyncGeneratorRequest*
create(JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
HandleObject promise);
static AsyncGeneratorRequest* create(JSContext* cx, CompletionKind completionKind,
HandleValue completionValue, HandleObject promise);
CompletionKind completionKind() const {
return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32());
@ -96,6 +101,7 @@ class AsyncGeneratorObject : public NativeObject
Slot_State = 0,
Slot_Generator,
Slot_QueueOrRequest,
Slot_CachedRequest,
Slots
};
@ -139,7 +145,7 @@ class AsyncGeneratorObject : public NativeObject
setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request));
}
void clearSingleQueueRequest() {
setFixedSlot(Slot_QueueOrRequest, NullHandleValue);
setFixedSlot(Slot_QueueOrRequest, NullValue());
}
AsyncGeneratorRequest* singleQueueRequest() const {
return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>();
@ -218,6 +224,41 @@ class AsyncGeneratorObject : public NativeObject
return isSingleQueueEmpty();
return queue()->length() == 0;
}
// This function does either of the following:
// * return a cached request object with the slots updated
// * create a new request object with the slots set
static AsyncGeneratorRequest* createRequest(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind,
HandleValue completionValue,
HandleObject promise);
// Stores the given request to the generator's cache after clearing its data
// slots. The cached request will be reused in the subsequent createRequest
// call.
void cacheRequest(AsyncGeneratorRequest* request) {
if (hasCachedRequest())
return;
request->clearData();
setFixedSlot(Slot_CachedRequest, ObjectValue(*request));
}
private:
bool hasCachedRequest() const {
return getFixedSlot(Slot_CachedRequest).isObject();
}
AsyncGeneratorRequest* takeCachedRequest() {
auto request = &getFixedSlot(Slot_CachedRequest).toObject().as<AsyncGeneratorRequest>();
clearCachedRequest();
return request;
}
void clearCachedRequest() {
setFixedSlot(Slot_CachedRequest, NullValue());
}
};
JSObject*

View file

@ -848,7 +848,7 @@ Debugger::slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame)
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
break;
case JSTRAP_ERROR:
@ -960,7 +960,7 @@ Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode
return true;
case JSTRAP_THROW:
cx->setPendingException(value);
cx->setPendingExceptionAndCaptureStack(value);
return false;
case JSTRAP_ERROR:
@ -993,7 +993,7 @@ Debugger::slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame)
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
break;
default:
@ -1028,7 +1028,7 @@ Debugger::slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
break;
case JSTRAP_ERROR:
@ -1321,7 +1321,7 @@ public:
bool operator()(JSContext* cx) override
{
cx->setPendingException(exn_);
cx->setPendingExceptionAndCaptureStack(exn_);
return false;
}
@ -1753,6 +1753,7 @@ Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
MOZ_ASSERT(hook->isCallable());
RootedValue exc(cx);
RootedSavedFrame stack(cx, cx->getPendingExceptionStack());
if (!cx->getPendingException(&exc))
return JSTRAP_ERROR;
cx->clearPendingException();
@ -1772,7 +1773,7 @@ Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
JSTrapStatus st = processHandlerResult(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
if (st == JSTRAP_CONTINUE)
cx->setPendingException(exc);
cx->setPendingException(exc, stack);
return st;
}
@ -2005,13 +2006,7 @@ Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
* onStep handlers mess with that (other than by returning a resumption
* value).
*/
RootedValue exception(cx, UndefinedValue());
bool exceptionPending = cx->isExceptionPending();
if (exceptionPending) {
if (!cx->getPendingException(&exception))
return JSTRAP_ERROR;
cx->clearPendingException();
}
JS::AutoSaveExceptionState savedExc(cx);
/*
* Build list of Debugger.Frame instances referring to this frame with
@ -2070,13 +2065,13 @@ Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
bool ok = js::Call(cx, fval, frame, &rval);
JSTrapStatus st = dbg->processHandlerResult(ac, ok, rval, iter.abstractFramePtr(),
iter.pc(), vp);
if (st != JSTRAP_CONTINUE)
if (st != JSTRAP_CONTINUE) {
savedExc.drop();
return st;
}
}
vp.setUndefined();
if (exceptionPending)
cx->setPendingException(exception);
return JSTRAP_CONTINUE;
}

View file

@ -158,9 +158,12 @@ ForOfIterator::closeThrow()
MOZ_ASSERT(iterator);
RootedValue completionException(cx_);
RootedSavedFrame completionExceptionStack(cx_);
if (cx_->isExceptionPending()) {
if (!GetAndClearException(cx_, &completionException))
if (!GetAndClearExceptionAndStack(cx_, &completionException, &completionExceptionStack)) {
completionException.setUndefined();
completionExceptionStack = nullptr;
}
}
// Steps 1-2 (implicit)
@ -172,7 +175,7 @@ ForOfIterator::closeThrow()
// Step 4.
if (returnVal.isUndefined()) {
cx_->setPendingException(completionException);
cx_->setPendingException(completionException, completionExceptionStack);
return;
}
@ -195,7 +198,7 @@ ForOfIterator::closeThrow()
}
// Step 6.
cx_->setPendingException(completionException);
cx_->setPendingException(completionException, completionExceptionStack);
// Steps 7-9 (skipped).
return;

View file

@ -10,7 +10,9 @@
#include "jsatominlines.h"
#include "jsscriptinlines.h"
#include "vm/ArrayObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/UnboxedObject-inl.h"
#include "vm/Stack-inl.h"
using namespace js;
@ -66,7 +68,7 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame
MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT);
Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
MOZ_ASSERT(!genObj->hasExpressionStack());
MOZ_ASSERT(!genObj->hasExpressionStack() || genObj->isExpressionStackEmpty());
MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync());
MOZ_ASSERT_IF(*pc == JSOP_YIELD,
genObj->callee().isStarGenerator() ||
@ -78,16 +80,33 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame
return false;
}
ArrayObject* stack = nullptr;
if (nvalues) {
do {
if (genObj->hasExpressionStack()) {
MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() == 0);
auto result = SetOrExtendAnyBoxedOrUnboxedDenseElements(cx,
&genObj->expressionStack().as<JSObject>(),
0, vp, nvalues, ShouldUpdateTypes::DontUpdate);
if (result == DenseElementResult::Success) {
MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() == nvalues);
break;
}
if (result == DenseElementResult::Failure)
return false;
}
stack = NewDenseCopiedArray(cx, nvalues, vp);
if (!stack)
return false;
} while (false);
}
uint32_t yieldAndAwaitIndex = GET_UINT24(pc);
genObj->setYieldAndAwaitIndex(yieldAndAwaitIndex);
genObj->setEnvironmentChain(*frame.environmentChain());
if (nvalues) {
ArrayObject* stack = NewDenseCopiedArray(cx, nvalues, vp);
if (!stack)
return false;
if (stack)
genObj->setExpressionStack(*stack);
}
return true;
}
@ -131,7 +150,7 @@ js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<Generato
HandleValue arg, uint32_t resumeKind)
{
if (resumeKind == GeneratorObject::THROW) {
cx->setPendingException(arg);
cx->setPendingExceptionAndCaptureStack(arg);
genObj->setRunning();
} else {
MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE);
@ -143,7 +162,8 @@ js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<Generato
MOZ_ASSERT(arg.isUndefined());
}
cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING));
cx->setPendingException(closing, nullptr);
genObj->setClosing();
}
return false;
@ -166,13 +186,13 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation,
if (genObj->hasArgsObj())
activation.regs().fp()->initArgsObj(genObj->argsObj());
if (genObj->hasExpressionStack()) {
uint32_t len = genObj->expressionStack().length();
if (genObj->hasExpressionStack() && !genObj->isExpressionStackEmpty()) {
uint32_t len = genObj->expressionStack().getDenseInitializedLength();
MOZ_ASSERT(activation.regs().spForStackDepth(len));
const Value* src = genObj->expressionStack().getDenseElements();
mozilla::PodCopy(activation.regs().sp, src, len);
activation.regs().sp += len;
genObj->clearExpressionStack();
genObj->expressionStack().setDenseInitializedLength(0);
}
JSScript* script = callee->nonLazyScript();

View file

@ -99,6 +99,9 @@ class GeneratorObject : public NativeObject
bool hasExpressionStack() const {
return getFixedSlot(EXPRESSION_STACK_SLOT).isObject();
}
bool isExpressionStackEmpty() const {
return expressionStack().getDenseInitializedLength() == 0;
}
ArrayObject& expressionStack() const {
return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as<ArrayObject>();
}

View file

@ -51,7 +51,7 @@ struct ProtoTableEntry {
namespace js {
#define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init,clasp) \
#define DECLARE_PROTOTYPE_CLASS_INIT(name,init,clasp) \
extern JSObject* init(JSContext* cx, Handle<JSObject*> obj);
JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT)
#undef DECLARE_PROTOTYPE_CLASS_INIT
@ -65,8 +65,8 @@ js::InitViaClassSpec(JSContext* cx, Handle<JSObject*> obj)
}
static const ProtoTableEntry protoTable[JSProto_LIMIT] = {
#define INIT_FUNC(name,code,init,clasp) { clasp, init },
#define INIT_FUNC_DUMMY(name,code,init,clasp) { nullptr, nullptr },
#define INIT_FUNC(name,init,clasp) { clasp, init },
#define INIT_FUNC_DUMMY(name,init,clasp) { nullptr, nullptr },
JS_FOR_PROTOTYPES(INIT_FUNC, INIT_FUNC_DUMMY)
#undef INIT_FUNC_DUMMY
#undef INIT_FUNC
@ -93,21 +93,20 @@ js::GlobalObject::getTypedObjectModule() const {
/* static */ bool
GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key)
{
if (key == JSProto_WebAssembly)
// Return true if the given constructor has been disabled at run-time.
switch (key) {
case JSProto_WebAssembly:
return !wasm::HasSupport(cx);
#ifdef ENABLE_SHARED_ARRAY_BUFFER
// Return true if the given constructor has been disabled at run-time.
switch (key) {
case JSProto_Atomics:
case JSProto_SharedArrayBuffer:
return !cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled();
#endif
default:
return false;
}
#else
return false;
#endif
}
/* static */ bool

View file

@ -1883,7 +1883,7 @@ CASE(EnableInterruptsPseudoOpcode)
goto error;
goto successful_return_continuation;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
goto error;
default:;
}
@ -1905,7 +1905,7 @@ CASE(EnableInterruptsPseudoOpcode)
goto error;
goto successful_return_continuation;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
goto error;
default:
break;
@ -3825,7 +3825,8 @@ CASE(JSOP_RETSUB)
* be necessary, but it seems clearer. And it points out a FIXME:
* 350509, due to Igor Bukanov.
*/
cx->setPendingException(rval);
ReservedRooted<Value> v(&rootValue0, rval);
cx->setPendingExceptionAndCaptureStack(v);
goto error;
}
MOZ_ASSERT(rval.isInt32());
@ -4331,7 +4332,7 @@ bool
js::Throw(JSContext* cx, HandleValue v)
{
MOZ_ASSERT(!cx->isExceptionPending());
cx->setPendingException(v);
cx->setPendingExceptionAndCaptureStack(v);
return false;
}
@ -4342,7 +4343,7 @@ js::ThrowingOperation(JSContext* cx, HandleValue v)
// execution instead of calling the (JIT) exception handler.
MOZ_ASSERT(!cx->isExceptionPending());
cx->setPendingException(v);
cx->setPendingExceptionAndCaptureStack(v);
return true;
}
@ -4548,16 +4549,25 @@ js::ThrowMsgOperation(JSContext* cx, const unsigned errorNum)
}
bool
js::GetAndClearException(JSContext* cx, MutableHandleValue res)
js::GetAndClearExceptionAndStack(JSContext* cx, MutableHandleValue res,
MutableHandleSavedFrame stack)
{
if (!cx->getPendingException(res))
return false;
stack.set(cx->getPendingExceptionStack());
cx->clearPendingException();
// Allow interrupting deeply nested exception handling.
return CheckForInterrupt(cx);
}
bool
js::GetAndClearException(JSContext* cx, MutableHandleValue res)
{
RootedSavedFrame stack(cx);
return GetAndClearExceptionAndStack(cx, res, &stack);
}
template <bool strict>
bool
js::DeletePropertyJit(JSContext* cx, HandleValue v, HandlePropertyName name, bool* bp)

View file

@ -489,6 +489,9 @@ ThrowMsgOperation(JSContext* cx, const unsigned errorNum);
bool
GetAndClearException(JSContext* cx, MutableHandleValue res);
bool
GetAndClearExceptionAndStack(JSContext* cx, MutableHandleValue res, MutableHandleSavedFrame stack);
bool
DeleteNameOperation(JSContext* cx, HandlePropertyName name, HandleObject scopeObj,
MutableHandleValue res);

View file

@ -242,6 +242,23 @@ NativeObject::getDenseOrTypedArrayElement(uint32_t idx)
return getDenseElement(idx);
}
/* static */ inline NativeObject*
NativeObject::createWithTemplate(JSContext* cx, gc::InitialHeap heap,
HandleObject templateObject)
{
RootedObjectGroup group(cx, templateObject->group());
RootedShape shape(cx, templateObject->as<NativeObject>().lastProperty());
gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
MOZ_ASSERT(CanBeFinalizedInBackground(kind, shape->getObjectClass()));
kind = gc::GetBackgroundAllocKind(kind);
JSObject* baseObj = create(cx, kind, heap, shape, group);
if (!baseObj)
return nullptr;
return &baseObj->as<NativeObject>();
}
/* static */ inline NativeObject*
NativeObject::copy(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
HandleNativeObject templateObject)

View file

@ -1675,6 +1675,41 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyN
return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs);
}
bool
js::NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj, HandleId id, HandleValue value,
unsigned attrs, ObjectOpResult& result)
{
Rooted<PropertyDescriptor> desc(cx);
desc.initFields(nullptr, value, attrs, nullptr, nullptr);
return NativeDefineProperty(cx, obj, id, desc, result);
}
bool
js::NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj, HandleId id, HandleValue value,
unsigned attrs)
{
ObjectOpResult result;
if (!NativeDefineDataProperty(cx, obj, id, value, attrs, result)) {
return false;
}
if (!result) {
// Off-thread callers should not get here: they must call this
// function only with known-valid arguments. Populating a new
// PlainObject with configurable properties is fine.
MOZ_ASSERT(!cx->isHelperThreadContext());
result.reportError(cx, obj, id);
return false;
}
return true;
}
bool
js::NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj, PropertyName* name, HandleValue value,
unsigned attrs)
{
RootedId id(cx, NameToId(name));
return NativeDefineDataProperty(cx, obj, id, value, attrs);
}
/*** [[HasProperty]] *****************************************************************************/

View file

@ -485,6 +485,9 @@ class NativeObject : public ShapedObject
return cells && cells->hasCell(cell);
}
static inline NativeObject*
createWithTemplate(JSContext* cx, js::gc::InitialHeap heap, HandleObject templateObject);
protected:
#ifdef DEBUG
void checkShapeConsistency();
@ -1399,6 +1402,18 @@ NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName*
HandleValue value, JSGetterOp getter, JSSetterOp setter,
unsigned attrs);
bool
NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj, HandleId id, HandleValue value,
unsigned attrs, ObjectOpResult& result);
extern bool
NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj, HandleId id,
HandleValue value, unsigned attrs);
extern bool
NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj, PropertyName* name,
HandleValue value, unsigned attrs);
extern bool
NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp);

View file

@ -970,6 +970,7 @@ RegExpShared::trace(JSTracer* trc)
TraceNullableEdge(trc, &source, "RegExpShared source");
for (auto& comp : compilationArray)
TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code");
TraceNullableEdge(trc, &groupsTemplate_, "RegExpShared groupsTemplate");
}
bool

View file

@ -548,7 +548,7 @@ InvokeInterruptCallback(JSContext* cx)
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
return false;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
return false;
default:;
}

View file

@ -169,7 +169,7 @@ struct JSAtomState
#define PROPERTYNAME_FIELD(idpart, id, text) js::ImmutablePropertyNamePtr id;
FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD)
#undef PROPERTYNAME_FIELD
#define PROPERTYNAME_FIELD(name, code, init, clasp) js::ImmutablePropertyNamePtr name;
#define PROPERTYNAME_FIELD(name, init, clasp) js::ImmutablePropertyNamePtr name;
JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
#undef PROPERTYNAME_FIELD
#define PROPERTYNAME_FIELD(name) js::ImmutablePropertyNamePtr name;

View file

@ -601,23 +601,6 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_SetPrototype(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[1].isObjectOrNull());
RootedObject obj(cx, &args[0].toObject());
RootedObject proto(cx, args[1].toObjectOrNull());
if (!SetPrototype(cx, obj, proto))
return false;
args.rval().setUndefined();
return true;
}
/*
* Used to decompile values in the nearest non-builtin stack frame, falling
* back to decompiling in the current frame. Helpful for printing higher-order
@ -768,34 +751,6 @@ intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Value*
return true;
}
/**
* Intrinsic for creating an empty array in the compartment of the object
* passed as the first argument.
*
* Returns the array, wrapped in the default wrapper to use between the two
* compartments.
*/
static bool
intrinsic_NewArrayInCompartment(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedObject wrapped(cx, &args[0].toObject());
MOZ_ASSERT(IsWrapper(wrapped));
RootedObject obj(cx, UncheckedUnwrap(wrapped));
RootedArrayObject arr(cx);
{
AutoCompartment ac(cx, obj);
arr = NewDenseEmptyArray(cx);
if (!arr)
return false;
}
args.rval().setObject(*arr);
return wrapped->compartment()->wrap(cx, args.rval());
}
static bool
intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp)
{
@ -2189,8 +2144,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("std_Math_max", math_max, 2,0, MathMax),
JS_INLINABLE_FN("std_Math_min", math_min, 2,0, MathMin),
JS_INLINABLE_FN("std_Math_abs", math_abs, 1,0, MathAbs),
JS_INLINABLE_FN("std_Math_imul", math_imul, 2,0, MathImul),
JS_INLINABLE_FN("std_Math_log2", math_log2, 1,0, MathLog2),
JS_FN("std_Map_has", MapObject::has, 1,0),
JS_FN("std_Map_iterator", MapObject::entries, 0,0),
@ -2203,7 +2156,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("std_Object_getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0),
JS_FN("std_Object_hasOwnProperty", obj_hasOwnProperty, 1,0),
JS_FN("std_Object_setPrototypeOf", intrinsic_SetPrototype, 2,0),
JS_FN("std_Object_toString", obj_toString, 0,0),
JS_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,0),
@ -2265,7 +2217,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("IsCallable", intrinsic_IsCallable, 1,0, IntrinsicIsCallable),
JS_INLINABLE_FN("IsConstructor", intrinsic_IsConstructor, 1,0,
IntrinsicIsConstructor),
JS_FN("IsFunctionObject",intrinsic_IsInstanceOfBuiltin<JSFunction>, 1,0),
JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1,0),
JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2,0),
JS_FN("_ConstructFunction", intrinsic_ConstructFunction, 2,0),
@ -2306,8 +2257,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("UnsafeGetBooleanFromReservedSlot", intrinsic_UnsafeGetBooleanFromReservedSlot,2,0,
IntrinsicUnsafeGetBooleanFromReservedSlot),
JS_FN("NewArrayInCompartment", intrinsic_NewArrayInCompartment, 1,0),
JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0),
JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0),
@ -2444,10 +2393,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("CallWeakSetMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
JS_FN("Promise_static_resolve", Promise_static_resolve, 1, 0),
JS_FN("Promise_static_reject", Promise_reject, 1, 0),
JS_FN("Promise_then", Promise_then, 2, 0),
// See builtin/TypedObject.h for descriptors of the typedobj functions.
JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0),

View file

@ -620,6 +620,11 @@ class ConstraintTypeSet : public TypeSet
*/
void addType(ExclusiveContext* cx, Type type);
/* Generalize to any type. */
void makeUnknown(ExclusiveContext* cx) {
addType(cx, UnknownType());
}
// Trigger a post barrier when writing to this set, if necessary.
// addType(cx, type) takes care of this automatically.
void postWriteBarrier(ExclusiveContext* cx, Type type);

View file

@ -1063,65 +1063,28 @@ TypedArrayObjectTemplate<T>::AllocateArrayBuffer(JSContext* cx, HandleValue ctor
}
static bool
IsArrayBufferConstructor(const Value& v)
IsArrayBufferSpecies(JSContext* cx, JSFunction* species)
{
return v.isObject() &&
v.toObject().is<JSFunction>() &&
v.toObject().as<JSFunction>().isNative() &&
v.toObject().as<JSFunction>().native() == ArrayBufferObject::class_constructor;
return IsSelfHostedFunctionWithName(species, cx->names().ArrayBufferSpecies);
}
static bool
IsArrayBufferSpecies(JSContext* cx, HandleObject origBuffer)
{
RootedValue ctor(cx);
if (!GetPropertyPure(cx, origBuffer, NameToId(cx->names().constructor), ctor.address()))
return false;
if (!IsArrayBufferConstructor(ctor))
return false;
RootedObject ctorObj(cx, &ctor.toObject());
RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
JSFunction* getter;
if (!GetGetterPure(cx, ctorObj, speciesId, &getter))
return false;
if (!getter)
return false;
return IsSelfHostedFunctionWithName(getter, cx->names().ArrayBufferSpecies);
}
static bool
static JSObject*
GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped,
SpeciesConstructorOverride override, MutableHandleValue ctor)
SpeciesConstructorOverride override)
{
if (!isWrapped) {
if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
return false;
RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
// The second disjunct is an optimization.
if (override == SpeciesConstructorOverride::ArrayBuffer || IsArrayBufferSpecies(cx, obj))
ctor.set(defaultCtor);
else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
return false;
if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
return nullptr;
RootedObject defaultCtor(cx, &cx->global()->getConstructor(JSProto_ArrayBuffer).toObject());
return true;
}
// Use the current global's ArrayBuffer if the override is set.
if (override == SpeciesConstructorOverride::ArrayBuffer)
return defaultCtor;
{
JSAutoCompartment ac(cx, obj);
if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
return false;
RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
if (override == SpeciesConstructorOverride::ArrayBuffer)
ctor.set(defaultCtor);
else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
return false;
}
RootedObject wrappedObj(cx, obj);
if (isWrapped && !cx->compartment()->wrap(cx, &wrappedObj))
return nullptr;
return JS_WrapValue(cx, ctor);
return SpeciesConstructor(cx, wrappedObj, defaultCtor, IsArrayBufferSpecies);
}
// ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 24.1.1.4.
@ -1137,9 +1100,10 @@ TypedArrayObjectTemplate<T>::CloneArrayBufferNoCopy(JSContext* cx,
// Step 1 (skipped).
// Step 2.a.
RootedValue cloneCtor(cx);
if (!GetSpeciesConstructor(cx, srcBuffer, isWrapped, override, &cloneCtor))
JSObject* ctorObj = GetSpeciesConstructor(cx, srcBuffer, isWrapped, override);
if (!ctorObj)
return false;
RootedValue cloneCtor(cx, ObjectValue(*ctorObj));
// Step 2.b.
if (srcBuffer->isDetached()) {
@ -1181,7 +1145,8 @@ TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
return fromObject(cx, other, newTarget);
}
// ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 22.2.4.3.
// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
// 22.2.4.3 TypedArray ( typedArray )
template<typename T>
/* static */ JSObject*
TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped,
@ -1229,61 +1194,62 @@ TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, b
return nullptr;
}
// Steps 10.
// Step 9.
uint32_t elementLength = srcArray->length();
// Steps 11-12.
// Steps 10-11.
Scalar::Type srcType = srcArray->type();
// Step 13 (skipped).
// Step 12 (skipped).
// Step 14.
// Step 13.
uint32_t srcByteOffset = srcArray->byteOffset();
// Step 17, modified for SharedArrayBuffer.
// Steps 16-17.
bool isShared = srcArray->isSharedMemory();
SpeciesConstructorOverride override = isShared ? SpeciesConstructorOverride::ArrayBuffer
: SpeciesConstructorOverride::None;
// Steps 8-9, 17.
// Steps 8, 16-17.
Rooted<ArrayBufferObject*> buffer(cx);
if (ArrayTypeID() == srcType) {
// Step 17.a.
// Step 16.a.
uint32_t srcLength = srcArray->byteLength();
// Step 17.b, modified for SharedArrayBuffer
// Steps 16.b-c.
if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, srcLength, override,
&buffer))
{
return nullptr;
}
} else {
// Step 18.a, modified for SharedArrayBuffer
RootedValue bufferCtor(cx);
if (!GetSpeciesConstructor(cx, srcData, isWrapped, override, &bufferCtor))
// Steps 17.a-b.
JSObject* ctorObj = GetSpeciesConstructor(cx, srcData, isWrapped, override);
if (!ctorObj)
return nullptr;
RootedValue bufferCtor(cx, ObjectValue(*ctorObj));
// Step 15-16, 18.b.
// Steps 14-15, 17.c.
if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, BYTES_PER_ELEMENT, &buffer))
return nullptr;
// Step 18.c.
// Step 17.d.
if (srcArray->hasDetachedBuffer()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return nullptr;
}
}
// Steps 3, 4 (remaining part), 19-22.
// Steps 3-4 (remaining part), 18-21.
Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, elementLength, proto));
if (!obj)
return nullptr;
// Step 18.d-g or 24.1.1.4 step 11.
// Steps 17.e-h or 24.1.1.4 step 8.
if (!TypedArrayMethods<TypedArrayObject>::setFromTypedArray(cx, obj, srcArray))
return nullptr;
// Step 23.
// Step 22.
return obj;
}

View file

@ -1729,7 +1729,8 @@ RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise)
return false;
RootedValue rejectionValue(cx);
if (!GetAndClearException(cx, &rejectionValue))
RootedSavedFrame stack(cx);
if (!GetAndClearExceptionAndStack(cx, &rejectionValue, &stack))
return false;
return PromiseObject::reject(cx, promise, rejectionValue);

View file

@ -2310,7 +2310,7 @@ nsXPCComponents_Utils::ReportError(HandleValue error, JSContext* cx)
if (errorObj) {
JS::RootedObject stackVal(cx,
FindExceptionStackForConsoleReport(win, error));
FindExceptionStackForConsoleReport(win, error, nullptr));
if (stackVal) {
scripterr = new nsScriptErrorWithStack(stackVal);
}

View file

@ -590,11 +590,13 @@ class ErrorReport : public ErrorBase {
void
DispatchScriptErrorEvent(nsPIDOMWindowInner* win, JS::RootingContext* rootingCx,
xpc::ErrorReport* xpcReport, JS::Handle<JS::Value> exception);
xpc::ErrorReport* xpcReport, JS::Handle<JS::Value> exception,
JS::Handle<JSObject*> exceptionStack);
// Get a stack of the sort that can be passed to
// xpc::ErrorReport::LogToConsoleWithStack from the given exception value. Can
// return null if the exception value doesn't have an associated stack. The
// be nullptr if the exception value doesn't have an associated stack, and if
// there is no stack supplied by the JS engine in exceptionStack. The
// returned stack, if any, may also not be in the same compartment as
// exceptionValue.
//
@ -605,7 +607,8 @@ DispatchScriptErrorEvent(nsPIDOMWindowInner* win, JS::RootingContext* rootingCx,
// the stack in the console message keeping the window alive.
JSObject*
FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
JS::HandleValue exceptionValue);
JS::HandleValue exceptionValue,
JS::HandleObject exceptionStack);
// Return a name for the compartment.
// This function makes reasonable efforts to make this name both mostly human-readable