mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-19 14:57:32 +09:00
Compare commits
No commits in common. "main" and "13.2-release" have entirely different histories.
main
...
13.2-relea
512 changed files with 641 additions and 212495 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
<img src="browser/branding/unofficial/content/about-wordmark.svg" alt="Dactyloidae web browser" height="60">
|
<img src="browser/branding/unofficial/content/about-wordmark.svg" alt="Dactyloidae web browser" height="60">
|
||||||
<br>
|
<br>
|
||||||
<a href="https://discord.gg/ycmQAMej77">Official Discord server</a>
|
<a href="https://discord.gg/ecx">Official Discord server</a>
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="docs/readme/demo.png" height="500">
|
<img src="https://dactyloidae.xyz/demo.jpg" height="500">
|
||||||
|
|
||||||
Dactyloidae is a heavily modified fork of Eclipse Hydra (fork of roytam1's Serpent, which is a fork of Basilisk, which is a fork of Firefox).
|
Dactyloidae is a heavily modified fork of Eclipse Hydra (fork of roytam1's Serpent, which is a fork of Basilisk, which is a fork of Firefox).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,9 @@ pref("@GUAO_PREF@.mewe.com", "Mozilla/5.0 (%OS_SLICE% rv:102.0) Gecko/20100101 F
|
||||||
|
|
||||||
// UA-sniffing domains that are "app/vendor-specific" and do not like Dactyloidae
|
// UA-sniffing domains that are "app/vendor-specific" and do not like Dactyloidae
|
||||||
pref("@GUAO_PREF@.web.whatsapp.com","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36");
|
pref("@GUAO_PREF@.web.whatsapp.com","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36");
|
||||||
pref("@GUAO_PREF@.youtube.com","Mozilla/5.0 (%OS_SLICE%; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0");
|
pref("@GUAO_PREF@.youtube.com","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36");
|
||||||
pref("@GUAO_PREF@.studio.youtube.com","Mozilla/5.0 (%OS_SLICE%; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0");
|
pref("@GUAO_PREF@.studio.youtube.com","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36");
|
||||||
pref("@GUAO_PREF@.gaming.youtube.com","Mozilla/5.0 (%OS_SLICE%; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0");
|
pref("@GUAO_PREF@.gaming.youtube.com","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36");
|
||||||
|
|
||||||
// The following domains do not like the Goanna slice
|
// The following domains do not like the Goanna slice
|
||||||
pref("@GUAO_PREF@.bab.la","Mozilla/5.0 (%OS_SLICE% rv:@GK_VERSION@) @GK_SLICE@ @FX_SLICE@");
|
pref("@GUAO_PREF@.bab.la","Mozilla/5.0 (%OS_SLICE% rv:@GK_VERSION@) @GK_SLICE@ @FX_SLICE@");
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
52.13.2.1
|
52.13.2
|
||||||
|
|
@ -1 +1 @@
|
||||||
13.2.1
|
13.2
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 898 KiB |
|
|
@ -138,207 +138,6 @@ StructuredCloneCallbacksError(JSContext* aCx,
|
||||||
NS_WARNING("Failed to clone data.");
|
NS_WARNING("Failed to clone data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SameThreadStreamTransferData
|
|
||||||
{
|
|
||||||
SameThreadStreamTransferData(JSContext* aCx, JS::HandleObject aRecord)
|
|
||||||
: mRecord(aCx, aRecord)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~SameThreadStreamTransferData()
|
|
||||||
{
|
|
||||||
mRecord.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::PersistentRootedObject mRecord;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
CallStreamTransferHelper(JSContext* aCx,
|
|
||||||
const char* aHelperName,
|
|
||||||
JS::HandleValue aArgument,
|
|
||||||
JS::MutableHandleValue aResult)
|
|
||||||
{
|
|
||||||
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
|
||||||
if (!global) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the stream extras lazily before looking up the internal helper.
|
|
||||||
JS::Rooted<JS::Value> ignored(aCx);
|
|
||||||
if (!JS_GetProperty(aCx, global, "WritableStream", &ignored)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> helper(aCx);
|
|
||||||
if (!JS_GetProperty(aCx, global, aHelperName, &helper)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!helper.isObject() || !JS::IsCallable(&helper.toObject())) {
|
|
||||||
aResult.setUndefined();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> argument(aCx, aArgument);
|
|
||||||
if (!JS_WrapValue(aCx, &argument)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return JS::Call(aCx, JS::UndefinedHandleValue, helper,
|
|
||||||
JS::HandleValueArray(argument), aResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
TryWriteSameThreadStreamTransfer(JSContext* aCx,
|
|
||||||
JS::Handle<JSObject*> aObj,
|
|
||||||
const char* aHelperName,
|
|
||||||
uint32_t aTransferTag,
|
|
||||||
uint32_t* aTag,
|
|
||||||
JS::TransferableOwnership* aOwnership,
|
|
||||||
void** aContent,
|
|
||||||
uint64_t* aExtraData,
|
|
||||||
bool* aHandled)
|
|
||||||
{
|
|
||||||
*aHandled = false;
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> argument(aCx, JS::ObjectValue(*aObj));
|
|
||||||
JS::Rooted<JS::Value> transferRecord(aCx);
|
|
||||||
if (!CallStreamTransferHelper(aCx, aHelperName, argument, &transferRecord)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transferRecord.isUndefined()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!transferRecord.isObject()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::Rooted<JSObject*> record(aCx, &transferRecord.toObject());
|
|
||||||
SameThreadStreamTransferData* data =
|
|
||||||
new SameThreadStreamTransferData(aCx, record);
|
|
||||||
|
|
||||||
*aTag = aTransferTag;
|
|
||||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
|
||||||
*aContent = data;
|
|
||||||
*aExtraData = 0;
|
|
||||||
*aHandled = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
TryWriteReadableStreamPortTransfer(JSContext* aCx,
|
|
||||||
JS::Handle<JSObject*> aObj,
|
|
||||||
nsTArray<MessagePortIdentifier>& aPortIdentifiers,
|
|
||||||
uint32_t* aTag,
|
|
||||||
JS::TransferableOwnership* aOwnership,
|
|
||||||
void** aContent,
|
|
||||||
uint64_t* aExtraData,
|
|
||||||
bool* aHandled)
|
|
||||||
{
|
|
||||||
*aHandled = false;
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> argument(aCx, JS::ObjectValue(*aObj));
|
|
||||||
JS::Rooted<JS::Value> transferPortValue(aCx);
|
|
||||||
if (!CallStreamTransferHelper(aCx, "__uxpTransferReadableStreamPort",
|
|
||||||
argument, &transferPortValue)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transferPortValue.isUndefined()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!transferPortValue.isObject()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::Rooted<JSObject*> transferPortObj(aCx, &transferPortValue.toObject());
|
|
||||||
MessagePort* port = nullptr;
|
|
||||||
nsresult rv = UNWRAP_OBJECT(MessagePort, &transferPortObj, port);
|
|
||||||
if (NS_FAILED(rv) || !port) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*aExtraData = aPortIdentifiers.Length();
|
|
||||||
MessagePortIdentifier* identifier = aPortIdentifiers.AppendElement();
|
|
||||||
port->CloneAndDisentangle(*identifier);
|
|
||||||
|
|
||||||
*aTag = SCTAG_DOM_TRANSFERRED_READABLESTREAM;
|
|
||||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
|
||||||
*aContent = nullptr;
|
|
||||||
*aHandled = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ReadSameThreadStreamTransfer(JSContext* aCx,
|
|
||||||
void* aContent,
|
|
||||||
const char* aHelperName,
|
|
||||||
JS::MutableHandleObject aReturnObject)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aContent);
|
|
||||||
SameThreadStreamTransferData* data =
|
|
||||||
static_cast<SameThreadStreamTransferData*>(aContent);
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> record(aCx, JS::ObjectValue(*data->mRecord));
|
|
||||||
JS::Rooted<JS::Value> result(aCx);
|
|
||||||
if (!CallStreamTransferHelper(aCx, aHelperName, record, &result)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.isObject()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
aReturnObject.set(&result.toObject());
|
|
||||||
delete data;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ReadReadableStreamPortTransfer(JSContext* aCx,
|
|
||||||
nsISupports* aParent,
|
|
||||||
nsTArray<MessagePortIdentifier>& aPortIdentifiers,
|
|
||||||
uint64_t aExtraData,
|
|
||||||
JS::MutableHandleObject aReturnObject)
|
|
||||||
{
|
|
||||||
if (aExtraData >= aPortIdentifiers.Length()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
|
|
||||||
|
|
||||||
ErrorResult rv;
|
|
||||||
RefPtr<MessagePort> port =
|
|
||||||
MessagePort::Create(global, aPortIdentifiers[aExtraData], rv);
|
|
||||||
if (NS_WARN_IF(rv.Failed())) {
|
|
||||||
rv.SuppressException();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> portValue(aCx);
|
|
||||||
if (!GetOrCreateDOMReflector(aCx, port, &portValue)) {
|
|
||||||
JS_ClearPendingException(aCx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> result(aCx);
|
|
||||||
if (!CallStreamTransferHelper(aCx,
|
|
||||||
"__uxpReceiveReadableStreamTransferFromPort",
|
|
||||||
portValue, &result)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.isObject()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
aReturnObject.set(&result.toObject());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
|
const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
|
||||||
|
|
@ -1576,35 +1375,6 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread) {
|
|
||||||
if (aTag == SCTAG_DOM_TRANSFERRED_WRITABLESTREAM) {
|
|
||||||
return ReadSameThreadStreamTransfer(aCx, aContent,
|
|
||||||
"__uxpReceiveWritableStreamTransfer",
|
|
||||||
aReturnObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aTag == SCTAG_DOM_TRANSFERRED_READABLESTREAM) {
|
|
||||||
return ReadSameThreadStreamTransfer(aCx, aContent,
|
|
||||||
"__uxpReceiveReadableStreamTransfer",
|
|
||||||
aReturnObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aTag == SCTAG_DOM_TRANSFERRED_TRANSFORMSTREAM) {
|
|
||||||
return ReadSameThreadStreamTransfer(aCx, aContent,
|
|
||||||
"__uxpReceiveTransformStreamTransfer",
|
|
||||||
aReturnObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread ||
|
|
||||||
mStructuredCloneScope == StructuredCloneScope::DifferentProcess) &&
|
|
||||||
aTag == SCTAG_DOM_TRANSFERRED_READABLESTREAM) {
|
|
||||||
MOZ_ASSERT(!aContent);
|
|
||||||
return ReadReadableStreamPortTransfer(aCx, mParent, mPortIdentifiers,
|
|
||||||
aExtraData,
|
|
||||||
aReturnObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1673,55 +1443,6 @@ StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread) {
|
|
||||||
bool handled = false;
|
|
||||||
if (!TryWriteSameThreadStreamTransfer(aCx, obj,
|
|
||||||
"__uxpTransferWritableStream",
|
|
||||||
SCTAG_DOM_TRANSFERRED_WRITABLESTREAM,
|
|
||||||
aTag, aOwnership, aContent,
|
|
||||||
aExtraData, &handled)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (handled) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteSameThreadStreamTransfer(aCx, obj,
|
|
||||||
"__uxpTransferReadableStream",
|
|
||||||
SCTAG_DOM_TRANSFERRED_READABLESTREAM,
|
|
||||||
aTag, aOwnership, aContent,
|
|
||||||
aExtraData, &handled)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (handled) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteSameThreadStreamTransfer(aCx, obj,
|
|
||||||
"__uxpTransferTransformStream",
|
|
||||||
SCTAG_DOM_TRANSFERRED_TRANSFORMSTREAM,
|
|
||||||
aTag, aOwnership, aContent,
|
|
||||||
aExtraData, &handled)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (handled) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread ||
|
|
||||||
mStructuredCloneScope == StructuredCloneScope::DifferentProcess) {
|
|
||||||
bool handled = false;
|
|
||||||
if (!TryWriteReadableStreamPortTransfer(aCx, obj, mPortIdentifiers,
|
|
||||||
aTag, aOwnership, aContent,
|
|
||||||
aExtraData, &handled)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (handled) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1759,26 +1480,6 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
|
||||||
delete data;
|
delete data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((aTag == SCTAG_DOM_TRANSFERRED_WRITABLESTREAM ||
|
|
||||||
aTag == SCTAG_DOM_TRANSFERRED_READABLESTREAM ||
|
|
||||||
aTag == SCTAG_DOM_TRANSFERRED_TRANSFORMSTREAM) &&
|
|
||||||
mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread) {
|
|
||||||
MOZ_ASSERT(aContent);
|
|
||||||
SameThreadStreamTransferData* data =
|
|
||||||
static_cast<SameThreadStreamTransferData*>(aContent);
|
|
||||||
delete data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aTag == SCTAG_DOM_TRANSFERRED_READABLESTREAM &&
|
|
||||||
(mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread ||
|
|
||||||
mStructuredCloneScope == StructuredCloneScope::DifferentProcess)) {
|
|
||||||
MOZ_ASSERT(!aContent);
|
|
||||||
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
|
|
||||||
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,6 @@ enum StructuredCloneTags {
|
||||||
// This tag is used by both main thread and workers.
|
// This tag is used by both main thread and workers.
|
||||||
SCTAG_DOM_URLSEARCHPARAMS,
|
SCTAG_DOM_URLSEARCHPARAMS,
|
||||||
|
|
||||||
// Same-thread transferable stream records. These are not supported by IDB.
|
|
||||||
SCTAG_DOM_TRANSFERRED_READABLESTREAM,
|
|
||||||
SCTAG_DOM_TRANSFERRED_WRITABLESTREAM,
|
|
||||||
SCTAG_DOM_TRANSFERRED_TRANSFORMSTREAM,
|
|
||||||
|
|
||||||
// When adding a new tag for IDB, please don't add it to the end of the list!
|
// When adding a new tag for IDB, please don't add it to the end of the list!
|
||||||
// Tags that are supported by IDB must not ever change. See the static assert
|
// Tags that are supported by IDB must not ever change. See the static assert
|
||||||
// in IDBObjectStore.cpp, method CommonStructuredCloneReadCallback.
|
// in IDBObjectStore.cpp, method CommonStructuredCloneReadCallback.
|
||||||
|
|
|
||||||
|
|
@ -127,29 +127,7 @@ void
|
||||||
nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
|
nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
|
||||||
const nsTArray<nsString>& aTokens)
|
const nsTArray<nsString>& aTokens)
|
||||||
{
|
{
|
||||||
if (!mElement || aTokens.IsEmpty()) {
|
if (!mElement) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hot path: single-token classList.add() when the class is already present.
|
|
||||||
// Skip attribute rebuild and SetAttr entirely (avoids mutation observers /
|
|
||||||
// style invalidation). Frameworks hit this constantly.
|
|
||||||
if (aTokens.Length() == 1) {
|
|
||||||
const nsString& token = aTokens[0];
|
|
||||||
if (aAttr && aAttr->Contains(token)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString resultStr;
|
|
||||||
if (aAttr) {
|
|
||||||
aAttr->ToString(resultStr);
|
|
||||||
if (!resultStr.IsEmpty() &&
|
|
||||||
!nsContentUtils::IsHTMLWhitespace(resultStr.Last())) {
|
|
||||||
resultStr.Append(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultStr.Append(token);
|
|
||||||
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,11 +161,6 @@ nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
|
||||||
addedClasses.AppendElement(aToken);
|
addedClasses.AppendElement(aToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No new tokens: leave the attribute untouched (do not create class="").
|
|
||||||
if (!oneWasAdded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
|
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,37 +190,20 @@ nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aAttr, "Need an attribute");
|
MOZ_ASSERT(aAttr, "Need an attribute");
|
||||||
|
|
||||||
if (aTokens.IsEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hot path: single-token classList.remove() when the class is absent.
|
|
||||||
if (aTokens.Length() == 1 && !aAttr->Contains(aTokens[0])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString input;
|
nsAutoString input;
|
||||||
aAttr->ToString(input);
|
aAttr->ToString(input);
|
||||||
|
|
||||||
WhitespaceTokenizer tokenizer(input);
|
WhitespaceTokenizer tokenizer(input);
|
||||||
nsAutoString output;
|
nsAutoString output;
|
||||||
bool removed = false;
|
|
||||||
|
|
||||||
while (tokenizer.hasMoreTokens()) {
|
while (tokenizer.hasMoreTokens()) {
|
||||||
auto& currentToken = tokenizer.nextToken();
|
auto& currentToken = tokenizer.nextToken();
|
||||||
if (aTokens.Contains(currentToken)) {
|
if (!aTokens.Contains(currentToken)) {
|
||||||
removed = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!output.IsEmpty()) {
|
if (!output.IsEmpty()) {
|
||||||
output.Append(char16_t(' '));
|
output.Append(char16_t(' '));
|
||||||
}
|
}
|
||||||
output.Append(currentToken);
|
output.Append(currentToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token set unchanged: skip SetAttr to avoid style/mutation work.
|
|
||||||
if (!removed) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, output, true);
|
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, output, true);
|
||||||
|
|
@ -346,16 +302,6 @@ nsDOMTokenList::ReplaceInternal(const nsAttrValue* aAttr,
|
||||||
const nsAString& aToken,
|
const nsAString& aToken,
|
||||||
const nsAString& aNewToken)
|
const nsAString& aNewToken)
|
||||||
{
|
{
|
||||||
// Replacing a token with itself cannot change the token set.
|
|
||||||
if (aToken.Equals(aNewToken)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Old token absent: no update (and avoid rewriting whitespace).
|
|
||||||
if (!aAttr->Contains(aToken)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString attribute;
|
nsAutoString attribute;
|
||||||
aAttr->ToString(attribute);
|
aAttr->ToString(attribute);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1929,9 +1929,6 @@ public:
|
||||||
{
|
{
|
||||||
ProcessGlobal* global = ProcessGlobal::Get();
|
ProcessGlobal* global = ProcessGlobal::Get();
|
||||||
MOZ_ASSERT(!aRunInGlobalScope);
|
MOZ_ASSERT(!aRunInGlobalScope);
|
||||||
if (NS_WARN_IF(!global)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
global->LoadScript(aURL);
|
global->LoadScript(aURL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2927,35 +2927,6 @@ FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot,
|
||||||
// Actually find elements matching aSelectorList (which must not be
|
// Actually find elements matching aSelectorList (which must not be
|
||||||
// null) and which are descendants of aRoot and put them in aList. If
|
// null) and which are descendants of aRoot and put them in aList. If
|
||||||
// onlyFirstMatch, then stop once the first one is found.
|
// onlyFirstMatch, then stop once the first one is found.
|
||||||
template<bool onlyFirstMatch, class Collector, class T>
|
|
||||||
static void
|
|
||||||
FindMatchingElementsWithClass(nsINode* aRoot, nsIAtom* aClass,
|
|
||||||
nsCaseTreatment aCaseTreatment, T& aList)
|
|
||||||
{
|
|
||||||
Collector results;
|
|
||||||
for (nsIContent* cur = aRoot->GetFirstChild(); cur;
|
|
||||||
cur = cur->GetNextNode(aRoot)) {
|
|
||||||
if (!cur->IsElement()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const nsAttrValue* classes = cur->AsElement()->GetClasses();
|
|
||||||
if (classes && classes->Contains(aClass, aCaseTreatment)) {
|
|
||||||
if (onlyFirstMatch) {
|
|
||||||
aList.AppendElement(cur->AsElement());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
results.AppendElement(cur->AsElement());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const uint32_t len = results.Length();
|
|
||||||
if (len) {
|
|
||||||
aList.SetCapacity(len);
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
aList.AppendElement(results.ElementAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool onlyFirstMatch, class Collector, class T>
|
template<bool onlyFirstMatch, class Collector, class T>
|
||||||
MOZ_ALWAYS_INLINE static void
|
MOZ_ALWAYS_INLINE static void
|
||||||
FindMatchingElements(nsINode* aRoot, nsCSSSelectorList* aSelectorList, T &aList,
|
FindMatchingElements(nsINode* aRoot, nsCSSSelectorList* aSelectorList, T &aList,
|
||||||
|
|
@ -2963,23 +2934,6 @@ FindMatchingElements(nsINode* aRoot, nsCSSSelectorList* aSelectorList, T &aList,
|
||||||
{
|
{
|
||||||
nsIDocument* doc = aRoot->OwnerDoc();
|
nsIDocument* doc = aRoot->OwnerDoc();
|
||||||
|
|
||||||
// Parsed selectors are already cached by the document. A lone class needs
|
|
||||||
// only an atom lookup per element, not the general CSS matching context.
|
|
||||||
nsCSSSelector* selector = aSelectorList->mSelectors;
|
|
||||||
if (!aSelectorList->mNext && !selector->mNext &&
|
|
||||||
selector->mClassList && !selector->mClassList->mNext &&
|
|
||||||
!selector->mLowercaseTag && !selector->mIDList &&
|
|
||||||
!selector->mAttrList && !selector->mPseudoClassList &&
|
|
||||||
!selector->mNegations && selector->mNameSpace == kNameSpaceID_Unknown &&
|
|
||||||
selector->IsRestrictedSelector() && !selector->IsHybridPseudoElement()) {
|
|
||||||
nsCaseTreatment caseTreatment =
|
|
||||||
doc->GetCompatibilityMode() == eCompatibility_NavQuirks
|
|
||||||
? eIgnoreCase : eCaseMatters;
|
|
||||||
FindMatchingElementsWithClass<onlyFirstMatch, Collector>(
|
|
||||||
aRoot, selector->mClassList->mAtom, caseTreatment, aList);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
|
TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
|
||||||
doc, TreeMatchContext::eNeverMatchVisited);
|
doc, TreeMatchContext::eNeverMatchVisited);
|
||||||
doc->FlushPendingLinkUpdates();
|
doc->FlushPendingLinkUpdates();
|
||||||
|
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
function checkClassSelectors(parse, equal) {
|
|
||||||
function check(root, selector, expected) {
|
|
||||||
var all = root.querySelectorAll(selector);
|
|
||||||
equal(all.length, expected.length, selector + " count");
|
|
||||||
for (var i = 0; i < expected.length; i++) {
|
|
||||||
equal(all[i].id, expected[i], selector + " order " + i);
|
|
||||||
}
|
|
||||||
equal(root.querySelector(selector), all.length ? all[0] : null,
|
|
||||||
selector + " first match");
|
|
||||||
}
|
|
||||||
|
|
||||||
var doc = parse('<!doctype html><html><body>' +
|
|
||||||
'<section id="root" class="toggle"><div id="a" class="toggle completed">' +
|
|
||||||
'<span id="b" class="other toggle"></span></div>' +
|
|
||||||
'<div id="c" class="TOGGLE"></div><button id="d" class="toggle\t extra"></button>' +
|
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg"><g id="svg" class="toggle"/></svg>' +
|
|
||||||
'</section></body></html>', 'text/html');
|
|
||||||
var root = doc.getElementById('root');
|
|
||||||
for (var repeat = 0; repeat < 10; repeat++) {
|
|
||||||
check(root, '.toggle', ['a', 'b', 'd', 'svg']);
|
|
||||||
check(root, '*|*.toggle', ['a', 'b', 'd', 'svg']);
|
|
||||||
check(root, '.t\\6f ggle', ['a', 'b', 'd', 'svg']);
|
|
||||||
check(root, '.TOGGLE', ['c']);
|
|
||||||
check(root, '.missing', []);
|
|
||||||
check(root, '.toggle.completed', ['a']);
|
|
||||||
check(root, 'button.toggle', ['d']);
|
|
||||||
check(root, '.toggle:not(.completed)', ['b', 'd', 'svg']);
|
|
||||||
check(root, '.toggle, .other', ['a', 'b', 'd', 'svg']);
|
|
||||||
}
|
|
||||||
check(doc, '.toggle', ['root', 'a', 'b', 'd', 'svg']);
|
|
||||||
|
|
||||||
var snapshot = root.querySelectorAll('.toggle');
|
|
||||||
doc.getElementById('a').className = '';
|
|
||||||
root.removeChild(doc.getElementById('d'));
|
|
||||||
doc.getElementById('c').className = 'toggle';
|
|
||||||
check(root, '.toggle', ['b', 'c', 'svg']);
|
|
||||||
equal(snapshot.length, 4, 'querySelectorAll remains a static snapshot');
|
|
||||||
equal(snapshot[0].id, 'a', 'snapshot retains a changed element');
|
|
||||||
equal(snapshot[2].id, 'd', 'snapshot retains a removed element');
|
|
||||||
|
|
||||||
var fragment = doc.createDocumentFragment();
|
|
||||||
fragment.appendChild(root);
|
|
||||||
check(fragment, '.toggle', ['root', 'b', 'c', 'svg']);
|
|
||||||
check(root, '.toggle', ['b', 'c', 'svg']);
|
|
||||||
|
|
||||||
var quirks = parse('<html><body><div id="lower" class="toggle"></div>' +
|
|
||||||
'<div id="upper" class="TOGGLE"></div></body></html>', 'text/html');
|
|
||||||
equal(quirks.compatMode, 'BackCompat', 'quirks document');
|
|
||||||
check(quirks, '.toggle', ['lower', 'upper']);
|
|
||||||
check(quirks, '.TOGGLE', ['lower', 'upper']);
|
|
||||||
|
|
||||||
var xml = parse('<root><item id="plain" class="toggle"/>' +
|
|
||||||
'<item id="upper" class="TOGGLE"/>' +
|
|
||||||
'<item xmlns="urn:test" id="namespaced" class="toggle"/></root>',
|
|
||||||
'application/xml');
|
|
||||||
check(xml, '.toggle', ['plain', 'namespaced']);
|
|
||||||
check(xml, '|*.toggle', ['plain']);
|
|
||||||
check(xml, '*|*.toggle', ['plain', 'namespaced']);
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
support-files =
|
support-files =
|
||||||
file_class_selector_checks.js
|
|
||||||
audio.ogg
|
audio.ogg
|
||||||
audioEndedDuringPlaying.webm
|
audioEndedDuringPlaying.webm
|
||||||
iframe_bug962251.html
|
iframe_bug962251.html
|
||||||
|
|
@ -616,7 +615,6 @@ skip-if = os == "mac" # Different tab focus behavior on mac
|
||||||
[test_caretPositionFromPoint.html]
|
[test_caretPositionFromPoint.html]
|
||||||
[test_change_policy.html]
|
[test_change_policy.html]
|
||||||
[test_classList.html]
|
[test_classList.html]
|
||||||
[test_class_selector_fast_path.html]
|
|
||||||
[test_clearTimeoutIntervalNoArg.html]
|
[test_clearTimeoutIntervalNoArg.html]
|
||||||
[test_constructor-assignment.html]
|
[test_constructor-assignment.html]
|
||||||
[test_constructor.html]
|
[test_constructor.html]
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Class selector query fast path</title>
|
|
||||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<script src="file_class_selector_checks.js"></script>
|
|
||||||
<script>
|
|
||||||
checkClassSelectors(function(source, type) {
|
|
||||||
return new DOMParser().parseFromString(source, type);
|
|
||||||
}, is);
|
|
||||||
</script>
|
|
||||||
|
|
@ -94,24 +94,13 @@ ConsoleAPIStorageService.prototype = {
|
||||||
getEvents: function CS_getEvents(aId)
|
getEvents: function CS_getEvents(aId)
|
||||||
{
|
{
|
||||||
if (aId != null) {
|
if (aId != null) {
|
||||||
let storage = _consoleStorage.get(aId);
|
return (_consoleStorage.get(aId) || []).slice(0);
|
||||||
if (!storage) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
let { events, next } = storage;
|
|
||||||
return next === 0 ? events.slice() :
|
|
||||||
events.slice(next).concat(events.slice(0, next));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
for (let { events, next } of _consoleStorage.values()) {
|
for (let [id, events] of _consoleStorage) {
|
||||||
for (let i = next; i < events.length; i++) {
|
result.push.apply(result, events);
|
||||||
result.push(events[i]);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < next; i++) {
|
|
||||||
result.push(events[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.sort(function(a, b) {
|
return result.sort(function(a, b) {
|
||||||
|
|
@ -133,21 +122,16 @@ ConsoleAPIStorageService.prototype = {
|
||||||
*/
|
*/
|
||||||
recordEvent: function CS_recordEvent(aId, aOuterId, aEvent)
|
recordEvent: function CS_recordEvent(aId, aOuterId, aEvent)
|
||||||
{
|
{
|
||||||
let storage = _consoleStorage.get(aId);
|
if (!_consoleStorage.has(aId)) {
|
||||||
if (!storage) {
|
_consoleStorage.set(aId, []);
|
||||||
storage = { events: [], next: 0 };
|
|
||||||
_consoleStorage.set(aId, storage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite the oldest event once full, without moving the other entries.
|
let storage = _consoleStorage.get(aId);
|
||||||
// Advance before notifying observers, which may read or reenter storage.
|
storage.push(aEvent);
|
||||||
if (storage.events.length < STORAGE_MAX_EVENTS) {
|
|
||||||
storage.events.push(aEvent);
|
// truncate
|
||||||
} else {
|
if (storage.length > STORAGE_MAX_EVENTS) {
|
||||||
storage.events[storage.next] = aEvent;
|
storage.shift();
|
||||||
if (++storage.next === STORAGE_MAX_EVENTS) {
|
|
||||||
storage.next = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Services.obs.notifyObservers(aEvent, "console-api-log-event", aOuterId);
|
Services.obs.notifyObservers(aEvent, "console-api-log-event", aOuterId);
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,5 @@ LOCAL_INCLUDES += [
|
||||||
|
|
||||||
MOCHITEST_MANIFESTS += [ 'tests/mochitest.ini' ]
|
MOCHITEST_MANIFESTS += [ 'tests/mochitest.ini' ]
|
||||||
MOCHITEST_CHROME_MANIFESTS += [ 'tests/chrome.ini' ]
|
MOCHITEST_CHROME_MANIFESTS += [ 'tests/chrome.ini' ]
|
||||||
XPCSHELL_TESTS_MANIFESTS += [ 'tests/xpcshell.ini' ]
|
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
function run_test() {
|
|
||||||
const storage = Components.classes["@mozilla.org/consoleAPI-storage;1"]
|
|
||||||
.getService(Components.interfaces.nsIConsoleAPIStorage);
|
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
|
||||||
storage.clearEvents();
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < 3501; i++) {
|
|
||||||
storage.recordEvent("ring-test", "outer", { timeStamp: i });
|
|
||||||
if (i === 998 || i === 999 || i === 1000 || i === 1999 || i === 3500) {
|
|
||||||
let events = storage.getEvents("ring-test");
|
|
||||||
equal(events.length, Math.min(i + 1, 1000));
|
|
||||||
for (let j = 0; j < events.length; j++) {
|
|
||||||
equal(events[j].timeStamp, i + 1 - events.length + j);
|
|
||||||
}
|
|
||||||
events.length = 0;
|
|
||||||
equal(storage.getEvents("ring-test").length, Math.min(i + 1, 1000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
storage.recordEvent("other", "outer", { timeStamp: 2500.5 });
|
|
||||||
let all = storage.getEvents();
|
|
||||||
equal(all.length, 1001);
|
|
||||||
equal(all[0].timeStamp, 2500.5);
|
|
||||||
equal(all[1].timeStamp, 2501);
|
|
||||||
equal(all[1000].timeStamp, 3500);
|
|
||||||
|
|
||||||
let reentered = false;
|
|
||||||
let observer = {
|
|
||||||
observe(subject, topic, data) {
|
|
||||||
if (data === "ring-test" && !reentered) {
|
|
||||||
equal(storage.getEvents(data)[999].timeStamp, 3501);
|
|
||||||
reentered = true;
|
|
||||||
storage.recordEvent("ring-test", "outer", { timeStamp: 3502 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Services.obs.addObserver(observer, "console-storage-cache-event", false);
|
|
||||||
try {
|
|
||||||
storage.recordEvent("ring-test", "outer", { timeStamp: 3501 });
|
|
||||||
} finally {
|
|
||||||
Services.obs.removeObserver(observer, "console-storage-cache-event");
|
|
||||||
}
|
|
||||||
equal(reentered, true);
|
|
||||||
equal(storage.getEvents("ring-test")[999].timeStamp, 3502);
|
|
||||||
storage.clearEvents("ring-test");
|
|
||||||
equal(storage.getEvents("ring-test").length, 0);
|
|
||||||
equal(storage.getEvents("other").length, 1);
|
|
||||||
storage.recordEvent("ring-test", "outer", { timeStamp: 4000 });
|
|
||||||
equal(storage.getEvents("ring-test")[0].timeStamp, 4000);
|
|
||||||
} finally {
|
|
||||||
storage.clearEvents();
|
|
||||||
}
|
|
||||||
equal(storage.getEvents().length, 0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
[DEFAULT]
|
|
||||||
head =
|
|
||||||
tail =
|
|
||||||
|
|
||||||
[test_console_storage_ring.js]
|
|
||||||
|
|
@ -2334,7 +2334,8 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aRequest->IsModuleRequest()) {
|
if (aRequest->IsModuleRequest()) {
|
||||||
AutoCurrentScriptUpdater scriptUpdater(this, aRequest->Element());
|
// For modules, currentScript is set to null.
|
||||||
|
AutoCurrentScriptUpdater scriptUpdater(this, nullptr);
|
||||||
|
|
||||||
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
||||||
MOZ_ASSERT(request->mModuleScript);
|
MOZ_ASSERT(request->mModuleScript);
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,6 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
|
||||||
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
|
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
|
||||||
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
|
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
|
||||||
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
|
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
|
||||||
.setWeakRefs(GetWorkerPref<bool>(NS_LITERAL_CSTRING("weakrefs")))
|
|
||||||
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
|
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
|
||||||
.setArrayProtoValues(GetWorkerPref<bool>(
|
.setArrayProtoValues(GetWorkerPref<bool>(
|
||||||
NS_LITERAL_CSTRING("array_prototype_values")));
|
NS_LITERAL_CSTRING("array_prototype_values")));
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, O
|
||||||
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
|
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
|
||||||
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
|
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
|
||||||
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
|
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
|
||||||
WORKER_SIMPLE_PREF("javascript.options.weakrefs", WeakRefsEnabled, WEAKREFS_ENABLED)
|
|
||||||
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
|
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
|
||||||
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
|
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
|
||||||
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
|
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
|
||||||
|
|
|
||||||
|
|
@ -952,39 +952,13 @@ using namespace std;
|
||||||
void
|
void
|
||||||
imgCacheQueue::Remove(imgCacheEntry* entry)
|
imgCacheQueue::Remove(imgCacheEntry* entry)
|
||||||
{
|
{
|
||||||
uint64_t index = mQueue.IndexOf(entry);
|
queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry);
|
||||||
if (index == queueContainer::NoIndex) {
|
if (it != mQueue.end()) {
|
||||||
return;
|
mSize -= (*it)->GetDataSize();
|
||||||
}
|
mQueue.erase(it);
|
||||||
|
|
||||||
mSize -= mQueue[index]->GetDataSize();
|
|
||||||
|
|
||||||
// If the queue is clean and this is the first entry,
|
|
||||||
// then we can efficiently remove the entry without
|
|
||||||
// dirtying the sort order.
|
|
||||||
if (!IsDirty() && index == 0) {
|
|
||||||
std::pop_heap(mQueue.begin(), mQueue.end(),
|
|
||||||
imgLoader::CompareCacheEntries);
|
|
||||||
mQueue.RemoveElementAt(mQueue.Length() - 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from the middle of the list. This potentially
|
|
||||||
// breaks the binary heap sort order.
|
|
||||||
mQueue.RemoveElementAt(index);
|
|
||||||
|
|
||||||
// If we only have one entry or the queue is empty, though,
|
|
||||||
// then the sort order is still effectively good.
|
|
||||||
// Simply refresh the list to clear the dirty flag.
|
|
||||||
if (mQueue.Length() <= 1) {
|
|
||||||
Refresh();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise we must mark the queue dirty and potentially
|
|
||||||
// trigger an expensive sort later.
|
|
||||||
MarkDirty();
|
MarkDirty();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
imgCacheQueue::Push(imgCacheEntry* entry)
|
imgCacheQueue::Push(imgCacheEntry* entry)
|
||||||
|
|
@ -992,26 +966,23 @@ imgCacheQueue::Push(imgCacheEntry* entry)
|
||||||
mSize += entry->GetDataSize();
|
mSize += entry->GetDataSize();
|
||||||
|
|
||||||
RefPtr<imgCacheEntry> refptr(entry);
|
RefPtr<imgCacheEntry> refptr(entry);
|
||||||
mQueue.AppendElement(Move(refptr));
|
mQueue.push_back(refptr);
|
||||||
// If we're not dirty already, then we can efficiently add this to the binary heap immediately.
|
MarkDirty();
|
||||||
if (!IsDirty()) {
|
|
||||||
std::push_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<imgCacheEntry>
|
already_AddRefed<imgCacheEntry>
|
||||||
imgCacheQueue::Pop()
|
imgCacheQueue::Pop()
|
||||||
{
|
{
|
||||||
if (mQueue.IsEmpty()) {
|
if (mQueue.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (IsDirty()) {
|
if (IsDirty()) {
|
||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefPtr<imgCacheEntry> entry = mQueue[0];
|
||||||
std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
||||||
RefPtr<imgCacheEntry> entry = Move(mQueue.LastElement());
|
mQueue.pop_back();
|
||||||
mQueue.RemoveElementAt(mQueue.Length() - 1);
|
|
||||||
|
|
||||||
mSize -= entry->GetDataSize();
|
mSize -= entry->GetDataSize();
|
||||||
return entry.forget();
|
return entry.forget();
|
||||||
|
|
@ -1020,7 +991,6 @@ imgCacheQueue::Pop()
|
||||||
void
|
void
|
||||||
imgCacheQueue::Refresh()
|
imgCacheQueue::Refresh()
|
||||||
{
|
{
|
||||||
// Re-heap the list. This is an O(3 * n) operation and best avoided if possible.
|
|
||||||
std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
||||||
mDirty = false;
|
mDirty = false;
|
||||||
}
|
}
|
||||||
|
|
@ -1040,13 +1010,7 @@ imgCacheQueue::IsDirty()
|
||||||
uint32_t
|
uint32_t
|
||||||
imgCacheQueue::GetNumElements() const
|
imgCacheQueue::GetNumElements() const
|
||||||
{
|
{
|
||||||
return mQueue.Length();
|
return mQueue.size();
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
imgCacheQueue::Contains(imgCacheEntry* aEntry) const
|
|
||||||
{
|
|
||||||
return mQueue.Contains(aEntry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imgCacheQueue::iterator
|
imgCacheQueue::iterator
|
||||||
|
|
@ -1638,11 +1602,7 @@ void
|
||||||
imgLoader::CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff /* = 0 */)
|
imgLoader::CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff /* = 0 */)
|
||||||
{
|
{
|
||||||
imgCacheQueue& queue = GetCacheQueue(aForChrome);
|
imgCacheQueue& queue = GetCacheQueue(aForChrome);
|
||||||
// We only need to dirty the queue if there is any sorting taking place.
|
|
||||||
// Empty or single-entry lists can't become dirty.
|
|
||||||
if (queue.GetNumElements() > 1) {
|
|
||||||
queue.MarkDirty();
|
queue.MarkDirty();
|
||||||
}
|
|
||||||
queue.UpdateSize(aSizeDiff);
|
queue.UpdateSize(aSizeDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1671,9 +1631,7 @@ imgLoader::CheckCacheLimits(imgCacheTable& cache, imgCacheQueue& queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
// We just popped this entry from the queue, so pass AlreadyRemoved
|
RemoveFromCache(entry);
|
||||||
// to avoid searching the queue again in RemoveFromCache.
|
|
||||||
RemoveFromCache(entry, QueueState::AlreadyRemoved);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1972,7 +1930,7 @@ imgLoader::RemoveFromCache(const ImageCacheKey& aKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
imgLoader::RemoveFromCache(imgCacheEntry* entry, QueueState aQueueState)
|
imgLoader::RemoveFromCache(imgCacheEntry* entry)
|
||||||
{
|
{
|
||||||
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
|
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
|
||||||
|
|
||||||
|
|
@ -1988,25 +1946,14 @@ imgLoader::RemoveFromCache(imgCacheEntry* entry, QueueState aQueueState)
|
||||||
|
|
||||||
cache.Remove(key);
|
cache.Remove(key);
|
||||||
|
|
||||||
if (queue.IsDirty()) {
|
|
||||||
queue.Refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->HasNoProxies()) {
|
if (entry->HasNoProxies()) {
|
||||||
LOG_STATIC_FUNC(gImgLog,
|
LOG_STATIC_FUNC(gImgLog,
|
||||||
"imgLoader::RemoveFromCache removing from tracker");
|
"imgLoader::RemoveFromCache removing from tracker");
|
||||||
if (mCacheTracker) {
|
if (mCacheTracker) {
|
||||||
mCacheTracker->RemoveObject(entry);
|
mCacheTracker->RemoveObject(entry);
|
||||||
}
|
}
|
||||||
// Only search the queue to remove the entry if its possible it might
|
|
||||||
// be in the queue. If we know its not in the queue this would be
|
|
||||||
// wasted work.
|
|
||||||
MOZ_ASSERT_IF(aQueueState == QueueState::AlreadyRemoved,
|
|
||||||
!queue.Contains(entry));
|
|
||||||
if (aQueueState == QueueState::MaybeExists) {
|
|
||||||
queue.Remove(entry);
|
queue.Remove(entry);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
entry->SetEvicted(true);
|
entry->SetEvicted(true);
|
||||||
request->SetIsInCache(false);
|
request->SetIsInCache(false);
|
||||||
|
|
@ -2050,13 +1997,13 @@ imgLoader::EvictEntries(imgCacheQueue& aQueueToClear)
|
||||||
// We have to make a temporary, since RemoveFromCache removes the element
|
// We have to make a temporary, since RemoveFromCache removes the element
|
||||||
// from the queue, invalidating iterators.
|
// from the queue, invalidating iterators.
|
||||||
nsTArray<RefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
|
nsTArray<RefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
|
||||||
for (auto i = aQueueToClear.begin(); i != aQueueToClear.end(); ++i) {
|
for (imgCacheQueue::const_iterator i = aQueueToClear.begin();
|
||||||
|
i != aQueueToClear.end(); ++i) {
|
||||||
entries.AppendElement(*i);
|
entries.AppendElement(*i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate in reverse order to minimize array copying.
|
for (uint32_t i = 0; i < entries.Length(); ++i) {
|
||||||
for (auto& entry : entries) {
|
if (!RemoveFromCache(entries[i])) {
|
||||||
if (!RemoveFromCache(entry)) {
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,8 +178,7 @@ public:
|
||||||
uint32_t GetSize() const;
|
uint32_t GetSize() const;
|
||||||
void UpdateSize(int32_t diff);
|
void UpdateSize(int32_t diff);
|
||||||
uint32_t GetNumElements() const;
|
uint32_t GetNumElements() const;
|
||||||
bool Contains(imgCacheEntry* aEntry) const;
|
typedef std::vector<RefPtr<imgCacheEntry> > queueContainer;
|
||||||
typedef nsTArray<RefPtr<imgCacheEntry> > queueContainer;
|
|
||||||
typedef queueContainer::iterator iterator;
|
typedef queueContainer::iterator iterator;
|
||||||
typedef queueContainer::const_iterator const_iterator;
|
typedef queueContainer::const_iterator const_iterator;
|
||||||
|
|
||||||
|
|
@ -317,16 +316,7 @@ public:
|
||||||
nsresult InitCache();
|
nsresult InitCache();
|
||||||
|
|
||||||
bool RemoveFromCache(const ImageCacheKey& aKey);
|
bool RemoveFromCache(const ImageCacheKey& aKey);
|
||||||
|
bool RemoveFromCache(imgCacheEntry* entry);
|
||||||
// Enumeration describing if a given entry is in the cache queue or not.
|
|
||||||
// There are some cases we know the entry is definitely not in the queue.
|
|
||||||
enum class QueueState {
|
|
||||||
MaybeExists,
|
|
||||||
AlreadyRemoved
|
|
||||||
};
|
|
||||||
|
|
||||||
bool RemoveFromCache(imgCacheEntry* entry,
|
|
||||||
QueueState aQueueState = QueueState::MaybeExists);
|
|
||||||
|
|
||||||
bool PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* aEntry);
|
bool PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* aEntry);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,67 +253,6 @@ function ArraySort(comparefn) {
|
||||||
return MergeSort(O, len, comparefn);
|
return MergeSort(O, len, comparefn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES2023 22.1.3.30 Array.prototype.toSorted ( comparefn )
|
|
||||||
function ArrayToSorted(comparefn) {
|
|
||||||
if (comparefn !== undefined) {
|
|
||||||
if (!IsCallable(comparefn)) {
|
|
||||||
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, comparefn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Let O be ? ToObject(this). Let len be ? ToLength(O.length).
|
|
||||||
var O = ToObject(this);
|
|
||||||
var len = ToLength(O.length);
|
|
||||||
|
|
||||||
// Step 2: Snapshot values in ascending index order into a List.
|
|
||||||
var items = new List();
|
|
||||||
var itemsLen = len;
|
|
||||||
for (var k = 0; k < len; k++) {
|
|
||||||
items[k] = O[k];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Create SortCompare per spec.
|
|
||||||
var wrappedCompareFn = comparefn;
|
|
||||||
var sortCompare;
|
|
||||||
if (wrappedCompareFn === undefined) {
|
|
||||||
sortCompare = function(x, y) {
|
|
||||||
if (x === undefined)
|
|
||||||
return y === undefined ? 0 : 1;
|
|
||||||
if (y === undefined)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
var xString = ToString(x);
|
|
||||||
var yString = ToString(y);
|
|
||||||
if (xString < yString)
|
|
||||||
return -1;
|
|
||||||
if (xString > yString)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
sortCompare = function(x, y) {
|
|
||||||
if (x === undefined)
|
|
||||||
return y === undefined ? 0 : 1;
|
|
||||||
if (y === undefined)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
var v = ToNumber(wrappedCompareFn(x, y));
|
|
||||||
return v !== v ? 0 : v;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4: Sort the snapshot List using SortCompare.
|
|
||||||
if (itemsLen > 1)
|
|
||||||
MergeSort(items, itemsLen, sortCompare);
|
|
||||||
|
|
||||||
// Step 5: Create result array and write sorted values.
|
|
||||||
var A = ArraySpeciesCreate(O, len);
|
|
||||||
for (var j = 0; j < itemsLen; j++)
|
|
||||||
_DefineDataProperty(A, j, items[j]);
|
|
||||||
|
|
||||||
return A;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ES5 15.4.4.18. */
|
/* ES5 15.4.4.18. */
|
||||||
function ArrayForEach(callbackfn/*, thisArg*/) {
|
function ArrayForEach(callbackfn/*, thisArg*/) {
|
||||||
/* Step 1. */
|
/* Step 1. */
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,10 @@
|
||||||
#include "builtin/AtomicsObject.h"
|
#include "builtin/AtomicsObject.h"
|
||||||
|
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
#include "mozilla/Casting.h"
|
|
||||||
#include "mozilla/FloatingPoint.h"
|
#include "mozilla/FloatingPoint.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
|
||||||
#include "builtin/Promise.h"
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsfriendapi.h"
|
#include "jsfriendapi.h"
|
||||||
#include "jsnum.h"
|
#include "jsnum.h"
|
||||||
|
|
@ -60,9 +58,7 @@
|
||||||
#include "jit/AtomicOperations.h"
|
#include "jit/AtomicOperations.h"
|
||||||
#include "jit/InlinableNatives.h"
|
#include "jit/InlinableNatives.h"
|
||||||
#include "js/Class.h"
|
#include "js/Class.h"
|
||||||
#include "vm/BigIntType.h"
|
|
||||||
#include "vm/GlobalObject.h"
|
#include "vm/GlobalObject.h"
|
||||||
#include "vm/HelperThreads.h"
|
|
||||||
#include "vm/Time.h"
|
#include "vm/Time.h"
|
||||||
#include "vm/TypedArrayObject.h"
|
#include "vm/TypedArrayObject.h"
|
||||||
#include "wasm/WasmInstance.h"
|
#include "wasm/WasmInstance.h"
|
||||||
|
|
@ -125,68 +121,6 @@ GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle<TypedArrayObject*> view,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
IsWaitableTypedArray(Scalar::Type viewType)
|
|
||||||
{
|
|
||||||
return viewType == Scalar::Int32 || viewType == Scalar::BigInt64;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t
|
|
||||||
GetWaiterByteOffset(Handle<TypedArrayObject*> view, uint32_t offset)
|
|
||||||
{
|
|
||||||
return view->byteOffset() + offset * TypedArrayElemSize(view->type());
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t
|
|
||||||
BigIntToRawBits(Scalar::Type viewType, BigInt* value)
|
|
||||||
{
|
|
||||||
if (viewType == Scalar::BigInt64)
|
|
||||||
return uint64_t(BigInt::toInt64(value));
|
|
||||||
MOZ_ASSERT(viewType == Scalar::BigUint64);
|
|
||||||
return BigInt::toUint64(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
BigIntToRawBits(JSContext* cx, Scalar::Type viewType, HandleValue value, uint64_t* bits)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(Scalar::isBigIntType(viewType));
|
|
||||||
|
|
||||||
RootedBigInt bigint(cx, ToBigInt(cx, value));
|
|
||||||
if (!bigint)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*bits = BigIntToRawBits(viewType, bigint);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
SetBigIntResult(JSContext* cx, Scalar::Type viewType, uint64_t bits, MutableHandleValue result)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(Scalar::isBigIntType(viewType));
|
|
||||||
|
|
||||||
BigInt* bigint = viewType == Scalar::BigInt64
|
|
||||||
? BigInt::createFromInt64(cx, mozilla::BitwiseCast<int64_t>(bits))
|
|
||||||
: BigInt::createFromUint64(cx, bits);
|
|
||||||
if (!bigint)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
result.setBigInt(bigint);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
WaitValueMatches(Handle<TypedArrayObject*> view, uint32_t offset, uint64_t expected)
|
|
||||||
{
|
|
||||||
SharedMem<void*> viewData = view->viewDataShared();
|
|
||||||
if (view->type() == Scalar::BigInt64) {
|
|
||||||
return jit::AtomicOperations::loadSafeWhenRacy(viewData.cast<uint64_t*>() + offset) ==
|
|
||||||
expected;
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint32_t(jit::AtomicOperations::loadSafeWhenRacy(viewData.cast<int32_t*>() + offset)) ==
|
|
||||||
uint32_t(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
CompareExchange(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidate,
|
CompareExchange(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidate,
|
||||||
SharedMem<void*> viewData, uint32_t offset, bool* badArrayType = nullptr)
|
SharedMem<void*> viewData, uint32_t offset, bool* badArrayType = nullptr)
|
||||||
|
|
@ -257,20 +191,6 @@ js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp)
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Scalar::isBigIntType(view->type())) {
|
|
||||||
uint64_t oldCandidate;
|
|
||||||
if (!BigIntToRawBits(cx, view->type(), oldv, &oldCandidate))
|
|
||||||
return false;
|
|
||||||
uint64_t newCandidate;
|
|
||||||
if (!BigIntToRawBits(cx, view->type(), newv, &newCandidate))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint64_t result = jit::AtomicOperations::compareExchangeSeqCst(
|
|
||||||
view->viewDataShared().cast<uint64_t*>() + offset, oldCandidate, newCandidate);
|
|
||||||
return SetBigIntResult(cx, view->type(), result, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t oldCandidate;
|
int32_t oldCandidate;
|
||||||
if (!ToInt32(cx, oldv, &oldCandidate))
|
if (!ToInt32(cx, oldv, &oldCandidate))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -339,22 +259,6 @@ js::atomics_load(JSContext* cx, unsigned argc, Value* vp)
|
||||||
r.setNumber(v);
|
r.setNumber(v);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case Scalar::BigInt64: {
|
|
||||||
int64_t v = jit::AtomicOperations::loadSeqCst(viewData.cast<int64_t*>() + offset);
|
|
||||||
BigInt* bigint = BigInt::createFromInt64(cx, v);
|
|
||||||
if (!bigint)
|
|
||||||
return false;
|
|
||||||
r.setBigInt(bigint);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case Scalar::BigUint64: {
|
|
||||||
uint64_t v = jit::AtomicOperations::loadSeqCst(viewData.cast<uint64_t*>() + offset);
|
|
||||||
BigInt* bigint = BigInt::createFromUint64(cx, v);
|
|
||||||
if (!bigint)
|
|
||||||
return false;
|
|
||||||
r.setBigInt(bigint);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return ReportBadArrayType(cx);
|
return ReportBadArrayType(cx);
|
||||||
}
|
}
|
||||||
|
|
@ -433,25 +337,6 @@ ExchangeOrStore(JSContext* cx, unsigned argc, Value* vp)
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Scalar::isBigIntType(view->type())) {
|
|
||||||
RootedBigInt bigint(cx, ToBigInt(cx, valv));
|
|
||||||
if (!bigint)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint64_t value = BigIntToRawBits(view->type(), bigint);
|
|
||||||
if (op == DoStore) {
|
|
||||||
jit::AtomicOperations::storeSeqCst(view->viewDataShared().cast<uint64_t*>() + offset,
|
|
||||||
value);
|
|
||||||
r.setBigInt(bigint);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t result = jit::AtomicOperations::exchangeSeqCst(
|
|
||||||
view->viewDataShared().cast<uint64_t*>() + offset, value);
|
|
||||||
return SetBigIntResult(cx, view->type(), result, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
double integerValue;
|
double integerValue;
|
||||||
if (!ToInteger(cx, valv, &integerValue))
|
if (!ToInteger(cx, valv, &integerValue))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -495,24 +380,6 @@ AtomicsBinop(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValue valv
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Scalar::isBigIntType(view->type())) {
|
|
||||||
uint64_t value;
|
|
||||||
if (!BigIntToRawBits(cx, view->type(), valv, &value))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
SharedMem<uint64_t*> addr = view->viewDataShared().cast<uint64_t*>() + offset;
|
|
||||||
uint64_t old = jit::AtomicOperations::loadSeqCst(addr);
|
|
||||||
for (;;) {
|
|
||||||
uint64_t replacement = T::operate64(old, value);
|
|
||||||
uint64_t observed = jit::AtomicOperations::compareExchangeSeqCst(addr, old,
|
|
||||||
replacement);
|
|
||||||
if (observed == old)
|
|
||||||
return SetBigIntResult(cx, view->type(), old, r);
|
|
||||||
old = observed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t numberValue;
|
int32_t numberValue;
|
||||||
if (!ToInt32(cx, valv, &numberValue))
|
if (!ToInt32(cx, valv, &numberValue))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -567,7 +434,6 @@ class PerformAdd
|
||||||
public:
|
public:
|
||||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAddSeqCst)
|
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAddSeqCst)
|
||||||
static int32_t perform(int32_t x, int32_t y) { return x + y; }
|
static int32_t perform(int32_t x, int32_t y) { return x + y; }
|
||||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x + y; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -582,7 +448,6 @@ class PerformSub
|
||||||
public:
|
public:
|
||||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchSubSeqCst)
|
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchSubSeqCst)
|
||||||
static int32_t perform(int32_t x, int32_t y) { return x - y; }
|
static int32_t perform(int32_t x, int32_t y) { return x - y; }
|
||||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x - y; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -597,7 +462,6 @@ class PerformAnd
|
||||||
public:
|
public:
|
||||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAndSeqCst)
|
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAndSeqCst)
|
||||||
static int32_t perform(int32_t x, int32_t y) { return x & y; }
|
static int32_t perform(int32_t x, int32_t y) { return x & y; }
|
||||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x & y; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -612,7 +476,6 @@ class PerformOr
|
||||||
public:
|
public:
|
||||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchOrSeqCst)
|
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchOrSeqCst)
|
||||||
static int32_t perform(int32_t x, int32_t y) { return x | y; }
|
static int32_t perform(int32_t x, int32_t y) { return x | y; }
|
||||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x | y; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -627,7 +490,6 @@ class PerformXor
|
||||||
public:
|
public:
|
||||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchXorSeqCst)
|
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchXorSeqCst)
|
||||||
static int32_t perform(int32_t x, int32_t y) { return x ^ y; }
|
static int32_t perform(int32_t x, int32_t y) { return x ^ y; }
|
||||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x ^ y; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -831,13 +693,11 @@ js::atomics_cmpxchg_asm_callout(wasm::Instance* instance, int32_t vt, int32_t of
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
class AtomicsWaitAsyncTask;
|
|
||||||
|
|
||||||
// Represents one waiting worker.
|
// Represents one waiting worker.
|
||||||
//
|
//
|
||||||
// The type is declared opaque in SharedArrayObject.h. Instances of
|
// The type is declared opaque in SharedArrayObject.h. Instances of
|
||||||
// js::FutexWaiter are linked onto a list across a call to FutexRuntime::wait()
|
// js::FutexWaiter are stack-allocated and linked onto a list across a
|
||||||
// or an async wait task.
|
// call to FutexRuntime::wait().
|
||||||
//
|
//
|
||||||
// The 'waiters' field of the SharedArrayRawBuffer points to the highest
|
// The 'waiters' field of the SharedArrayRawBuffer points to the highest
|
||||||
// priority waiter in the list, and lower priority nodes are linked through
|
// priority waiter in the list, and lower priority nodes are linked through
|
||||||
|
|
@ -851,34 +711,14 @@ class FutexWaiter
|
||||||
public:
|
public:
|
||||||
FutexWaiter(uint32_t offset, JSRuntime* rt)
|
FutexWaiter(uint32_t offset, JSRuntime* rt)
|
||||||
: offset(offset),
|
: offset(offset),
|
||||||
kind(Sync),
|
|
||||||
rt(rt),
|
rt(rt),
|
||||||
asyncTask(nullptr),
|
|
||||||
lower_pri(nullptr),
|
lower_pri(nullptr),
|
||||||
back(nullptr)
|
back(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
FutexWaiter(uint32_t offset, AtomicsWaitAsyncTask* asyncTask)
|
uint32_t offset; // int32 element index within the SharedArrayBuffer
|
||||||
: offset(offset),
|
|
||||||
kind(Async),
|
|
||||||
rt(nullptr),
|
|
||||||
asyncTask(asyncTask),
|
|
||||||
lower_pri(nullptr),
|
|
||||||
back(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isWaiting() const;
|
|
||||||
void notify();
|
|
||||||
|
|
||||||
uint32_t offset; // byte offset within the SharedArrayBuffer
|
|
||||||
enum WaiterKind {
|
|
||||||
Sync,
|
|
||||||
Async
|
|
||||||
} kind;
|
|
||||||
JSRuntime* rt; // The runtime of the waiter
|
JSRuntime* rt; // The runtime of the waiter
|
||||||
AtomicsWaitAsyncTask* asyncTask; // The async waiter task, if any
|
|
||||||
FutexWaiter* lower_pri; // Lower priority nodes in circular doubly-linked list of waiters
|
FutexWaiter* lower_pri; // Lower priority nodes in circular doubly-linked list of waiters
|
||||||
FutexWaiter* back; // Other direction
|
FutexWaiter* back; // Other direction
|
||||||
};
|
};
|
||||||
|
|
@ -902,195 +742,8 @@ class AutoLockFutexAPI
|
||||||
js::UniqueLock<js::Mutex>& unique() { return *unique_; }
|
js::UniqueLock<js::Mutex>& unique() { return *unique_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
|
||||||
AddWaiter(SharedArrayRawBuffer* sarb, FutexWaiter* waiter)
|
|
||||||
{
|
|
||||||
if (FutexWaiter* waiters = sarb->waiters()) {
|
|
||||||
waiter->lower_pri = waiters;
|
|
||||||
waiter->back = waiters->back;
|
|
||||||
waiters->back->lower_pri = waiter;
|
|
||||||
waiters->back = waiter;
|
|
||||||
} else {
|
|
||||||
waiter->lower_pri = waiter->back = waiter;
|
|
||||||
sarb->setWaiters(waiter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
RemoveWaiter(SharedArrayRawBuffer* sarb, FutexWaiter* waiter)
|
|
||||||
{
|
|
||||||
if (waiter->lower_pri == waiter) {
|
|
||||||
sarb->setWaiters(nullptr);
|
|
||||||
} else {
|
|
||||||
waiter->lower_pri->back = waiter->back;
|
|
||||||
waiter->back->lower_pri = waiter->lower_pri;
|
|
||||||
if (sarb->waiters() == waiter)
|
|
||||||
sarb->setWaiters(waiter->lower_pri);
|
|
||||||
}
|
|
||||||
waiter->lower_pri = nullptr;
|
|
||||||
waiter->back = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
class AtomicsWaitAsyncTask : public PromiseTask
|
|
||||||
{
|
|
||||||
enum class State {
|
|
||||||
Waiting,
|
|
||||||
Notified,
|
|
||||||
TimedOut
|
|
||||||
};
|
|
||||||
|
|
||||||
SharedArrayRawBuffer* sarb_;
|
|
||||||
FutexWaiter waiter_;
|
|
||||||
mozilla::Maybe<mozilla::TimeDuration> timeout_;
|
|
||||||
ConditionVariable cond_;
|
|
||||||
State state_;
|
|
||||||
bool isInWaiterList_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AtomicsWaitAsyncTask(JSContext* cx, Handle<PromiseObject*> promise,
|
|
||||||
SharedArrayRawBuffer* sarb, uint32_t offset,
|
|
||||||
mozilla::Maybe<mozilla::TimeDuration>& timeout)
|
|
||||||
: PromiseTask(cx, promise),
|
|
||||||
sarb_(sarb),
|
|
||||||
waiter_(offset, this),
|
|
||||||
timeout_(timeout),
|
|
||||||
state_(State::Waiting),
|
|
||||||
isInWaiterList_(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~AtomicsWaitAsyncTask() {
|
|
||||||
if (isInWaiterList_) {
|
|
||||||
AutoLockFutexAPI lock;
|
|
||||||
if (isInWaiterList_)
|
|
||||||
removeFromWaiterList();
|
|
||||||
}
|
|
||||||
sarb_->dropReference();
|
|
||||||
}
|
|
||||||
|
|
||||||
FutexWaiter* waiter() {
|
|
||||||
return &waiter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setInWaiterList() {
|
|
||||||
isInWaiterList_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isWaiting() const {
|
|
||||||
return state_ == State::Waiting;
|
|
||||||
}
|
|
||||||
|
|
||||||
void notify() {
|
|
||||||
MOZ_ASSERT(isWaiting());
|
|
||||||
state_ = State::Notified;
|
|
||||||
cond_.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute() override {
|
|
||||||
AutoLockFutexAPI lock;
|
|
||||||
|
|
||||||
const bool isTimed = timeout_.isSome();
|
|
||||||
auto finalEnd = timeout_.map([](mozilla::TimeDuration& timeout) {
|
|
||||||
return mozilla::TimeStamp::Now() + timeout;
|
|
||||||
});
|
|
||||||
auto maxSlice = mozilla::TimeDuration::FromSeconds(4000.0);
|
|
||||||
|
|
||||||
while (state_ == State::Waiting) {
|
|
||||||
if (isTimed) {
|
|
||||||
auto sliceEnd = finalEnd.map([&](mozilla::TimeStamp& finalEnd) {
|
|
||||||
auto sliceEnd = mozilla::TimeStamp::Now() + maxSlice;
|
|
||||||
if (finalEnd < sliceEnd)
|
|
||||||
sliceEnd = finalEnd;
|
|
||||||
return sliceEnd;
|
|
||||||
});
|
|
||||||
|
|
||||||
mozilla::Unused << cond_.wait_until(lock.unique(), *sliceEnd);
|
|
||||||
if (state_ == State::Waiting && mozilla::TimeStamp::Now() >= *finalEnd) {
|
|
||||||
state_ = State::TimedOut;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cond_.wait(lock.unique());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInWaiterList_)
|
|
||||||
removeFromWaiterList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void removeFromWaiterList() {
|
|
||||||
MOZ_ASSERT(isInWaiterList_);
|
|
||||||
RemoveWaiter(sarb_, &waiter_);
|
|
||||||
isInWaiterList_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool finishPromise(JSContext* cx, Handle<PromiseObject*> promise) override {
|
|
||||||
RootedValue result(cx);
|
|
||||||
if (state_ == State::TimedOut)
|
|
||||||
result.setString(cx->names().futexTimedOut);
|
|
||||||
else
|
|
||||||
result.setString(cx->names().futexOK);
|
|
||||||
return PromiseObject::resolve(cx, promise, result);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
FutexWaiter::isWaiting() const
|
|
||||||
{
|
|
||||||
if (kind == Sync)
|
|
||||||
return rt->fx.isWaiting();
|
|
||||||
return asyncTask->isWaiting();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FutexWaiter::notify()
|
|
||||||
{
|
|
||||||
if (kind == Sync) {
|
|
||||||
rt->fx.notify(FutexRuntime::NotifyExplicit);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
asyncTask->notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
static bool
|
|
||||||
GetWaitTimeout(JSContext* cx, HandleValue timeoutv,
|
|
||||||
mozilla::Maybe<mozilla::TimeDuration>* timeout)
|
|
||||||
{
|
|
||||||
timeout->reset();
|
|
||||||
if (!timeoutv.isUndefined()) {
|
|
||||||
double timeout_ms;
|
|
||||||
if (!ToNumber(cx, timeoutv, &timeout_ms))
|
|
||||||
return false;
|
|
||||||
if (!mozilla::IsNaN(timeout_ms)) {
|
|
||||||
if (timeout_ms < 0)
|
|
||||||
*timeout = mozilla::Some(mozilla::TimeDuration::FromSeconds(0.0));
|
|
||||||
else if (!mozilla::IsInfinite(timeout_ms))
|
|
||||||
*timeout = mozilla::Some(mozilla::TimeDuration::FromMilliseconds(timeout_ms));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
CreateWaitAsyncResult(JSContext* cx, bool isAsync, HandleValue value, MutableHandleValue rval)
|
|
||||||
{
|
|
||||||
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
|
||||||
if (!obj)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RootedValue asyncValue(cx, BooleanValue(isAsync));
|
|
||||||
if (!NativeDefineDataProperty(cx, obj, cx->names().async, asyncValue, JSPROP_ENUMERATE))
|
|
||||||
return false;
|
|
||||||
if (!NativeDefineDataProperty(cx, obj, cx->names().value, value, JSPROP_ENUMERATE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
rval.setObject(*obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
@ -1106,24 +759,26 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
Rooted<TypedArrayObject*> view(cx, nullptr);
|
||||||
if (!GetSharedTypedArray(cx, objv, &view))
|
if (!GetSharedTypedArray(cx, objv, &view))
|
||||||
return false;
|
return false;
|
||||||
if (!IsWaitableTypedArray(view->type()))
|
if (view->type() != Scalar::Int32)
|
||||||
return ReportBadArrayType(cx);
|
return ReportBadArrayType(cx);
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||||
return false;
|
return false;
|
||||||
uint64_t value = 0;
|
int32_t value;
|
||||||
if (view->type() == Scalar::BigInt64) {
|
if (!ToInt32(cx, valv, &value))
|
||||||
if (!BigIntToRawBits(cx, view->type(), valv, &value))
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
int32_t int32Value;
|
|
||||||
if (!ToInt32(cx, valv, &int32Value))
|
|
||||||
return false;
|
|
||||||
value = uint32_t(int32Value);
|
|
||||||
}
|
|
||||||
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
||||||
if (!GetWaitTimeout(cx, timeoutv, &timeout))
|
if (!timeoutv.isUndefined()) {
|
||||||
|
double timeout_ms;
|
||||||
|
if (!ToNumber(cx, timeoutv, &timeout_ms))
|
||||||
return false;
|
return false;
|
||||||
|
if (!mozilla::IsNaN(timeout_ms)) {
|
||||||
|
if (timeout_ms < 0)
|
||||||
|
timeout = mozilla::Some(mozilla::TimeDuration::FromSeconds(0.0));
|
||||||
|
else if (!mozilla::IsInfinite(timeout_ms))
|
||||||
|
timeout = mozilla::Some(mozilla::TimeDuration::FromMilliseconds(timeout_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!rt->fx.canWait())
|
if (!rt->fx.canWait())
|
||||||
return ReportCannotWait(cx);
|
return ReportCannotWait(cx);
|
||||||
|
|
@ -1132,7 +787,8 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||||
// and it provides the necessary memory fence.
|
// and it provides the necessary memory fence.
|
||||||
AutoLockFutexAPI lock;
|
AutoLockFutexAPI lock;
|
||||||
|
|
||||||
if (!WaitValueMatches(view, offset, value)) {
|
SharedMem<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset;
|
||||||
|
if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
|
||||||
r.setString(cx->names().futexNotEqual);
|
r.setString(cx->names().futexNotEqual);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1140,8 +796,16 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||||
Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
|
Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
|
||||||
SharedArrayRawBuffer* sarb = sab->rawBufferObject();
|
SharedArrayRawBuffer* sarb = sab->rawBufferObject();
|
||||||
|
|
||||||
FutexWaiter w(GetWaiterByteOffset(view, offset), rt);
|
FutexWaiter w(offset, rt);
|
||||||
AddWaiter(sarb, &w);
|
if (FutexWaiter* waiters = sarb->waiters()) {
|
||||||
|
w.lower_pri = waiters;
|
||||||
|
w.back = waiters->back;
|
||||||
|
waiters->back->lower_pri = &w;
|
||||||
|
waiters->back = &w;
|
||||||
|
} else {
|
||||||
|
w.lower_pri = w.back = &w;
|
||||||
|
sarb->setWaiters(&w);
|
||||||
|
}
|
||||||
|
|
||||||
FutexRuntime::WaitResult result = FutexRuntime::FutexOK;
|
FutexRuntime::WaitResult result = FutexRuntime::FutexOK;
|
||||||
bool retval = rt->fx.wait(cx, lock.unique(), timeout, &result);
|
bool retval = rt->fx.wait(cx, lock.unique(), timeout, &result);
|
||||||
|
|
@ -1156,98 +820,17 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveWaiter(sarb, &w);
|
if (w.lower_pri == &w) {
|
||||||
|
sarb->setWaiters(nullptr);
|
||||||
|
} else {
|
||||||
|
w.lower_pri->back = w.back;
|
||||||
|
w.back->lower_pri = w.lower_pri;
|
||||||
|
if (sarb->waiters() == &w)
|
||||||
|
sarb->setWaiters(w.lower_pri);
|
||||||
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
js::atomics_waitAsync(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
HandleValue objv = args.get(0);
|
|
||||||
HandleValue idxv = args.get(1);
|
|
||||||
HandleValue valv = args.get(2);
|
|
||||||
HandleValue timeoutv = args.get(3);
|
|
||||||
MutableHandleValue r = args.rval();
|
|
||||||
|
|
||||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
|
||||||
if (!GetSharedTypedArray(cx, objv, &view))
|
|
||||||
return false;
|
|
||||||
if (!IsWaitableTypedArray(view->type()))
|
|
||||||
return ReportBadArrayType(cx);
|
|
||||||
uint32_t offset;
|
|
||||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
|
||||||
return false;
|
|
||||||
uint64_t value = 0;
|
|
||||||
if (view->type() == Scalar::BigInt64) {
|
|
||||||
if (!BigIntToRawBits(cx, view->type(), valv, &value))
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
int32_t int32Value;
|
|
||||||
if (!ToInt32(cx, valv, &int32Value))
|
|
||||||
return false;
|
|
||||||
value = uint32_t(int32Value);
|
|
||||||
}
|
|
||||||
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
|
||||||
if (!GetWaitTimeout(cx, timeoutv, &timeout))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
|
|
||||||
if (!promise)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RootedValue promiseValue(cx, ObjectValue(*promise));
|
|
||||||
RootedValue result(cx);
|
|
||||||
if (!CreateWaitAsyncResult(cx, true, promiseValue, &result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
|
|
||||||
SharedArrayRawBuffer* sarb = sab->rawBufferObject();
|
|
||||||
if (!sarb->addReference()) {
|
|
||||||
JS_ReportErrorASCII(cx, "Reference count overflow on SharedArrayBuffer");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto task = cx->make_unique<AtomicsWaitAsyncTask>(cx, promise, sarb, offset, timeout);
|
|
||||||
if (!task) {
|
|
||||||
sarb->dropReference();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isAsync = false;
|
|
||||||
RootedValue immediateResult(cx);
|
|
||||||
{
|
|
||||||
AutoLockFutexAPI lock;
|
|
||||||
|
|
||||||
if (!WaitValueMatches(view, offset, value)) {
|
|
||||||
immediateResult.setString(cx->names().futexNotEqual);
|
|
||||||
} else if (timeout.isSome() && timeout->ToMilliseconds() == 0.0) {
|
|
||||||
immediateResult.setString(cx->names().futexTimedOut);
|
|
||||||
} else {
|
|
||||||
if (!cx->startAsyncTaskCallback || !cx->finishAsyncTaskCallback ||
|
|
||||||
!CanUseExtraThreads())
|
|
||||||
{
|
|
||||||
JS_ReportErrorASCII(cx, "Atomics.waitAsync not supported in this runtime.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
task->waiter()->offset = GetWaiterByteOffset(view, offset);
|
|
||||||
AddWaiter(sarb, task->waiter());
|
|
||||||
task->setInWaiterList();
|
|
||||||
isAsync = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAsync)
|
|
||||||
return CreateWaitAsyncResult(cx, false, immediateResult, r);
|
|
||||||
|
|
||||||
if (!StartPromiseTask(cx, Move(task)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
r.set(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
@ -1260,12 +843,11 @@ js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
Rooted<TypedArrayObject*> view(cx, nullptr);
|
||||||
if (!GetSharedTypedArray(cx, objv, &view))
|
if (!GetSharedTypedArray(cx, objv, &view))
|
||||||
return false;
|
return false;
|
||||||
if (!IsWaitableTypedArray(view->type()))
|
if (view->type() != Scalar::Int32)
|
||||||
return ReportBadArrayType(cx);
|
return ReportBadArrayType(cx);
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||||
return false;
|
return false;
|
||||||
offset = GetWaiterByteOffset(view, offset);
|
|
||||||
double count;
|
double count;
|
||||||
if (countv.isUndefined()) {
|
if (countv.isUndefined()) {
|
||||||
count = mozilla::PositiveInfinity<double>();
|
count = mozilla::PositiveInfinity<double>();
|
||||||
|
|
@ -1288,9 +870,9 @@ js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||||
do {
|
do {
|
||||||
FutexWaiter* c = iter;
|
FutexWaiter* c = iter;
|
||||||
iter = iter->lower_pri;
|
iter = iter->lower_pri;
|
||||||
if (c->offset != offset || !c->isWaiting())
|
if (c->offset != offset || !c->rt->fx.isWaiting())
|
||||||
continue;
|
continue;
|
||||||
c->notify();
|
c->rt->fx.notify(FutexRuntime::NotifyExplicit);
|
||||||
++woken;
|
++woken;
|
||||||
--count;
|
--count;
|
||||||
} while (count > 0 && iter != waiters);
|
} while (count > 0 && iter != waiters);
|
||||||
|
|
@ -1524,7 +1106,6 @@ const JSFunctionSpec AtomicsMethods[] = {
|
||||||
JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor),
|
JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor),
|
||||||
JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree),
|
JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree),
|
||||||
JS_FN("wait", atomics_wait, 4,0),
|
JS_FN("wait", atomics_wait, 4,0),
|
||||||
JS_FN("waitAsync", atomics_waitAsync, 4,0),
|
|
||||||
JS_FN("notify", atomics_notify, 3,0),
|
JS_FN("notify", atomics_notify, 3,0),
|
||||||
JS_FN("wake", atomics_notify, 3,0), //Legacy name
|
JS_FN("wake", atomics_notify, 3,0), //Legacy name
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,18 @@ class AtomicsObject : public JSObject
|
||||||
static MOZ_MUST_USE bool toString(JSContext* cx, unsigned int argc, Value* vp);
|
static MOZ_MUST_USE bool toString(JSContext* cx, unsigned int argc, Value* vp);
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_load(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_load(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_store(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_store(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_add(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_add(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_sub(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_sub(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_and(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_and(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_or(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_or(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_wait(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_wait(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_waitAsync(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool atomics_notify(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] bool atomics_notify(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
|
|
||||||
/* asm.js callouts */
|
/* asm.js callouts */
|
||||||
namespace wasm { class Instance; }
|
namespace wasm { class Instance; }
|
||||||
|
|
|
||||||
|
|
@ -55,50 +55,6 @@ function MapForEach(callbackfn, thisArg = undefined) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES2024
|
|
||||||
// Map.groupBy ( items, callbackfn )
|
|
||||||
function MapGroupBy(items, callbackfn) {
|
|
||||||
// Step 1.
|
|
||||||
RequireObjectCoercible(items);
|
|
||||||
|
|
||||||
// Step 2.
|
|
||||||
if (!IsCallable(callbackfn))
|
|
||||||
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
|
|
||||||
|
|
||||||
// Step 3.
|
|
||||||
var groups = std_Map_create();
|
|
||||||
|
|
||||||
// Step 4.
|
|
||||||
var k = 0;
|
|
||||||
|
|
||||||
// Steps 5-8.
|
|
||||||
for (var value of allowContentIter(items)) {
|
|
||||||
// Step 6.a.
|
|
||||||
if (k >= MAX_NUMERIC_INDEX)
|
|
||||||
ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
|
|
||||||
|
|
||||||
// Step 6.b.
|
|
||||||
var key = callContentFunction(callbackfn, undefined, value, k);
|
|
||||||
|
|
||||||
// Steps 6.c-d.
|
|
||||||
// Group values are always arrays, so undefined also tells us whether
|
|
||||||
// this is a new key without a second hash-table lookup.
|
|
||||||
var elements = callFunction(std_Map_get, groups, key);
|
|
||||||
if (elements !== undefined) {
|
|
||||||
callFunction(std_Array_push, elements, value);
|
|
||||||
} else {
|
|
||||||
elements = [value];
|
|
||||||
callFunction(std_Map_set, groups, key, elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 6.e.
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 9.
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
var iteratorTemp = { mapIterationResultPair : null };
|
var iteratorTemp = { mapIterationResultPair : null };
|
||||||
|
|
||||||
function MapIteratorNext() {
|
function MapIteratorNext() {
|
||||||
|
|
|
||||||
|
|
@ -332,15 +332,9 @@ const JSPropertySpec MapObject::staticProperties[] = {
|
||||||
JS_PS_END
|
JS_PS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
const JSFunctionSpec MapObject::staticMethods[] = {
|
|
||||||
JS_SELF_HOSTED_FN("groupBy", "MapGroupBy", 2, 0),
|
|
||||||
JS_FS_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static JSObject*
|
static JSObject*
|
||||||
InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSProtoKey key, Native construct,
|
InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSProtoKey key, Native construct,
|
||||||
const JSPropertySpec* properties, const JSFunctionSpec* methods,
|
const JSPropertySpec* properties, const JSFunctionSpec* methods,
|
||||||
const JSFunctionSpec* staticMethods,
|
|
||||||
const JSPropertySpec* staticProperties)
|
const JSPropertySpec* staticProperties)
|
||||||
{
|
{
|
||||||
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
||||||
|
|
@ -349,13 +343,8 @@ InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSPro
|
||||||
|
|
||||||
Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(key, cx), 0));
|
Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(key, cx), 0));
|
||||||
if (!ctor ||
|
if (!ctor ||
|
||||||
!JS_DefineProperties(cx, ctor, staticProperties))
|
!JS_DefineProperties(cx, ctor, staticProperties) ||
|
||||||
{
|
!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (staticMethods && !JS_DefineFunctions(cx, ctor, staticMethods))
|
|
||||||
return nullptr;
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
|
||||||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||||
!GlobalObject::initBuiltinConstructor(cx, global, key, ctor, proto))
|
!GlobalObject::initBuiltinConstructor(cx, global, key, ctor, proto))
|
||||||
{
|
{
|
||||||
|
|
@ -370,7 +359,7 @@ MapObject::initClass(JSContext* cx, JSObject* obj)
|
||||||
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
||||||
RootedObject proto(cx,
|
RootedObject proto(cx,
|
||||||
InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods,
|
InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods,
|
||||||
staticMethods, staticProperties));
|
staticProperties));
|
||||||
if (proto) {
|
if (proto) {
|
||||||
// Define the "entries" method.
|
// Define the "entries" method.
|
||||||
JSFunction* fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0);
|
JSFunction* fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0);
|
||||||
|
|
@ -1095,7 +1084,7 @@ SetObject::initClass(JSContext* cx, JSObject* obj)
|
||||||
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
||||||
RootedObject proto(cx,
|
RootedObject proto(cx,
|
||||||
InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods,
|
InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods,
|
||||||
nullptr, staticProperties));
|
staticProperties));
|
||||||
if (proto) {
|
if (proto) {
|
||||||
// Define the "values" method.
|
// Define the "values" method.
|
||||||
JSFunction* fun = JS_DefineFunction(cx, proto, "values", values, 0, 0);
|
JSFunction* fun = JS_DefineFunction(cx, proto, "values", values, 0, 0);
|
||||||
|
|
|
||||||
|
|
@ -109,10 +109,8 @@ class MapObject : public NativeObject {
|
||||||
|
|
||||||
static MOZ_MUST_USE bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
|
static MOZ_MUST_USE bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
|
||||||
JS::MutableHandle<GCVector<JS::Value>> entries);
|
JS::MutableHandle<GCVector<JS::Value>> entries);
|
||||||
[[nodiscard]] static bool entries(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool entries(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool get(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool has(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool has(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
[[nodiscard]] static bool set(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static MapObject* create(JSContext* cx, HandleObject proto = nullptr);
|
static MapObject* create(JSContext* cx, HandleObject proto = nullptr);
|
||||||
|
|
||||||
// Publicly exposed Map calls for JSAPI access (webidl maplike/setlike
|
// Publicly exposed Map calls for JSAPI access (webidl maplike/setlike
|
||||||
|
|
@ -139,7 +137,6 @@ class MapObject : public NativeObject {
|
||||||
|
|
||||||
static const JSPropertySpec properties[];
|
static const JSPropertySpec properties[];
|
||||||
static const JSFunctionSpec methods[];
|
static const JSFunctionSpec methods[];
|
||||||
static const JSFunctionSpec staticMethods[];
|
|
||||||
static const JSPropertySpec staticProperties[];
|
static const JSPropertySpec staticProperties[];
|
||||||
ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
|
ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
|
||||||
static ValueMap& extract(HandleObject o);
|
static ValueMap& extract(HandleObject o);
|
||||||
|
|
@ -153,20 +150,22 @@ class MapObject : public NativeObject {
|
||||||
|
|
||||||
static MOZ_MUST_USE bool iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind);
|
static MOZ_MUST_USE bool iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind);
|
||||||
|
|
||||||
[[nodiscard]] static bool size_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool size_impl(JSContext* cx, const CallArgs& args);
|
||||||
[[nodiscard]] static bool size(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool size(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool get_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool get_impl(JSContext* cx, const CallArgs& args);
|
||||||
[[nodiscard]] static bool has_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool get(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool set_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool has_impl(JSContext* cx, const CallArgs& args);
|
||||||
[[nodiscard]] static bool delete_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool set_impl(JSContext* cx, const CallArgs& args);
|
||||||
[[nodiscard]] static bool delete_(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool set(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool keys_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool delete_impl(JSContext* cx, const CallArgs& args);
|
||||||
[[nodiscard]] static bool keys(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool delete_(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool values_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool keys_impl(JSContext* cx, const CallArgs& args);
|
||||||
[[nodiscard]] static bool values(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool keys(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool entries_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool values_impl(JSContext* cx, const CallArgs& args);
|
||||||
[[nodiscard]] static bool clear_impl(JSContext* cx, const CallArgs& args);
|
static MOZ_MUST_USE bool values(JSContext* cx, unsigned argc, Value* vp);
|
||||||
[[nodiscard]] static bool clear(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool entries_impl(JSContext* cx, const CallArgs& args);
|
||||||
|
static MOZ_MUST_USE bool clear_impl(JSContext* cx, const CallArgs& args);
|
||||||
|
static MOZ_MUST_USE bool clear(JSContext* cx, unsigned argc, Value* vp);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MapIteratorObject : public NativeObject
|
class MapIteratorObject : public NativeObject
|
||||||
|
|
|
||||||
|
|
@ -240,16 +240,12 @@ function ObjectGroupBy(items, callbackfn) {
|
||||||
// Steps 5-8.
|
// Steps 5-8.
|
||||||
for (var value of allowContentIter(items)) {
|
for (var value of allowContentIter(items)) {
|
||||||
// Step 6.a.
|
// Step 6.a.
|
||||||
if (k >= MAX_NUMERIC_INDEX)
|
|
||||||
ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
|
|
||||||
|
|
||||||
// Step 6.b.
|
|
||||||
var key = callContentFunction(callbackfn, undefined, value, k);
|
var key = callContentFunction(callbackfn, undefined, value, k);
|
||||||
|
|
||||||
// Step 6.c.
|
// Step 6.b.
|
||||||
key = ToPropertyKey(key);
|
key = ToPropertyKey(key);
|
||||||
|
|
||||||
// Steps 6.d-e.
|
// Steps 6.c-d.
|
||||||
var elements = groups[key];
|
var elements = groups[key];
|
||||||
if (elements === undefined) {
|
if (elements === undefined) {
|
||||||
_DefineDataProperty(groups, key, [value]);
|
_DefineDataProperty(groups, key, [value]);
|
||||||
|
|
@ -257,7 +253,7 @@ function ObjectGroupBy(items, callbackfn) {
|
||||||
callFunction(std_Array_push, elements, value);
|
callFunction(std_Array_push, elements, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6.f.
|
// Step 6.e.
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3673,48 +3673,6 @@ Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES2024
|
|
||||||
// Promise.withResolvers ( )
|
|
||||||
static bool
|
|
||||||
Promise_static_withResolvers(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
|
|
||||||
// Step 1.
|
|
||||||
if (!args.thisv().isObject()) {
|
|
||||||
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, -1, args.thisv(), nullptr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
RootedObject C(cx, &args.thisv().toObject());
|
|
||||||
|
|
||||||
// Step 2.
|
|
||||||
Rooted<PromiseCapability> capability(cx);
|
|
||||||
if (!NewPromiseCapability(cx, C, &capability, false))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Step 3.
|
|
||||||
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
|
||||||
if (!obj)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Steps 4-7.
|
|
||||||
RootedValue promise(cx, ObjectValue(*capability.promise()));
|
|
||||||
if (!::JS_DefineProperty(cx, obj, "promise", promise, JSPROP_ENUMERATE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RootedValue resolve(cx, ObjectValue(*capability.resolve()));
|
|
||||||
if (!::JS_DefineProperty(cx, obj, "resolve", resolve, JSPROP_ENUMERATE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RootedValue reject(cx, ObjectValue(*capability.reject()));
|
|
||||||
if (!::JS_DefineProperty(cx, obj, "reject", reject, JSPROP_ENUMERATE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Step 8.
|
|
||||||
args.rval().setObject(*obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unforgeable version of ES2016, 25.4.4.5, Promise.resolve.
|
* Unforgeable version of ES2016, 25.4.4.5, Promise.resolve.
|
||||||
*/
|
*/
|
||||||
|
|
@ -5222,7 +5180,6 @@ static const JSFunctionSpec promise_static_methods[] = {
|
||||||
JS_FN("race", Promise_static_race, 1, 0),
|
JS_FN("race", Promise_static_race, 1, 0),
|
||||||
JS_FN("reject", Promise_reject, 1, 0),
|
JS_FN("reject", Promise_reject, 1, 0),
|
||||||
JS_FN("resolve", Promise_static_resolve, 1, 0),
|
JS_FN("resolve", Promise_static_resolve, 1, 0),
|
||||||
JS_FN("withResolvers", Promise_static_withResolvers, 0, 0),
|
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -165,8 +165,6 @@ class CountQueuingStrategy : public NativeObject
|
||||||
static const Class protoClass_;
|
static const Class protoClass_;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] bool InitStreamExtras(JSContext* cx, HandleObject global);
|
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
#endif /* builtin_Stream_h */
|
#endif /* builtin_Stream_h */
|
||||||
|
|
|
||||||
|
|
@ -654,82 +654,6 @@ function String_repeat(count) {
|
||||||
return T;
|
return T;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES2024
|
|
||||||
// String.prototype.isWellFormed ( )
|
|
||||||
function String_isWellFormed() {
|
|
||||||
// Steps 1-2.
|
|
||||||
RequireObjectCoercible(this);
|
|
||||||
var S = ToString(this);
|
|
||||||
|
|
||||||
// Step 3.
|
|
||||||
var length = S.length;
|
|
||||||
for (var k = 0; k < length; k++) {
|
|
||||||
var c = callFunction(std_String_charCodeAt, S, k);
|
|
||||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
|
||||||
if (k + 1 >= length)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var d = callFunction(std_String_charCodeAt, S, k + 1);
|
|
||||||
if (d < 0xDC00 || d > 0xDFFF)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
k++;
|
|
||||||
} else if (c >= 0xDC00 && c <= 0xDFFF) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES2024
|
|
||||||
// String.prototype.toWellFormed ( )
|
|
||||||
function String_toWellFormed() {
|
|
||||||
// Steps 1-2.
|
|
||||||
RequireObjectCoercible(this);
|
|
||||||
var S = ToString(this);
|
|
||||||
|
|
||||||
// Step 3.
|
|
||||||
var length = S.length;
|
|
||||||
var result = "";
|
|
||||||
var copied = 0;
|
|
||||||
|
|
||||||
for (var k = 0; k < length; k++) {
|
|
||||||
var c = callFunction(std_String_charCodeAt, S, k);
|
|
||||||
var isUnpairedSurrogate = false;
|
|
||||||
|
|
||||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
|
||||||
if (k + 1 < length) {
|
|
||||||
var d = callFunction(std_String_charCodeAt, S, k + 1);
|
|
||||||
if (d >= 0xDC00 && d <= 0xDFFF) {
|
|
||||||
k++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isUnpairedSurrogate = true;
|
|
||||||
} else if (c >= 0xDC00 && c <= 0xDFFF) {
|
|
||||||
isUnpairedSurrogate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUnpairedSurrogate) {
|
|
||||||
if (copied < k)
|
|
||||||
result += callFunction(String_substring, S, copied, k);
|
|
||||||
result += "\uFFFD";
|
|
||||||
copied = k + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copied === 0)
|
|
||||||
return S;
|
|
||||||
|
|
||||||
if (copied < length)
|
|
||||||
result += callFunction(String_substring, S, copied, length);
|
|
||||||
|
|
||||||
// Step 4.
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES6 draft specification, section 21.1.3.27, version 2013-09-27.
|
// ES6 draft specification, section 21.1.3.27, version 2013-09-27.
|
||||||
function String_iterator() {
|
function String_iterator() {
|
||||||
RequireObjectCoercible(this);
|
RequireObjectCoercible(this);
|
||||||
|
|
|
||||||
|
|
@ -37,25 +37,10 @@ function TypedArrayLengthMethod() {
|
||||||
return TypedArrayLength(this);
|
return TypedArrayLength(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TypedArrayContentTypeIsBigIntMethod() {
|
|
||||||
return IsBigInt64TypedArray(this) || IsBigUint64TypedArray(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ThrowIfTypedArrayOutOfBounds(tarray) {
|
|
||||||
if (TypedArrayIsOutOfBounds(tarray))
|
|
||||||
ThrowTypeError(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ThrowIfPossiblyWrappedTypedArrayOutOfBounds(tarray) {
|
|
||||||
if (PossiblyWrappedTypedArrayIsOutOfBounds(tarray))
|
|
||||||
ThrowTypeError(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
function GetAttachedArrayBuffer(tarray) {
|
function GetAttachedArrayBuffer(tarray) {
|
||||||
var buffer = ViewedArrayBufferIfReified(tarray);
|
var buffer = ViewedArrayBufferIfReified(tarray);
|
||||||
if (IsDetachedBuffer(buffer))
|
if (IsDetachedBuffer(buffer))
|
||||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
ThrowIfTypedArrayOutOfBounds(tarray);
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,13 +60,6 @@ function IsTypedArrayEnsuringArrayBuffer(arg) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsObject(arg) && IsPossiblyWrappedTypedArray(arg)) {
|
|
||||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(arg))
|
|
||||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(arg);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
callFunction(CallTypedArrayMethodIfWrapped, arg, "GetAttachedArrayBufferMethod");
|
callFunction(CallTypedArrayMethodIfWrapped, arg, "GetAttachedArrayBufferMethod");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +79,6 @@ function ValidateTypedArray(obj, error) {
|
||||||
if (IsPossiblyWrappedTypedArray(obj)) {
|
if (IsPossiblyWrappedTypedArray(obj)) {
|
||||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(obj))
|
if (PossiblyWrappedTypedArrayHasDetachedBuffer(obj))
|
||||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(obj);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1047,18 +1024,12 @@ function TypedArraySet(overloaded, offset = 0) {
|
||||||
// Steps 9-10.
|
// Steps 9-10.
|
||||||
var targetBuffer = GetAttachedArrayBuffer(target);
|
var targetBuffer = GetAttachedArrayBuffer(target);
|
||||||
|
|
||||||
ThrowIfTypedArrayOutOfBounds(target);
|
|
||||||
|
|
||||||
// Step 11.
|
// Step 11.
|
||||||
var targetLength = TypedArrayLength(target);
|
var targetLength = TypedArrayLength(target);
|
||||||
|
|
||||||
// Steps 12 et seq.
|
// Steps 12 et seq.
|
||||||
if (IsPossiblyWrappedTypedArray(overloaded)) {
|
if (IsPossiblyWrappedTypedArray(overloaded))
|
||||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(overloaded))
|
|
||||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(overloaded);
|
|
||||||
return SetFromTypedArray(target, overloaded, targetOffset, targetLength);
|
return SetFromTypedArray(target, overloaded, targetOffset, targetLength);
|
||||||
}
|
|
||||||
|
|
||||||
return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer);
|
return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer);
|
||||||
}
|
}
|
||||||
|
|
@ -1395,8 +1366,6 @@ function TypedArraySubarray(begin, end) {
|
||||||
"TypedArraySubarray");
|
"TypedArraySubarray");
|
||||||
}
|
}
|
||||||
|
|
||||||
GetAttachedArrayBuffer(obj);
|
|
||||||
|
|
||||||
// Steps 4-6.
|
// Steps 4-6.
|
||||||
var buffer = TypedArrayBuffer(obj);
|
var buffer = TypedArrayBuffer(obj);
|
||||||
var srcLength = TypedArrayLength(obj);
|
var srcLength = TypedArrayLength(obj);
|
||||||
|
|
@ -1877,10 +1846,7 @@ function ArrayBufferSlice(start, end) {
|
||||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
|
|
||||||
// Steps 19-21.
|
// Steps 19-21.
|
||||||
var currentLen = ArrayBufferByteLength(O);
|
ArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
|
||||||
var copyLen = first >= currentLen ? 0 : std_Math_min(newLen, currentLen - first);
|
|
||||||
if (copyLen > 0)
|
|
||||||
ArrayBufferCopyData(new_, 0, O, first | 0, copyLen | 0, isWrapped);
|
|
||||||
|
|
||||||
// Step 22.
|
// Step 22.
|
||||||
return new_;
|
return new_;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
|
|
||||||
#include "builtin/WeakRefObject.h"
|
|
||||||
#include "vm/SelfHosting.h"
|
#include "vm/SelfHosting.h"
|
||||||
|
|
||||||
#include "vm/Interpreter-inl.h"
|
#include "vm/Interpreter-inl.h"
|
||||||
|
|
@ -22,82 +21,23 @@ IsWeakMap(HandleValue v)
|
||||||
return v.isObject() && v.toObject().is<WeakMapObject>();
|
return v.isObject() && v.toObject().is<WeakMapObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WeakMapObject::Data
|
|
||||||
{
|
|
||||||
ObjectValueMap objectMap;
|
|
||||||
SymbolValueMap symbolMap;
|
|
||||||
|
|
||||||
Data(JSContext* cx, JSObject* owner)
|
|
||||||
: objectMap(cx, owner),
|
|
||||||
symbolMap(cx, owner)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool init() {
|
|
||||||
return objectMap.init() && symbolMap.init();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ObjectValueMap*
|
|
||||||
WeakMapObject::getMap()
|
|
||||||
{
|
|
||||||
Data* data = getData();
|
|
||||||
return data ? &data->objectMap : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolValueMap*
|
|
||||||
WeakMapObject::getSymbolMap()
|
|
||||||
{
|
|
||||||
Data* data = getData();
|
|
||||||
return data ? &data->symbolMap : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
EnsureWeakMapData(JSContext* cx, Handle<WeakMapObject*> mapObj)
|
|
||||||
{
|
|
||||||
if (mapObj->getData())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
auto data = cx->make_unique<WeakMapObject::Data>(cx, mapObj.get());
|
|
||||||
if (!data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!data->init()) {
|
|
||||||
JS_ReportOutOfMemory(cx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapObj->setPrivate(data.release());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
MOZ_ALWAYS_INLINE bool
|
||||||
WeakMap_has_impl(JSContext* cx, const CallArgs& args)
|
WeakMap_has_impl(JSContext* cx, const CallArgs& args)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||||
|
|
||||||
if (!CanBeHeldWeakly(args.get(0))) {
|
if (!args.get(0).isObject()) {
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||||
if (args.get(0).isObject()) {
|
|
||||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
|
||||||
JSObject* key = &args[0].toObject();
|
JSObject* key = &args[0].toObject();
|
||||||
if (map->has(key)) {
|
if (map->has(key)) {
|
||||||
args.rval().setBoolean(true);
|
args.rval().setBoolean(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (SymbolValueMap* map = weakMap.getSymbolMap()) {
|
|
||||||
JS::Symbol* key = args[0].toSymbol();
|
|
||||||
if (map->has(key)) {
|
|
||||||
args.rval().setBoolean(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -115,29 +55,18 @@ WeakMap_get_impl(JSContext* cx, const CallArgs& args)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||||
|
|
||||||
if (!CanBeHeldWeakly(args.get(0))) {
|
if (!args.get(0).isObject()) {
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||||
if (args.get(0).isObject()) {
|
|
||||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
|
||||||
JSObject* key = &args[0].toObject();
|
JSObject* key = &args[0].toObject();
|
||||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||||
args.rval().set(ptr->value());
|
args.rval().set(ptr->value());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (SymbolValueMap* map = weakMap.getSymbolMap()) {
|
|
||||||
JS::Symbol* key = args[0].toSymbol();
|
|
||||||
if (SymbolValueMap::Ptr ptr = map->lookup(key)) {
|
|
||||||
args.rval().set(ptr->value());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -155,14 +84,12 @@ WeakMap_delete_impl(JSContext* cx, const CallArgs& args)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||||
|
|
||||||
if (!CanBeHeldWeakly(args.get(0))) {
|
if (!args.get(0).isObject()) {
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||||
if (args.get(0).isObject()) {
|
|
||||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
|
||||||
JSObject* key = &args[0].toObject();
|
JSObject* key = &args[0].toObject();
|
||||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||||
map->remove(ptr);
|
map->remove(ptr);
|
||||||
|
|
@ -170,16 +97,6 @@ WeakMap_delete_impl(JSContext* cx, const CallArgs& args)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (SymbolValueMap* map = weakMap.getSymbolMap()) {
|
|
||||||
JS::Symbol* key = args[0].toSymbol();
|
|
||||||
if (SymbolValueMap::Ptr ptr = map->lookup(key)) {
|
|
||||||
map->remove(ptr);
|
|
||||||
args.rval().setBoolean(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -209,13 +126,22 @@ TryPreserveReflector(JSContext* cx, HandleObject obj)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static MOZ_ALWAYS_INLINE bool
|
||||||
SetWeakMapObjectEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||||
HandleObject key, HandleValue value)
|
HandleObject key, HandleValue value)
|
||||||
{
|
{
|
||||||
if (!EnsureWeakMapData(cx, mapObj))
|
|
||||||
return false;
|
|
||||||
ObjectValueMap* map = mapObj->getMap();
|
ObjectValueMap* map = mapObj->getMap();
|
||||||
|
if (!map) {
|
||||||
|
auto newMap = cx->make_unique<ObjectValueMap>(cx, mapObj.get());
|
||||||
|
if (!newMap)
|
||||||
|
return false;
|
||||||
|
if (!newMap->init()) {
|
||||||
|
JS_ReportOutOfMemory(cx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
map = newMap.release();
|
||||||
|
mapObj->setPrivate(map);
|
||||||
|
}
|
||||||
|
|
||||||
// Preserve wrapped native keys to prevent wrapper optimization.
|
// Preserve wrapped native keys to prevent wrapper optimization.
|
||||||
if (!TryPreserveReflector(cx, key))
|
if (!TryPreserveReflector(cx, key))
|
||||||
|
|
@ -236,28 +162,13 @@ SetWeakMapObjectEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
MOZ_ALWAYS_INLINE bool
|
||||||
SetWeakMapSymbolEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
WeakMap_set_impl(JSContext* cx, const CallArgs& args)
|
||||||
Handle<JS::Symbol*> key, HandleValue value)
|
|
||||||
{
|
{
|
||||||
if (!EnsureWeakMapData(cx, mapObj))
|
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||||
return false;
|
|
||||||
SymbolValueMap* map = mapObj->getSymbolMap();
|
|
||||||
|
|
||||||
MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
|
if (!args.get(0).isObject()) {
|
||||||
if (!map->put(key, value)) {
|
UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
|
||||||
JS_ReportOutOfMemory(cx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static MOZ_ALWAYS_INLINE bool
|
|
||||||
SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
|
||||||
HandleValue key, HandleValue value)
|
|
||||||
{
|
|
||||||
if (!CanBeHeldWeakly(key)) {
|
|
||||||
UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, key, nullptr);
|
|
||||||
if (!bytes)
|
if (!bytes)
|
||||||
return false;
|
return false;
|
||||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
|
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
|
||||||
|
|
@ -265,24 +176,11 @@ SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.isObject()) {
|
RootedObject key(cx, &args[0].toObject());
|
||||||
RootedObject objectKey(cx, &key.toObject());
|
|
||||||
return SetWeakMapObjectEntryInternal(cx, mapObj, objectKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rooted<JS::Symbol*> symbolKey(cx, key.toSymbol());
|
|
||||||
return SetWeakMapSymbolEntryInternal(cx, mapObj, symbolKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
|
||||||
WeakMap_set_impl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
|
||||||
|
|
||||||
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
|
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
|
||||||
Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
|
Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
|
||||||
|
|
||||||
if (!SetWeakMapEntryInternal(cx, map, args.get(0), args.get(1)))
|
if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
|
||||||
return false;
|
return false;
|
||||||
args.rval().set(args.thisv());
|
args.rval().set(args.thisv());
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -295,13 +193,6 @@ js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
|
return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
js::SetWeakMapEntryValue(JSContext* cx, HandleObject mapObj, HandleValue key, HandleValue val)
|
|
||||||
{
|
|
||||||
Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
|
|
||||||
return SetWeakMapEntryInternal(cx, rootedMap, key, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_FRIEND_API(bool)
|
JS_FRIEND_API(bool)
|
||||||
JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
|
JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
|
||||||
{
|
{
|
||||||
|
|
@ -314,11 +205,11 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||||
RootedObject arr(cx, NewDenseEmptyArray(cx));
|
RootedObject arr(cx, NewDenseEmptyArray(cx));
|
||||||
if (!arr)
|
if (!arr)
|
||||||
return false;
|
return false;
|
||||||
WeakMapObject::Data* data = obj->as<WeakMapObject>().getData();
|
ObjectValueMap* map = obj->as<WeakMapObject>().getMap();
|
||||||
if (data) {
|
if (map) {
|
||||||
// Prevent GC from mutating the weakmap while iterating.
|
// Prevent GC from mutating the weakmap while iterating.
|
||||||
AutoSuppressGC suppress(cx);
|
AutoSuppressGC suppress(cx);
|
||||||
for (ObjectValueMap::Base::Range r = data->objectMap.all(); !r.empty(); r.popFront()) {
|
for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
|
||||||
JS::ExposeObjectToActiveJS(r.front().key());
|
JS::ExposeObjectToActiveJS(r.front().key());
|
||||||
RootedObject key(cx, r.front().key());
|
RootedObject key(cx, r.front().key());
|
||||||
if (!cx->compartment()->wrap(cx, &key))
|
if (!cx->compartment()->wrap(cx, &key))
|
||||||
|
|
@ -326,14 +217,6 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||||
if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
|
if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (SymbolValueMap::Base::Range r = data->symbolMap.all(); !r.empty(); r.popFront()) {
|
|
||||||
gc::ExposeGCThingToActiveJS(JS::GCCellPtr(r.front().key().get()));
|
|
||||||
RootedValue key(cx, SymbolValue(r.front().key()));
|
|
||||||
if (!cx->compartment()->wrap(cx, &key))
|
|
||||||
return false;
|
|
||||||
if (!NewbornArrayPush(cx, arr, key))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ret.set(arr);
|
ret.set(arr);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -342,23 +225,21 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||||
static void
|
static void
|
||||||
WeakMap_mark(JSTracer* trc, JSObject* obj)
|
WeakMap_mark(JSTracer* trc, JSObject* obj)
|
||||||
{
|
{
|
||||||
if (WeakMapObject::Data* data = obj->as<WeakMapObject>().getData()) {
|
if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
|
||||||
data->objectMap.trace(trc);
|
map->trace(trc);
|
||||||
data->symbolMap.trace(trc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WeakMap_finalize(FreeOp* fop, JSObject* obj)
|
WeakMap_finalize(FreeOp* fop, JSObject* obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(fop->maybeOffMainThread());
|
MOZ_ASSERT(fop->maybeOffMainThread());
|
||||||
if (WeakMapObject::Data* data = obj->as<WeakMapObject>().getData()) {
|
if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
data->~Data();
|
map->~ObjectValueMap();
|
||||||
memset(static_cast<void*>(data), 0xdc, sizeof(*data));
|
memset(static_cast<void*>(map), 0xdc, sizeof(*map));
|
||||||
fop->free_(data);
|
fop->free_(map);
|
||||||
#else
|
#else
|
||||||
fop->delete_(data);
|
fop->delete_(map);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -401,8 +282,7 @@ JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
assertSameCompartment(cx, key, val);
|
assertSameCompartment(cx, key, val);
|
||||||
Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
|
Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
|
||||||
RootedValue keyValue(cx, ObjectValue(*key));
|
return SetWeakMapEntryInternal(cx, rootedMap, key, val);
|
||||||
return SetWeakMapEntryInternal(cx, rootedMap, keyValue, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -506,3 +386,4 @@ js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj)
|
||||||
{
|
{
|
||||||
return InitWeakMapClass(cx, obj, false);
|
return InitWeakMapClass(cx, obj, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,7 @@ class WeakMapObject : public NativeObject
|
||||||
public:
|
public:
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
struct Data;
|
ObjectValueMap* getMap() { return static_cast<ObjectValueMap*>(getPrivate()); }
|
||||||
|
|
||||||
Data* getData() { return static_cast<Data*>(getPrivate()); }
|
|
||||||
ObjectValueMap* getMap();
|
|
||||||
SymbolValueMap* getSymbolMap();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
|
||||||
|
|
@ -1,208 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* 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/. */
|
|
||||||
|
|
||||||
#include "builtin/WeakRefObject.h"
|
|
||||||
|
|
||||||
#include "jsapi.h"
|
|
||||||
#include "jscntxt.h"
|
|
||||||
|
|
||||||
#include "gc/Nursery.h"
|
|
||||||
#include "gc/Tracer.h"
|
|
||||||
#include "vm/GlobalObject.h"
|
|
||||||
|
|
||||||
#include "jsobjinlines.h"
|
|
||||||
|
|
||||||
#include "vm/Interpreter-inl.h"
|
|
||||||
#include "vm/NativeObject-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
|
|
||||||
static WeakRefObject::Referent*
|
|
||||||
GetReferent(JSObject* obj)
|
|
||||||
{
|
|
||||||
return obj->as<WeakRefObject>().getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
static MOZ_ALWAYS_INLINE bool
|
|
||||||
IsWeakRef(HandleValue v)
|
|
||||||
{
|
|
||||||
return v.isObject() && v.toObject().is<WeakRefObject>();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
WeakRef_deref_impl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsWeakRef(args.thisv()));
|
|
||||||
|
|
||||||
WeakRefObject::Referent* data = GetReferent(&args.thisv().toObject());
|
|
||||||
JSObject* target = data ? data->target.get() : nullptr;
|
|
||||||
if (target)
|
|
||||||
args.rval().setObject(*target);
|
|
||||||
else
|
|
||||||
args.rval().setUndefined();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const JSPropertySpec WeakRefObject::properties[] = {
|
|
||||||
JS_PS_END
|
|
||||||
};
|
|
||||||
|
|
||||||
const JSFunctionSpec WeakRefObject::methods[] = {
|
|
||||||
JS_FN("deref", WeakRefObject::deref, 0, 0),
|
|
||||||
JS_FS_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static JSObject*
|
|
||||||
InitWeakRefClass(JSContext* cx, HandleObject obj, bool defineMembers)
|
|
||||||
{
|
|
||||||
Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
|
||||||
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
|
||||||
if (!proto)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
RootedFunction ctor(cx, GlobalObject::createConstructor(cx, WeakRefObject::construct,
|
|
||||||
ClassName(JSProto_WeakRef, cx), 1));
|
|
||||||
if (!ctor)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (defineMembers) {
|
|
||||||
if (!DefinePropertiesAndFunctions(cx, proto, WeakRefObject::properties, WeakRefObject::methods))
|
|
||||||
return nullptr;
|
|
||||||
if (!DefineToStringTag(cx, proto, cx->names().WeakRef))
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakRef, ctor, proto))
|
|
||||||
return nullptr;
|
|
||||||
return proto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ WeakRefObject*
|
|
||||||
WeakRefObject::create(JSContext* cx, HandleObject target, HandleObject proto /* = nullptr */)
|
|
||||||
{
|
|
||||||
Rooted<WeakRefObject*> obj(cx, NewObjectWithClassProto<WeakRefObject>(cx, proto));
|
|
||||||
if (!obj)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
Referent* data = cx->new_<Referent>(target, cx->options().weakRefs());
|
|
||||||
if (!data)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
obj->setPrivate(data);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
|
|
||||||
if (!ThrowIfNotConstructing(cx, args, "WeakRef"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!args.get(0).isObject()) {
|
|
||||||
UniqueChars bytes =
|
|
||||||
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
|
|
||||||
if (!bytes)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
|
|
||||||
bytes.get());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedObject target(cx, &args[0].toObject());
|
|
||||||
|
|
||||||
RootedObject proto(cx);
|
|
||||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
|
||||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Rooted<WeakRefObject*> obj(cx, WeakRefObject::create(cx, target, proto));
|
|
||||||
if (!obj)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setObject(*obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
WeakRefObject::deref(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsWeakRef, WeakRef_deref_impl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ void
|
|
||||||
WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
|
||||||
{
|
|
||||||
if (Referent* data = GetReferent(obj)) {
|
|
||||||
JSObject* target = data->target.unbarrieredGet();
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// When pref-disabled, keep referent alive via strong trace so deref()
|
|
||||||
// stays usable as a stub without touching GC internals.
|
|
||||||
if (!data->enabled) {
|
|
||||||
TraceManuallyBarrieredEdge(trc, data->target.unsafeGet(), "WeakRef stub referent");
|
|
||||||
} else if (IsInsideNursery(target)) {
|
|
||||||
// Weak edges must be tenured; trace strongly while referent is in the nursery.
|
|
||||||
TraceManuallyBarrieredEdge(trc, data->target.unsafeGet(), "WeakRef nursery referent");
|
|
||||||
} else {
|
|
||||||
TraceWeakEdge(trc, &data->target, "WeakRef referent");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ void
|
|
||||||
WeakRefObject::finalize(FreeOp* fop, JSObject* obj)
|
|
||||||
{
|
|
||||||
if (Referent* data = GetReferent(obj))
|
|
||||||
fop->delete_(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const ClassOps WeakRefObjectClassOps = {
|
|
||||||
nullptr, /* addProperty */
|
|
||||||
nullptr, /* delProperty */
|
|
||||||
nullptr, /* getProperty */
|
|
||||||
nullptr, /* setProperty */
|
|
||||||
nullptr, /* enumerate */
|
|
||||||
nullptr, /* resolve */
|
|
||||||
nullptr, /* mayResolve */
|
|
||||||
WeakRefObject::finalize,
|
|
||||||
nullptr, /* call */
|
|
||||||
nullptr, /* hasInstance */
|
|
||||||
nullptr, /* construct */
|
|
||||||
WeakRefObject::trace
|
|
||||||
};
|
|
||||||
|
|
||||||
const Class WeakRefObject::class_ = {
|
|
||||||
"WeakRef",
|
|
||||||
JSCLASS_HAS_PRIVATE |
|
|
||||||
JSCLASS_HAS_CACHED_PROTO(JSProto_WeakRef) |
|
|
||||||
JSCLASS_BACKGROUND_FINALIZE,
|
|
||||||
&WeakRefObjectClassOps
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ JSObject*
|
|
||||||
WeakRefObject::initClass(JSContext* cx, HandleObject obj)
|
|
||||||
{
|
|
||||||
return ::InitWeakRefClass(cx, obj, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject*
|
|
||||||
js::InitWeakRefClass(JSContext* cx, HandleObject obj)
|
|
||||||
{
|
|
||||||
return WeakRefObject::initClass(cx, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject*
|
|
||||||
js::InitBareWeakRefCtor(JSContext* cx, HandleObject obj)
|
|
||||||
{
|
|
||||||
return ::InitWeakRefClass(cx, obj, false);
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* 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/. */
|
|
||||||
|
|
||||||
#ifndef builtin_WeakRefObject_h
|
|
||||||
#define builtin_WeakRefObject_h
|
|
||||||
|
|
||||||
#include "gc/Barrier.h"
|
|
||||||
#include "vm/NativeObject.h"
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
class WeakRefObject : public NativeObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct Referent {
|
|
||||||
explicit Referent(JSObject* obj, bool enabled)
|
|
||||||
: target(obj), enabled(enabled) {}
|
|
||||||
WeakRef<JSObject*> target;
|
|
||||||
bool enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Class class_;
|
|
||||||
|
|
||||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
|
||||||
static WeakRefObject* create(JSContext* cx, HandleObject target, HandleObject proto = nullptr);
|
|
||||||
|
|
||||||
static void trace(JSTracer* trc, JSObject* obj);
|
|
||||||
static void finalize(FreeOp* fop, JSObject* obj);
|
|
||||||
[[nodiscard]] static bool construct(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static bool deref(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
|
|
||||||
Referent* getData() const {
|
|
||||||
return static_cast<Referent*>(getPrivate());
|
|
||||||
}
|
|
||||||
|
|
||||||
WeakRef<JSObject*>& target() {
|
|
||||||
MOZ_ASSERT(getData());
|
|
||||||
return getData()->target;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const JSPropertySpec properties[];
|
|
||||||
static const JSFunctionSpec methods[];
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
extern JSObject*
|
|
||||||
InitWeakRefClass(JSContext* cx, HandleObject obj);
|
|
||||||
|
|
||||||
extern JSObject*
|
|
||||||
InitBareWeakRefCtor(JSContext* cx, HandleObject obj);
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
CanBeHeldWeakly(const Value& value)
|
|
||||||
{
|
|
||||||
return value.isObject() || value.isSymbol();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace js
|
|
||||||
|
|
||||||
#endif /* builtin_WeakRefObject_h */
|
|
||||||
|
|
@ -32,7 +32,7 @@ function WeakSet_add(value) {
|
||||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
if (!CanBeHeldWeakly(value))
|
if (!IsObject(value))
|
||||||
ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, value));
|
ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, value));
|
||||||
|
|
||||||
// Steps 7-8.
|
// Steps 7-8.
|
||||||
|
|
@ -55,7 +55,7 @@ function WeakSet_delete(value) {
|
||||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
if (!CanBeHeldWeakly(value))
|
if (!IsObject(value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Steps 7-8.
|
// Steps 7-8.
|
||||||
|
|
@ -75,7 +75,7 @@ function WeakSet_has(value) {
|
||||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
|
||||||
|
|
||||||
// Step 6.
|
// Step 6.
|
||||||
if (!CanBeHeldWeakly(value))
|
if (!IsObject(value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Steps 7-8.
|
// Steps 7-8.
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
#include "builtin/MapObject.h"
|
#include "builtin/MapObject.h"
|
||||||
#include "builtin/SelfHostingDefines.h"
|
#include "builtin/SelfHostingDefines.h"
|
||||||
#include "builtin/WeakMapObject.h"
|
#include "builtin/WeakMapObject.h"
|
||||||
#include "builtin/WeakRefObject.h"
|
|
||||||
#include "vm/GlobalObject.h"
|
#include "vm/GlobalObject.h"
|
||||||
#include "vm/SelfHosting.h"
|
#include "vm/SelfHosting.h"
|
||||||
|
|
||||||
|
|
@ -109,6 +108,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
if (optimized) {
|
if (optimized) {
|
||||||
RootedValue keyVal(cx);
|
RootedValue keyVal(cx);
|
||||||
|
RootedObject keyObject(cx);
|
||||||
RootedValue placeholder(cx, BooleanValue(true));
|
RootedValue placeholder(cx, BooleanValue(true));
|
||||||
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
|
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
|
||||||
RootedArrayObject array(cx, &iterable.toObject().as<ArrayObject>());
|
RootedArrayObject array(cx, &iterable.toObject().as<ArrayObject>());
|
||||||
|
|
@ -116,7 +116,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
keyVal.set(array->getDenseElement(index));
|
keyVal.set(array->getDenseElement(index));
|
||||||
MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
|
MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
|
||||||
|
|
||||||
if (!CanBeHeldWeakly(keyVal)) {
|
if (keyVal.isPrimitive()) {
|
||||||
UniqueChars bytes =
|
UniqueChars bytes =
|
||||||
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr);
|
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr);
|
||||||
if (!bytes)
|
if (!bytes)
|
||||||
|
|
@ -126,7 +126,8 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetWeakMapEntryValue(cx, map, keyVal, placeholder))
|
keyObject = &keyVal.toObject();
|
||||||
|
if (!SetWeakMapEntry(cx, map, keyObject, placeholder))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
// Run with: js number-to-string-bench.js
|
|
||||||
// Also supports: xpcshell -f number-to-string-bench.js
|
|
||||||
// Compare identical optimized builds, alternating baseline and patched runs.
|
|
||||||
// Reports milliseconds (lower is better); this is not a browser-suite score.
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
"use strict";
|
|
||||||
var iterations = 500000;
|
|
||||||
var samples = 7;
|
|
||||||
var checksum = 0;
|
|
||||||
|
|
||||||
function convert(values, radix, count) {
|
|
||||||
var total = 0;
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
total += values[i % values.length].toString(radix).length;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
function unique(count) {
|
|
||||||
var total = 0;
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
total += (123456.125 + i).toString().length;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
function measure(name, run) {
|
|
||||||
checksum += run(20000);
|
|
||||||
var times = [];
|
|
||||||
var expected = run(iterations);
|
|
||||||
for (var sample = 0; sample < samples; sample++) {
|
|
||||||
if (typeof gc === "function")
|
|
||||||
gc();
|
|
||||||
var start = Date.now();
|
|
||||||
var result = run(iterations);
|
|
||||||
times.push(Date.now() - start);
|
|
||||||
if (result !== expected)
|
|
||||||
throw new Error("inconsistent conversion: " + name);
|
|
||||||
checksum += result;
|
|
||||||
}
|
|
||||||
times.sort(function(a, b) { return a - b; });
|
|
||||||
print(name + ": median=" + times[3] + " ms; samples=" + times.join(","));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var size of [1, 2, 4, 5, 16]) {
|
|
||||||
var values = [];
|
|
||||||
for (var i = 0; i < size; i++)
|
|
||||||
values.push(123456.125 + i);
|
|
||||||
measure("decimal working set " + size, function(count) {
|
|
||||||
return convert(values, 10, count);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
measure("unique decimals", unique);
|
|
||||||
var integers = [123456, 654321, 123457, 654322];
|
|
||||||
measure("integer working set 4", function(count) {
|
|
||||||
return convert(integers, 10, count);
|
|
||||||
});
|
|
||||||
measure("hexadecimal working set 4", function(count) {
|
|
||||||
return convert(integers, 16, count);
|
|
||||||
});
|
|
||||||
print("checksum=" + checksum);
|
|
||||||
})();
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
// Focused JSON workloads motivated by Speedometer 2.1's in-memory TodoMVC store.
|
|
||||||
// Run with a JS shell, or xpcshell -f. Lower times are better. This does not
|
|
||||||
// measure Speedometer's DOM, layout, event dispatch, or overall suite score.
|
|
||||||
(function() {
|
|
||||||
var iterations = 2000;
|
|
||||||
var checksum = 0;
|
|
||||||
function measure(name, records) {
|
|
||||||
var text = JSON.stringify(records);
|
|
||||||
var samples = [];
|
|
||||||
for (var i = 0; i < 100; i++)
|
|
||||||
checksum += JSON.parse(text).length;
|
|
||||||
for (var sample = 0; sample < 7; sample++) {
|
|
||||||
if (typeof gc === 'function')
|
|
||||||
gc();
|
|
||||||
var start = Date.now();
|
|
||||||
for (var i = 0; i < iterations; i++)
|
|
||||||
checksum += JSON.parse(text).length;
|
|
||||||
samples.push(Date.now() - start);
|
|
||||||
}
|
|
||||||
samples.sort(function(a, b) { return a - b; });
|
|
||||||
print(name + ': median=' + samples[3] + ' ms; samples=' + samples.join(','));
|
|
||||||
}
|
|
||||||
var todos = [], unicode = [], collisions = [], unique = [];
|
|
||||||
for (var i = 0; i < 100; i++) {
|
|
||||||
todos.push({ id: i, title: 'Something to do ' + i, completed: false });
|
|
||||||
unicode.push({ '\u0101name': i, '\u03bbvalue': 'value', '\u4e2d': false });
|
|
||||||
collisions.push({ item: i, identifier: i + 1, index: i + 2 });
|
|
||||||
var record = {};
|
|
||||||
record['uniqueName' + i] = i;
|
|
||||||
unique.push(record);
|
|
||||||
}
|
|
||||||
measure('TodoMVC-shaped records', todos);
|
|
||||||
measure('two-byte names', unicode);
|
|
||||||
measure('cache collisions', collisions);
|
|
||||||
measure('unique names', unique);
|
|
||||||
measure('small parse', [todos[0]]);
|
|
||||||
print('checksum=' + checksum);
|
|
||||||
})();
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// Repeated names, collisions, encodings, and escaped names must all agree.
|
|
||||||
var keys = ['id', 'title', 'completed', 'items', 'item', '', 'identifier',
|
|
||||||
'i', 'a', 'same', 'samesize', '\u00e9', '\u0101', '\ud800',
|
|
||||||
'quote"key', 'slash\\key', 'line\nkey', '__proto__'];
|
|
||||||
var records = [];
|
|
||||||
for (var i = 0; i < 100; i++) {
|
|
||||||
var record = Object.create(null);
|
|
||||||
for (var k = 0; k < keys.length; k++)
|
|
||||||
record[keys[k]] = i + k;
|
|
||||||
records.push(record);
|
|
||||||
}
|
|
||||||
var text = JSON.stringify(records);
|
|
||||||
for (var repeat = 0; repeat < 30; repeat++) {
|
|
||||||
var parsed = JSON.parse(text);
|
|
||||||
assertEq(parsed.length, records.length);
|
|
||||||
for (var i = 0; i < parsed.length; i++) {
|
|
||||||
for (var k = 0; k < keys.length; k++)
|
|
||||||
assertEq(parsed[i][keys[k]], records[i][keys[k]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertEq(JSON.parse('[{"title":1},{"titleLonger":2}]')[1].titleLonger, 2);
|
|
||||||
assertEq(JSON.parse('[{"titleLonger":1},{"title":2}]')[1].title, 2);
|
|
||||||
assertEq(JSON.parse('[{"title":1},{"t\\u0069tle":2}]')[1].title, 2);
|
|
||||||
assertEq(JSON.parse('{"id":1,"id":2}').id, 2);
|
|
||||||
assertEq(JSON.parse('{"id":1}', function(key, value) {
|
|
||||||
return key === 'id' ? JSON.parse('{"id":2}').id : value;
|
|
||||||
}).id, 2);
|
|
||||||
|
|
||||||
for (var bad of ['[{"title":1},{"title', '[{"title":1},{"title"',
|
|
||||||
'[{"title":1},{"titleX":}]', '[{"title":1},{"ti\ntle":2}]']) {
|
|
||||||
var threw = false;
|
|
||||||
try { JSON.parse(bad); } catch (e) { threw = e instanceof SyntaxError; }
|
|
||||||
assertEq(threw, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep cached atoms alive across allocations made while parsing later values.
|
|
||||||
var large = '[{"uncommonPropertyForGC":0},' +
|
|
||||||
'{"other":[' + new Array(20000).fill('"allocation"').join(',') + ']},' +
|
|
||||||
'{"uncommonPropertyForGC":1}]';
|
|
||||||
for (var i = 0; i < 3; i++) {
|
|
||||||
gc();
|
|
||||||
assertEq(JSON.parse(large)[2].uncommonPropertyForGC, 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// Repeated keys must reuse their group, including SameValueZero keys.
|
|
||||||
var objectKey = {};
|
|
||||||
var symbolKey = Symbol();
|
|
||||||
var keys = [undefined, null, false, 0, -0, NaN, "key", objectKey, symbolKey];
|
|
||||||
for (var iteration = 0; iteration < 100; iteration++) {
|
|
||||||
var input = [];
|
|
||||||
for (var repeat = 0; repeat < 4; repeat++) {
|
|
||||||
for (var key of keys)
|
|
||||||
input.push(key);
|
|
||||||
}
|
|
||||||
var calls = 0;
|
|
||||||
var groups = Map.groupBy(input, function(value, index) {
|
|
||||||
assertEq(index, calls++);
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
assertEq(calls, input.length);
|
|
||||||
assertEq(groups.size, 8);
|
|
||||||
for (var key of keys)
|
|
||||||
assertEq(groups.get(key).length, key === 0 ? 8 : 4);
|
|
||||||
assertEq(groups.get(objectKey)[0], objectKey);
|
|
||||||
assertEq(groups.get(symbolKey)[0], symbolKey);
|
|
||||||
assertEq(groups.get(undefined)[0], undefined);
|
|
||||||
var order = Array.from(groups.keys());
|
|
||||||
assertEq(order[0], undefined);
|
|
||||||
assertEq(order[3], 0);
|
|
||||||
assertEq(order[4], NaN);
|
|
||||||
assertEq(order[7], symbolKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A throwing callback must still close the input iterator.
|
|
||||||
var closed = false;
|
|
||||||
function* values() {
|
|
||||||
try {
|
|
||||||
yield 1;
|
|
||||||
yield 2;
|
|
||||||
} finally {
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var sentinel = {};
|
|
||||||
try {
|
|
||||||
Map.groupBy(values(), function() { throw sentinel; });
|
|
||||||
throw new Error("callback did not throw");
|
|
||||||
} catch (error) {
|
|
||||||
assertEq(error, sentinel);
|
|
||||||
}
|
|
||||||
assertEq(closed, true);
|
|
||||||
|
|
@ -1470,12 +1470,6 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_
|
||||||
res.isNumber() &&
|
res.isNumber() &&
|
||||||
!TypedArrayGetElemStubExists(stub, obj))
|
!TypedArrayGetElemStubExists(stub, obj))
|
||||||
{
|
{
|
||||||
if (obj->is<TypedArrayObject>() &&
|
|
||||||
obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cx->runtime()->jitSupportsFloatingPoint &&
|
if (!cx->runtime()->jitSupportsFloatingPoint &&
|
||||||
(TypedThingRequiresFloatingPoint(obj) || rhs.isDouble()))
|
(TypedThingRequiresFloatingPoint(obj) || rhs.isDouble()))
|
||||||
{
|
{
|
||||||
|
|
@ -2166,8 +2160,6 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||||
Register obj = masm.extractObject(R0, ExtractTemp0);
|
Register obj = masm.extractObject(R0, ExtractTemp0);
|
||||||
masm.loadPtr(Address(ICStubReg, ICGetElem_TypedArray::offsetOfShape()), scratchReg);
|
masm.loadPtr(Address(ICStubReg, ICGetElem_TypedArray::offsetOfShape()), scratchReg);
|
||||||
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
|
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
|
||||||
if (layout_ == Layout_TypedArray)
|
|
||||||
GuardResizableOrGrowableTypedArray(masm, obj, scratchReg, &failure);
|
|
||||||
|
|
||||||
// Ensure the index is an integer.
|
// Ensure the index is an integer.
|
||||||
if (cx->runtime()->jitSupportsFloatingPoint) {
|
if (cx->runtime()->jitSupportsFloatingPoint) {
|
||||||
|
|
@ -2639,9 +2631,6 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||||
bool expectOutOfBounds;
|
bool expectOutOfBounds;
|
||||||
double idx = index.toNumber();
|
double idx = index.toNumber();
|
||||||
if (obj->is<TypedArrayObject>()) {
|
if (obj->is<TypedArrayObject>()) {
|
||||||
if (obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
expectOutOfBounds = (idx < 0 || idx >= double(obj->as<TypedArrayObject>().length()));
|
expectOutOfBounds = (idx < 0 || idx >= double(obj->as<TypedArrayObject>().length()));
|
||||||
} else {
|
} else {
|
||||||
// Typed objects throw on out of bounds accesses. Don't attach
|
// Typed objects throw on out of bounds accesses. Don't attach
|
||||||
|
|
@ -3232,8 +3221,6 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||||
Register obj = masm.extractObject(R0, ExtractTemp0);
|
Register obj = masm.extractObject(R0, ExtractTemp0);
|
||||||
masm.loadPtr(Address(ICStubReg, ICSetElem_TypedArray::offsetOfShape()), scratchReg);
|
masm.loadPtr(Address(ICStubReg, ICSetElem_TypedArray::offsetOfShape()), scratchReg);
|
||||||
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
|
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
|
||||||
if (layout_ == Layout_TypedArray)
|
|
||||||
GuardResizableOrGrowableTypedArray(masm, obj, scratchReg, &failure);
|
|
||||||
|
|
||||||
// Ensure the index is an integer.
|
// Ensure the index is an integer.
|
||||||
if (cx->runtime()->jitSupportsFloatingPoint) {
|
if (cx->runtime()->jitSupportsFloatingPoint) {
|
||||||
|
|
|
||||||
|
|
@ -8889,17 +8889,9 @@ CodeGenerator::branchIfNotEmptyObjectElements(Register obj, Label* target)
|
||||||
Address(obj, NativeObject::offsetOfElements()),
|
Address(obj, NativeObject::offsetOfElements()),
|
||||||
ImmPtr(js::emptyObjectElements),
|
ImmPtr(js::emptyObjectElements),
|
||||||
&emptyObj);
|
&emptyObj);
|
||||||
masm.branchPtr(Assembler::Equal,
|
|
||||||
Address(obj, NativeObject::offsetOfElements()),
|
|
||||||
ImmPtr(js::emptyObjectElementsShared),
|
|
||||||
&emptyObj);
|
|
||||||
masm.branchPtr(Assembler::Equal,
|
|
||||||
Address(obj, NativeObject::offsetOfElements()),
|
|
||||||
ImmPtr(js::emptyObjectElementsResizableOrGrowable),
|
|
||||||
&emptyObj);
|
|
||||||
masm.branchPtr(Assembler::NotEqual,
|
masm.branchPtr(Assembler::NotEqual,
|
||||||
Address(obj, NativeObject::offsetOfElements()),
|
Address(obj, NativeObject::offsetOfElements()),
|
||||||
ImmPtr(js::emptyObjectElementsSharedResizableOrGrowable),
|
ImmPtr(js::emptyObjectElementsShared),
|
||||||
target);
|
target);
|
||||||
masm.bind(&emptyObj);
|
masm.bind(&emptyObj);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -537,7 +537,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||||
Label* ifDoesntEmulateUndefined,
|
Label* ifDoesntEmulateUndefined,
|
||||||
Register scratch, OutOfLineTestObject* ool);
|
Register scratch, OutOfLineTestObject* ool);
|
||||||
|
|
||||||
// Branch to target unless obj has one of the empty elements pointers.
|
// Branch to target unless obj has an emptyObjectElements or emptyObjectElementsShared
|
||||||
|
// elements pointer.
|
||||||
void branchIfNotEmptyObjectElements(Register obj, Label* target);
|
void branchIfNotEmptyObjectElements(Register obj, Label* target);
|
||||||
|
|
||||||
void emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType,
|
void emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType,
|
||||||
|
|
|
||||||
|
|
@ -1245,7 +1245,6 @@ GenerateTypedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAtta
|
||||||
masm.branchPtr(Assembler::AboveOrEqual, tmpReg,
|
masm.branchPtr(Assembler::AboveOrEqual, tmpReg,
|
||||||
ImmPtr(&TypedArrayObject::classes[Scalar::MaxTypedArrayViewType]),
|
ImmPtr(&TypedArrayObject::classes[Scalar::MaxTypedArrayViewType]),
|
||||||
failures);
|
failures);
|
||||||
GuardResizableOrGrowableTypedArray(masm, object, tmpReg, failures);
|
|
||||||
|
|
||||||
// Load length.
|
// Load length.
|
||||||
masm.loadTypedOrValue(Address(object, TypedArrayObject::lengthOffset()), output);
|
masm.loadTypedOrValue(Address(object, TypedArrayObject::lengthOffset()), output);
|
||||||
|
|
@ -1645,9 +1644,6 @@ GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript
|
||||||
if (!JSID_IS_ATOM(id, cx->names().length))
|
if (!JSID_IS_ATOM(id, cx->names().length))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (hasTypedArrayLengthStub(obj))
|
if (hasTypedArrayLengthStub(obj))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -4042,12 +4038,6 @@ GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& i
|
||||||
if (!obj->is<TypedArrayObject>() && !obj->is<UnboxedArrayObject>())
|
if (!obj->is<TypedArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (obj->is<TypedArrayObject>() &&
|
|
||||||
obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(idval.isInt32() || idval.isString());
|
MOZ_ASSERT(idval.isInt32() || idval.isString());
|
||||||
|
|
||||||
// Don't emit a stub if the access is out of bounds. We make to make
|
// Don't emit a stub if the access is out of bounds. We make to make
|
||||||
|
|
@ -4102,9 +4092,6 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
|
||||||
// Decide to what type index the stub should be optimized
|
// Decide to what type index the stub should be optimized
|
||||||
Register tmpReg = output.scratchReg().gpr();
|
Register tmpReg = output.scratchReg().gpr();
|
||||||
MOZ_ASSERT(tmpReg != InvalidReg);
|
MOZ_ASSERT(tmpReg != InvalidReg);
|
||||||
if (array->is<TypedArrayObject>())
|
|
||||||
GuardResizableOrGrowableTypedArray(masm, object, tmpReg, &failures);
|
|
||||||
|
|
||||||
Register indexReg = tmpReg;
|
Register indexReg = tmpReg;
|
||||||
if (idval.isString()) {
|
if (idval.isString()) {
|
||||||
MOZ_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
|
MOZ_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
|
||||||
|
|
@ -4588,7 +4575,6 @@ GenerateSetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::Stub
|
||||||
if (!shape)
|
if (!shape)
|
||||||
return false;
|
return false;
|
||||||
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
|
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
|
||||||
GuardResizableOrGrowableTypedArray(masm, object, temp, &failures);
|
|
||||||
|
|
||||||
// Ensure the index is an int32.
|
// Ensure the index is an int32.
|
||||||
Register indexReg;
|
Register indexReg;
|
||||||
|
|
@ -4675,9 +4661,6 @@ SetPropertyIC::tryAttachTypedArrayElement(JSContext* cx, HandleScript outerScrip
|
||||||
if (!IsTypedArrayElementSetInlineable(obj, idval, val))
|
if (!IsTypedArrayElementSetInlineable(obj, idval, val))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
*emitted = true;
|
*emitted = true;
|
||||||
|
|
||||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||||
|
|
|
||||||
|
|
@ -365,9 +365,13 @@ IonBuilder::inlineNativeGetter(CallInfo& callInfo, JSFunction* target)
|
||||||
|
|
||||||
// Try to optimize typed array lengths.
|
// Try to optimize typed array lengths.
|
||||||
if (TypedArrayObject::isOriginalLengthGetter(native)) {
|
if (TypedArrayObject::isOriginalLengthGetter(native)) {
|
||||||
// RAB/GSAB views can have dynamic length or temporarily become
|
Scalar::Type type = thisTypes->getTypedArrayType(constraints());
|
||||||
// out-of-bounds. Let the property IC/VM path handle the getter.
|
if (type == Scalar::MaxTypedArrayViewType)
|
||||||
return InliningStatus_NotInlined;
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MInstruction* length = addTypedArrayLength(thisArg);
|
||||||
|
current->push(length);
|
||||||
|
return InliningStatus_Inlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to optimize RegExp getters.
|
// Try to optimize RegExp getters.
|
||||||
|
|
@ -2476,10 +2480,21 @@ IsTypedArrayObject(CompilerConstraintList* constraints, MDefinition* def)
|
||||||
IonBuilder::InliningStatus
|
IonBuilder::InliningStatus
|
||||||
IonBuilder::inlinePossiblyWrappedTypedArrayLength(CallInfo& callInfo)
|
IonBuilder::inlinePossiblyWrappedTypedArrayLength(CallInfo& callInfo)
|
||||||
{
|
{
|
||||||
(void) callInfo;
|
MOZ_ASSERT(!callInfo.constructing());
|
||||||
|
MOZ_ASSERT(callInfo.argc() == 1);
|
||||||
// RAB/GSAB views require dynamic length semantics.
|
if (callInfo.getArg(0)->type() != MIRType::Object)
|
||||||
return InliningStatus_NotInlined;
|
return InliningStatus_NotInlined;
|
||||||
|
if (getInlineReturnType() != MIRType::Int32)
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
if (!IsTypedArrayObject(constraints(), callInfo.getArg(0)))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MInstruction* length = addTypedArrayLength(callInfo.getArg(0));
|
||||||
|
current->push(length);
|
||||||
|
|
||||||
|
callInfo.setImplicitlyUsedUnchecked();
|
||||||
|
return InliningStatus_Inlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
IonBuilder::InliningStatus
|
IonBuilder::InliningStatus
|
||||||
|
|
@ -3236,14 +3251,45 @@ bool
|
||||||
IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayType,
|
IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayType,
|
||||||
bool* requiresTagCheck, AtomicCheckResult checkResult)
|
bool* requiresTagCheck, AtomicCheckResult checkResult)
|
||||||
{
|
{
|
||||||
(void) callInfo;
|
if (!JitSupportsAtomics())
|
||||||
(void) arrayType;
|
|
||||||
(void) requiresTagCheck;
|
|
||||||
(void) checkResult;
|
|
||||||
|
|
||||||
// Atomics bounds checks through addTypedArrayLengthAndData assume stable
|
|
||||||
// SharedArrayBuffer lengths. Avoid this path now that growable SAB exists.
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (callInfo.getArg(0)->type() != MIRType::Object)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (callInfo.getArg(1)->type() != MIRType::Int32)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ensure that the first argument is a TypedArray that maps shared
|
||||||
|
// memory.
|
||||||
|
//
|
||||||
|
// Then check both that the element type is something we can
|
||||||
|
// optimize and that the return type is suitable for that element
|
||||||
|
// type.
|
||||||
|
|
||||||
|
TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
|
||||||
|
if (!arg0Types)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TemporaryTypeSet::TypedArraySharedness sharedness;
|
||||||
|
*arrayType = arg0Types->getTypedArrayType(constraints(), &sharedness);
|
||||||
|
*requiresTagCheck = sharedness != TemporaryTypeSet::KnownShared;
|
||||||
|
switch (*arrayType) {
|
||||||
|
case Scalar::Int8:
|
||||||
|
case Scalar::Uint8:
|
||||||
|
case Scalar::Int16:
|
||||||
|
case Scalar::Uint16:
|
||||||
|
case Scalar::Int32:
|
||||||
|
return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType::Int32;
|
||||||
|
case Scalar::Uint32:
|
||||||
|
// Bug 1077305: it would be attractive to allow inlining even
|
||||||
|
// if the inline return type is Int32, which it will frequently
|
||||||
|
// be.
|
||||||
|
return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType::Double;
|
||||||
|
default:
|
||||||
|
// Excludes floating types and Uint8Clamped.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -5505,15 +5505,25 @@ jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints,
|
||||||
MDefinition* obj, MDefinition* id,
|
MDefinition* obj, MDefinition* id,
|
||||||
Scalar::Type* arrayType)
|
Scalar::Type* arrayType)
|
||||||
{
|
{
|
||||||
(void) constraints;
|
if (obj->mightBeType(MIRType::String))
|
||||||
(void) obj;
|
|
||||||
(void) id;
|
|
||||||
(void) arrayType;
|
|
||||||
|
|
||||||
// Resizable ArrayBuffer and growable SharedArrayBuffer views need dynamic
|
|
||||||
// length/out-of-bounds handling. This older MIR path assumes stable
|
|
||||||
// typed-array length/data slots, so keep element accesses on IC/VM paths.
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (id->type() != MIRType::Int32 && id->type() != MIRType::Double)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TemporaryTypeSet* types = obj->resultTypeSet();
|
||||||
|
if (!types)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*arrayType = types->getTypedArrayType(constraints);
|
||||||
|
|
||||||
|
// FIXME: https://bugzil.la/1536699
|
||||||
|
if (*arrayType == Scalar::MaxTypedArrayViewType ||
|
||||||
|
Scalar::isBigIntType(*arrayType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -3631,17 +3631,6 @@ CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Labe
|
||||||
masm.branch32(Assembler::NotEqual, AbsoluteAddress(address), Imm32(0), failure);
|
masm.branch32(Assembler::NotEqual, AbsoluteAddress(address), Imm32(0), failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
GuardResizableOrGrowableTypedArray(MacroAssembler& masm, Register obj, Register scratch,
|
|
||||||
Label* failure)
|
|
||||||
{
|
|
||||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
|
|
||||||
masm.branchTest32(Assembler::NonZero,
|
|
||||||
Address(scratch, ObjectElements::offsetOfFlags()),
|
|
||||||
Imm32(ObjectElements::RESIZABLE_OR_GROWABLE_BUFFER),
|
|
||||||
failure);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result)
|
LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2307,11 +2307,7 @@ CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register
|
||||||
void
|
void
|
||||||
CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure);
|
CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure);
|
||||||
|
|
||||||
void
|
MOZ_MUST_USE bool
|
||||||
GuardResizableOrGrowableTypedArray(MacroAssembler& masm, Register obj, Register scratch,
|
|
||||||
Label* failure);
|
|
||||||
|
|
||||||
[[nodiscard]] bool
|
|
||||||
DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
|
DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
|
||||||
MutableHandleValue result);
|
MutableHandleValue result);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -548,8 +548,6 @@ MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array")
|
||||||
|
|
||||||
// Typed array
|
// Typed array
|
||||||
MSG_DEF(JSMSG_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
|
MSG_DEF(JSMSG_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
|
||||||
MSG_DEF(JSMSG_ARRAYBUFFER_NOT_RESIZABLE, 0, JSEXN_TYPEERR, "ArrayBuffer is not resizable")
|
|
||||||
MSG_DEF(JSMSG_ARRAYBUFFER_CANNOT_DETACH, 0, JSEXN_TYPEERR, "ArrayBuffer cannot be detached")
|
|
||||||
MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
|
MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
|
||||||
MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
|
MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
|
||||||
MSG_DEF(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected ArrayBuffer with at least {0} bytes, but species constructor returns ArrayBuffer with {1} bytes")
|
MSG_DEF(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected ArrayBuffer with at least {0} bytes, but species constructor returns ArrayBuffer with {1} bytes")
|
||||||
|
|
@ -557,15 +555,12 @@ MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 0, JSEXN_TYPEERR, "invalid arguments")
|
||||||
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
|
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
|
||||||
MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
|
MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
|
||||||
MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer")
|
MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer")
|
||||||
MSG_DEF(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS, 0, JSEXN_TYPEERR, "attempting to access out-of-bounds TypedArray")
|
|
||||||
MSG_DEF(JSMSG_DATA_VIEW_OUT_OF_BOUNDS, 0, JSEXN_TYPEERR, "attempting to access out-of-bounds DataView")
|
|
||||||
MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%")
|
MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%")
|
||||||
MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object")
|
MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object")
|
||||||
MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}")
|
MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}")
|
||||||
|
|
||||||
// Shared array buffer
|
// Shared array buffer
|
||||||
MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH, 0, JSEXN_RANGEERR, "length argument out of range")
|
MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH, 0, JSEXN_RANGEERR, "length argument out of range")
|
||||||
MSG_DEF(JSMSG_SHARED_ARRAY_NOT_GROWABLE, 0, JSEXN_TYPEERR, "SharedArrayBuffer is not growable")
|
|
||||||
MSG_DEF(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected SharedArrayBuffer, but species constructor returned non-SharedArrayBuffer")
|
MSG_DEF(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected SharedArrayBuffer, but species constructor returned non-SharedArrayBuffer")
|
||||||
MSG_DEF(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different SharedArrayBuffer, but species constructor returned same SharedArrayBuffer")
|
MSG_DEF(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different SharedArrayBuffer, but species constructor returned same SharedArrayBuffer")
|
||||||
MSG_DEF(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected SharedArrayBuffer with at least {0} bytes, but species constructor returns SharedArrayBuffer with {1} bytes")
|
MSG_DEF(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected SharedArrayBuffer with at least {0} bytes, but species constructor returns SharedArrayBuffer with {1} bytes")
|
||||||
|
|
|
||||||
|
|
@ -66,56 +66,6 @@ BEGIN_TEST(testIndexToString)
|
||||||
}
|
}
|
||||||
END_TEST(testIndexToString)
|
END_TEST(testIndexToString)
|
||||||
|
|
||||||
BEGIN_TEST(testDtoaCacheInterleaved)
|
|
||||||
{
|
|
||||||
JS::RootedString first(cx, js::NumberToString<js::CanGC>(cx, 1234.5));
|
|
||||||
CHECK(first);
|
|
||||||
JS::RootedString second(cx, js::NumberToString<js::CanGC>(cx, 6789.5));
|
|
||||||
CHECK(second);
|
|
||||||
JS::RootedString third(cx, js::IndexToString(cx, 123456));
|
|
||||||
CHECK(third);
|
|
||||||
JS::RootedString fourth(cx, js::IndexToString(cx, 654321));
|
|
||||||
CHECK(fourth);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 10; i++) {
|
|
||||||
CHECK(js::NumberToString<js::CanGC>(cx, 1234.5) == first);
|
|
||||||
CHECK(js::NumberToString<js::CanGC>(cx, 6789.5) == second);
|
|
||||||
CHECK(js::IndexToString(cx, 123456) == third);
|
|
||||||
CHECK(js::IndexToString(cx, 654321) == fourth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Every raw string pointer must be invalidated, not just the latest one.
|
|
||||||
JS_GC(cx);
|
|
||||||
CHECK(!cx->compartment()->dtoaCache.lookup(10, 1234.5));
|
|
||||||
CHECK(!cx->compartment()->dtoaCache.lookup(10, 6789.5));
|
|
||||||
CHECK(!cx->compartment()->dtoaCache.lookup(10, 123456));
|
|
||||||
CHECK(!cx->compartment()->dtoaCache.lookup(10, 654321));
|
|
||||||
|
|
||||||
// The radix is part of the key. Signed zero can share its string.
|
|
||||||
js::DtoaCache cache;
|
|
||||||
cache.cache(10, 0.0, &first->asFlat());
|
|
||||||
cache.cache(16, 0.0, &second->asFlat());
|
|
||||||
CHECK(cache.lookup(10, -0.0) == first);
|
|
||||||
CHECK(cache.lookup(16, -0.0) == second);
|
|
||||||
CHECK(!cache.lookup(2, 0.0));
|
|
||||||
cache.purge();
|
|
||||||
CHECK(!cache.lookup(10, 0.0));
|
|
||||||
CHECK(!cache.lookup(16, 0.0));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 12; i++)
|
|
||||||
cache.cache(10, double(i), &first->asFlat());
|
|
||||||
for (size_t i = 0; i < 8; i++)
|
|
||||||
CHECK(!cache.lookup(10, double(i)));
|
|
||||||
for (size_t i = 8; i < 12; i++)
|
|
||||||
CHECK(cache.lookup(10, double(i)) == first);
|
|
||||||
|
|
||||||
// A failed string allocation must never turn into a cache hit.
|
|
||||||
cache.cache(10, 12.0, nullptr);
|
|
||||||
CHECK(!cache.lookup(10, 12.0));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
END_TEST(testDtoaCacheInterleaved)
|
|
||||||
|
|
||||||
BEGIN_TEST(testStringIsIndex)
|
BEGIN_TEST(testStringIsIndex)
|
||||||
{
|
{
|
||||||
for (size_t i = 0, sz = ArrayLength(tests); i < sz; i++) {
|
for (size_t i = 0, sz = ArrayLength(tests); i < sz; i++) {
|
||||||
|
|
|
||||||
|
|
@ -951,20 +951,6 @@ LookupStdName(const JSAtomState& names, JSAtom* name, const JSStdName* table)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
IsStreamExtraName(JSFlatString* name)
|
|
||||||
{
|
|
||||||
return JS_FlatStringEqualsAscii(name, "WritableStream") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "WritableStreamDefaultWriter") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "WritableStreamDefaultController") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "TransformStream") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "TransformStreamDefaultController") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "TextEncoderStream") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "TextDecoderStream") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "CompressionStream") ||
|
|
||||||
JS_FlatStringEqualsAscii(name, "DecompressionStream");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Table of standard classes, indexed by JSProtoKey. For entries where the
|
* Table of standard classes, indexed by JSProtoKey. For entries where the
|
||||||
* JSProtoKey does not correspond to a class with a meaningful constructor, we
|
* JSProtoKey does not correspond to a class with a meaningful constructor, we
|
||||||
|
|
@ -1055,27 +1041,11 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso
|
||||||
if (!GlobalObject::ensureConstructor(cx, global, key))
|
if (!GlobalObject::ensureConstructor(cx, global, key))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (key == JSProto_ReadableStream) {
|
|
||||||
RootedObject globalObj(cx, global);
|
|
||||||
if (!InitStreamExtras(cx, globalObj))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*resolved = true;
|
*resolved = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cx->options().streams() && IsStreamExtraName(idAtom)) {
|
|
||||||
RootedObject globalObj(cx, global);
|
|
||||||
if (!InitStreamExtras(cx, globalObj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!HasOwnProperty(cx, globalObj, id, resolved))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is no such property to resolve. An ordinary resolve hook would
|
// There is no such property to resolve. An ordinary resolve hook would
|
||||||
// just return true at this point. But the global object is special in one
|
// just return true at this point. But the global object is special in one
|
||||||
// more way: its prototype chain is lazily initialized. That is,
|
// more way: its prototype chain is lazily initialized. That is,
|
||||||
|
|
@ -1105,7 +1075,6 @@ JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj
|
||||||
|
|
||||||
return atom == names.undefined ||
|
return atom == names.undefined ||
|
||||||
atom == names.globalThis ||
|
atom == names.globalThis ||
|
||||||
IsStreamExtraName(atom) ||
|
|
||||||
LookupStdName(names, atom, standard_class_names) ||
|
LookupStdName(names, atom, standard_class_names) ||
|
||||||
LookupStdName(names, atom, builtin_property_names);
|
LookupStdName(names, atom, builtin_property_names);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -998,9 +998,7 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||||
werror_(false),
|
werror_(false),
|
||||||
strictMode_(false),
|
strictMode_(false),
|
||||||
extraWarnings_(false),
|
extraWarnings_(false),
|
||||||
arrayProtoValues_(true),
|
arrayProtoValues_(true)
|
||||||
streams_(true),
|
|
||||||
weakRefs_(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1140,16 +1138,6 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool weakRefs() const { return weakRefs_; }
|
|
||||||
ContextOptions& setWeakRefs(bool flag) {
|
|
||||||
weakRefs_ = flag;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ContextOptions& toggleWeakRefs() {
|
|
||||||
weakRefs_ = !weakRefs_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool baseline_ : 1;
|
bool baseline_ : 1;
|
||||||
bool ion_ : 1;
|
bool ion_ : 1;
|
||||||
|
|
@ -1167,7 +1155,6 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||||
bool extraWarnings_ : 1;
|
bool extraWarnings_ : 1;
|
||||||
bool arrayProtoValues_ : 1;
|
bool arrayProtoValues_ : 1;
|
||||||
bool streams_ : 1;
|
bool streams_ : 1;
|
||||||
bool weakRefs_ : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
JS_PUBLIC_API(ContextOptions&)
|
JS_PUBLIC_API(ContextOptions&)
|
||||||
|
|
|
||||||
|
|
@ -1221,12 +1221,7 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
|
||||||
sepstr = cx->names().comma;
|
sepstr = cx->names().comma;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: empty arrays always join to the empty string.
|
// Step 6 is implicit in the loops below.
|
||||||
// (Separator ToString above still runs for side effects / OOM.)
|
|
||||||
if (length == 0) {
|
|
||||||
args.rval().setString(cx->names().empty);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// An optimized version of a special case of steps 7-11: when length==1 and
|
// An optimized version of a special case of steps 7-11: when length==1 and
|
||||||
// the 0th element is a string, ToString() of that element is a no-op and
|
// the 0th element is a string, ToString() of that element is a no-op and
|
||||||
|
|
@ -1386,8 +1381,8 @@ template <JSValueType Type>
|
||||||
DenseElementResult
|
DenseElementResult
|
||||||
ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
||||||
{
|
{
|
||||||
/* Empty, singleton, or uninitialized arrays are already reversed. */
|
/* An empty array or an array with no elements is already reversed. */
|
||||||
if (length <= 1 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
|
if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
|
||||||
return DenseElementResult::Success;
|
return DenseElementResult::Success;
|
||||||
|
|
||||||
if (Type == JSVAL_TYPE_MAGIC) {
|
if (Type == JSVAL_TYPE_MAGIC) {
|
||||||
|
|
@ -1456,12 +1451,6 @@ js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (!GetLengthProperty(cx, obj, &len))
|
if (!GetLengthProperty(cx, obj, &len))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// length 0/1: reverse is a no-op; return this immediately.
|
|
||||||
if (len <= 1) {
|
|
||||||
args.rval().setObject(*obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||||
ArrayReverseDenseKernelFunctor functor(cx, obj, len);
|
ArrayReverseDenseKernelFunctor functor(cx, obj, len);
|
||||||
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||||
|
|
@ -3227,7 +3216,6 @@ static const JSFunctionSpec array_methods[] = {
|
||||||
/* ES2023 proposals */
|
/* ES2023 proposals */
|
||||||
JS_SELF_HOSTED_FN("findLast", "ArrayFindLast", 1,0),
|
JS_SELF_HOSTED_FN("findLast", "ArrayFindLast", 1,0),
|
||||||
JS_SELF_HOSTED_FN("findLastIndex", "ArrayFindLastIndex", 1,0),
|
JS_SELF_HOSTED_FN("findLastIndex", "ArrayFindLastIndex", 1,0),
|
||||||
JS_SELF_HOSTED_FN("toSorted", "ArrayToSorted", 1,0),
|
|
||||||
|
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
@ -3402,8 +3390,7 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
|
||||||
!DefineProperty(cx, unscopables, cx->names().flatMap, value) ||
|
!DefineProperty(cx, unscopables, cx->names().flatMap, value) ||
|
||||||
!DefineProperty(cx, unscopables, cx->names().includes, value) ||
|
!DefineProperty(cx, unscopables, cx->names().includes, value) ||
|
||||||
!DefineProperty(cx, unscopables, cx->names().keys, value) ||
|
!DefineProperty(cx, unscopables, cx->names().keys, value) ||
|
||||||
!DefineProperty(cx, unscopables, cx->names().values, value) ||
|
!DefineProperty(cx, unscopables, cx->names().values, value))
|
||||||
!DefineProperty(cx, unscopables, cx->names().toSorted, value))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -214,9 +214,7 @@ JSCompartment::ensureJitCompartmentExists(JSContext* cx)
|
||||||
void
|
void
|
||||||
js::DtoaCache::checkCacheAfterMovingGC()
|
js::DtoaCache::checkCacheAfterMovingGC()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!recent.s || !IsForwarded(recent.s));
|
MOZ_ASSERT(!s || !IsForwarded(s));
|
||||||
for (const auto& entry : previous)
|
|
||||||
MOZ_ASSERT(!entry.s || !IsForwarded(entry.s));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
||||||
|
|
@ -38,47 +38,29 @@ class ScriptSourceObject;
|
||||||
struct NativeIterator;
|
struct NativeIterator;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A small cache for number-to-string conversions, keyed by number and radix.
|
* A single-entry cache for some base-10 double-to-string conversions. This
|
||||||
* Keep several results so interleaved conversions do not evict each other.
|
* helps date-format-xparb.js. It also avoids skewing the results for
|
||||||
* These strings are not traced: every entry must be cleared before GC.
|
* v8-splay.js when measured by the SunSpider harness, where the splay tree
|
||||||
|
* initialization (which includes many repeated double-to-string conversions)
|
||||||
|
* is erroneously included in the measurement; see bug 562553.
|
||||||
*/
|
*/
|
||||||
class DtoaCache {
|
class DtoaCache {
|
||||||
struct Entry {
|
|
||||||
double d;
|
double d;
|
||||||
int base;
|
int base;
|
||||||
JSFlatString* s; // if s==nullptr, d and base are not valid
|
JSFlatString* s; // if s==nullptr, d and base are not valid
|
||||||
};
|
|
||||||
static const size_t NumPrevious = 3;
|
|
||||||
Entry recent;
|
|
||||||
Entry previous[NumPrevious];
|
|
||||||
size_t next;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DtoaCache() { purge(); }
|
DtoaCache() : s(nullptr) {}
|
||||||
void purge() {
|
void purge() { s = nullptr; }
|
||||||
recent.s = nullptr;
|
|
||||||
for (auto& entry : previous)
|
|
||||||
entry.s = nullptr;
|
|
||||||
next = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSFlatString* lookup(int base, double d) {
|
JSFlatString* lookup(int base, double d) {
|
||||||
// Preserve the cheap path for consecutive conversions of one value.
|
return this->s && base == this->base && d == this->d ? this->s : nullptr;
|
||||||
if (recent.s && base == recent.base && d == recent.d)
|
|
||||||
return recent.s;
|
|
||||||
for (const auto& entry : previous) {
|
|
||||||
if (entry.s && base == entry.base && d == entry.d)
|
|
||||||
return entry.s;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cache(int base, double d, JSFlatString* s) {
|
void cache(int base, double d, JSFlatString* s) {
|
||||||
if (recent.s) {
|
this->base = base;
|
||||||
previous[next] = recent;
|
this->d = d;
|
||||||
next = (next + 1) % NumPrevious;
|
this->s = s;
|
||||||
}
|
|
||||||
recent = { d, base, s };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||||
|
|
|
||||||
|
|
@ -1748,9 +1748,6 @@ JS_IsFloat64Array(JSObject* obj);
|
||||||
extern JS_FRIEND_API(bool)
|
extern JS_FRIEND_API(bool)
|
||||||
JS_GetTypedArraySharedness(JSObject* obj);
|
JS_GetTypedArraySharedness(JSObject* obj);
|
||||||
|
|
||||||
extern JS_FRIEND_API(uint32_t)
|
|
||||||
JS_GetTypedArrayLength(JSObject* obj);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Test for specific typed array types (ArrayBufferView subtypes) and return
|
* Test for specific typed array types (ArrayBufferView subtypes) and return
|
||||||
* the unwrapped object if so, else nullptr. Never throws.
|
* the unwrapped object if so, else nullptr. Never throws.
|
||||||
|
|
@ -1817,7 +1814,8 @@ inline void \
|
||||||
Get ## Type ## ArrayLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory, type** data) \
|
Get ## Type ## ArrayLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory, type** data) \
|
||||||
{ \
|
{ \
|
||||||
MOZ_ASSERT(GetObjectClass(obj) == detail::Type ## ArrayClassPtr); \
|
MOZ_ASSERT(GetObjectClass(obj) == detail::Type ## ArrayClassPtr); \
|
||||||
*length = JS_GetTypedArrayLength(obj); \
|
const JS::Value& lenSlot = GetReservedSlot(obj, detail::TypedArrayLengthSlot); \
|
||||||
|
*length = mozilla::AssertedCast<uint32_t>(lenSlot.toInt32()); \
|
||||||
*isSharedMemory = JS_GetTypedArraySharedness(obj); \
|
*isSharedMemory = JS_GetTypedArraySharedness(obj); \
|
||||||
*data = static_cast<type*>(GetObjectPrivate(obj)); \
|
*data = static_cast<type*>(GetObjectPrivate(obj)); \
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,6 @@ IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
|
||||||
imaginary(WritableStreamDefaultController,dummy, dummy) \
|
imaginary(WritableStreamDefaultController,dummy, dummy) \
|
||||||
real(ByteLengthQueuingStrategy, InitViaClassSpec, &js::ByteLengthQueuingStrategy::class_) \
|
real(ByteLengthQueuingStrategy, InitViaClassSpec, &js::ByteLengthQueuingStrategy::class_) \
|
||||||
real(CountQueuingStrategy, InitViaClassSpec, &js::CountQueuingStrategy::class_) \
|
real(CountQueuingStrategy, InitViaClassSpec, &js::CountQueuingStrategy::class_) \
|
||||||
real(WeakRef, InitWeakRefClass, OCLASP(WeakRef)) \
|
|
||||||
|
|
||||||
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
|
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2329,12 +2329,6 @@ js::str_startsWith(JSContext* cx, unsigned argc, Value* vp)
|
||||||
// Step 13
|
// Step 13
|
||||||
uint32_t searchLen = searchStr->length();
|
uint32_t searchLen = searchStr->length();
|
||||||
|
|
||||||
// Empty search string always matches (and is extremely common).
|
|
||||||
if (searchLen == 0) {
|
|
||||||
args.rval().setBoolean(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 14
|
// Step 14
|
||||||
if (searchLen + start < searchLen || searchLen + start > textLen) {
|
if (searchLen + start < searchLen || searchLen + start > textLen) {
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
|
|
@ -2401,12 +2395,6 @@ js::str_endsWith(JSContext* cx, unsigned argc, Value* vp)
|
||||||
// Step 13
|
// Step 13
|
||||||
uint32_t searchLen = searchStr->length();
|
uint32_t searchLen = searchStr->length();
|
||||||
|
|
||||||
// Empty search string always matches.
|
|
||||||
if (searchLen == 0) {
|
|
||||||
args.rval().setBoolean(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 15 (reordered)
|
// Step 15 (reordered)
|
||||||
if (searchLen > end) {
|
if (searchLen > end) {
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
|
|
@ -3344,8 +3332,6 @@ static const JSFunctionSpec string_methods[] = {
|
||||||
JS_SELF_HOSTED_FN("toLocaleUpperCase", "String_toLocaleUpperCase", 0,0),
|
JS_SELF_HOSTED_FN("toLocaleUpperCase", "String_toLocaleUpperCase", 0,0),
|
||||||
JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0),
|
JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0),
|
||||||
JS_SELF_HOSTED_FN("repeat", "String_repeat", 1,0),
|
JS_SELF_HOSTED_FN("repeat", "String_repeat", 1,0),
|
||||||
JS_SELF_HOSTED_FN("isWellFormed", "String_isWellFormed", 0,0),
|
|
||||||
JS_SELF_HOSTED_FN("toWellFormed", "String_toWellFormed", 0,0),
|
|
||||||
JS_FN("normalize", str_normalize, 0,0),
|
JS_FN("normalize", str_normalize, 0,0),
|
||||||
|
|
||||||
/* Perl-ish methods (search is actually Python-esque). */
|
/* Perl-ish methods (search is actually Python-esque). */
|
||||||
|
|
|
||||||
|
|
@ -16,25 +16,11 @@
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "gc/StoreBuffer.h"
|
#include "gc/StoreBuffer.h"
|
||||||
#include "js/HashTable.h"
|
#include "js/HashTable.h"
|
||||||
#include "vm/Symbol.h"
|
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
class WeakMapBase;
|
class WeakMapBase;
|
||||||
|
|
||||||
template <>
|
|
||||||
struct MovableCellHasher<JS::Symbol*>
|
|
||||||
{
|
|
||||||
using Key = JS::Symbol*;
|
|
||||||
using Lookup = JS::Symbol*;
|
|
||||||
|
|
||||||
static bool hasHash(const Lookup& l) { return true; }
|
|
||||||
static bool ensureHash(const Lookup& l) { return true; }
|
|
||||||
static HashNumber hash(const Lookup& l) { return l->hash(); }
|
|
||||||
static bool match(const Key& k, const Lookup& l) { return k == l; }
|
|
||||||
static void rekey(Key& k, const Key& newKey) { k = newKey; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// A subclass template of js::HashMap whose keys and values may be garbage-collected. When
|
// A subclass template of js::HashMap whose keys and values may be garbage-collected. When
|
||||||
// a key is collected, the table entry disappears, dropping its reference to the value.
|
// a key is collected, the table entry disappears, dropping its reference to the value.
|
||||||
//
|
//
|
||||||
|
|
@ -310,16 +296,9 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject* getDelegate(JS::Symbol* sym) const {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); }
|
void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); }
|
||||||
void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
|
void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
|
||||||
void exposeGCThingToActiveJS(JS::Symbol* sym) const {
|
|
||||||
gc::ExposeGCThingToActiveJS(JS::GCCellPtr(sym));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool keyNeedsMark(JSObject* key) const {
|
bool keyNeedsMark(JSObject* key) const {
|
||||||
JSObject* delegate = getDelegate(key);
|
JSObject* delegate = getDelegate(key);
|
||||||
|
|
@ -334,10 +313,6 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool keyNeedsMark(JS::Symbol* sym) const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool findZoneEdges() override {
|
bool findZoneEdges() override {
|
||||||
// This is overridden by ObjectValueMap.
|
// This is overridden by ObjectValueMap.
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -403,9 +378,6 @@ WeakMap_set(JSContext* cx, unsigned argc, Value* vp);
|
||||||
extern bool
|
extern bool
|
||||||
WeakMap_delete(JSContext* cx, unsigned argc, Value* vp);
|
WeakMap_delete(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
extern bool
|
|
||||||
SetWeakMapEntryValue(JSContext* cx, HandleObject mapObj, HandleValue key, HandleValue val);
|
|
||||||
|
|
||||||
extern JSObject*
|
extern JSObject*
|
||||||
InitWeakMapClass(JSContext* cx, HandleObject obj);
|
InitWeakMapClass(JSContext* cx, HandleObject obj);
|
||||||
|
|
||||||
|
|
@ -422,16 +394,6 @@ class ObjectValueMap : public WeakMap<HeapPtr<JSObject*>, HeapPtr<Value>,
|
||||||
virtual bool findZoneEdges();
|
virtual bool findZoneEdges();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SymbolValueMap : public WeakMap<HeapPtr<JS::Symbol*>, HeapPtr<Value>,
|
|
||||||
MovableCellHasher<HeapPtr<JS::Symbol*>>>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SymbolValueMap(JSContext* cx, JSObject* obj)
|
|
||||||
: WeakMap<HeapPtr<JS::Symbol*>, HeapPtr<Value>,
|
|
||||||
MovableCellHasher<HeapPtr<JS::Symbol*>>>(cx, obj)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Generic weak map for mapping objects to other objects.
|
// Generic weak map for mapping objects to other objects.
|
||||||
class ObjectWeakMap
|
class ObjectWeakMap
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,6 @@ main_deunified_sources = [
|
||||||
'builtin/TestingFunctions.cpp',
|
'builtin/TestingFunctions.cpp',
|
||||||
'builtin/TypedObject.cpp',
|
'builtin/TypedObject.cpp',
|
||||||
'builtin/WeakMapObject.cpp',
|
'builtin/WeakMapObject.cpp',
|
||||||
'builtin/WeakRefObject.cpp',
|
|
||||||
'builtin/WeakSetObject.cpp',
|
'builtin/WeakSetObject.cpp',
|
||||||
'devtools/sharkctl.cpp',
|
'devtools/sharkctl.cpp',
|
||||||
'ds/LifoAlloc.cpp',
|
'ds/LifoAlloc.cpp',
|
||||||
|
|
|
||||||
|
|
@ -3330,15 +3330,6 @@ static const JSClass sandbox_class = {
|
||||||
&sandbox_classOps
|
&sandbox_classOps
|
||||||
};
|
};
|
||||||
|
|
||||||
enum GlobalAppSlot {
|
|
||||||
GlobalAppSlotModuleMetadataHook,
|
|
||||||
GlobalAppSlotModuleDynamicImportHook,
|
|
||||||
GlobalAppSlotCount
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(GlobalAppSlotCount <= JSCLASS_GLOBAL_APPLICATION_SLOTS,
|
|
||||||
"global application slots overflow");
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
SetStandardCompartmentOptions(JS::CompartmentOptions& options)
|
SetStandardCompartmentOptions(JS::CompartmentOptions& options)
|
||||||
{
|
{
|
||||||
|
|
@ -4167,7 +4158,7 @@ ParseModule(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
const char16_t* chars = stableChars.twoByteRange().begin().get();
|
const char16_t* chars = stableChars.twoByteRange().begin().get();
|
||||||
JS::SourceBufferHolder srcBuf(chars, scriptContents->length(),
|
JS::SourceBufferHolder srcBuf(chars, scriptContents->length(),
|
||||||
JS::SourceBufferHolder::NoOwnership);
|
SourceBufferHolder::NoOwnership);
|
||||||
|
|
||||||
RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
|
RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
|
||||||
if (!module)
|
if (!module)
|
||||||
|
|
@ -4378,7 +4369,7 @@ AbortDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp)
|
||||||
RootedString specifier(cx, args[1].toString());
|
RootedString specifier(cx, args[1].toString());
|
||||||
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
|
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
|
||||||
|
|
||||||
cx->setPendingException(args[3], nullptr);
|
cx->setPendingException(args[3]);
|
||||||
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
|
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -8431,7 +8422,7 @@ main(int argc, char** argv, char** envp)
|
||||||
|
|
||||||
JS::SetModuleResolveHook(cx->runtime(), ShellModuleResolveHook);
|
JS::SetModuleResolveHook(cx->runtime(), ShellModuleResolveHook);
|
||||||
JS::SetModuleDynamicImportHook(cx, ShellModuleDynamicImportHook);
|
JS::SetModuleDynamicImportHook(cx, ShellModuleDynamicImportHook);
|
||||||
JS::SetModuleMetadataHook(cx, CallModuleMetadataHook);
|
JS::SetModuleMetadataHook(cx, ShellModuleMetadataHook);
|
||||||
|
|
||||||
result = Shell(cx, &op, envp);
|
result = Shell(cx, &op, envp);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Array.prototype.toSorted test</title>
|
|
||||||
<style>
|
|
||||||
body { font: 14px/1.4 sans-serif; padding: 16px; }
|
|
||||||
.pass { color: #0a0; }
|
|
||||||
.fail { color: #c00; }
|
|
||||||
pre { white-space: pre-wrap; }
|
|
||||||
</style>
|
|
||||||
<pre id="log"></pre>
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const log = document.getElementById("log");
|
|
||||||
function write(msg, cls) {
|
|
||||||
const line = document.createElement("div");
|
|
||||||
if (cls) line.className = cls;
|
|
||||||
line.textContent = msg;
|
|
||||||
log.appendChild(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.assertEq = function(actual, expected, msg) {
|
|
||||||
if (actual !== expected) {
|
|
||||||
throw new Error((msg ? msg + ": " : "") +
|
|
||||||
"expected " + expected + ", got " + actual);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.assertThrowsInstanceOf = function(fn, ctor, msg) {
|
|
||||||
let threw = false;
|
|
||||||
try {
|
|
||||||
fn();
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof ctor) {
|
|
||||||
threw = true;
|
|
||||||
} else {
|
|
||||||
throw new Error((msg ? msg + ": " : "") +
|
|
||||||
"threw " + e + ", expected " + ctor.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!threw) {
|
|
||||||
throw new Error((msg ? msg + ": " : "") + "did not throw");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.reportCompare = function() {};
|
|
||||||
|
|
||||||
let hadError = false;
|
|
||||||
window.addEventListener("error", function(e) {
|
|
||||||
hadError = true;
|
|
||||||
write("FAIL: " + e.message, "fail");
|
|
||||||
});
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
write("PASS: toSorted.js loaded", "pass");
|
|
||||||
if (!hadError)
|
|
||||||
write("PASS: all toSorted tests passed", "pass");
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<script src="toSorted.js"></script>
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/licenses/publicdomain/ */
|
|
||||||
|
|
||||||
assertEq(typeof Array.prototype.toSorted, "function");
|
|
||||||
|
|
||||||
// Non-mutating behavior.
|
|
||||||
let original = [3, 1, 2];
|
|
||||||
let sorted = original.toSorted();
|
|
||||||
assertEq(original !== sorted, true);
|
|
||||||
assertEq(original.join(","), "3,1,2");
|
|
||||||
assertEq(sorted.join(","), "1,2,3");
|
|
||||||
|
|
||||||
// Compare function.
|
|
||||||
let nums = [10, 1, 5];
|
|
||||||
let desc = nums.toSorted((a, b) => b - a);
|
|
||||||
assertEq(desc.join(","), "10,5,1");
|
|
||||||
|
|
||||||
// Stable sort.
|
|
||||||
let stableInput = [
|
|
||||||
{v: 1, id: "a"},
|
|
||||||
{v: 1, id: "b"},
|
|
||||||
{v: 1, id: "c"}
|
|
||||||
];
|
|
||||||
let stableSorted = stableInput.toSorted((x, y) => x.v - y.v);
|
|
||||||
assertEq(stableSorted.map(o => o.id).join(""), "abc");
|
|
||||||
|
|
||||||
// Holes are treated as undefined (properties are created).
|
|
||||||
let sparse = [3, , 1];
|
|
||||||
let sparseSorted = sparse.toSorted();
|
|
||||||
assertEq(sparseSorted.length, 3);
|
|
||||||
assertEq(sparseSorted[0], 1);
|
|
||||||
assertEq(sparseSorted[1], 3);
|
|
||||||
assertEq(2 in sparseSorted, true);
|
|
||||||
assertEq(sparseSorted[2], undefined);
|
|
||||||
|
|
||||||
// Array-like input.
|
|
||||||
let arrayLike = {0: 2, 1: 1, length: 2};
|
|
||||||
let arrayLikeSorted = Array.prototype.toSorted.call(arrayLike);
|
|
||||||
assertEq(Array.isArray(arrayLikeSorted), true);
|
|
||||||
assertEq(arrayLikeSorted.join(","), "1,2");
|
|
||||||
|
|
||||||
// Getter access order (ascending indices).
|
|
||||||
let accessLog = [];
|
|
||||||
let getterArr = {
|
|
||||||
length: 3,
|
|
||||||
get 0() { accessLog.push(0); return 3; },
|
|
||||||
get 1() { accessLog.push(1); return 1; },
|
|
||||||
get 2() { accessLog.push(2); return 2; }
|
|
||||||
};
|
|
||||||
Array.prototype.toSorted.call(getterArr);
|
|
||||||
assertEq(accessLog.join(","), "0,1,2");
|
|
||||||
|
|
||||||
// Comparator errors propagate.
|
|
||||||
assertThrowsInstanceOf(() => [1, 2].toSorted(1), TypeError);
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(0, 0);
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
// |reftest| skip-if(!ArrayBuffer.prototype.transfer)
|
|
||||||
|
|
||||||
var fixed = new ArrayBuffer(4);
|
|
||||||
assertEq(fixed.byteLength, 4);
|
|
||||||
assertEq(fixed.maxByteLength, 4);
|
|
||||||
assertEq(fixed.resizable, false);
|
|
||||||
assertEq(fixed.detached, false);
|
|
||||||
new Uint8Array(fixed).set([1, 2, 3, 4]);
|
|
||||||
fixed.extra = 17;
|
|
||||||
assertEq(Array.from(new Uint8Array(fixed)).join(","), "1,2,3,4");
|
|
||||||
assertThrowsInstanceOf(() => fixed.resize(2), TypeError);
|
|
||||||
assertEq(ArrayBuffer.prototype.transfer.length, 0);
|
|
||||||
assertEq(ArrayBuffer.prototype.transferToFixedLength.length, 0);
|
|
||||||
var resizeArgumentConverted = false;
|
|
||||||
assertThrowsInstanceOf(() => fixed.resize({ valueOf() { resizeArgumentConverted = true; return 1; } }),
|
|
||||||
TypeError);
|
|
||||||
assertEq(resizeArgumentConverted, false);
|
|
||||||
|
|
||||||
var resizable = new ArrayBuffer(4, { maxByteLength: 8 });
|
|
||||||
assertEq(resizable.byteLength, 4);
|
|
||||||
assertEq(resizable.maxByteLength, 8);
|
|
||||||
assertEq(resizable.resizable, true);
|
|
||||||
|
|
||||||
var bytes = new Uint8Array(resizable);
|
|
||||||
bytes[0] = 11;
|
|
||||||
bytes[3] = 44;
|
|
||||||
resizable.resize(6);
|
|
||||||
assertEq(resizable.byteLength, 6);
|
|
||||||
assertEq(new Uint8Array(resizable)[0], 11);
|
|
||||||
assertEq(new Uint8Array(resizable)[3], 44);
|
|
||||||
assertEq(new Uint8Array(resizable)[4], 0);
|
|
||||||
assertThrowsInstanceOf(() => resizable.resize(9), RangeError);
|
|
||||||
|
|
||||||
var source = new ArrayBuffer(4);
|
|
||||||
var sourceBytes = new Uint8Array(source);
|
|
||||||
sourceBytes[0] = 1;
|
|
||||||
sourceBytes[1] = 2;
|
|
||||||
var sourceView = new Uint8Array(source);
|
|
||||||
var moved = source.transfer(6);
|
|
||||||
assertEq(source.detached, true);
|
|
||||||
assertEq(source.byteLength, 0);
|
|
||||||
assertEq(source.maxByteLength, 0);
|
|
||||||
assertEq(sourceView.length, 0);
|
|
||||||
assertEq(moved.byteLength, 6);
|
|
||||||
assertEq(moved.resizable, false);
|
|
||||||
assertEq(moved.maxByteLength, 6);
|
|
||||||
assertEq(new Uint8Array(moved)[0], 1);
|
|
||||||
assertEq(new Uint8Array(moved)[1], 2);
|
|
||||||
assertEq(new Uint8Array(moved)[4], 0);
|
|
||||||
|
|
||||||
var resizableSource = new ArrayBuffer(4, { maxByteLength: 8 });
|
|
||||||
new Uint8Array(resizableSource)[0] = 7;
|
|
||||||
var resizableMoved = resizableSource.transfer();
|
|
||||||
assertEq(resizableSource.detached, true);
|
|
||||||
assertEq(resizableSource.resizable, true);
|
|
||||||
assertEq(resizableMoved.byteLength, 4);
|
|
||||||
assertEq(resizableMoved.maxByteLength, 8);
|
|
||||||
assertEq(resizableMoved.resizable, true);
|
|
||||||
assertEq(new Uint8Array(resizableMoved)[0], 7);
|
|
||||||
|
|
||||||
var fixedMoved = resizableMoved.transferToFixedLength(10);
|
|
||||||
assertEq(resizableMoved.detached, true);
|
|
||||||
assertEq(fixedMoved.byteLength, 10);
|
|
||||||
assertEq(fixedMoved.maxByteLength, 10);
|
|
||||||
assertEq(fixedMoved.resizable, false);
|
|
||||||
assertEq(new Uint8Array(fixedMoved)[0], 7);
|
|
||||||
|
|
||||||
var sliceSource = new ArrayBuffer(8, { maxByteLength: 8 });
|
|
||||||
var sliceSourceBytes = new Uint8Array(sliceSource);
|
|
||||||
for (var i = 0; i < sliceSourceBytes.length; i++)
|
|
||||||
sliceSourceBytes[i] = i + 1;
|
|
||||||
|
|
||||||
sliceSource.constructor = {
|
|
||||||
[Symbol.species]: function(byteLength) {
|
|
||||||
sliceSource.resize(4);
|
|
||||||
return new ArrayBuffer(byteLength);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var sliced = sliceSource.slice(2, 8);
|
|
||||||
var slicedBytes = new Uint8Array(sliced);
|
|
||||||
assertEq(sliced.byteLength, 6);
|
|
||||||
assertEq(slicedBytes[0], 3);
|
|
||||||
assertEq(slicedBytes[1], 4);
|
|
||||||
assertEq(slicedBytes[2], 0);
|
|
||||||
assertEq(slicedBytes[5], 0);
|
|
||||||
|
|
||||||
sliceSource.resize(8);
|
|
||||||
for (var i = 0; i < sliceSourceBytes.length; i++)
|
|
||||||
sliceSourceBytes[i] = i + 1;
|
|
||||||
var zeroCopied = sliceSource.slice(6, 8);
|
|
||||||
assertEq(zeroCopied.byteLength, 2);
|
|
||||||
assertEq(new Uint8Array(zeroCopied)[0], 0);
|
|
||||||
|
|
||||||
assertThrowsInstanceOf(() => new ArrayBuffer(4, { maxByteLength: 3 }), RangeError);
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(0, 0);
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
// |reftest| skip-if(!this.SharedArrayBuffer)
|
|
||||||
|
|
||||||
var rab = new ArrayBuffer(4, { maxByteLength: 16 });
|
|
||||||
var tracking = new Uint8Array(rab);
|
|
||||||
var fixed = new Uint8Array(rab, 1, 2);
|
|
||||||
var bytes = new Uint8Array(rab);
|
|
||||||
|
|
||||||
bytes[1] = 11;
|
|
||||||
bytes[2] = 22;
|
|
||||||
|
|
||||||
assertEq(tracking.length, 4);
|
|
||||||
assertEq(tracking.byteLength, 4);
|
|
||||||
assertEq(tracking.byteOffset, 0);
|
|
||||||
assertEq(fixed.length, 2);
|
|
||||||
assertEq(fixed.byteLength, 2);
|
|
||||||
assertEq(fixed.byteOffset, 1);
|
|
||||||
|
|
||||||
rab.resize(2);
|
|
||||||
assertEq(tracking.length, 2);
|
|
||||||
assertEq(tracking.byteLength, 2);
|
|
||||||
assertEq(fixed.length, 0);
|
|
||||||
assertEq(fixed.byteLength, 0);
|
|
||||||
assertEq(fixed.byteOffset, 0);
|
|
||||||
assertEq(fixed[0], undefined);
|
|
||||||
|
|
||||||
rab.resize(8);
|
|
||||||
assertEq(tracking.length, 8);
|
|
||||||
assertEq(tracking.byteLength, 8);
|
|
||||||
assertEq(fixed.length, 2);
|
|
||||||
assertEq(fixed.byteLength, 2);
|
|
||||||
assertEq(fixed.byteOffset, 1);
|
|
||||||
assertEq(fixed[0], 11);
|
|
||||||
assertEq(fixed[1], 0);
|
|
||||||
tracking[6] = 66;
|
|
||||||
assertEq(new Uint8Array(rab)[6], 66);
|
|
||||||
|
|
||||||
var dv = new DataView(rab, 4);
|
|
||||||
assertEq(dv.byteOffset, 4);
|
|
||||||
assertEq(dv.byteLength, 4);
|
|
||||||
dv.setUint8(0, 44);
|
|
||||||
assertEq(tracking[4], 44);
|
|
||||||
|
|
||||||
rab.resize(3);
|
|
||||||
assertThrowsInstanceOf(() => dv.byteOffset, TypeError);
|
|
||||||
assertThrowsInstanceOf(() => dv.byteLength, TypeError);
|
|
||||||
assertThrowsInstanceOf(() => dv.getUint8(0), TypeError);
|
|
||||||
|
|
||||||
rab.resize(6);
|
|
||||||
assertEq(dv.byteOffset, 4);
|
|
||||||
assertEq(dv.byteLength, 2);
|
|
||||||
assertEq(dv.getUint8(0), 0);
|
|
||||||
|
|
||||||
var fixedDv = new DataView(rab, 4, 2);
|
|
||||||
rab.resize(5);
|
|
||||||
assertThrowsInstanceOf(() => fixedDv.byteOffset, TypeError);
|
|
||||||
assertThrowsInstanceOf(() => fixedDv.byteLength, TypeError);
|
|
||||||
assertThrowsInstanceOf(() => fixedDv.getUint8(0), TypeError);
|
|
||||||
rab.resize(6);
|
|
||||||
assertEq(fixedDv.byteOffset, 4);
|
|
||||||
assertEq(fixedDv.byteLength, 2);
|
|
||||||
|
|
||||||
var methodRab = new ArrayBuffer(4, { maxByteLength: 8 });
|
|
||||||
var methodFixed = new Uint8Array(methodRab, 2, 2);
|
|
||||||
methodRab.resize(2);
|
|
||||||
[
|
|
||||||
() => methodFixed.at(0),
|
|
||||||
() => methodFixed.copyWithin(0, 0),
|
|
||||||
() => methodFixed.entries(),
|
|
||||||
() => methodFixed.every(x => true),
|
|
||||||
() => methodFixed.fill(1),
|
|
||||||
() => methodFixed.filter(x => true),
|
|
||||||
() => methodFixed.find(x => true),
|
|
||||||
() => methodFixed.findIndex(x => true),
|
|
||||||
() => methodFixed.findLast(x => true),
|
|
||||||
() => methodFixed.findLastIndex(x => true),
|
|
||||||
() => methodFixed.forEach(x => x),
|
|
||||||
() => methodFixed.includes(0),
|
|
||||||
() => methodFixed.indexOf(0),
|
|
||||||
() => methodFixed.join(","),
|
|
||||||
() => methodFixed.keys(),
|
|
||||||
() => methodFixed.lastIndexOf(0),
|
|
||||||
() => methodFixed.map(x => x),
|
|
||||||
() => methodFixed.reduce((a, b) => a + b, 0),
|
|
||||||
() => methodFixed.reduceRight((a, b) => a + b, 0),
|
|
||||||
() => methodFixed.reverse(),
|
|
||||||
() => methodFixed.set([1], 0),
|
|
||||||
() => methodFixed.slice(),
|
|
||||||
() => methodFixed.some(x => true),
|
|
||||||
() => methodFixed.sort(),
|
|
||||||
() => methodFixed.subarray(),
|
|
||||||
() => methodFixed.toLocaleString(),
|
|
||||||
() => methodFixed.toReversed(),
|
|
||||||
() => methodFixed.toSorted(),
|
|
||||||
() => methodFixed.toString(),
|
|
||||||
() => methodFixed.values(),
|
|
||||||
() => methodFixed.with(0, 1),
|
|
||||||
() => methodFixed[Symbol.iterator](),
|
|
||||||
].forEach(fn => assertThrowsInstanceOf(fn, TypeError));
|
|
||||||
|
|
||||||
methodRab.resize(4);
|
|
||||||
assertEq(methodFixed.length, 2);
|
|
||||||
|
|
||||||
var sourceRab = new ArrayBuffer(4, { maxByteLength: 8 });
|
|
||||||
var oobSource = new Uint8Array(sourceRab, 2, 2);
|
|
||||||
sourceRab.resize(2);
|
|
||||||
assertThrowsInstanceOf(() => new Uint8Array(oobSource), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => new Uint16Array(oobSource), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => new Uint8Array(4).set(oobSource), TypeError);
|
|
||||||
sourceRab.resize(4);
|
|
||||||
assertEq(new Uint8Array(oobSource).length, 2);
|
|
||||||
|
|
||||||
var ctorRab = new ArrayBuffer(8, { maxByteLength: 8 });
|
|
||||||
var ShrinkingNewTarget = new Proxy(function() {}, {
|
|
||||||
get(target, prop, receiver) {
|
|
||||||
if (prop === "prototype") {
|
|
||||||
ctorRab.resize(2);
|
|
||||||
return DataView.prototype;
|
|
||||||
}
|
|
||||||
return Reflect.get(target, prop, receiver);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertThrowsInstanceOf(() => Reflect.construct(DataView, [ctorRab, 4], ShrinkingNewTarget),
|
|
||||||
RangeError);
|
|
||||||
|
|
||||||
var fixedCtorRab = new ArrayBuffer(8, { maxByteLength: 8 });
|
|
||||||
var FixedShrinkingNewTarget = new Proxy(function() {}, {
|
|
||||||
get(target, prop, receiver) {
|
|
||||||
if (prop === "prototype") {
|
|
||||||
fixedCtorRab.resize(5);
|
|
||||||
return DataView.prototype;
|
|
||||||
}
|
|
||||||
return Reflect.get(target, prop, receiver);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertThrowsInstanceOf(() => Reflect.construct(DataView, [fixedCtorRab, 4, 2],
|
|
||||||
FixedShrinkingNewTarget),
|
|
||||||
RangeError);
|
|
||||||
|
|
||||||
var gsab = new SharedArrayBuffer(4, { maxByteLength: 16 });
|
|
||||||
var sharedTracking = new Uint8Array(gsab);
|
|
||||||
assertEq(sharedTracking.length, 4);
|
|
||||||
gsab.grow(8);
|
|
||||||
assertEq(sharedTracking.length, 8);
|
|
||||||
sharedTracking[6] = 33;
|
|
||||||
assertEq(new Uint8Array(gsab)[6], 33);
|
|
||||||
|
|
||||||
var sharedDv = new DataView(gsab);
|
|
||||||
assertEq(sharedDv.buffer, gsab);
|
|
||||||
assertEq(sharedDv.byteOffset, 0);
|
|
||||||
assertEq(sharedDv.byteLength, 8);
|
|
||||||
sharedDv.setUint8(7, 99);
|
|
||||||
assertEq(sharedTracking[7], 99);
|
|
||||||
gsab.grow(12);
|
|
||||||
assertEq(sharedDv.byteLength, 12);
|
|
||||||
assertEq(sharedDv.getUint8(7), 99);
|
|
||||||
|
|
||||||
var fixedSharedDv = new DataView(gsab, 4, 2);
|
|
||||||
assertEq(fixedSharedDv.byteOffset, 4);
|
|
||||||
assertEq(fixedSharedDv.byteLength, 2);
|
|
||||||
gsab.grow(16);
|
|
||||||
assertEq(fixedSharedDv.byteOffset, 4);
|
|
||||||
assertEq(fixedSharedDv.byteLength, 2);
|
|
||||||
|
|
||||||
function readLength(view) {
|
|
||||||
return view.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readElement(view, index) {
|
|
||||||
return view[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeElement(view, index, value) {
|
|
||||||
view[index] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var normal = new Uint8Array(4);
|
|
||||||
normal[0] = 7;
|
|
||||||
for (var i = 0; i < 2000; i++) {
|
|
||||||
assertEq(readLength(normal), 4);
|
|
||||||
assertEq(readElement(normal, 0), 7);
|
|
||||||
writeElement(normal, 1, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
var icRab = new ArrayBuffer(4, { maxByteLength: 8 });
|
|
||||||
var icTracking = new Uint8Array(icRab);
|
|
||||||
icTracking[0] = 9;
|
|
||||||
assertEq(readLength(icTracking), 4);
|
|
||||||
assertEq(readElement(icTracking, 0), 9);
|
|
||||||
icRab.resize(0);
|
|
||||||
assertEq(readLength(icTracking), 0);
|
|
||||||
assertEq(readElement(icTracking, 0), undefined);
|
|
||||||
writeElement(icTracking, 0, 1);
|
|
||||||
icRab.resize(4);
|
|
||||||
assertEq(readLength(icTracking), 4);
|
|
||||||
assertEq(readElement(icTracking, 0), 0);
|
|
||||||
writeElement(icTracking, 0, 12);
|
|
||||||
assertEq(readElement(icTracking, 0), 12);
|
|
||||||
|
|
||||||
var icFixed = new Uint8Array(icRab, 1, 2);
|
|
||||||
assertEq(readLength(icFixed), 2);
|
|
||||||
icRab.resize(2);
|
|
||||||
assertEq(readLength(icFixed), 0);
|
|
||||||
assertEq(readElement(icFixed, 0), undefined);
|
|
||||||
writeElement(icFixed, 0, 55);
|
|
||||||
icRab.resize(4);
|
|
||||||
assertEq(readLength(icFixed), 2);
|
|
||||||
assertEq(readElement(icFixed, 0), 0);
|
|
||||||
|
|
||||||
var icGsab = new SharedArrayBuffer(4, { maxByteLength: 8 });
|
|
||||||
var icSharedTracking = new Uint8Array(icGsab);
|
|
||||||
assertEq(readLength(icSharedTracking), 4);
|
|
||||||
icGsab.grow(8);
|
|
||||||
assertEq(readLength(icSharedTracking), 8);
|
|
||||||
writeElement(icSharedTracking, 5, 77);
|
|
||||||
assertEq(readElement(icSharedTracking, 5), 77);
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(true, true);
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
// |reftest| skip-if(!this.SharedArrayBuffer || !this.Atomics || !this.drainJobQueue)
|
|
||||||
|
|
||||||
if (typeof SharedArrayBuffer === "function" && typeof Atomics === "object" &&
|
|
||||||
typeof drainJobQueue === "function") {
|
|
||||||
const sab = new SharedArrayBuffer(4);
|
|
||||||
const i32 = new Int32Array(sab);
|
|
||||||
|
|
||||||
let result = Atomics.waitAsync(i32, 0, 1, 10);
|
|
||||||
assertEq(result.async, false);
|
|
||||||
assertEq(result.value, "not-equal");
|
|
||||||
|
|
||||||
result = Atomics.waitAsync(i32, 0, 0, 0);
|
|
||||||
assertEq(result.async, false);
|
|
||||||
assertEq(result.value, "timed-out");
|
|
||||||
|
|
||||||
result = Atomics.waitAsync(i32, 0, 0);
|
|
||||||
assertEq(result.async, true);
|
|
||||||
let notified;
|
|
||||||
result.value.then(value => {
|
|
||||||
notified = value;
|
|
||||||
});
|
|
||||||
assertEq(Atomics.notify(i32, 0, 1), 1);
|
|
||||||
drainJobQueue();
|
|
||||||
assertEq(notified, "ok");
|
|
||||||
|
|
||||||
result = Atomics.waitAsync(i32, 0, 0, 1);
|
|
||||||
assertEq(result.async, true);
|
|
||||||
let timedOut;
|
|
||||||
result.value.then(value => {
|
|
||||||
timedOut = value;
|
|
||||||
});
|
|
||||||
drainJobQueue();
|
|
||||||
assertEq(timedOut, "timed-out");
|
|
||||||
|
|
||||||
if (typeof BigInt64Array === "function") {
|
|
||||||
const bigSab = new SharedArrayBuffer(16);
|
|
||||||
const i64 = new BigInt64Array(bigSab);
|
|
||||||
const u64 = new BigUint64Array(bigSab);
|
|
||||||
|
|
||||||
assertEq(Atomics.store(i64, 0, -1n), -1n);
|
|
||||||
assertEq(Atomics.load(i64, 0), -1n);
|
|
||||||
assertEq(Atomics.load(u64, 0), 18446744073709551615n);
|
|
||||||
assertEq(Atomics.exchange(i64, 0, 7n), -1n);
|
|
||||||
assertEq(Atomics.compareExchange(i64, 0, 7n, 10n), 7n);
|
|
||||||
assertEq(Atomics.add(i64, 0, 5n), 10n);
|
|
||||||
assertEq(Atomics.load(i64, 0), 15n);
|
|
||||||
assertEq(Atomics.sub(i64, 0, 20n), 15n);
|
|
||||||
assertEq(Atomics.load(i64, 0), -5n);
|
|
||||||
assertEq(Atomics.and(u64, 0, 7n), 18446744073709551611n);
|
|
||||||
assertEq(Atomics.or(u64, 0, 8n), 3n);
|
|
||||||
assertEq(Atomics.xor(u64, 0, 15n), 11n);
|
|
||||||
|
|
||||||
assertThrowsInstanceOf(() => Atomics.waitAsync(u64, 0, 0n), TypeError);
|
|
||||||
|
|
||||||
result = Atomics.waitAsync(i64, 1, 1n, 10);
|
|
||||||
assertEq(result.async, false);
|
|
||||||
assertEq(result.value, "not-equal");
|
|
||||||
|
|
||||||
result = Atomics.waitAsync(i64, 1, 0n, 0);
|
|
||||||
assertEq(result.async, false);
|
|
||||||
assertEq(result.value, "timed-out");
|
|
||||||
|
|
||||||
let offsetView = new BigInt64Array(bigSab, 8);
|
|
||||||
result = Atomics.waitAsync(offsetView, 0, 0n);
|
|
||||||
assertEq(result.async, true);
|
|
||||||
notified = undefined;
|
|
||||||
result.value.then(value => {
|
|
||||||
notified = value;
|
|
||||||
});
|
|
||||||
assertEq(Atomics.notify(i64, 1, 1), 1);
|
|
||||||
drainJobQueue();
|
|
||||||
assertEq(notified, "ok");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(true, true);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// |reftest| skip-if(!Promise.withResolvers)
|
|
||||||
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(Promise, "withResolvers");
|
|
||||||
assertEq(desc.enumerable, false);
|
|
||||||
assertEq(desc.configurable, true);
|
|
||||||
assertEq(desc.writable, true);
|
|
||||||
assertEq(Promise.withResolvers.length, 0);
|
|
||||||
assertEq(Promise.withResolvers.name, "withResolvers");
|
|
||||||
|
|
||||||
var capability = Promise.withResolvers();
|
|
||||||
assertEq(capability.promise instanceof Promise, true);
|
|
||||||
assertEq(typeof capability.resolve, "function");
|
|
||||||
assertEq(typeof capability.reject, "function");
|
|
||||||
assertEqArray(Object.keys(capability), ["promise", "resolve", "reject"]);
|
|
||||||
|
|
||||||
capability.resolve(42);
|
|
||||||
capability.promise.then(v => assertEq(v, 42));
|
|
||||||
|
|
||||||
class MyPromise extends Promise {}
|
|
||||||
var subCapability = Promise.withResolvers.call(MyPromise);
|
|
||||||
assertEq(subCapability.promise instanceof MyPromise, true);
|
|
||||||
subCapability.reject("rejected");
|
|
||||||
subCapability.promise.then(
|
|
||||||
() => { throw new Error("expected rejection"); },
|
|
||||||
reason => assertEq(reason, "rejected")
|
|
||||||
);
|
|
||||||
|
|
||||||
assertThrowsInstanceOf(() => Promise.withResolvers.call({}), TypeError);
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(0, 0);
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
// |reftest| skip-if(!this.SharedArrayBuffer)
|
|
||||||
|
|
||||||
if (typeof SharedArrayBuffer === "function") {
|
|
||||||
const fixed = new SharedArrayBuffer(4);
|
|
||||||
assertEq(fixed.byteLength, 4);
|
|
||||||
assertEq(fixed.maxByteLength, 4);
|
|
||||||
assertEq(fixed.growable, false);
|
|
||||||
assertThrowsInstanceOf(() => fixed.grow(4), TypeError);
|
|
||||||
|
|
||||||
assertThrowsInstanceOf(() => new SharedArrayBuffer(-1), RangeError);
|
|
||||||
assertThrowsInstanceOf(() => new SharedArrayBuffer(8, {maxByteLength: 4}), RangeError);
|
|
||||||
|
|
||||||
let optionGetterCalled = false;
|
|
||||||
const growable = new SharedArrayBuffer(4, {
|
|
||||||
get maxByteLength() {
|
|
||||||
optionGetterCalled = true;
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertEq(optionGetterCalled, true);
|
|
||||||
assertEq(growable.byteLength, 4);
|
|
||||||
assertEq(growable.maxByteLength, 16);
|
|
||||||
assertEq(growable.growable, true);
|
|
||||||
|
|
||||||
const before = new Uint8Array(growable);
|
|
||||||
before[0] = 37;
|
|
||||||
assertEq(growable.grow(12), undefined);
|
|
||||||
assertEq(growable.byteLength, 12);
|
|
||||||
assertEq(growable.maxByteLength, 16);
|
|
||||||
|
|
||||||
const after = new Uint8Array(growable);
|
|
||||||
assertEq(after.length, 12);
|
|
||||||
assertEq(after[0], 37);
|
|
||||||
|
|
||||||
assertThrowsInstanceOf(() => growable.grow(11), RangeError);
|
|
||||||
assertThrowsInstanceOf(() => growable.grow(17), RangeError);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(true, true);
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
// |reftest| skip-if(!String.prototype.isWellFormed||!String.prototype.toWellFormed)
|
|
||||||
|
|
||||||
assertEq("".isWellFormed(), true);
|
|
||||||
assertEq("abc".isWellFormed(), true);
|
|
||||||
assertEq("\uD83D\uDE00".isWellFormed(), true);
|
|
||||||
assertEq("\uD800".isWellFormed(), false);
|
|
||||||
assertEq("\uDC00".isWellFormed(), false);
|
|
||||||
assertEq("\uD800a".isWellFormed(), false);
|
|
||||||
assertEq("a\uDC00".isWellFormed(), false);
|
|
||||||
assertEq("\uD800\uD800\uDC00".isWellFormed(), false);
|
|
||||||
|
|
||||||
assertEq("abc".toWellFormed(), "abc");
|
|
||||||
assertEq("\uD83D\uDE00".toWellFormed(), "\uD83D\uDE00");
|
|
||||||
assertEq("\uD800".toWellFormed(), "\uFFFD");
|
|
||||||
assertEq("\uDC00".toWellFormed(), "\uFFFD");
|
|
||||||
assertEq("\uD800a\uDC00".toWellFormed(), "\uFFFDa\uFFFD");
|
|
||||||
assertEq("\uD800\uD800\uDC00".toWellFormed(), "\uFFFD\uD800\uDC00");
|
|
||||||
|
|
||||||
assertEq(String.prototype.isWellFormed.call(123), true);
|
|
||||||
assertEq(String.prototype.toWellFormed.call({ toString() { return "\uD800x"; } }), "\uFFFDx");
|
|
||||||
|
|
||||||
assertThrowsInstanceOf(() => String.prototype.isWellFormed.call(null), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => String.prototype.toWellFormed.call(undefined), TypeError);
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(0, 0);
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
var key = Symbol("weak");
|
|
||||||
var map = new WeakMap();
|
|
||||||
assertEq(map.has(key), false);
|
|
||||||
assertEq(map.get(key), undefined);
|
|
||||||
assertEq(map.set(key, 13), map);
|
|
||||||
assertEq(map.has(key), true);
|
|
||||||
assertEq(map.get(key), 13);
|
|
||||||
assertEq(map.delete(key), true);
|
|
||||||
assertEq(map.has(key), false);
|
|
||||||
|
|
||||||
var constructedKey = Symbol("constructed");
|
|
||||||
var constructed = new WeakMap([[constructedKey, 7]]);
|
|
||||||
assertEq(constructed.get(constructedKey), 7);
|
|
||||||
|
|
||||||
var registered = Symbol.for("registered");
|
|
||||||
assertEq(map.has(registered), false);
|
|
||||||
assertEq(map.get(registered), undefined);
|
|
||||||
assertEq(map.delete(registered), false);
|
|
||||||
assertThrowsInstanceOf(() => map.set(registered, 1), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => new WeakMap([[registered, 1]]), TypeError);
|
|
||||||
|
|
||||||
var setKey = Symbol("set");
|
|
||||||
var set = new WeakSet([setKey]);
|
|
||||||
assertEq(set.has(setKey), true);
|
|
||||||
assertEq(set.delete(setKey), true);
|
|
||||||
assertEq(set.has(setKey), false);
|
|
||||||
assertEq(set.add(setKey), set);
|
|
||||||
assertEq(set.has(setKey), true);
|
|
||||||
|
|
||||||
assertEq(set.has(registered), false);
|
|
||||||
assertEq(set.delete(registered), false);
|
|
||||||
assertThrowsInstanceOf(() => set.add(registered), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => new WeakSet([registered]), TypeError);
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(0, 0);
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
// |reftest| skip-if(!Object.groupBy||!Map.groupBy)
|
|
||||||
|
|
||||||
var objectGroups = Object.groupBy(["a", "bb", "c"], value => value.length);
|
|
||||||
assertEq(Object.getPrototypeOf(objectGroups), null);
|
|
||||||
assertEqArray(objectGroups["1"], ["a", "c"]);
|
|
||||||
assertEqArray(objectGroups["2"], ["bb"]);
|
|
||||||
|
|
||||||
var symbol = Symbol();
|
|
||||||
var symbolGroups = Object.groupBy([1, 2], value => value === 1 ? symbol : "__proto__");
|
|
||||||
assertEqArray(symbolGroups[symbol], [1]);
|
|
||||||
assertEqArray(symbolGroups.__proto__, [2]);
|
|
||||||
|
|
||||||
var indexes = [];
|
|
||||||
var mapGroups = Map.groupBy(["a", "bb", "c"], (value, index) => {
|
|
||||||
indexes.push(index);
|
|
||||||
return value.length;
|
|
||||||
});
|
|
||||||
assertEq(mapGroups instanceof Map, true);
|
|
||||||
assertEqArray(indexes, [0, 1, 2]);
|
|
||||||
assertEqArray(mapGroups.get(1), ["a", "c"]);
|
|
||||||
assertEqArray(mapGroups.get(2), ["bb"]);
|
|
||||||
|
|
||||||
var key = {};
|
|
||||||
var objectKeyGroups = Map.groupBy([1, 2], value => value === 1 ? key : NaN);
|
|
||||||
assertEqArray(objectKeyGroups.get(key), [1]);
|
|
||||||
assertEqArray(objectKeyGroups.get(NaN), [2]);
|
|
||||||
|
|
||||||
assertThrowsInstanceOf(() => Object.groupBy(null, x => x), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => Map.groupBy(undefined, x => x), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => Object.groupBy([], null), TypeError);
|
|
||||||
assertThrowsInstanceOf(() => Map.groupBy([], null), TypeError);
|
|
||||||
|
|
||||||
if (typeof reportCompare === "function")
|
|
||||||
reportCompare(0, 0);
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
# include <valgrind/memcheck.h>
|
# include <valgrind/memcheck.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsarray.h"
|
#include "jsarray.h"
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
|
|
@ -46,7 +45,6 @@
|
||||||
#include "vm/Interpreter.h"
|
#include "vm/Interpreter.h"
|
||||||
#include "vm/SelfHosting.h"
|
#include "vm/SelfHosting.h"
|
||||||
#include "vm/SharedArrayObject.h"
|
#include "vm/SharedArrayObject.h"
|
||||||
#include "vm/TypedArrayObject.h"
|
|
||||||
#include "vm/WrapperObject.h"
|
#include "vm/WrapperObject.h"
|
||||||
#include "wasm/WasmSignalHandlers.h"
|
#include "wasm/WasmSignalHandlers.h"
|
||||||
#include "wasm/WasmTypes.h"
|
#include "wasm/WasmTypes.h"
|
||||||
|
|
@ -172,18 +170,12 @@ static const JSPropertySpec static_properties[] = {
|
||||||
|
|
||||||
|
|
||||||
static const JSFunctionSpec prototype_functions[] = {
|
static const JSFunctionSpec prototype_functions[] = {
|
||||||
JS_FN("resize", ArrayBufferObject::fun_resize, 1, 0),
|
|
||||||
JS_SELF_HOSTED_FN("slice", "ArrayBufferSlice", 2, 0),
|
JS_SELF_HOSTED_FN("slice", "ArrayBufferSlice", 2, 0),
|
||||||
JS_FN("transfer", ArrayBufferObject::fun_transfer, 0, 0),
|
|
||||||
JS_FN("transferToFixedLength", ArrayBufferObject::fun_transferToFixedLength, 0, 0),
|
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
static const JSPropertySpec prototype_properties[] = {
|
static const JSPropertySpec prototype_properties[] = {
|
||||||
JS_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0),
|
JS_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0),
|
||||||
JS_PSG("detached", ArrayBufferObject::detachedGetter, 0),
|
|
||||||
JS_PSG("maxByteLength", ArrayBufferObject::maxByteLengthGetter, 0),
|
|
||||||
JS_PSG("resizable", ArrayBufferObject::resizableGetter, 0),
|
|
||||||
JS_STRING_SYM_PS(toStringTag, "ArrayBuffer", JSPROP_READONLY),
|
JS_STRING_SYM_PS(toStringTag, "ArrayBuffer", JSPROP_READONLY),
|
||||||
JS_PS_END
|
JS_PS_END
|
||||||
};
|
};
|
||||||
|
|
@ -260,52 +252,6 @@ ArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
|
return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
|
||||||
ArrayBufferObject::detachedGetterImpl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
|
||||||
args.rval().setBoolean(args.thisv().toObject().as<ArrayBufferObject>().isDetached());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::detachedGetter(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsArrayBuffer, detachedGetterImpl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
|
||||||
ArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
|
||||||
ArrayBufferObject& buffer = args.thisv().toObject().as<ArrayBufferObject>();
|
|
||||||
args.rval().setInt32(buffer.isDetached() ? 0 : int32_t(buffer.maxByteLength()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsArrayBuffer, maxByteLengthGetterImpl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
|
||||||
ArrayBufferObject::resizableGetterImpl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
|
||||||
args.rval().setBoolean(args.thisv().toObject().as<ArrayBufferObject>().isResizable());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::resizableGetter(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsArrayBuffer, resizableGetterImpl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
|
* ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
|
||||||
*/
|
*/
|
||||||
|
|
@ -318,39 +264,6 @@ ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
GetArrayBufferMaxByteLengthOption(JSContext* cx, HandleValue options,
|
|
||||||
uint32_t byteLength, uint32_t* maxByteLength,
|
|
||||||
bool* resizable)
|
|
||||||
{
|
|
||||||
*maxByteLength = byteLength;
|
|
||||||
*resizable = false;
|
|
||||||
|
|
||||||
if (!options.isObject())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
RootedObject opts(cx, &options.toObject());
|
|
||||||
RootedValue maxByteLengthValue(cx);
|
|
||||||
if (!GetProperty(cx, opts, opts, cx->names().maxByteLength, &maxByteLengthValue))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (maxByteLengthValue.isUndefined())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint64_t max;
|
|
||||||
if (!ToIndex(cx, maxByteLengthValue, &max))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (max > INT32_MAX || max < byteLength) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*maxByteLength = uint32_t(max);
|
|
||||||
*resizable = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ES2017 draft 24.1.2.1
|
// ES2017 draft 24.1.2.1
|
||||||
bool
|
bool
|
||||||
|
|
@ -374,22 +287,12 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
uint32_t maxByteLength;
|
|
||||||
bool resizable;
|
|
||||||
if (!GetArrayBufferMaxByteLengthOption(cx, args.get(1), uint32_t(byteLength),
|
|
||||||
&maxByteLength, &resizable))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4.
|
|
||||||
RootedObject proto(cx);
|
RootedObject proto(cx);
|
||||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JSObject* bufobj = create(cx, uint32_t(byteLength), BufferContents::createPlain(nullptr),
|
JSObject* bufobj = create(cx, uint32_t(byteLength), proto);
|
||||||
OwnsData, proto, GenericObject, maxByteLength, resizable);
|
|
||||||
if (!bufobj)
|
if (!bufobj)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setObject(*bufobj);
|
args.rval().setObject(*bufobj);
|
||||||
|
|
@ -399,66 +302,13 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
||||||
static ArrayBufferObject::BufferContents
|
static ArrayBufferObject::BufferContents
|
||||||
AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
|
AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
|
||||||
{
|
{
|
||||||
uint8_t* p = cx->runtime()->pod_callocCanGC<uint8_t>(nbytes ? nbytes : 1);
|
uint8_t* p = cx->runtime()->pod_callocCanGC<uint8_t>(nbytes);
|
||||||
if (!p)
|
if (!p)
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
|
|
||||||
return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
|
return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
ReportArrayBufferNotResizable(JSContext* cx)
|
|
||||||
{
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARRAYBUFFER_NOT_RESIZABLE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ReportArrayBufferCannotDetach(JSContext* cx)
|
|
||||||
{
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARRAYBUFFER_CANNOT_DETACH);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ReportArrayBufferLengthOutOfRange(JSContext* cx)
|
|
||||||
{
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ArrayBufferViewFits(ArrayBufferViewObject* view, uint32_t newByteLength)
|
|
||||||
{
|
|
||||||
if (view->is<DataViewObject>()) {
|
|
||||||
DataViewObject& dataView = view->as<DataViewObject>();
|
|
||||||
uint32_t byteOffset = dataView.byteOffsetMaybeOutOfBounds();
|
|
||||||
if (byteOffset > newByteLength)
|
|
||||||
return false;
|
|
||||||
uint32_t byteLength = dataView.isLengthTracking()
|
|
||||||
? newByteLength - byteOffset
|
|
||||||
: dataView.fixedByteLengthMaybeOutOfBounds();
|
|
||||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (view->is<TypedArrayObject>()) {
|
|
||||||
TypedArrayObject& typedArray = view->as<TypedArrayObject>();
|
|
||||||
if (typedArray.isSharedMemory())
|
|
||||||
return true;
|
|
||||||
uint32_t byteOffset = typedArray.byteOffsetMaybeOutOfBounds();
|
|
||||||
if (byteOffset > newByteLength)
|
|
||||||
return false;
|
|
||||||
uint32_t byteLength = typedArray.isLengthTracking()
|
|
||||||
? newByteLength - byteOffset
|
|
||||||
: typedArray.fixedLengthMaybeOutOfBounds() *
|
|
||||||
typedArray.bytesPerElement();
|
|
||||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outline typed objects don't have a recoverable fixed byte range here.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
NoteViewBufferWasDetached(ArrayBufferViewObject* view,
|
NoteViewBufferWasDetached(ArrayBufferViewObject* view,
|
||||||
ArrayBufferObject::BufferContents newContents,
|
ArrayBufferObject::BufferContents newContents,
|
||||||
|
|
@ -541,8 +391,7 @@ ArrayBufferObject::setNewData(FreeOp* fop, BufferContents newContents, OwnsState
|
||||||
|
|
||||||
void
|
void
|
||||||
ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
||||||
uint8_t* oldDataPointer, BufferContents newContents,
|
uint8_t* oldDataPointer, BufferContents newContents)
|
||||||
uint32_t newByteLength)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!view->isSharedMemory());
|
MOZ_ASSERT(!view->isSharedMemory());
|
||||||
|
|
||||||
|
|
@ -553,18 +402,7 @@ ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view
|
||||||
uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
|
uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
|
||||||
if (viewDataPointer) {
|
if (viewDataPointer) {
|
||||||
MOZ_ASSERT(newContents);
|
MOZ_ASSERT(newContents);
|
||||||
uint32_t offset;
|
ptrdiff_t offset = viewDataPointer - oldDataPointer;
|
||||||
if (view->is<DataViewObject>()) {
|
|
||||||
offset = view->as<DataViewObject>().byteOffsetMaybeOutOfBounds();
|
|
||||||
} else if (view->is<TypedArrayObject>()) {
|
|
||||||
offset = view->as<TypedArrayObject>().byteOffsetMaybeOutOfBounds();
|
|
||||||
} else {
|
|
||||||
ptrdiff_t oldOffset = viewDataPointer - oldDataPointer;
|
|
||||||
MOZ_ASSERT(oldOffset >= 0);
|
|
||||||
offset = uint32_t(oldOffset);
|
|
||||||
}
|
|
||||||
if (offset > newByteLength)
|
|
||||||
offset = 0;
|
|
||||||
viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset;
|
viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset;
|
||||||
view->setDataPointerUnshared(viewDataPointer);
|
view->setDataPointerUnshared(viewDataPointer);
|
||||||
}
|
}
|
||||||
|
|
@ -590,180 +428,10 @@ ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents,
|
||||||
auto& innerViews = cx->compartment()->innerViews.get();
|
auto& innerViews = cx->compartment()->innerViews.get();
|
||||||
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
||||||
for (size_t i = 0; i < views->length(); i++)
|
for (size_t i = 0; i < views->length(); i++)
|
||||||
changeViewContents(cx, (*views)[i], oldDataPointer, newContents, byteLength());
|
changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
|
||||||
}
|
}
|
||||||
if (firstView())
|
if (firstView())
|
||||||
changeViewContents(cx, firstView(), oldDataPointer, newContents, byteLength());
|
changeViewContents(cx, firstView(), oldDataPointer, newContents);
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ArrayBufferObject::changeContentsForResize(JSContext* cx, BufferContents newContents,
|
|
||||||
OwnsState ownsState, uint32_t newByteLength)
|
|
||||||
{
|
|
||||||
MOZ_RELEASE_ASSERT(!isWasm());
|
|
||||||
MOZ_ASSERT(!forInlineTypedObject());
|
|
||||||
|
|
||||||
uint8_t* oldDataPointer = dataPointer();
|
|
||||||
setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
|
|
||||||
setByteLength(newByteLength);
|
|
||||||
|
|
||||||
auto& innerViews = cx->compartment()->innerViews.get();
|
|
||||||
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
|
||||||
for (size_t i = 0; i < views->length(); i++) {
|
|
||||||
ArrayBufferViewObject* view = (*views)[i];
|
|
||||||
if (view->is<DataViewObject>() || view->is<TypedArrayObject>())
|
|
||||||
changeViewContents(cx, view, oldDataPointer, newContents, newByteLength);
|
|
||||||
else if (ArrayBufferViewFits(view, newByteLength))
|
|
||||||
changeViewContents(cx, view, oldDataPointer, newContents, newByteLength);
|
|
||||||
else
|
|
||||||
NoteViewBufferWasDetached(view, newContents, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstView()) {
|
|
||||||
if (firstView()->is<DataViewObject>() || firstView()->is<TypedArrayObject>())
|
|
||||||
changeViewContents(cx, firstView(), oldDataPointer, newContents, newByteLength);
|
|
||||||
else if (ArrayBufferViewFits(firstView(), newByteLength))
|
|
||||||
changeViewContents(cx, firstView(), oldDataPointer, newContents, newByteLength);
|
|
||||||
else
|
|
||||||
NoteViewBufferWasDetached(firstView(), newContents, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ResizeArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer, uint32_t newByteLength)
|
|
||||||
{
|
|
||||||
if (!buffer->isResizable())
|
|
||||||
return ReportArrayBufferNotResizable(cx);
|
|
||||||
|
|
||||||
if (buffer->isDetached()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newByteLength > buffer->maxByteLength())
|
|
||||||
return ReportArrayBufferLengthOutOfRange(cx);
|
|
||||||
|
|
||||||
if (!buffer->isPlain() || buffer->isPreparedForAsmJS() || buffer->forInlineTypedObject())
|
|
||||||
return ReportArrayBufferCannotDetach(cx);
|
|
||||||
|
|
||||||
if (newByteLength == buffer->byteLength())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
ArrayBufferObject::BufferContents newContents = AllocateArrayBufferContents(cx, newByteLength);
|
|
||||||
if (!newContents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint32_t copyLength = std::min(newByteLength, buffer->byteLength());
|
|
||||||
if (copyLength > 0)
|
|
||||||
memcpy(newContents.data(), buffer->dataPointer(), copyLength);
|
|
||||||
|
|
||||||
buffer->changeContentsForResize(cx, newContents, ArrayBufferObject::OwnsData, newByteLength);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::fun_resize_impl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
|
||||||
|
|
||||||
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
|
||||||
if (!buffer->isResizable())
|
|
||||||
return ReportArrayBufferNotResizable(cx);
|
|
||||||
|
|
||||||
uint64_t newByteLength;
|
|
||||||
if (!ToIndex(cx, args.get(0), &newByteLength))
|
|
||||||
return false;
|
|
||||||
if (newByteLength > INT32_MAX)
|
|
||||||
return ReportArrayBufferLengthOutOfRange(cx);
|
|
||||||
|
|
||||||
if (!ResizeArrayBuffer(cx, buffer, uint32_t(newByteLength)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setUndefined();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::fun_resize(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsArrayBuffer, fun_resize_impl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ArrayBufferTransfer(JSContext* cx, const CallArgs& args, bool preserveResizability)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
|
||||||
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
|
||||||
|
|
||||||
uint32_t newByteLength = buffer->byteLength();
|
|
||||||
if (args.hasDefined(0)) {
|
|
||||||
uint64_t newLength;
|
|
||||||
if (!ToIndex(cx, args.get(0), &newLength))
|
|
||||||
return false;
|
|
||||||
if (newLength > INT32_MAX)
|
|
||||||
return ReportArrayBufferLengthOutOfRange(cx);
|
|
||||||
newByteLength = uint32_t(newLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer->isDetached()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer->isWasm() || buffer->isPreparedForAsmJS())
|
|
||||||
return ReportArrayBufferCannotDetach(cx);
|
|
||||||
|
|
||||||
bool newResizable = preserveResizability && buffer->isResizable();
|
|
||||||
uint32_t newMaxByteLength = newResizable ? buffer->maxByteLength() : newByteLength;
|
|
||||||
if (newResizable && newByteLength > newMaxByteLength)
|
|
||||||
return ReportArrayBufferLengthOutOfRange(cx);
|
|
||||||
|
|
||||||
Rooted<ArrayBufferObject*> newBuffer(cx,
|
|
||||||
ArrayBufferObject::create(cx, newByteLength, ArrayBufferObject::BufferContents::createPlain(nullptr),
|
|
||||||
ArrayBufferObject::OwnsData, nullptr, GenericObject,
|
|
||||||
newMaxByteLength, newResizable));
|
|
||||||
if (!newBuffer)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint32_t copyLength = std::min(newByteLength, buffer->byteLength());
|
|
||||||
if (copyLength > 0)
|
|
||||||
memcpy(newBuffer->dataPointer(), buffer->dataPointer(), copyLength);
|
|
||||||
|
|
||||||
ArrayBufferObject::BufferContents detachedContents =
|
|
||||||
buffer->hasStealableContents() ? ArrayBufferObject::BufferContents::createPlain(nullptr)
|
|
||||||
: buffer->contents();
|
|
||||||
ArrayBufferObject::detach(cx, buffer, detachedContents);
|
|
||||||
|
|
||||||
args.rval().setObject(*newBuffer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::fun_transfer_impl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
return ArrayBufferTransfer(cx, args, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::fun_transfer(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsArrayBuffer, fun_transfer_impl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::fun_transferToFixedLength_impl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
return ArrayBufferTransfer(cx, args, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ArrayBufferObject::fun_transferToFixedLength(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsArrayBuffer, fun_transferToFixedLength_impl>(cx, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1219,14 +887,6 @@ ArrayBufferObject::byteLength() const
|
||||||
return getSlot(BYTE_LENGTH_SLOT).toInt32();
|
return getSlot(BYTE_LENGTH_SLOT).toInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
|
||||||
ArrayBufferObject::maxByteLength() const
|
|
||||||
{
|
|
||||||
if (!isResizable())
|
|
||||||
return byteLength();
|
|
||||||
return getSlot(MAX_BYTE_LENGTH_SLOT).toInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ArrayBufferObject::setByteLength(uint32_t length)
|
ArrayBufferObject::setByteLength(uint32_t length)
|
||||||
{
|
{
|
||||||
|
|
@ -1269,7 +929,7 @@ js::WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf)
|
||||||
if (buf->is<ArrayBufferObject>())
|
if (buf->is<ArrayBufferObject>())
|
||||||
return buf->as<ArrayBufferObject>().wasmMaxSize();
|
return buf->as<ArrayBufferObject>().wasmMaxSize();
|
||||||
|
|
||||||
return Some(buf->as<SharedArrayBufferObject>().maxByteLength());
|
return Some(buf->as<SharedArrayBufferObject>().byteLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
|
|
@ -1375,12 +1035,9 @@ ArrayBufferObject*
|
||||||
ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents,
|
ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents,
|
||||||
OwnsState ownsState /* = OwnsData */,
|
OwnsState ownsState /* = OwnsData */,
|
||||||
HandleObject proto /* = nullptr */,
|
HandleObject proto /* = nullptr */,
|
||||||
NewObjectKind newKind /* = GenericObject */,
|
NewObjectKind newKind /* = GenericObject */)
|
||||||
uint32_t maxByteLength /* = 0 */,
|
|
||||||
bool resizable /* = false */)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
|
MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
|
||||||
MOZ_ASSERT_IF(resizable, maxByteLength >= nbytes);
|
|
||||||
|
|
||||||
// 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
|
// 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
|
||||||
// Refuse to allocate too large buffers, currently limited to ~2 GiB.
|
// Refuse to allocate too large buffers, currently limited to ~2 GiB.
|
||||||
|
|
@ -1389,6 +1046,10 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we need to allocate data, try to use a larger object size class so
|
||||||
|
// that the array buffer's data can be allocated inline with the object.
|
||||||
|
// The extra space will be left unused by the object's fixed slots and
|
||||||
|
// available for the buffer's data, see NewObject().
|
||||||
size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
|
size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
|
||||||
|
|
||||||
size_t nslots = reservedSlots;
|
size_t nslots = reservedSlots;
|
||||||
|
|
@ -1405,11 +1066,19 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(ownsState == OwnsData);
|
MOZ_ASSERT(ownsState == OwnsData);
|
||||||
|
size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
|
||||||
|
if (nbytes <= usableSlots * sizeof(Value)) {
|
||||||
|
int newSlots = (nbytes - 1) / sizeof(Value) + 1;
|
||||||
|
MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
|
||||||
|
nslots = reservedSlots + newSlots;
|
||||||
|
contents = BufferContents::createPlain(nullptr);
|
||||||
|
} else {
|
||||||
contents = AllocateArrayBufferContents(cx, nbytes);
|
contents = AllocateArrayBufferContents(cx, nbytes);
|
||||||
if (!contents)
|
if (!contents)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
allocated = true;
|
allocated = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
|
MOZ_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
|
||||||
gc::AllocKind allocKind = GetGCObjectKind(nslots);
|
gc::AllocKind allocKind = GetGCObjectKind(nslots);
|
||||||
|
|
@ -1428,11 +1097,10 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
|
||||||
|
|
||||||
if (!contents) {
|
if (!contents) {
|
||||||
void* data = obj->inlineDataPointer();
|
void* data = obj->inlineDataPointer();
|
||||||
memset(data, 0, nbytes);
|
js_memset(data, 0, nbytes);
|
||||||
obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData,
|
obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
|
||||||
maxByteLength, resizable);
|
|
||||||
} else {
|
} else {
|
||||||
obj->initialize(nbytes, contents, ownsState, maxByteLength, resizable);
|
obj->initialize(nbytes, contents, ownsState);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
|
@ -1462,28 +1130,25 @@ ArrayBufferObject::createEmpty(JSContext* cx)
|
||||||
bool
|
bool
|
||||||
ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args)
|
ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsAnyArrayBuffer(args.thisv()));
|
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This method is only called for |DataView(alienBuf, ...)| which calls
|
* This method is only called for |DataView(alienBuf, ...)| which calls
|
||||||
* this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength,
|
* this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength,
|
||||||
* DataView.prototype, lengthTracking)|,
|
* DataView.prototype)|,
|
||||||
* ergo there must be exactly 4 arguments.
|
* ergo there must be exactly 3 arguments.
|
||||||
*/
|
*/
|
||||||
MOZ_ASSERT(args.length() == 4);
|
MOZ_ASSERT(args.length() == 3);
|
||||||
|
|
||||||
uint32_t byteOffset = args[0].toPrivateUint32();
|
uint32_t byteOffset = args[0].toPrivateUint32();
|
||||||
uint32_t byteLength = args[1].toPrivateUint32();
|
uint32_t byteLength = args[1].toPrivateUint32();
|
||||||
bool lengthTracking = args[3].toBoolean();
|
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
||||||
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx,
|
|
||||||
&args.thisv().toObject().as<ArrayBufferObjectMaybeShared>());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pop off the passed-along prototype and delegate to normal DataViewObject
|
* Pop off the passed-along prototype and delegate to normal DataViewObject
|
||||||
* construction.
|
* construction.
|
||||||
*/
|
*/
|
||||||
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer,
|
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, &args[2].toObject());
|
||||||
&args[2].toObject(), lengthTracking);
|
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setObject(*obj);
|
args.rval().setObject(*obj);
|
||||||
|
|
@ -1494,7 +1159,7 @@ bool
|
||||||
ArrayBufferObject::createDataViewForThis(JSContext* cx, unsigned argc, Value* vp)
|
ArrayBufferObject::createDataViewForThis(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
return CallNonGenericMethod<IsAnyArrayBuffer, createDataViewForThisImpl>(cx, args);
|
return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ ArrayBufferObject::BufferContents
|
/* static */ ArrayBufferObject::BufferContents
|
||||||
|
|
@ -1863,8 +1528,6 @@ ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
|
||||||
// The data may or may not be inline with the buffer. The buffer
|
// The data may or may not be inline with the buffer. The buffer
|
||||||
// can only move during a compacting GC, in which case its
|
// can only move during a compacting GC, in which case its
|
||||||
// objectMoved hook has already updated the buffer's data pointer.
|
// objectMoved hook has already updated the buffer's data pointer.
|
||||||
if (offset > buf.byteLength())
|
|
||||||
offset = 0;
|
|
||||||
obj->initPrivate(buf.dataPointer() + offset);
|
obj->initPrivate(buf.dataPointer() + offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1915,8 +1578,6 @@ ArrayBufferViewObject::dataPointerUnshared(const JS::AutoRequireNoGC& nogc)
|
||||||
bool
|
bool
|
||||||
ArrayBufferViewObject::isSharedMemory()
|
ArrayBufferViewObject::isSharedMemory()
|
||||||
{
|
{
|
||||||
if (is<DataViewObject>())
|
|
||||||
return as<DataViewObject>().isSharedMemory();
|
|
||||||
if (is<TypedArrayObject>())
|
if (is<TypedArrayObject>())
|
||||||
return as<TypedArrayObject>().isSharedMemory();
|
return as<TypedArrayObject>().isSharedMemory();
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1948,7 +1609,7 @@ ArrayBufferViewObject::bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*
|
||||||
return thisObject->as<TypedArrayObject>().bufferEither();
|
return thisObject->as<TypedArrayObject>().bufferEither();
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(thisObject->is<DataViewObject>());
|
MOZ_ASSERT(thisObject->is<DataViewObject>());
|
||||||
return &thisObject->as<DataViewObject>().arrayBufferEither();
|
return &thisObject->as<DataViewObject>().arrayBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* JS Friend API */
|
/* JS Friend API */
|
||||||
|
|
@ -2199,7 +1860,7 @@ JS_GetArrayBufferViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoChe
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (obj->is<DataViewObject>()) {
|
if (obj->is<DataViewObject>()) {
|
||||||
*isSharedMemory = obj->as<DataViewObject>().isSharedMemory();
|
*isSharedMemory = false;
|
||||||
return obj->as<DataViewObject>().dataPointer();
|
return obj->as<DataViewObject>().dataPointer();
|
||||||
}
|
}
|
||||||
TypedArrayObject& ta = obj->as<TypedArrayObject>();
|
TypedArrayObject& ta = obj->as<TypedArrayObject>();
|
||||||
|
|
@ -2269,7 +1930,7 @@ js::GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, bool* isSha
|
||||||
: obj->as<TypedArrayObject>().byteLength();
|
: obj->as<TypedArrayObject>().byteLength();
|
||||||
|
|
||||||
if (obj->is<DataViewObject>()) {
|
if (obj->is<DataViewObject>()) {
|
||||||
*isSharedMemory = obj->as<DataViewObject>().isSharedMemory();
|
*isSharedMemory = false;
|
||||||
*data = static_cast<uint8_t*>(obj->as<DataViewObject>().dataPointer());
|
*data = static_cast<uint8_t*>(obj->as<DataViewObject>().dataPointer());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ ArrayBufferObjectMaybeShared& AsAnyArrayBuffer(HandleValue val);
|
||||||
class ArrayBufferObjectMaybeShared : public NativeObject
|
class ArrayBufferObjectMaybeShared : public NativeObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
uint32_t byteLength() const {
|
uint32_t byteLength() {
|
||||||
return AnyArrayBufferByteLength(this);
|
return AnyArrayBufferByteLength(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,22 +128,15 @@ typedef MutableHandle<ArrayBufferObjectMaybeShared*> MutableHandleArrayBufferObj
|
||||||
class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
{
|
{
|
||||||
static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
||||||
static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
|
||||||
static bool resizableGetterImpl(JSContext* cx, const CallArgs& args);
|
|
||||||
static bool detachedGetterImpl(JSContext* cx, const CallArgs& args);
|
|
||||||
static bool fun_slice_impl(JSContext* cx, const CallArgs& args);
|
static bool fun_slice_impl(JSContext* cx, const CallArgs& args);
|
||||||
static bool fun_resize_impl(JSContext* cx, const CallArgs& args);
|
|
||||||
static bool fun_transfer_impl(JSContext* cx, const CallArgs& args);
|
|
||||||
static bool fun_transferToFixedLength_impl(JSContext* cx, const CallArgs& args);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const uint8_t DATA_SLOT = 0;
|
static const uint8_t DATA_SLOT = 0;
|
||||||
static const uint8_t BYTE_LENGTH_SLOT = 1;
|
static const uint8_t BYTE_LENGTH_SLOT = 1;
|
||||||
static const uint8_t FIRST_VIEW_SLOT = 2;
|
static const uint8_t FIRST_VIEW_SLOT = 2;
|
||||||
static const uint8_t FLAGS_SLOT = 3;
|
static const uint8_t FLAGS_SLOT = 3;
|
||||||
static const uint8_t MAX_BYTE_LENGTH_SLOT = 4;
|
|
||||||
|
|
||||||
static const uint8_t RESERVED_SLOTS = 5;
|
static const uint8_t RESERVED_SLOTS = 4;
|
||||||
|
|
||||||
static const size_t ARRAY_BUFFER_ALIGNMENT = 8;
|
static const size_t ARRAY_BUFFER_ALIGNMENT = 8;
|
||||||
|
|
||||||
|
|
@ -196,11 +189,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
|
|
||||||
// This PLAIN or WASM buffer has been prepared for asm.js and cannot
|
// This PLAIN or WASM buffer has been prepared for asm.js and cannot
|
||||||
// henceforth be transferred/detached.
|
// henceforth be transferred/detached.
|
||||||
FOR_ASMJS = 0x40,
|
FOR_ASMJS = 0x40
|
||||||
|
|
||||||
// This buffer was created with [[ArrayBufferMaxByteLength]] and can
|
|
||||||
// be resized up to that maximum.
|
|
||||||
RESIZABLE = 0x80
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
|
static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
|
||||||
|
|
@ -241,14 +230,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||||
static bool maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static bool resizableGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static bool detachedGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
|
|
||||||
static bool fun_slice(JSContext* cx, unsigned argc, Value* vp);
|
static bool fun_slice(JSContext* cx, unsigned argc, Value* vp);
|
||||||
static bool fun_resize(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static bool fun_transfer(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static bool fun_transferToFixedLength(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
|
|
||||||
static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
|
static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
|
@ -260,9 +243,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
BufferContents contents,
|
BufferContents contents,
|
||||||
OwnsState ownsState = OwnsData,
|
OwnsState ownsState = OwnsData,
|
||||||
HandleObject proto = nullptr,
|
HandleObject proto = nullptr,
|
||||||
NewObjectKind newKind = GenericObject,
|
NewObjectKind newKind = GenericObject);
|
||||||
uint32_t maxByteLength = 0,
|
|
||||||
bool resizable = false);
|
|
||||||
static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
|
static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
|
||||||
HandleObject proto = nullptr,
|
HandleObject proto = nullptr,
|
||||||
NewObjectKind newKind = GenericObject);
|
NewObjectKind newKind = GenericObject);
|
||||||
|
|
@ -313,8 +294,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
|
|
||||||
void setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState);
|
void setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState);
|
||||||
void changeContents(JSContext* cx, BufferContents newContents, OwnsState ownsState);
|
void changeContents(JSContext* cx, BufferContents newContents, OwnsState ownsState);
|
||||||
void changeContentsForResize(JSContext* cx, BufferContents newContents,
|
|
||||||
OwnsState ownsState, uint32_t newByteLength);
|
|
||||||
|
|
||||||
// Detach this buffer from its original memory. (This necessarily makes
|
// Detach this buffer from its original memory. (This necessarily makes
|
||||||
// views of this buffer unusable for modifying that original memory.)
|
// views of this buffer unusable for modifying that original memory.)
|
||||||
|
|
@ -323,8 +302,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
||||||
uint8_t* oldDataPointer, BufferContents newContents,
|
uint8_t* oldDataPointer, BufferContents newContents);
|
||||||
uint32_t newByteLength);
|
|
||||||
void setFirstView(ArrayBufferViewObject* view);
|
void setFirstView(ArrayBufferViewObject* view);
|
||||||
|
|
||||||
uint8_t* inlineDataPointer() const;
|
uint8_t* inlineDataPointer() const;
|
||||||
|
|
@ -333,7 +311,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
uint8_t* dataPointer() const;
|
uint8_t* dataPointer() const;
|
||||||
SharedMem<uint8_t*> dataPointerShared() const;
|
SharedMem<uint8_t*> dataPointerShared() const;
|
||||||
uint32_t byteLength() const;
|
uint32_t byteLength() const;
|
||||||
uint32_t maxByteLength() const;
|
|
||||||
|
|
||||||
BufferContents contents() const {
|
BufferContents contents() const {
|
||||||
return BufferContents(dataPointer(), bufferKind());
|
return BufferContents(dataPointer(), bufferKind());
|
||||||
|
|
@ -357,7 +334,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
bool isWasm() const { return bufferKind() == WASM; }
|
bool isWasm() const { return bufferKind() == WASM; }
|
||||||
bool isMapped() const { return bufferKind() == MAPPED; }
|
bool isMapped() const { return bufferKind() == MAPPED; }
|
||||||
bool isDetached() const { return flags() & DETACHED; }
|
bool isDetached() const { return flags() & DETACHED; }
|
||||||
bool isResizable() const { return flags() & RESIZABLE; }
|
|
||||||
bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
|
bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
|
||||||
|
|
||||||
// WebAssembly support:
|
// WebAssembly support:
|
||||||
|
|
@ -415,17 +391,12 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
|
|
||||||
void setIsDetached() { setFlags(flags() | DETACHED); }
|
void setIsDetached() { setFlags(flags() | DETACHED); }
|
||||||
void setIsPreparedForAsmJS() { setFlags(flags() | FOR_ASMJS); }
|
void setIsPreparedForAsmJS() { setFlags(flags() | FOR_ASMJS); }
|
||||||
void setIsResizable() { setFlags(flags() | RESIZABLE); }
|
|
||||||
|
|
||||||
void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState,
|
void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState) {
|
||||||
uint32_t maxByteLength = 0, bool resizable = false) {
|
|
||||||
setByteLength(byteLength);
|
setByteLength(byteLength);
|
||||||
setFlags(0);
|
setFlags(0);
|
||||||
setFixedSlot(MAX_BYTE_LENGTH_SLOT, Int32Value(maxByteLength ? maxByteLength : byteLength));
|
|
||||||
setFirstView(nullptr);
|
setFirstView(nullptr);
|
||||||
setDataPointer(contents, ownsState);
|
setDataPointer(contents, ownsState);
|
||||||
if (resizable)
|
|
||||||
setIsResizable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: initialize() may be called after initEmpty(); initEmpty() must
|
// Note: initialize() may be called after initEmpty(); initEmpty() must
|
||||||
|
|
@ -433,7 +404,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
void initEmpty() {
|
void initEmpty() {
|
||||||
setByteLength(0);
|
setByteLength(0);
|
||||||
setFlags(0);
|
setFlags(0);
|
||||||
setFixedSlot(MAX_BYTE_LENGTH_SLOT, Int32Value(0));
|
|
||||||
setFirstView(nullptr);
|
setFirstView(nullptr);
|
||||||
setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
|
setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,6 @@
|
||||||
macro(lookupSetter, lookupSetter, "__lookupSetter__") \
|
macro(lookupSetter, lookupSetter, "__lookupSetter__") \
|
||||||
macro(MapConstructorInit, MapConstructorInit, "MapConstructorInit") \
|
macro(MapConstructorInit, MapConstructorInit, "MapConstructorInit") \
|
||||||
macro(MapIterator, MapIterator, "Map Iterator") \
|
macro(MapIterator, MapIterator, "Map Iterator") \
|
||||||
macro(maxByteLength, maxByteLength, "maxByteLength") \
|
|
||||||
macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \
|
macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \
|
||||||
macro(maximumSignificantDigits, maximumSignificantDigits, "maximumSignificantDigits") \
|
macro(maximumSignificantDigits, maximumSignificantDigits, "maximumSignificantDigits") \
|
||||||
macro(message, message, "message") \
|
macro(message, message, "message") \
|
||||||
|
|
@ -430,7 +429,6 @@
|
||||||
macro(toJSON, toJSON, "toJSON") \
|
macro(toJSON, toJSON, "toJSON") \
|
||||||
macro(toLocaleString, toLocaleString, "toLocaleString") \
|
macro(toLocaleString, toLocaleString, "toLocaleString") \
|
||||||
macro(toSource, toSource, "toSource") \
|
macro(toSource, toSource, "toSource") \
|
||||||
macro(toSorted, toSorted, "toSorted") \
|
|
||||||
macro(toString, toString, "toString") \
|
macro(toString, toString, "toString") \
|
||||||
macro(toUTCString, toUTCString, "toUTCString") \
|
macro(toUTCString, toUTCString, "toUTCString") \
|
||||||
macro(true, true_, "true") \
|
macro(true, true_, "true") \
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
#include "builtin/SymbolObject.h"
|
#include "builtin/SymbolObject.h"
|
||||||
#include "builtin/TypedObject.h"
|
#include "builtin/TypedObject.h"
|
||||||
#include "builtin/WeakMapObject.h"
|
#include "builtin/WeakMapObject.h"
|
||||||
#include "builtin/WeakRefObject.h"
|
|
||||||
#include "builtin/WeakSetObject.h"
|
#include "builtin/WeakSetObject.h"
|
||||||
#include "vm/Debugger.h"
|
#include "vm/Debugger.h"
|
||||||
#include "vm/EnvironmentObject.h"
|
#include "vm/EnvironmentObject.h"
|
||||||
|
|
@ -465,7 +464,6 @@ GlobalObject::initStandardClasses(JSContext* cx, Handle<GlobalObject*> global)
|
||||||
if (!ensureConstructor(cx, global, static_cast<JSProtoKey>(k)))
|
if (!ensureConstructor(cx, global, static_cast<JSProtoKey>(k)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,7 +538,6 @@ GlobalObject::initSelfHostingBuiltins(JSContext* cx, Handle<GlobalObject*> globa
|
||||||
InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
|
InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
|
||||||
InitBareSymbolCtor(cx, global) &&
|
InitBareSymbolCtor(cx, global) &&
|
||||||
InitBareWeakMapCtor(cx, global) &&
|
InitBareWeakMapCtor(cx, global) &&
|
||||||
InitBareWeakRefCtor(cx, global) &&
|
|
||||||
InitStopIterationClass(cx, global) &&
|
InitStopIterationClass(cx, global) &&
|
||||||
DefineFunctions(cx, global, builtins, AsIntrinsic);
|
DefineFunctions(cx, global, builtins, AsIntrinsic);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,6 @@ JSONParserBase::~JSONParserBase()
|
||||||
void
|
void
|
||||||
JSONParserBase::trace(JSTracer* trc)
|
JSONParserBase::trace(JSTracer* trc)
|
||||||
{
|
{
|
||||||
for (auto& atom : propertyNameCache) {
|
|
||||||
if (atom)
|
|
||||||
TraceRoot(trc, &atom, "JSONParser cached property name");
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < stack.length(); i++) {
|
for (size_t i = 0; i < stack.length(); i++) {
|
||||||
if (stack[i].state == FinishArrayElement) {
|
if (stack[i].state == FinishArrayElement) {
|
||||||
ElementVector& elements = stack[i].elements();
|
ElementVector& elements = stack[i].elements();
|
||||||
|
|
@ -127,24 +123,6 @@ JSONParser<CharT>::readString()
|
||||||
return token(Error);
|
return token(Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrays of records repeatedly use the same property names. Verify the
|
|
||||||
// entire name and closing quote before skipping scanning and atomization.
|
|
||||||
// Only the unescaped path below populates this cache.
|
|
||||||
if (ST == JSONParser::PropertyName && *current != '"') {
|
|
||||||
JSAtom* atom = propertyNameCache[size_t(*current) % PropertyNameCacheSize];
|
|
||||||
if (atom && size_t(end - current) > atom->length() &&
|
|
||||||
current[atom->length()] == '"') {
|
|
||||||
JS::AutoCheckCannotGC nogc;
|
|
||||||
bool matches = atom->hasLatin1Chars()
|
|
||||||
? EqualChars(atom->latin1Chars(nogc), current.get(), atom->length())
|
|
||||||
: EqualChars(atom->twoByteChars(nogc), current.get(), atom->length());
|
|
||||||
if (matches) {
|
|
||||||
current += atom->length() + 1;
|
|
||||||
return stringToken(atom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optimization: if the source contains no escaped characters, create the
|
* Optimization: if the source contains no escaped characters, create the
|
||||||
* string directly from the source text.
|
* string directly from the source text.
|
||||||
|
|
@ -159,9 +137,6 @@ JSONParser<CharT>::readString()
|
||||||
: NewStringCopyN<CanGC>(cx, start.get(), length);
|
: NewStringCopyN<CanGC>(cx, start.get(), length);
|
||||||
if (!str)
|
if (!str)
|
||||||
return token(OOM);
|
return token(OOM);
|
||||||
if (ST == JSONParser::PropertyName && length) {
|
|
||||||
propertyNameCache[size_t(*start) % PropertyNameCacheSize] = &str->asAtom();
|
|
||||||
}
|
|
||||||
return stringToken(str);
|
return stringToken(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,6 @@ class MOZ_STACK_CLASS JSONParserBase
|
||||||
|
|
||||||
const ErrorHandling errorHandling;
|
const ErrorHandling errorHandling;
|
||||||
|
|
||||||
// Reuse unescaped property names within this parse. The cache is bounded
|
|
||||||
// and traced with the parser, including during compacting GC.
|
|
||||||
static const size_t PropertyNameCacheSize = 8;
|
|
||||||
JSAtom* propertyNameCache[PropertyNameCacheSize];
|
|
||||||
|
|
||||||
enum Token { String, Number, True, False, Null,
|
enum Token { String, Number, True, False, Null,
|
||||||
ArrayOpen, ArrayClose,
|
ArrayOpen, ArrayClose,
|
||||||
ObjectOpen, ObjectClose,
|
ObjectOpen, ObjectClose,
|
||||||
|
|
@ -114,7 +109,6 @@ class MOZ_STACK_CLASS JSONParserBase
|
||||||
JSONParserBase(JSContext* cx, ErrorHandling errorHandling)
|
JSONParserBase(JSContext* cx, ErrorHandling errorHandling)
|
||||||
: cx(cx),
|
: cx(cx),
|
||||||
errorHandling(errorHandling),
|
errorHandling(errorHandling),
|
||||||
propertyNameCache{},
|
|
||||||
stack(cx),
|
stack(cx),
|
||||||
freeElements(cx),
|
freeElements(cx),
|
||||||
freeProperties(cx)
|
freeProperties(cx)
|
||||||
|
|
@ -135,10 +129,8 @@ class MOZ_STACK_CLASS JSONParserBase
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
, lastToken(mozilla::Move(other.lastToken))
|
, lastToken(mozilla::Move(other.lastToken))
|
||||||
#endif
|
#endif
|
||||||
{
|
{}
|
||||||
for (size_t i = 0; i < PropertyNameCacheSize; i++)
|
|
||||||
propertyNameCache[i] = other.propertyNameCache[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
Value numberValue() const {
|
Value numberValue() const {
|
||||||
MOZ_ASSERT(lastToken == Number);
|
MOZ_ASSERT(lastToken == Number);
|
||||||
|
|
|
||||||
|
|
@ -43,22 +43,6 @@ static const ObjectElements emptyElementsHeaderShared(0, 0, ObjectElements::Shar
|
||||||
HeapSlot* const js::emptyObjectElementsShared =
|
HeapSlot* const js::emptyObjectElementsShared =
|
||||||
reinterpret_cast<HeapSlot*>(uintptr_t(&emptyElementsHeaderShared) + sizeof(ObjectElements));
|
reinterpret_cast<HeapSlot*>(uintptr_t(&emptyElementsHeaderShared) + sizeof(ObjectElements));
|
||||||
|
|
||||||
static const ObjectElements emptyElementsHeaderResizableOrGrowable(
|
|
||||||
0, 0, ObjectElements::RESIZABLE_OR_GROWABLE_BUFFER);
|
|
||||||
|
|
||||||
/* Objects with no elements share one empty set of elements. */
|
|
||||||
HeapSlot* const js::emptyObjectElementsResizableOrGrowable =
|
|
||||||
reinterpret_cast<HeapSlot*>(uintptr_t(&emptyElementsHeaderResizableOrGrowable) +
|
|
||||||
sizeof(ObjectElements));
|
|
||||||
|
|
||||||
static const ObjectElements emptyElementsHeaderSharedResizableOrGrowable(
|
|
||||||
0, 0, ObjectElements::SHARED_MEMORY | ObjectElements::RESIZABLE_OR_GROWABLE_BUFFER);
|
|
||||||
|
|
||||||
/* Objects with no elements share one empty set of elements. */
|
|
||||||
HeapSlot* const js::emptyObjectElementsSharedResizableOrGrowable =
|
|
||||||
reinterpret_cast<HeapSlot*>(uintptr_t(&emptyElementsHeaderSharedResizableOrGrowable) +
|
|
||||||
sizeof(ObjectElements));
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
|
@ -80,9 +64,7 @@ ObjectElements::ConvertElementsToDoubles(JSContext* cx, uintptr_t elementsPtr)
|
||||||
*/
|
*/
|
||||||
HeapSlot* elementsHeapPtr = (HeapSlot*) elementsPtr;
|
HeapSlot* elementsHeapPtr = (HeapSlot*) elementsPtr;
|
||||||
MOZ_ASSERT(elementsHeapPtr != emptyObjectElements &&
|
MOZ_ASSERT(elementsHeapPtr != emptyObjectElements &&
|
||||||
elementsHeapPtr != emptyObjectElementsShared &&
|
elementsHeapPtr != emptyObjectElementsShared);
|
||||||
elementsHeapPtr != emptyObjectElementsResizableOrGrowable &&
|
|
||||||
elementsHeapPtr != emptyObjectElementsSharedResizableOrGrowable);
|
|
||||||
|
|
||||||
ObjectElements* header = ObjectElements::fromElements(elementsHeapPtr);
|
ObjectElements* header = ObjectElements::fromElements(elementsHeapPtr);
|
||||||
MOZ_ASSERT(!header->shouldConvertDoubleElements());
|
MOZ_ASSERT(!header->shouldConvertDoubleElements());
|
||||||
|
|
|
||||||
|
|
@ -185,11 +185,6 @@ class ObjectElements
|
||||||
|
|
||||||
// These elements are set to integrity level "frozen".
|
// These elements are set to integrity level "frozen".
|
||||||
FROZEN = 0x10,
|
FROZEN = 0x10,
|
||||||
|
|
||||||
// For TypedArrays only: this TypedArray views a resizable
|
|
||||||
// ArrayBuffer or growable SharedArrayBuffer. JIT fast paths with
|
|
||||||
// cached length/data assumptions must fall back for these objects.
|
|
||||||
RESIZABLE_OR_GROWABLE_BUFFER = 0x20,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -260,10 +255,6 @@ class ObjectElements
|
||||||
: flags(SHARED_MEMORY), initializedLength(0), capacity(capacity), length(length)
|
: flags(SHARED_MEMORY), initializedLength(0), capacity(capacity), length(length)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
constexpr ObjectElements(uint32_t capacity, uint32_t length, uint32_t flags)
|
|
||||||
: flags(flags), initializedLength(0), capacity(capacity), length(length)
|
|
||||||
{}
|
|
||||||
|
|
||||||
HeapSlot* elements() {
|
HeapSlot* elements() {
|
||||||
return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
|
return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
|
||||||
}
|
}
|
||||||
|
|
@ -278,10 +269,6 @@ class ObjectElements
|
||||||
return flags & SHARED_MEMORY;
|
return flags & SHARED_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasResizableOrGrowableBuffer() const {
|
|
||||||
return flags & RESIZABLE_OR_GROWABLE_BUFFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
GCPtrNativeObject& ownerObject() const {
|
GCPtrNativeObject& ownerObject() const {
|
||||||
MOZ_ASSERT(isCopyOnWrite());
|
MOZ_ASSERT(isCopyOnWrite());
|
||||||
return *(GCPtrNativeObject*)(&elements()[initializedLength]);
|
return *(GCPtrNativeObject*)(&elements()[initializedLength]);
|
||||||
|
|
@ -339,8 +326,6 @@ static_assert(ObjectElements::VALUES_PER_HEADER * sizeof(HeapSlot) == sizeof(Obj
|
||||||
*/
|
*/
|
||||||
extern HeapSlot* const emptyObjectElements;
|
extern HeapSlot* const emptyObjectElements;
|
||||||
extern HeapSlot* const emptyObjectElementsShared;
|
extern HeapSlot* const emptyObjectElementsShared;
|
||||||
extern HeapSlot* const emptyObjectElementsResizableOrGrowable;
|
|
||||||
extern HeapSlot* const emptyObjectElementsSharedResizableOrGrowable;
|
|
||||||
|
|
||||||
struct Class;
|
struct Class;
|
||||||
class GCMarker;
|
class GCMarker;
|
||||||
|
|
@ -495,12 +480,6 @@ class NativeObject : public ShapedObject
|
||||||
elements_ = emptyObjectElementsShared;
|
elements_ = emptyObjectElementsShared;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHasResizableOrGrowableBuffer() {
|
|
||||||
MOZ_ASSERT(elements_ == emptyObjectElements || elements_ == emptyObjectElementsShared);
|
|
||||||
elements_ = isSharedMemory() ? emptyObjectElementsSharedResizableOrGrowable
|
|
||||||
: emptyObjectElementsResizableOrGrowable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInWholeCellBuffer() const {
|
bool isInWholeCellBuffer() const {
|
||||||
const gc::TenuredCell* cell = &asTenured();
|
const gc::TenuredCell* cell = &asTenured();
|
||||||
gc::ArenaCellSet* cells = cell->arena()->bufferedCells;
|
gc::ArenaCellSet* cells = cell->arena()->bufferedCells;
|
||||||
|
|
@ -1275,10 +1254,7 @@ class NativeObject : public ShapedObject
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool hasEmptyElements() const {
|
inline bool hasEmptyElements() const {
|
||||||
return elements_ == emptyObjectElements ||
|
return elements_ == emptyObjectElements || elements_ == emptyObjectElementsShared;
|
||||||
elements_ == emptyObjectElementsShared ||
|
|
||||||
elements_ == emptyObjectElementsResizableOrGrowable ||
|
|
||||||
elements_ == emptyObjectElementsSharedResizableOrGrowable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@
|
||||||
#include "builtin/SelfHostingDefines.h"
|
#include "builtin/SelfHostingDefines.h"
|
||||||
#include "builtin/Stream.h"
|
#include "builtin/Stream.h"
|
||||||
#include "builtin/TypedObject.h"
|
#include "builtin/TypedObject.h"
|
||||||
#include "builtin/WeakRefObject.h"
|
|
||||||
#include "builtin/WeakSetObject.h"
|
#include "builtin/WeakSetObject.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "gc/Policy.h"
|
#include "gc/Policy.h"
|
||||||
|
|
@ -106,14 +105,6 @@ intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
intrinsic_CanBeHeldWeakly(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
args.rval().setBoolean(CanBeHeldWeakly(args.get(0)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
intrinsic_IsArray(JSContext* cx, unsigned argc, Value* vp)
|
intrinsic_IsArray(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
@ -279,20 +270,6 @@ intrinsic_GetBuiltinConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
intrinsic_NewMap(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
MOZ_ASSERT(args.length() == 0);
|
|
||||||
|
|
||||||
Rooted<MapObject*> map(cx, MapObject::create(cx));
|
|
||||||
if (!map)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setObject(*map);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp)
|
intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
@ -1310,36 +1287,6 @@ intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer(JSContext* cx, unsigned arg
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
intrinsic_TypedArrayIsOutOfBounds(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
MOZ_ASSERT(args.length() == 1);
|
|
||||||
|
|
||||||
RootedObject obj(cx, &args[0].toObject());
|
|
||||||
MOZ_ASSERT(obj->is<TypedArrayObject>());
|
|
||||||
args.rval().setBoolean(obj->as<TypedArrayObject>().isOutOfBounds());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
intrinsic_PossiblyWrappedTypedArrayIsOutOfBounds(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
MOZ_ASSERT(args.length() == 1);
|
|
||||||
MOZ_ASSERT(args[0].isObject());
|
|
||||||
|
|
||||||
JSObject* obj = CheckedUnwrap(&args[0].toObject());
|
|
||||||
if (!obj) {
|
|
||||||
JS_ReportErrorASCII(cx, "Permission denied to access object");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(obj->is<TypedArrayObject>());
|
|
||||||
args.rval().setBoolean(obj->as<TypedArrayObject>().isOutOfBounds());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp)
|
intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
@ -1462,10 +1409,6 @@ intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp)
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (unsafeTypedArrayCrossCompartment->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Steps 21, 23.
|
// Steps 21, 23.
|
||||||
uint32_t unsafeSrcLengthCrossCompartment = unsafeTypedArrayCrossCompartment->length();
|
uint32_t unsafeSrcLengthCrossCompartment = unsafeTypedArrayCrossCompartment->length();
|
||||||
|
|
@ -2325,10 +2268,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||||
JS_INLINABLE_FN("std_Math_min", math_min, 2,0, MathMin),
|
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_abs", math_abs, 1,0, MathAbs),
|
||||||
|
|
||||||
JS_FN("std_Map_create", intrinsic_NewMap, 0,0),
|
|
||||||
JS_FN("std_Map_get", MapObject::get, 1,0),
|
|
||||||
JS_FN("std_Map_has", MapObject::has, 1,0),
|
JS_FN("std_Map_has", MapObject::has, 1,0),
|
||||||
JS_FN("std_Map_set", MapObject::set, 2,0),
|
|
||||||
JS_FN("std_Map_iterator", MapObject::entries, 0,0),
|
JS_FN("std_Map_iterator", MapObject::entries, 0,0),
|
||||||
|
|
||||||
JS_FN("std_Number_valueOf", num_valueOf, 0,0),
|
JS_FN("std_Number_valueOf", num_valueOf, 0,0),
|
||||||
|
|
@ -2376,7 +2316,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||||
// Helper funtions after this point.
|
// Helper funtions after this point.
|
||||||
JS_INLINABLE_FN("ToObject", intrinsic_ToObject, 1,0, IntrinsicToObject),
|
JS_INLINABLE_FN("ToObject", intrinsic_ToObject, 1,0, IntrinsicToObject),
|
||||||
JS_INLINABLE_FN("IsObject", intrinsic_IsObject, 1,0, IntrinsicIsObject),
|
JS_INLINABLE_FN("IsObject", intrinsic_IsObject, 1,0, IntrinsicIsObject),
|
||||||
JS_FN("CanBeHeldWeakly", intrinsic_CanBeHeldWeakly, 1,0),
|
|
||||||
JS_INLINABLE_FN("IsArray", intrinsic_IsArray, 1,0, ArrayIsArray),
|
JS_INLINABLE_FN("IsArray", intrinsic_IsArray, 1,0, ArrayIsArray),
|
||||||
JS_INLINABLE_FN("IsWrappedArrayConstructor", intrinsic_IsWrappedArrayConstructor, 1,0,
|
JS_INLINABLE_FN("IsWrappedArrayConstructor", intrinsic_IsWrappedArrayConstructor, 1,0,
|
||||||
IntrinsicIsWrappedArrayConstructor),
|
IntrinsicIsWrappedArrayConstructor),
|
||||||
|
|
@ -2536,10 +2475,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||||
1, 0, IntrinsicPossiblyWrappedTypedArrayLength),
|
1, 0, IntrinsicPossiblyWrappedTypedArrayLength),
|
||||||
JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer",
|
JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer",
|
||||||
intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0),
|
intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0),
|
||||||
JS_FN("TypedArrayIsOutOfBounds",
|
|
||||||
intrinsic_TypedArrayIsOutOfBounds, 1, 0),
|
|
||||||
JS_FN("PossiblyWrappedTypedArrayIsOutOfBounds",
|
|
||||||
intrinsic_PossiblyWrappedTypedArrayIsOutOfBounds, 1, 0),
|
|
||||||
|
|
||||||
JS_FN("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0),
|
JS_FN("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0),
|
||||||
JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0),
|
JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
|
|
||||||
#include "jsfriendapi.h"
|
#include "jsfriendapi.h"
|
||||||
#include "jsnum.h"
|
|
||||||
#include "jsprf.h"
|
#include "jsprf.h"
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
|
|
@ -105,18 +104,15 @@ SharedArrayAllocSize(uint32_t length)
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedArrayRawBuffer*
|
SharedArrayRawBuffer*
|
||||||
SharedArrayRawBuffer::New(JSContext* cx, uint32_t length, uint32_t maxLength, bool growable)
|
SharedArrayRawBuffer::New(JSContext* cx, uint32_t length)
|
||||||
{
|
{
|
||||||
// The value (uint32_t)-1 is used as a signal in various places,
|
// The value (uint32_t)-1 is used as a signal in various places,
|
||||||
// so guard against it on principle.
|
// so guard against it on principle.
|
||||||
MOZ_ASSERT(length != (uint32_t)-1);
|
MOZ_ASSERT(length != (uint32_t)-1);
|
||||||
MOZ_ASSERT(maxLength != (uint32_t)-1);
|
|
||||||
MOZ_ASSERT(maxLength >= length);
|
|
||||||
|
|
||||||
// Add a page for the header and round to a page boundary.
|
// Add a page for the header and round to a page boundary.
|
||||||
uint32_t allocationLength = growable ? maxLength : length;
|
uint32_t allocSize = SharedArrayAllocSize(length);
|
||||||
uint32_t allocSize = SharedArrayAllocSize(allocationLength);
|
if (allocSize <= length)
|
||||||
if (allocSize <= allocationLength)
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Test >= to guard against the case where multiple extant runtimes
|
// Test >= to guard against the case where multiple extant runtimes
|
||||||
|
|
@ -131,8 +127,7 @@ SharedArrayRawBuffer::New(JSContext* cx, uint32_t length, uint32_t maxLength, bo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool preparedForAsmJS =
|
bool preparedForAsmJS = jit::JitOptions.asmJSAtomicsEnable && IsValidAsmJSHeapLength(length);
|
||||||
!growable && jit::JitOptions.asmJSAtomicsEnable && IsValidAsmJSHeapLength(length);
|
|
||||||
|
|
||||||
void* p = nullptr;
|
void* p = nullptr;
|
||||||
if (preparedForAsmJS) {
|
if (preparedForAsmJS) {
|
||||||
|
|
@ -166,9 +161,8 @@ SharedArrayRawBuffer::New(JSContext* cx, uint32_t length, uint32_t maxLength, bo
|
||||||
|
|
||||||
uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
|
uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
|
||||||
uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
|
uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
|
||||||
SharedArrayRawBuffer* rawbuf =
|
SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer, length, preparedForAsmJS);
|
||||||
new (base) SharedArrayRawBuffer(buffer, length, maxLength, growable, preparedForAsmJS);
|
MOZ_ASSERT(rawbuf->length == length); // Deallocation needs this
|
||||||
MOZ_ASSERT(rawbuf->allocatedByteLength() == allocationLength); // Deallocation needs this.
|
|
||||||
return rawbuf;
|
return rawbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,7 +201,7 @@ SharedArrayRawBuffer::dropReference()
|
||||||
MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
|
MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
|
||||||
|
|
||||||
uint8_t* address = p.unwrap(/*safe - only reference*/);
|
uint8_t* address = p.unwrap(/*safe - only reference*/);
|
||||||
uint32_t allocSize = SharedArrayAllocSize(this->allocatedByteLength());
|
uint32_t allocSize = SharedArrayAllocSize(this->length);
|
||||||
|
|
||||||
if (this->preparedForAsmJS) {
|
if (this->preparedForAsmJS) {
|
||||||
uint32_t mappedSize = SharedArrayMappedSize(allocSize);
|
uint32_t mappedSize = SharedArrayMappedSize(allocSize);
|
||||||
|
|
@ -227,23 +221,6 @@ SharedArrayRawBuffer::dropReference()
|
||||||
numLive--;
|
numLive--;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
SharedArrayRawBuffer::growTo(uint32_t newLength)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(growable);
|
|
||||||
MOZ_ASSERT(newLength <= maxLength);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
uint32_t oldLength = length;
|
|
||||||
if (newLength < oldLength)
|
|
||||||
return false;
|
|
||||||
if (newLength == oldLength)
|
|
||||||
return true;
|
|
||||||
if (length.compareExchange(oldLength, newLength))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
MOZ_ALWAYS_INLINE bool
|
||||||
SharedArrayBufferObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
|
SharedArrayBufferObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
|
||||||
|
|
@ -260,112 +237,6 @@ SharedArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc, Value* v
|
||||||
return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
|
return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
|
||||||
SharedArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
|
|
||||||
args.rval().setInt32(args.thisv().toObject().as<SharedArrayBufferObject>().maxByteLength());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SharedArrayBufferObject::maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsSharedArrayBuffer, maxByteLengthGetterImpl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
|
||||||
SharedArrayBufferObject::growableGetterImpl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
|
|
||||||
args.rval().setBoolean(args.thisv().toObject().as<SharedArrayBufferObject>().isGrowable());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SharedArrayBufferObject::growableGetter(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsSharedArrayBuffer, growableGetterImpl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ReportSharedArrayBufferNotGrowable(JSContext* cx)
|
|
||||||
{
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_NOT_GROWABLE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ReportSharedArrayBufferLengthOutOfRange(JSContext* cx)
|
|
||||||
{
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_LENGTH);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
GetSharedArrayBufferMaxByteLengthOption(JSContext* cx, HandleValue options,
|
|
||||||
uint32_t byteLength, uint32_t* maxByteLength,
|
|
||||||
bool* growable)
|
|
||||||
{
|
|
||||||
*maxByteLength = byteLength;
|
|
||||||
*growable = false;
|
|
||||||
|
|
||||||
if (!options.isObject())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
RootedObject opts(cx, &options.toObject());
|
|
||||||
RootedValue maxByteLengthValue(cx);
|
|
||||||
if (!GetProperty(cx, opts, opts, cx->names().maxByteLength, &maxByteLengthValue))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (maxByteLengthValue.isUndefined())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint64_t max;
|
|
||||||
if (!ToIndex(cx, maxByteLengthValue, &max))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (max > INT32_MAX || max < byteLength)
|
|
||||||
return ReportSharedArrayBufferLengthOutOfRange(cx);
|
|
||||||
|
|
||||||
*maxByteLength = uint32_t(max);
|
|
||||||
*growable = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool
|
|
||||||
SharedArrayBufferObject::fun_grow_impl(JSContext* cx, const CallArgs& args)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
|
|
||||||
|
|
||||||
Rooted<SharedArrayBufferObject*> buffer(cx,
|
|
||||||
&args.thisv().toObject().as<SharedArrayBufferObject>());
|
|
||||||
if (!buffer->isGrowable())
|
|
||||||
return ReportSharedArrayBufferNotGrowable(cx);
|
|
||||||
|
|
||||||
uint64_t newByteLength;
|
|
||||||
if (!ToIndex(cx, args.get(0), &newByteLength))
|
|
||||||
return false;
|
|
||||||
if (newByteLength > INT32_MAX)
|
|
||||||
return ReportSharedArrayBufferLengthOutOfRange(cx);
|
|
||||||
|
|
||||||
uint32_t newLength = uint32_t(newByteLength);
|
|
||||||
if (newLength > buffer->maxByteLength() || !buffer->growTo(newLength))
|
|
||||||
return ReportSharedArrayBufferLengthOutOfRange(cx);
|
|
||||||
|
|
||||||
args.rval().setUndefined();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SharedArrayBufferObject::fun_grow(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
return CallNonGenericMethod<IsSharedArrayBuffer, fun_grow_impl>(cx, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
@ -374,19 +245,11 @@ SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value*
|
||||||
if (!ThrowIfNotConstructing(cx, args, "SharedArrayBuffer"))
|
if (!ThrowIfNotConstructing(cx, args, "SharedArrayBuffer"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint64_t length64;
|
|
||||||
if (!ToIndex(cx, args.get(0), &length64))
|
|
||||||
return false;
|
|
||||||
// Bugs 1068458, 1161298: Limit length to 2^31-1.
|
// Bugs 1068458, 1161298: Limit length to 2^31-1.
|
||||||
if (length64 > INT32_MAX)
|
uint32_t length;
|
||||||
return ReportSharedArrayBufferLengthOutOfRange(cx);
|
bool overflow_unused;
|
||||||
uint32_t length = uint32_t(length64);
|
if (!ToLengthClamped(cx, args.get(0), &length, &overflow_unused) || length > INT32_MAX) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_LENGTH);
|
||||||
uint32_t maxByteLength;
|
|
||||||
bool growable;
|
|
||||||
if (!GetSharedArrayBufferMaxByteLengthOption(cx, args.get(1), length,
|
|
||||||
&maxByteLength, &growable))
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,7 +258,7 @@ SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value*
|
||||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JSObject* bufobj = New(cx, length, maxByteLength, growable, proto);
|
JSObject* bufobj = New(cx, length, proto);
|
||||||
if (!bufobj)
|
if (!bufobj)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setObject(*bufobj);
|
args.rval().setObject(*bufobj);
|
||||||
|
|
@ -403,10 +266,9 @@ SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value*
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedArrayBufferObject*
|
SharedArrayBufferObject*
|
||||||
SharedArrayBufferObject::New(JSContext* cx, uint32_t length, uint32_t maxLength, bool growable,
|
SharedArrayBufferObject::New(JSContext* cx, uint32_t length, HandleObject proto)
|
||||||
HandleObject proto)
|
|
||||||
{
|
{
|
||||||
SharedArrayRawBuffer* buffer = SharedArrayRawBuffer::New(cx, length, maxLength, growable);
|
SharedArrayRawBuffer* buffer = SharedArrayRawBuffer::New(cx, length);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
@ -479,7 +341,7 @@ SharedArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSi
|
||||||
// just live with the risk.
|
// just live with the risk.
|
||||||
const SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
|
const SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
|
||||||
info->objectsNonHeapElementsShared +=
|
info->objectsNonHeapElementsShared +=
|
||||||
buf.rawBufferObject()->allocatedByteLength() / buf.rawBufferObject()->refcount();
|
buf.byteLength() / buf.rawBufferObject()->refcount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
|
|
@ -547,15 +409,12 @@ static const JSPropertySpec static_properties[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const JSFunctionSpec prototype_functions[] = {
|
static const JSFunctionSpec prototype_functions[] = {
|
||||||
JS_FN("grow", SharedArrayBufferObject::fun_grow, 1, 0),
|
|
||||||
JS_SELF_HOSTED_FN("slice", "SharedArrayBufferSlice", 2, 0),
|
JS_SELF_HOSTED_FN("slice", "SharedArrayBufferSlice", 2, 0),
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
static const JSPropertySpec prototype_properties[] = {
|
static const JSPropertySpec prototype_properties[] = {
|
||||||
JS_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0),
|
JS_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0),
|
||||||
JS_PSG("growable", SharedArrayBufferObject::growableGetter, 0),
|
|
||||||
JS_PSG("maxByteLength", SharedArrayBufferObject::maxByteLengthGetter, 0),
|
|
||||||
JS_STRING_SYM_PS(toStringTag, "SharedArrayBuffer", JSPROP_READONLY),
|
JS_STRING_SYM_PS(toStringTag, "SharedArrayBuffer", JSPROP_READONLY),
|
||||||
JS_PS_END
|
JS_PS_END
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,7 @@ class SharedArrayRawBuffer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
|
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
|
||||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> length;
|
uint32_t length;
|
||||||
uint32_t maxLength;
|
|
||||||
bool growable;
|
|
||||||
bool preparedForAsmJS;
|
bool preparedForAsmJS;
|
||||||
|
|
||||||
// A list of structures representing tasks waiting on some
|
// A list of structures representing tasks waiting on some
|
||||||
|
|
@ -54,12 +52,9 @@ class SharedArrayRawBuffer
|
||||||
FutexWaiter* waiters_;
|
FutexWaiter* waiters_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, uint32_t maxLength, bool growable,
|
SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, bool preparedForAsmJS)
|
||||||
bool preparedForAsmJS)
|
|
||||||
: refcount_(1),
|
: refcount_(1),
|
||||||
length(length),
|
length(length),
|
||||||
maxLength(maxLength),
|
|
||||||
growable(growable),
|
|
||||||
preparedForAsmJS(preparedForAsmJS),
|
preparedForAsmJS(preparedForAsmJS),
|
||||||
waiters_(nullptr)
|
waiters_(nullptr)
|
||||||
{
|
{
|
||||||
|
|
@ -67,11 +62,7 @@ class SharedArrayRawBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static SharedArrayRawBuffer* New(JSContext* cx, uint32_t length, uint32_t maxLength,
|
static SharedArrayRawBuffer* New(JSContext* cx, uint32_t length);
|
||||||
bool growable);
|
|
||||||
static SharedArrayRawBuffer* New(JSContext* cx, uint32_t length) {
|
|
||||||
return New(cx, length, length, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This may be called from multiple threads. The caller must take
|
// This may be called from multiple threads. The caller must take
|
||||||
// care of mutual exclusion.
|
// care of mutual exclusion.
|
||||||
|
|
@ -94,20 +85,6 @@ class SharedArrayRawBuffer
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t maxByteLength() const {
|
|
||||||
return growable ? maxLength : byteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t allocatedByteLength() const {
|
|
||||||
return growable ? maxLength : byteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isGrowable() const {
|
|
||||||
return growable;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool growTo(uint32_t newLength);
|
|
||||||
|
|
||||||
bool isPreparedForAsmJS() const {
|
bool isPreparedForAsmJS() const {
|
||||||
return preparedForAsmJS;
|
return preparedForAsmJS;
|
||||||
}
|
}
|
||||||
|
|
@ -140,9 +117,6 @@ class SharedArrayRawBuffer
|
||||||
class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
|
class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
{
|
{
|
||||||
static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
||||||
static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
|
||||||
static bool growableGetterImpl(JSContext* cx, const CallArgs& args);
|
|
||||||
static bool fun_grow_impl(JSContext* cx, const CallArgs& args);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// RAWBUF_SLOT holds a pointer (as "private" data) to the
|
// RAWBUF_SLOT holds a pointer (as "private" data) to the
|
||||||
|
|
@ -154,23 +128,13 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||||
static bool maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static bool growableGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
static bool fun_grow(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
|
|
||||||
static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
|
static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
// Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
|
// Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
|
||||||
static SharedArrayBufferObject* New(JSContext* cx,
|
static SharedArrayBufferObject* New(JSContext* cx,
|
||||||
uint32_t length,
|
uint32_t length,
|
||||||
uint32_t maxLength,
|
|
||||||
bool growable,
|
|
||||||
HandleObject proto = nullptr);
|
HandleObject proto = nullptr);
|
||||||
static SharedArrayBufferObject* New(JSContext* cx,
|
|
||||||
uint32_t length,
|
|
||||||
HandleObject proto = nullptr) {
|
|
||||||
return New(cx, length, length, false, proto);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer.
|
// Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer.
|
||||||
static SharedArrayBufferObject* New(JSContext* cx,
|
static SharedArrayBufferObject* New(JSContext* cx,
|
||||||
|
|
@ -200,15 +164,6 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
uint32_t byteLength() const {
|
uint32_t byteLength() const {
|
||||||
return rawBufferObject()->byteLength();
|
return rawBufferObject()->byteLength();
|
||||||
}
|
}
|
||||||
uint32_t maxByteLength() const {
|
|
||||||
return rawBufferObject()->maxByteLength();
|
|
||||||
}
|
|
||||||
bool isGrowable() const {
|
|
||||||
return rawBufferObject()->isGrowable();
|
|
||||||
}
|
|
||||||
[[nodiscard]] bool growTo(uint32_t newLength) {
|
|
||||||
return rawBufferObject()->growTo(newLength);
|
|
||||||
}
|
|
||||||
bool isPreparedForAsmJS() const {
|
bool isPreparedForAsmJS() const {
|
||||||
return rawBufferObject()->isPreparedForAsmJS();
|
return rawBufferObject()->isPreparedForAsmJS();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@
|
||||||
#include "builtin/MapObject.h"
|
#include "builtin/MapObject.h"
|
||||||
#include "js/Date.h"
|
#include "js/Date.h"
|
||||||
#include "js/GCHashTable.h"
|
#include "js/GCHashTable.h"
|
||||||
#include "vm/ArrayBufferObject-inl.h"
|
|
||||||
#include "vm/SavedFrame.h"
|
#include "vm/SavedFrame.h"
|
||||||
#include "vm/SharedArrayObject.h"
|
#include "vm/SharedArrayObject.h"
|
||||||
#include "vm/TypedArrayObject.h"
|
#include "vm/TypedArrayObject.h"
|
||||||
|
|
|
||||||
|
|
@ -1065,40 +1065,16 @@ class TypedArrayMethods
|
||||||
if (!ToInt32(cx, args[1], &offset))
|
if (!ToInt32(cx, args[1], &offset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (offset < 0) {
|
if (offset < 0 || uint32_t(offset) > target->length()) {
|
||||||
|
// the given offset is bogus
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target->hasDetachedBuffer()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (target->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t targetLength = target->length();
|
|
||||||
if (uint32_t(offset) > targetLength) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedObject arg0(cx, &args[0].toObject());
|
RootedObject arg0(cx, &args[0].toObject());
|
||||||
if (arg0->is<TypedArrayObject>()) {
|
if (arg0->is<TypedArrayObject>()) {
|
||||||
Rooted<TypedArrayObject*> source(cx, &arg0->as<TypedArrayObject>());
|
if (arg0->as<TypedArrayObject>().length() > target->length() - offset) {
|
||||||
if (source->hasDetachedBuffer()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (source->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source->length() > targetLength - offset) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1110,7 +1086,7 @@ class TypedArrayMethods
|
||||||
if (!GetLengthProperty(cx, arg0, &len))
|
if (!GetLengthProperty(cx, arg0, &len))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (len > targetLength - offset) {
|
if (uint32_t(offset) > target->length() || len > target->length() - offset) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1129,26 +1105,7 @@ class TypedArrayMethods
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(source->is<TypedArrayObject>(), "use setFromNonTypedArray");
|
MOZ_ASSERT(source->is<TypedArrayObject>(), "use setFromNonTypedArray");
|
||||||
|
|
||||||
if (target->hasDetachedBuffer()) {
|
bool isShared = target->isSharedMemory() || source->as<TypedArrayObject>().isSharedMemory();
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (target->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rooted<TypedArrayObject*> sourceArray(cx, &source->as<TypedArrayObject>());
|
|
||||||
if (sourceArray->hasDetachedBuffer()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (sourceArray->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isShared = target->isSharedMemory() || sourceArray->isSharedMemory();
|
|
||||||
|
|
||||||
switch (target->type()) {
|
switch (target->type()) {
|
||||||
case Scalar::Int8:
|
case Scalar::Int8:
|
||||||
|
|
|
||||||
|
|
@ -484,9 +484,8 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypedArrayObject*
|
static TypedArrayObject*
|
||||||
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
|
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset, uint32_t len,
|
||||||
uint32_t byteOffset, uint32_t len, HandleObject proto,
|
HandleObject proto)
|
||||||
bool lengthTracking = false)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(!buffer, byteOffset == 0);
|
MOZ_ASSERT_IF(!buffer, byteOffset == 0);
|
||||||
|
|
||||||
|
|
@ -511,19 +510,12 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
bool isSharedMemory = buffer && IsSharedArrayBuffer(buffer.get());
|
bool isSharedMemory = buffer && IsSharedArrayBuffer(buffer.get());
|
||||||
bool hasResizableOrGrowableBuffer =
|
|
||||||
buffer &&
|
|
||||||
((buffer->is<ArrayBufferObject>() && buffer->as<ArrayBufferObject>().isResizable()) ||
|
|
||||||
(buffer->is<SharedArrayBufferObject>() &&
|
|
||||||
buffer->as<SharedArrayBufferObject>().isGrowable()));
|
|
||||||
|
|
||||||
obj->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectOrNullValue(buffer));
|
obj->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectOrNullValue(buffer));
|
||||||
// This is invariant. Self-hosting code that sets BUFFER_SLOT
|
// This is invariant. Self-hosting code that sets BUFFER_SLOT
|
||||||
// (if it does) must maintain it, should it need to.
|
// (if it does) must maintain it, should it need to.
|
||||||
if (isSharedMemory)
|
if (isSharedMemory)
|
||||||
obj->setIsSharedMemory();
|
obj->setIsSharedMemory();
|
||||||
if (hasResizableOrGrowableBuffer)
|
|
||||||
obj->setHasResizableOrGrowableBuffer();
|
|
||||||
|
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
obj->initViewData(buffer->dataPointerEither() + byteOffset);
|
obj->initViewData(buffer->dataPointerEither() + byteOffset);
|
||||||
|
|
@ -557,9 +549,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT,
|
obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(len));
|
||||||
Int32Value(lengthTracking ? TypedArrayObject::LENGTH_TRACKING
|
|
||||||
: int32_t(len)));
|
|
||||||
obj->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
obj->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -908,7 +898,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
return nullptr; // invalid byteOffset
|
return nullptr; // invalid byteOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lengthTracking = false;
|
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
if (lengthInt == -1) {
|
if (lengthInt == -1) {
|
||||||
len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
|
len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
|
||||||
|
|
@ -917,12 +906,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
|
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
|
||||||
return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
|
return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
|
||||||
}
|
}
|
||||||
if ((buffer->is<ArrayBufferObject>() && buffer->as<ArrayBufferObject>().isResizable()) ||
|
|
||||||
(buffer->is<SharedArrayBufferObject>() &&
|
|
||||||
buffer->as<SharedArrayBufferObject>().isGrowable()))
|
|
||||||
{
|
|
||||||
lengthTracking = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
len = uint32_t(lengthInt);
|
len = uint32_t(lengthInt);
|
||||||
}
|
}
|
||||||
|
|
@ -941,7 +924,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
return nullptr; // byteOffset + len is too big for the arraybuffer
|
return nullptr; // byteOffset + len is too big for the arraybuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeInstance(cx, buffer, byteOffset, len, proto, lengthTracking);
|
return makeInstance(cx, buffer, byteOffset, len, proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -1337,10 +1320,6 @@ TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, b
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (srcArray->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 9.
|
// Step 9.
|
||||||
uint32_t elementLength = srcArray->length();
|
uint32_t elementLength = srcArray->length();
|
||||||
|
|
@ -1880,8 +1859,7 @@ DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto)
|
||||||
|
|
||||||
DataViewObject*
|
DataViewObject*
|
||||||
DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, JSObject* protoArg,
|
Handle<ArrayBufferObject*> arrayBuffer, JSObject* protoArg)
|
||||||
bool lengthTracking)
|
|
||||||
{
|
{
|
||||||
if (arrayBuffer->isDetached()) {
|
if (arrayBuffer->isDetached()) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
|
|
@ -1890,18 +1868,8 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
|
|
||||||
MOZ_ASSERT(byteOffset <= INT32_MAX);
|
MOZ_ASSERT(byteOffset <= INT32_MAX);
|
||||||
MOZ_ASSERT(byteLength <= INT32_MAX);
|
MOZ_ASSERT(byteLength <= INT32_MAX);
|
||||||
|
MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
|
||||||
uint32_t bufferByteLength = arrayBuffer->byteLength();
|
MOZ_ASSERT(!arrayBuffer || !arrayBuffer->is<SharedArrayBufferObject>());
|
||||||
if (byteOffset > bufferByteLength ||
|
|
||||||
(!lengthTracking && byteLength > bufferByteLength - byteOffset))
|
|
||||||
{
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
|
|
||||||
"1");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lengthTracking)
|
|
||||||
byteLength = bufferByteLength - byteOffset;
|
|
||||||
|
|
||||||
RootedObject proto(cx, protoArg);
|
RootedObject proto(cx, protoArg);
|
||||||
RootedObject obj(cx);
|
RootedObject obj(cx);
|
||||||
|
|
@ -1925,72 +1893,57 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Caller should have established these preconditions, and no
|
||||||
|
// (non-self-hosted) JS code has had an opportunity to run so nothing can
|
||||||
|
// have invalidated them.
|
||||||
MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
|
MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
|
||||||
MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
|
MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
|
||||||
|
|
||||||
DataViewObject& dvobj = obj->as<DataViewObject>();
|
DataViewObject& dvobj = obj->as<DataViewObject>();
|
||||||
dvobj.setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
dvobj.setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
||||||
dvobj.setFixedSlot(TypedArrayObject::LENGTH_SLOT,
|
dvobj.setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(byteLength));
|
||||||
Int32Value(lengthTracking ? TypedArrayObject::LENGTH_TRACKING
|
|
||||||
: int32_t(byteLength)));
|
|
||||||
dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
|
dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
|
||||||
auto dataPointer = arrayBuffer->dataPointerEither();
|
dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
|
||||||
dvobj.initPrivate((dataPointer + byteOffset).unwrap(/*safe - stored as private data*/));
|
|
||||||
|
|
||||||
// Include a barrier if the data view's data pointer is in the nursery, as
|
// Include a barrier if the data view's data pointer is in the nursery, as
|
||||||
// is done for typed arrays.
|
// is done for typed arrays.
|
||||||
if (arrayBuffer->is<ArrayBufferObject>() &&
|
if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(arrayBuffer->dataPointer()))
|
||||||
!IsInsideNursery(obj) &&
|
|
||||||
cx->runtime()->gc.nursery.isInside(dataPointer))
|
|
||||||
{
|
|
||||||
cx->runtime()->gc.storeBuffer.putWholeCell(obj);
|
cx->runtime()->gc.storeBuffer.putWholeCell(obj);
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the private slot is at the expected place
|
// Verify that the private slot is at the expected place
|
||||||
MOZ_ASSERT(dvobj.numFixedSlots() == TypedArrayObject::DATA_SLOT);
|
MOZ_ASSERT(dvobj.numFixedSlots() == TypedArrayObject::DATA_SLOT);
|
||||||
|
|
||||||
if (arrayBuffer->is<ArrayBufferObject>()) {
|
if (!arrayBuffer->addView(cx, &dvobj))
|
||||||
if (!arrayBuffer->as<ArrayBufferObject>().addView(cx, &dvobj))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
return &dvobj;
|
return &dvobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
||||||
uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr,
|
uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr)
|
||||||
bool* lengthTrackingPtr)
|
|
||||||
{
|
{
|
||||||
if (!IsAnyArrayBuffer(bufobj)) {
|
if (!IsArrayBuffer(bufobj)) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
|
||||||
"DataView", "ArrayBuffer or SharedArrayBuffer",
|
"DataView", "ArrayBuffer", bufobj->getClass()->name);
|
||||||
bufobj->getClass()->name);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &bufobj->as<ArrayBufferObjectMaybeShared>());
|
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
|
||||||
uint32_t byteOffset = 0;
|
uint32_t byteOffset = 0;
|
||||||
uint32_t byteLength = buffer->byteLength();
|
uint32_t byteLength = buffer->byteLength();
|
||||||
bool isResizableOrGrowable =
|
|
||||||
(buffer->is<ArrayBufferObject>() && buffer->as<ArrayBufferObject>().isResizable()) ||
|
|
||||||
(buffer->is<SharedArrayBufferObject>() &&
|
|
||||||
buffer->as<SharedArrayBufferObject>().isGrowable());
|
|
||||||
bool lengthTracking = isResizableOrGrowable && !args.hasDefined(2);
|
|
||||||
|
|
||||||
if (args.length() > 1) {
|
if (args.length() > 1) {
|
||||||
uint64_t offset;
|
if (!ToUint32(cx, args[1], &byteOffset))
|
||||||
if (!ToIndex(cx, args[1], &offset))
|
|
||||||
return false;
|
return false;
|
||||||
if (offset > INT32_MAX) {
|
if (byteOffset > INT32_MAX) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
|
||||||
"1");
|
"1");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
byteOffset = uint32_t(offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer->is<ArrayBufferObject>() && buffer->as<ArrayBufferObject>().isDetached()) {
|
if (buffer->isDetached()) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2004,18 +1957,14 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
|
||||||
|
|
||||||
if (args.get(2).isUndefined()) {
|
if (args.get(2).isUndefined()) {
|
||||||
byteLength -= byteOffset;
|
byteLength -= byteOffset;
|
||||||
lengthTracking = isResizableOrGrowable;
|
|
||||||
} else {
|
} else {
|
||||||
uint64_t viewByteLength;
|
if (!ToUint32(cx, args[2], &byteLength))
|
||||||
if (!ToIndex(cx, args[2], &viewByteLength))
|
|
||||||
return false;
|
return false;
|
||||||
lengthTracking = false;
|
if (byteLength > INT32_MAX) {
|
||||||
if (viewByteLength > INT32_MAX) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||||
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
|
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
byteLength = uint32_t(viewByteLength);
|
|
||||||
|
|
||||||
MOZ_ASSERT(byteOffset + byteLength >= byteOffset,
|
MOZ_ASSERT(byteOffset + byteLength >= byteOffset,
|
||||||
"can't overflow: both numbers are less than INT32_MAX");
|
"can't overflow: both numbers are less than INT32_MAX");
|
||||||
|
|
@ -2034,7 +1983,6 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
|
||||||
|
|
||||||
*byteOffsetPtr = byteOffset;
|
*byteOffsetPtr = byteOffset;
|
||||||
*byteLengthPtr = byteLength;
|
*byteLengthPtr = byteLength;
|
||||||
*lengthTrackingPtr = lengthTracking;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -2046,8 +1994,7 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
|
||||||
assertSameCompartment(cx, bufobj);
|
assertSameCompartment(cx, bufobj);
|
||||||
|
|
||||||
uint32_t byteOffset, byteLength;
|
uint32_t byteOffset, byteLength;
|
||||||
bool lengthTracking;
|
if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength))
|
||||||
if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength, &lengthTracking))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
RootedObject proto(cx);
|
RootedObject proto(cx);
|
||||||
|
|
@ -2055,9 +2002,8 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
|
||||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &bufobj->as<ArrayBufferObjectMaybeShared>());
|
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
|
||||||
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto,
|
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto);
|
||||||
lengthTracking);
|
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setObject(*obj);
|
args.rval().setObject(*obj);
|
||||||
|
|
@ -2097,12 +2043,8 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
|
||||||
|
|
||||||
// NB: This entails the IsArrayBuffer check
|
// NB: This entails the IsArrayBuffer check
|
||||||
uint32_t byteOffset, byteLength;
|
uint32_t byteOffset, byteLength;
|
||||||
bool lengthTracking;
|
if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength))
|
||||||
if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength,
|
|
||||||
&lengthTracking))
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure to get the [[Prototype]] for the created view from this
|
// Make sure to get the [[Prototype]] for the created view from this
|
||||||
// compartment.
|
// compartment.
|
||||||
|
|
@ -2118,12 +2060,11 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FixedInvokeArgs<4> args2(cx);
|
FixedInvokeArgs<3> args2(cx);
|
||||||
|
|
||||||
args2[0].set(PrivateUint32Value(byteOffset));
|
args2[0].set(PrivateUint32Value(byteOffset));
|
||||||
args2[1].set(PrivateUint32Value(byteLength));
|
args2[1].set(PrivateUint32Value(byteLength));
|
||||||
args2[2].setObject(*proto);
|
args2[2].setObject(*proto);
|
||||||
args2[3].setBoolean(lengthTracking);
|
|
||||||
|
|
||||||
RootedValue fval(cx, global->createDataViewForThis());
|
RootedValue fval(cx, global->createDataViewForThis());
|
||||||
RootedValue thisv(cx, ObjectValue(*bufobj));
|
RootedValue thisv(cx, ObjectValue(*bufobj));
|
||||||
|
|
@ -2151,11 +2092,6 @@ template <typename NativeType>
|
||||||
/* static */ uint8_t*
|
/* static */ uint8_t*
|
||||||
DataViewObject::getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, uint64_t offset)
|
DataViewObject::getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, uint64_t offset)
|
||||||
{
|
{
|
||||||
if (obj->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATA_VIEW_OUT_OF_BOUNDS);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t TypeSize = sizeof(NativeType);
|
const size_t TypeSize = sizeof(NativeType);
|
||||||
if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) {
|
if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
|
||||||
|
|
@ -2319,7 +2255,7 @@ DataViewObject::read(JSContext* cx, Handle<DataViewObject*> obj,
|
||||||
bool isLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
|
bool isLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
|
||||||
|
|
||||||
// Steps 6-7.
|
// Steps 6-7.
|
||||||
if (obj->arrayBufferEither().isDetached()) {
|
if (obj->arrayBuffer().isDetached()) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2418,7 +2354,7 @@ DataViewObject::write(JSContext* cx, Handle<DataViewObject*> obj,
|
||||||
bool isLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
|
bool isLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
|
||||||
|
|
||||||
// Steps 7-8.
|
// Steps 7-8.
|
||||||
if (obj->arrayBufferEither().isDetached()) {
|
if (obj->arrayBuffer().isDetached()) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -3243,13 +3179,7 @@ template<Value ValueGetter(DataViewObject* view)>
|
||||||
bool
|
bool
|
||||||
DataViewObject::getterImpl(JSContext* cx, const CallArgs& args)
|
DataViewObject::getterImpl(JSContext* cx, const CallArgs& args)
|
||||||
{
|
{
|
||||||
Rooted<DataViewObject*> view(cx, &args.thisv().toObject().as<DataViewObject>());
|
args.rval().set(ValueGetter(&args.thisv().toObject().as<DataViewObject>()));
|
||||||
if (ValueGetter != bufferValue && view->isOutOfBounds()) {
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATA_VIEW_OUT_OF_BOUNDS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.rval().set(ValueGetter(view));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,6 @@ class TypedArrayObject : public NativeObject
|
||||||
"right buffer slot");
|
"right buffer slot");
|
||||||
|
|
||||||
// Slot containing length of the view in number of typed elements.
|
// Slot containing length of the view in number of typed elements.
|
||||||
// Length-tracking views on resizable/growable buffers store
|
|
||||||
// LENGTH_TRACKING here and compute their visible length from the buffer.
|
|
||||||
static const size_t LENGTH_SLOT = 1;
|
static const size_t LENGTH_SLOT = 1;
|
||||||
static_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT,
|
static_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT,
|
||||||
"self-hosted code with burned-in constants must get the "
|
"self-hosted code with burned-in constants must get the "
|
||||||
|
|
@ -67,8 +65,6 @@ class TypedArrayObject : public NativeObject
|
||||||
|
|
||||||
static const size_t RESERVED_SLOTS = 3;
|
static const size_t RESERVED_SLOTS = 3;
|
||||||
|
|
||||||
static const int32_t LENGTH_TRACKING = -1;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
static const uint8_t ZeroLengthArrayData = 0x4A;
|
static const uint8_t ZeroLengthArrayData = 0x4A;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -143,13 +139,15 @@ class TypedArrayObject : public NativeObject
|
||||||
return tarr->getFixedSlot(BUFFER_SLOT);
|
return tarr->getFixedSlot(BUFFER_SLOT);
|
||||||
}
|
}
|
||||||
static Value byteOffsetValue(TypedArrayObject* tarr) {
|
static Value byteOffsetValue(TypedArrayObject* tarr) {
|
||||||
return Int32Value(tarr->byteOffset());
|
Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT);
|
||||||
|
MOZ_ASSERT(v.toInt32() >= 0);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
static Value byteLengthValue(TypedArrayObject* tarr) {
|
static Value byteLengthValue(TypedArrayObject* tarr) {
|
||||||
return Int32Value(tarr->byteLength());
|
return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement());
|
||||||
}
|
}
|
||||||
static Value lengthValue(TypedArrayObject* tarr) {
|
static Value lengthValue(TypedArrayObject* tarr) {
|
||||||
return Int32Value(tarr->length());
|
return tarr->getFixedSlot(LENGTH_SLOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -161,70 +159,14 @@ class TypedArrayObject : public NativeObject
|
||||||
JSObject* bufferObject() const {
|
JSObject* bufferObject() const {
|
||||||
return bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
return bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
||||||
}
|
}
|
||||||
bool isLengthTracking() const {
|
|
||||||
return getFixedSlot(LENGTH_SLOT).toInt32() == LENGTH_TRACKING;
|
|
||||||
}
|
|
||||||
bool hasResizableOrGrowableBuffer() const {
|
|
||||||
if (!hasBuffer())
|
|
||||||
return false;
|
|
||||||
if (isSharedMemory())
|
|
||||||
return bufferShared()->isGrowable();
|
|
||||||
return bufferUnshared()->isResizable();
|
|
||||||
}
|
|
||||||
uint32_t byteOffsetMaybeOutOfBounds() const {
|
|
||||||
Value v = getFixedSlot(BYTEOFFSET_SLOT);
|
|
||||||
MOZ_ASSERT(v.toInt32() >= 0);
|
|
||||||
return v.toInt32();
|
|
||||||
}
|
|
||||||
uint32_t fixedLengthMaybeOutOfBounds() const {
|
|
||||||
int32_t length = getFixedSlot(LENGTH_SLOT).toInt32();
|
|
||||||
MOZ_ASSERT(length >= 0);
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
uint32_t bufferByteLength() const {
|
|
||||||
MOZ_ASSERT(hasBuffer());
|
|
||||||
if (isSharedMemory())
|
|
||||||
return bufferShared()->byteLength();
|
|
||||||
return bufferUnshared()->byteLength();
|
|
||||||
}
|
|
||||||
bool isOutOfBounds() const {
|
|
||||||
if (!hasBuffer())
|
|
||||||
return false;
|
|
||||||
if (hasDetachedBuffer())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint32_t bufferByteLength = this->bufferByteLength();
|
|
||||||
uint32_t offset = byteOffsetMaybeOutOfBounds();
|
|
||||||
if (offset > bufferByteLength)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (isLengthTracking())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint32_t byteLength = fixedLengthMaybeOutOfBounds() * bytesPerElement();
|
|
||||||
return byteLength > bufferByteLength - offset;
|
|
||||||
}
|
|
||||||
uint32_t byteOffset() const {
|
uint32_t byteOffset() const {
|
||||||
if (isOutOfBounds())
|
return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
||||||
return 0;
|
|
||||||
return byteOffsetMaybeOutOfBounds();
|
|
||||||
}
|
}
|
||||||
uint32_t byteLength() const {
|
uint32_t byteLength() const {
|
||||||
return length() * bytesPerElement();
|
return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
||||||
}
|
}
|
||||||
uint32_t length() const {
|
uint32_t length() const {
|
||||||
if (!isLengthTracking()) {
|
return lengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
||||||
if (isOutOfBounds())
|
|
||||||
return 0;
|
|
||||||
return fixedLengthMaybeOutOfBounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOutOfBounds())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
uint32_t bufferByteLength = this->bufferByteLength();
|
|
||||||
uint32_t offset = byteOffsetMaybeOutOfBounds();
|
|
||||||
return (bufferByteLength - offset) / bytesPerElement();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasInlineElements() const;
|
bool hasInlineElements() const;
|
||||||
|
|
@ -524,26 +466,28 @@ class DataViewObject : public NativeObject
|
||||||
defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto);
|
defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto);
|
||||||
|
|
||||||
static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
||||||
uint32_t *byteOffset, uint32_t* byteLength,
|
uint32_t *byteOffset, uint32_t* byteLength);
|
||||||
bool* lengthTracking);
|
|
||||||
static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
||||||
static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
||||||
|
|
||||||
friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
|
friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
|
||||||
static DataViewObject*
|
static DataViewObject*
|
||||||
create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, JSObject* proto,
|
Handle<ArrayBufferObject*> arrayBuffer, JSObject* proto);
|
||||||
bool lengthTracking = false);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
static Value byteOffsetValue(DataViewObject* view) {
|
static Value byteOffsetValue(DataViewObject* view) {
|
||||||
return Int32Value(view->byteOffset());
|
Value v = view->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT);
|
||||||
|
MOZ_ASSERT(v.toInt32() >= 0);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value byteLengthValue(DataViewObject* view) {
|
static Value byteLengthValue(DataViewObject* view) {
|
||||||
return Int32Value(view->byteLength());
|
Value v = view->getFixedSlot(TypedArrayObject::LENGTH_SLOT);
|
||||||
|
MOZ_ASSERT(v.toInt32() >= 0);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value bufferValue(DataViewObject* view) {
|
static Value bufferValue(DataViewObject* view) {
|
||||||
|
|
@ -551,66 +495,21 @@ class DataViewObject : public NativeObject
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t byteOffset() const {
|
uint32_t byteOffset() const {
|
||||||
if (isOutOfBounds())
|
return byteOffsetValue(const_cast<DataViewObject*>(this)).toInt32();
|
||||||
return 0;
|
|
||||||
return byteOffsetMaybeOutOfBounds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t byteLength() const {
|
uint32_t byteLength() const {
|
||||||
if (isOutOfBounds())
|
return byteLengthValue(const_cast<DataViewObject*>(this)).toInt32();
|
||||||
return 0;
|
|
||||||
if (isLengthTracking())
|
|
||||||
return arrayBufferEither().byteLength() - byteOffsetMaybeOutOfBounds();
|
|
||||||
return fixedByteLengthMaybeOutOfBounds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayBufferObjectMaybeShared& arrayBufferEither() const {
|
ArrayBufferObject& arrayBuffer() const {
|
||||||
return bufferValue(const_cast<DataViewObject*>(this)).
|
return bufferValue(const_cast<DataViewObject*>(this)).toObject().as<ArrayBufferObject>();
|
||||||
toObject().as<ArrayBufferObjectMaybeShared>();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSharedMemory() const {
|
|
||||||
return bufferValue(const_cast<DataViewObject*>(this)).
|
|
||||||
toObject().is<SharedArrayBufferObject>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void* dataPointer() const {
|
void* dataPointer() const {
|
||||||
return getPrivate();
|
return getPrivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isLengthTracking() const {
|
|
||||||
return getFixedSlot(TypedArrayObject::LENGTH_SLOT).toInt32() ==
|
|
||||||
TypedArrayObject::LENGTH_TRACKING;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t byteOffsetMaybeOutOfBounds() const {
|
|
||||||
Value v = getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT);
|
|
||||||
MOZ_ASSERT(v.toInt32() >= 0);
|
|
||||||
return v.toInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t fixedByteLengthMaybeOutOfBounds() const {
|
|
||||||
int32_t length = getFixedSlot(TypedArrayObject::LENGTH_SLOT).toInt32();
|
|
||||||
MOZ_ASSERT(length >= 0);
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOutOfBounds() const {
|
|
||||||
const ArrayBufferObjectMaybeShared& buffer = arrayBufferEither();
|
|
||||||
if (buffer.isDetached())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint32_t bufferByteLength = buffer.byteLength();
|
|
||||||
uint32_t offset = byteOffsetMaybeOutOfBounds();
|
|
||||||
if (offset > bufferByteLength)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (isLengthTracking())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return fixedByteLengthMaybeOutOfBounds() > bufferByteLength - offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
|
static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
static bool getInt8Impl(JSContext* cx, const CallArgs& args);
|
static bool getInt8Impl(JSContext* cx, const CallArgs& args);
|
||||||
|
|
|
||||||
|
|
@ -1446,7 +1446,6 @@ ReloadPrefsCallback(const char* pref, void* data)
|
||||||
bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
|
bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
|
||||||
|
|
||||||
bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
|
bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
|
||||||
bool weakRefs = Preferences::GetBool(JS_OPTIONS_DOT_STR "weakrefs");
|
|
||||||
|
|
||||||
bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects");
|
bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects");
|
||||||
|
|
||||||
|
|
@ -1473,8 +1472,7 @@ ReloadPrefsCallback(const char* pref, void* data)
|
||||||
.setWerror(werror)
|
.setWerror(werror)
|
||||||
.setExtraWarnings(extraWarnings)
|
.setExtraWarnings(extraWarnings)
|
||||||
.setArrayProtoValues(arrayProtoValues)
|
.setArrayProtoValues(arrayProtoValues)
|
||||||
.setStreams(streams)
|
.setStreams(streams);
|
||||||
.setWeakRefs(weakRefs);
|
|
||||||
|
|
||||||
JS_SetParallelParsingEnabled(cx, parallelParsing);
|
JS_SetParallelParsingEnabled(cx, parallelParsing);
|
||||||
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
|
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
|
||||||
|
|
|
||||||
|
|
@ -1,960 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* 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/. */
|
|
||||||
|
|
||||||
#include "CSSNestingFlattener.h"
|
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace css {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class CSSNestingFlattener final
|
|
||||||
{
|
|
||||||
using SelectorList = nsTArray<nsString>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CSSNestingFlattener(const nsAString& aInput)
|
|
||||||
: mInput(aInput)
|
|
||||||
, mPos(0)
|
|
||||||
, mSawNesting(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Flatten(nsAString& aOutput)
|
|
||||||
{
|
|
||||||
nsAutoString flattened;
|
|
||||||
if (!ProcessStylesheet(flattened, false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkipWhitespaceAndComments();
|
|
||||||
if (mPos != mInput.Length() || !mSawNesting) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
aOutput.Assign(flattened);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool
|
|
||||||
IsCSSWhitespace(char16_t aChar)
|
|
||||||
{
|
|
||||||
return aChar == ' ' || aChar == '\t' || aChar == '\r' ||
|
|
||||||
aChar == '\n' || aChar == '\f';
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
AtEnd() const
|
|
||||||
{
|
|
||||||
return mPos >= mInput.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
char16_t
|
|
||||||
Peek() const
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!AtEnd(), "cannot peek past end");
|
|
||||||
return mInput.CharAt(mPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StartsWithComment() const
|
|
||||||
{
|
|
||||||
return mPos + 1 < mInput.Length() &&
|
|
||||||
mInput.CharAt(mPos) == '/' &&
|
|
||||||
mInput.CharAt(mPos + 1) == '*';
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SkipComment()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(StartsWithComment(), "expected comment");
|
|
||||||
|
|
||||||
mPos += 2;
|
|
||||||
while (mPos + 1 < mInput.Length()) {
|
|
||||||
if (mInput.CharAt(mPos) == '*' && mInput.CharAt(mPos + 1) == '/') {
|
|
||||||
mPos += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
++mPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SkipWhitespaceAndComments()
|
|
||||||
{
|
|
||||||
while (!AtEnd()) {
|
|
||||||
if (IsCSSWhitespace(Peek())) {
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (StartsWithComment()) {
|
|
||||||
if (!SkipComment()) {
|
|
||||||
mPos = mInput.Length();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SkipString(char16_t aQuote)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!AtEnd() && Peek() == aQuote, "expected string start");
|
|
||||||
|
|
||||||
++mPos;
|
|
||||||
while (!AtEnd()) {
|
|
||||||
char16_t c = Peek();
|
|
||||||
++mPos;
|
|
||||||
if (c == aQuote) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (c == '\\' && !AtEnd()) {
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '\n' || c == '\r' || c == '\f') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
TrimWhitespace(nsAString& aText)
|
|
||||||
{
|
|
||||||
uint32_t start = 0;
|
|
||||||
uint32_t end = aText.Length();
|
|
||||||
|
|
||||||
while (start < end && IsCSSWhitespace(aText.CharAt(start))) {
|
|
||||||
++start;
|
|
||||||
}
|
|
||||||
while (end > start && IsCSSWhitespace(aText.CharAt(end - 1))) {
|
|
||||||
--end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start == 0 && end == aText.Length()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
aText.Assign(Substring(aText, start, end - start));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SplitSelectorList(const nsAString& aSelectorText, SelectorList& aSelectors)
|
|
||||||
{
|
|
||||||
uint32_t itemStart = 0;
|
|
||||||
int32_t parenDepth = 0;
|
|
||||||
int32_t bracketDepth = 0;
|
|
||||||
bool inComment = false;
|
|
||||||
char16_t stringQuote = 0;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < aSelectorText.Length(); ++i) {
|
|
||||||
char16_t c = aSelectorText.CharAt(i);
|
|
||||||
|
|
||||||
if (inComment) {
|
|
||||||
if (c == '*' && i + 1 < aSelectorText.Length() &&
|
|
||||||
aSelectorText.CharAt(i + 1) == '/') {
|
|
||||||
inComment = false;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stringQuote) {
|
|
||||||
if (c == '\\') {
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == stringQuote) {
|
|
||||||
stringQuote = 0;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '/' && i + 1 < aSelectorText.Length() &&
|
|
||||||
aSelectorText.CharAt(i + 1) == '*') {
|
|
||||||
inComment = true;
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
stringQuote = c;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '(') {
|
|
||||||
++parenDepth;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ')' && parenDepth > 0) {
|
|
||||||
--parenDepth;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '[') {
|
|
||||||
++bracketDepth;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ']' && bracketDepth > 0) {
|
|
||||||
--bracketDepth;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == ',' && parenDepth == 0 && bracketDepth == 0) {
|
|
||||||
nsAutoString selector;
|
|
||||||
selector.Assign(Substring(aSelectorText, itemStart, i - itemStart));
|
|
||||||
TrimWhitespace(selector);
|
|
||||||
if (!selector.IsEmpty()) {
|
|
||||||
aSelectors.AppendElement(selector);
|
|
||||||
}
|
|
||||||
itemStart = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString selector;
|
|
||||||
selector.Assign(Substring(aSelectorText, itemStart));
|
|
||||||
TrimWhitespace(selector);
|
|
||||||
if (!selector.IsEmpty()) {
|
|
||||||
aSelectors.AppendElement(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !aSelectors.IsEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SelectorHasAmpersand(const nsAString& aSelector) const
|
|
||||||
{
|
|
||||||
bool inComment = false;
|
|
||||||
char16_t stringQuote = 0;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < aSelector.Length(); ++i) {
|
|
||||||
char16_t c = aSelector.CharAt(i);
|
|
||||||
|
|
||||||
if (inComment) {
|
|
||||||
if (c == '*' && i + 1 < aSelector.Length() &&
|
|
||||||
aSelector.CharAt(i + 1) == '/') {
|
|
||||||
inComment = false;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stringQuote) {
|
|
||||||
if (c == '\\') {
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == stringQuote) {
|
|
||||||
stringQuote = 0;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '/' && i + 1 < aSelector.Length() &&
|
|
||||||
aSelector.CharAt(i + 1) == '*') {
|
|
||||||
inComment = true;
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
stringQuote = c;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '&') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReplaceAmpersands(const nsAString& aSelector,
|
|
||||||
const nsAString& aParent,
|
|
||||||
nsAString& aOutput) const
|
|
||||||
{
|
|
||||||
bool inComment = false;
|
|
||||||
char16_t stringQuote = 0;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < aSelector.Length(); ++i) {
|
|
||||||
char16_t c = aSelector.CharAt(i);
|
|
||||||
|
|
||||||
if (inComment) {
|
|
||||||
aOutput.Append(c);
|
|
||||||
if (c == '*' && i + 1 < aSelector.Length() &&
|
|
||||||
aSelector.CharAt(i + 1) == '/') {
|
|
||||||
aOutput.Append('/');
|
|
||||||
inComment = false;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stringQuote) {
|
|
||||||
aOutput.Append(c);
|
|
||||||
if (c == '\\' && i + 1 < aSelector.Length()) {
|
|
||||||
aOutput.Append(aSelector.CharAt(i + 1));
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == stringQuote) {
|
|
||||||
stringQuote = 0;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '/' && i + 1 < aSelector.Length() &&
|
|
||||||
aSelector.CharAt(i + 1) == '*') {
|
|
||||||
aOutput.AppendLiteral("/*");
|
|
||||||
inComment = true;
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
aOutput.Append(c);
|
|
||||||
stringQuote = c;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '&') {
|
|
||||||
aOutput.Append(aParent);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
aOutput.Append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ExpandNestedSelectors(const SelectorList& aParents,
|
|
||||||
const nsAString& aNestedSelectorText,
|
|
||||||
SelectorList& aSelectors)
|
|
||||||
{
|
|
||||||
SelectorList nestedSelectors;
|
|
||||||
if (!SplitSelectorList(aNestedSelectorText, nestedSelectors)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const nsString& nestedSelector : nestedSelectors) {
|
|
||||||
bool hasAmpersand = SelectorHasAmpersand(nestedSelector);
|
|
||||||
for (const nsString& parentSelector : aParents) {
|
|
||||||
nsAutoString combined;
|
|
||||||
if (hasAmpersand) {
|
|
||||||
ReplaceAmpersands(nestedSelector, parentSelector, combined);
|
|
||||||
} else {
|
|
||||||
combined.Assign(parentSelector);
|
|
||||||
if (!combined.IsEmpty()) {
|
|
||||||
combined.Append(' ');
|
|
||||||
}
|
|
||||||
combined.Append(nestedSelector);
|
|
||||||
}
|
|
||||||
TrimWhitespace(combined);
|
|
||||||
if (!combined.IsEmpty()) {
|
|
||||||
aSelectors.AppendElement(combined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !aSelectors.IsEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
AppendSelectors(const SelectorList& aSelectors, nsAString& aOutput)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < aSelectors.Length(); ++i) {
|
|
||||||
if (i) {
|
|
||||||
aOutput.AppendLiteral(", ");
|
|
||||||
}
|
|
||||||
aOutput.Append(aSelectors[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
StartsNestedSelector(char16_t aChar)
|
|
||||||
{
|
|
||||||
switch (aChar) {
|
|
||||||
case '.':
|
|
||||||
case '#':
|
|
||||||
case '[':
|
|
||||||
case ':':
|
|
||||||
case '&':
|
|
||||||
case '|':
|
|
||||||
case '>':
|
|
||||||
case '+':
|
|
||||||
case '~':
|
|
||||||
case '*':
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
StartsPotentialTypeSelector(char16_t aChar)
|
|
||||||
{
|
|
||||||
return (aChar >= 'a' && aChar <= 'z') ||
|
|
||||||
(aChar >= 'A' && aChar <= 'Z') ||
|
|
||||||
aChar == '_' ||
|
|
||||||
aChar == '\\' ||
|
|
||||||
aChar >= 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
LooksLikeTypeSelectorRule() const
|
|
||||||
{
|
|
||||||
if (AtEnd() || !StartsPotentialTypeSelector(Peek())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t pos = mPos;
|
|
||||||
int32_t parenDepth = 0;
|
|
||||||
int32_t bracketDepth = 0;
|
|
||||||
bool inComment = false;
|
|
||||||
char16_t stringQuote = 0;
|
|
||||||
|
|
||||||
while (pos < mInput.Length()) {
|
|
||||||
char16_t c = mInput.CharAt(pos);
|
|
||||||
|
|
||||||
if (inComment) {
|
|
||||||
if (c == '*' && pos + 1 < mInput.Length() &&
|
|
||||||
mInput.CharAt(pos + 1) == '/') {
|
|
||||||
inComment = false;
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stringQuote) {
|
|
||||||
if (c == '\\' && pos + 1 < mInput.Length()) {
|
|
||||||
pos += 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == stringQuote) {
|
|
||||||
stringQuote = 0;
|
|
||||||
}
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '/' && pos + 1 < mInput.Length() &&
|
|
||||||
mInput.CharAt(pos + 1) == '*') {
|
|
||||||
inComment = true;
|
|
||||||
pos += 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
stringQuote = c;
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '(') {
|
|
||||||
++parenDepth;
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ')' && parenDepth > 0) {
|
|
||||||
--parenDepth;
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '[') {
|
|
||||||
++bracketDepth;
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ']' && bracketDepth > 0) {
|
|
||||||
--bracketDepth;
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parenDepth == 0 && bracketDepth == 0) {
|
|
||||||
if (c == '{') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (c == ';' || c == '}') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
IsAtRuleNameChar(char16_t aChar)
|
|
||||||
{
|
|
||||||
return (aChar >= 'a' && aChar <= 'z') ||
|
|
||||||
(aChar >= 'A' && aChar <= 'Z') ||
|
|
||||||
(aChar >= '0' && aChar <= '9') ||
|
|
||||||
aChar == '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
LowercaseASCII(nsACString& aText)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < aText.Length(); ++i) {
|
|
||||||
char c = aText.CharAt(i);
|
|
||||||
if (c >= 'A' && c <= 'Z') {
|
|
||||||
aText.BeginWriting()[i] = c - 'A' + 'a';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ShouldProcessGroupRule(const nsACString& aName)
|
|
||||||
{
|
|
||||||
return aName.EqualsLiteral("media") ||
|
|
||||||
aName.EqualsLiteral("supports") ||
|
|
||||||
aName.EqualsLiteral("document") ||
|
|
||||||
aName.EqualsLiteral("layer");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FlushDeclarations(const SelectorList& aSelectors,
|
|
||||||
nsAString& aDeclarations,
|
|
||||||
nsAString& aOutput)
|
|
||||||
{
|
|
||||||
nsAutoString declarations;
|
|
||||||
declarations.Assign(aDeclarations);
|
|
||||||
TrimWhitespace(declarations);
|
|
||||||
aDeclarations.Truncate();
|
|
||||||
|
|
||||||
if (declarations.IsEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppendSelectors(aSelectors, aOutput);
|
|
||||||
aOutput.AppendLiteral(" { ");
|
|
||||||
aOutput.Append(declarations);
|
|
||||||
aOutput.AppendLiteral(" }\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ReadRawBlockBody(nsAString& aBody)
|
|
||||||
{
|
|
||||||
uint32_t start = mPos;
|
|
||||||
int32_t depth = 0;
|
|
||||||
|
|
||||||
while (!AtEnd()) {
|
|
||||||
char16_t c = Peek();
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
if (!SkipString(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (StartsWithComment()) {
|
|
||||||
if (!SkipComment()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '{') {
|
|
||||||
++depth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '}') {
|
|
||||||
if (depth == 0) {
|
|
||||||
aBody.Assign(Substring(mInput, start, mPos - start));
|
|
||||||
++mPos;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
--depth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++mPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ReadQualifiedRulePrelude(nsAString& aPrelude)
|
|
||||||
{
|
|
||||||
uint32_t start = mPos;
|
|
||||||
int32_t parenDepth = 0;
|
|
||||||
int32_t bracketDepth = 0;
|
|
||||||
|
|
||||||
while (!AtEnd()) {
|
|
||||||
char16_t c = Peek();
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
if (!SkipString(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (StartsWithComment()) {
|
|
||||||
if (!SkipComment()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '(') {
|
|
||||||
++parenDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ')' && parenDepth > 0) {
|
|
||||||
--parenDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '[') {
|
|
||||||
++bracketDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ']' && bracketDepth > 0) {
|
|
||||||
--bracketDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '{' && parenDepth == 0 && bracketDepth == 0) {
|
|
||||||
aPrelude.Assign(Substring(mInput, start, mPos - start));
|
|
||||||
TrimWhitespace(aPrelude);
|
|
||||||
++mPos;
|
|
||||||
return !aPrelude.IsEmpty();
|
|
||||||
}
|
|
||||||
if ((c == ';' || c == '}') && parenDepth == 0 && bracketDepth == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
++mPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ReadAtRulePrelude(nsAString& aPrelude, nsACString& aName, bool& aHasBlock)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!AtEnd() && Peek() == '@', "expected at-rule");
|
|
||||||
|
|
||||||
uint32_t start = mPos;
|
|
||||||
++mPos;
|
|
||||||
aName.Truncate();
|
|
||||||
while (!AtEnd() && IsAtRuleNameChar(Peek())) {
|
|
||||||
char16_t c = Peek();
|
|
||||||
aName.Append(char(c <= 0x7f ? c : '?'));
|
|
||||||
++mPos;
|
|
||||||
}
|
|
||||||
LowercaseASCII(aName);
|
|
||||||
|
|
||||||
int32_t parenDepth = 0;
|
|
||||||
int32_t bracketDepth = 0;
|
|
||||||
while (!AtEnd()) {
|
|
||||||
char16_t c = Peek();
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
if (!SkipString(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (StartsWithComment()) {
|
|
||||||
if (!SkipComment()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '(') {
|
|
||||||
++parenDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ')' && parenDepth > 0) {
|
|
||||||
--parenDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '[') {
|
|
||||||
++bracketDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ']' && bracketDepth > 0) {
|
|
||||||
--bracketDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (parenDepth == 0 && bracketDepth == 0) {
|
|
||||||
if (c == ';') {
|
|
||||||
aPrelude.Assign(Substring(mInput, start, mPos - start));
|
|
||||||
TrimWhitespace(aPrelude);
|
|
||||||
++mPos;
|
|
||||||
aHasBlock = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (c == '{') {
|
|
||||||
aPrelude.Assign(Substring(mInput, start, mPos - start));
|
|
||||||
TrimWhitespace(aPrelude);
|
|
||||||
++mPos;
|
|
||||||
aHasBlock = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++mPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ConsumeDeclaration(nsAString& aDeclaration)
|
|
||||||
{
|
|
||||||
uint32_t start = mPos;
|
|
||||||
int32_t parenDepth = 0;
|
|
||||||
int32_t bracketDepth = 0;
|
|
||||||
int32_t braceDepth = 0;
|
|
||||||
|
|
||||||
while (!AtEnd()) {
|
|
||||||
char16_t c = Peek();
|
|
||||||
if (c == '"' || c == '\'') {
|
|
||||||
if (!SkipString(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (StartsWithComment()) {
|
|
||||||
if (!SkipComment()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '(') {
|
|
||||||
++parenDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ')' && parenDepth > 0) {
|
|
||||||
--parenDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '[') {
|
|
||||||
++bracketDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ']' && bracketDepth > 0) {
|
|
||||||
--bracketDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '{') {
|
|
||||||
++braceDepth;
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '}') {
|
|
||||||
if (braceDepth == 0 && parenDepth == 0 && bracketDepth == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (braceDepth > 0) {
|
|
||||||
--braceDepth;
|
|
||||||
}
|
|
||||||
++mPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ';' && parenDepth == 0 && bracketDepth == 0 &&
|
|
||||||
braceDepth == 0) {
|
|
||||||
++mPos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++mPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
aDeclaration.Assign(Substring(mInput, start, mPos - start));
|
|
||||||
TrimWhitespace(aDeclaration);
|
|
||||||
if (aDeclaration.IsEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (aDeclaration.Last() != ';') {
|
|
||||||
aDeclaration.Append(';');
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ParseAtRule(nsAString& aOutput, const SelectorList* aParents)
|
|
||||||
{
|
|
||||||
nsAutoString prelude;
|
|
||||||
nsAutoCString name;
|
|
||||||
bool hasBlock = false;
|
|
||||||
if (!ReadAtRulePrelude(prelude, name, hasBlock)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasBlock) {
|
|
||||||
aOutput.Append(prelude);
|
|
||||||
aOutput.AppendLiteral(";\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ShouldProcessGroupRule(name)) {
|
|
||||||
nsAutoString body;
|
|
||||||
if (!ReadRawBlockBody(body)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
aOutput.Append(prelude);
|
|
||||||
aOutput.AppendLiteral(" {");
|
|
||||||
aOutput.Append(body);
|
|
||||||
aOutput.AppendLiteral("}\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString inner;
|
|
||||||
if (aParents) {
|
|
||||||
mSawNesting = true;
|
|
||||||
if (!ProcessStyleContext(*aParents, inner)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!ProcessStylesheet(inner, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aOutput.Append(prelude);
|
|
||||||
aOutput.AppendLiteral(" {\n");
|
|
||||||
aOutput.Append(inner);
|
|
||||||
aOutput.AppendLiteral("}\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ParseQualifiedRule(nsAString& aOutput, const SelectorList* aParents)
|
|
||||||
{
|
|
||||||
nsAutoString prelude;
|
|
||||||
if (!ReadQualifiedRulePrelude(prelude)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectorList selectors;
|
|
||||||
if (aParents) {
|
|
||||||
mSawNesting = true;
|
|
||||||
if (!ExpandNestedSelectors(*aParents, prelude, selectors)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (!SplitSelectorList(prelude, selectors)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ProcessStyleContext(selectors, aOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ProcessStyleContext(const SelectorList& aSelectors, nsAString& aOutput)
|
|
||||||
{
|
|
||||||
nsAutoString declarations;
|
|
||||||
|
|
||||||
while (!AtEnd()) {
|
|
||||||
SkipWhitespaceAndComments();
|
|
||||||
if (AtEnd()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char16_t c = Peek();
|
|
||||||
if (c == '}') {
|
|
||||||
++mPos;
|
|
||||||
FlushDeclarations(aSelectors, declarations, aOutput);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '@') {
|
|
||||||
FlushDeclarations(aSelectors, declarations, aOutput);
|
|
||||||
if (!ParseAtRule(aOutput, &aSelectors)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StartsNestedSelector(c) || LooksLikeTypeSelectorRule()) {
|
|
||||||
FlushDeclarations(aSelectors, declarations, aOutput);
|
|
||||||
if (!ParseQualifiedRule(aOutput, &aSelectors)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString declaration;
|
|
||||||
if (!ConsumeDeclaration(declaration)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!declarations.IsEmpty()) {
|
|
||||||
declarations.Append(' ');
|
|
||||||
}
|
|
||||||
declarations.Append(declaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ProcessStylesheet(nsAString& aOutput, bool aStopAtBlockEnd)
|
|
||||||
{
|
|
||||||
while (!AtEnd()) {
|
|
||||||
SkipWhitespaceAndComments();
|
|
||||||
if (AtEnd()) {
|
|
||||||
return !aStopAtBlockEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Peek() == '}') {
|
|
||||||
if (!aStopAtBlockEnd) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
++mPos;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Peek() == '@') {
|
|
||||||
if (!ParseAtRule(aOutput, nullptr)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!ParseQualifiedRule(aOutput, nullptr)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !aStopAtBlockEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nsAString& mInput;
|
|
||||||
uint32_t mPos;
|
|
||||||
bool mSawNesting;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
bool
|
|
||||||
FlattenBasicCSSNesting(const nsAString& aInput, nsAString& aOutput)
|
|
||||||
{
|
|
||||||
CSSNestingFlattener flattener(aInput);
|
|
||||||
return flattener.Flatten(aOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace css
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* 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/. */
|
|
||||||
|
|
||||||
#ifndef CSSNestingFlattener_h
|
|
||||||
#define CSSNestingFlattener_h
|
|
||||||
|
|
||||||
class nsAString;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace css {
|
|
||||||
|
|
||||||
bool FlattenBasicCSSNesting(const nsAString& aInput, nsAString& aOutput);
|
|
||||||
|
|
||||||
} // namespace css
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // CSSNestingFlattener_h
|
|
||||||
|
|
@ -885,10 +885,9 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
|
||||||
}
|
}
|
||||||
|
|
||||||
// In standards mode, a style sheet must have one of these MIME
|
// In standards mode, a style sheet must have one of these MIME
|
||||||
// types to be processed at all, unless it is same-origin.
|
// types to be processed at all. In quirks mode, we accept any
|
||||||
// For same-origin sheets, we accept any MIME type to match modern
|
// MIME type, but only if the style sheet is same-origin with the
|
||||||
// browser behavior (Chrome, Firefox). Cross-origin sheets require
|
// requesting document or parent sheet. See bug 524223.
|
||||||
// a valid CSS MIME type for security.
|
|
||||||
|
|
||||||
bool validType = contentType.EqualsLiteral("text/css") ||
|
bool validType = contentType.EqualsLiteral("text/css") ||
|
||||||
contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
|
contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
|
||||||
|
|
@ -907,7 +906,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sameOrigin) {
|
if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
|
||||||
errorMessage = "MimeNotCssWarn";
|
errorMessage = "MimeNotCssWarn";
|
||||||
errorFlag = nsIScriptError::warningFlag;
|
errorFlag = nsIScriptError::warningFlag;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,6 @@ UNIFIED_SOURCES += [
|
||||||
'CounterStyleManager.cpp',
|
'CounterStyleManager.cpp',
|
||||||
'CSS.cpp',
|
'CSS.cpp',
|
||||||
'CSSLexer.cpp',
|
'CSSLexer.cpp',
|
||||||
'CSSNestingFlattener.cpp',
|
|
||||||
'CSSRuleList.cpp',
|
'CSSRuleList.cpp',
|
||||||
'CSSStyleSheet.cpp',
|
'CSSStyleSheet.cpp',
|
||||||
'CSSVariableDeclarations.cpp',
|
'CSSVariableDeclarations.cpp',
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
#include "mozilla/Maybe.h"
|
|
||||||
#include "mozilla/Move.h"
|
#include "mozilla/Move.h"
|
||||||
#include "mozilla/MathAlgorithms.h"
|
#include "mozilla/MathAlgorithms.h"
|
||||||
#include "mozilla/TypedEnumBits.h"
|
#include "mozilla/TypedEnumBits.h"
|
||||||
|
|
@ -18,7 +17,6 @@
|
||||||
#include <regex> // for std::regex and std::regex_match
|
#include <regex> // for std::regex and std::regex_match
|
||||||
|
|
||||||
#include "nsCSSParser.h"
|
#include "nsCSSParser.h"
|
||||||
#include "CSSNestingFlattener.h"
|
|
||||||
#include "nsAlgorithm.h"
|
#include "nsAlgorithm.h"
|
||||||
#include "nsCSSProps.h"
|
#include "nsCSSProps.h"
|
||||||
#include "nsCSSKeywords.h"
|
#include "nsCSSKeywords.h"
|
||||||
|
|
@ -73,7 +71,6 @@ static bool sMozGradientsEnabled;
|
||||||
static bool sControlCharVisibility;
|
static bool sControlCharVisibility;
|
||||||
static bool sLegacyNegationPseudoClassEnabled;
|
static bool sLegacyNegationPseudoClassEnabled;
|
||||||
static bool sCascadeLayersEnabled;
|
static bool sCascadeLayersEnabled;
|
||||||
static bool sNestingEnabled;
|
|
||||||
|
|
||||||
const uint32_t
|
const uint32_t
|
||||||
nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
|
nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
|
||||||
|
|
@ -1826,14 +1823,7 @@ CSSParserImpl::ParseSheet(const nsAString& aInput,
|
||||||
"Sheet principal does not match passed principal");
|
"Sheet principal does not match passed principal");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
nsAutoString flattenedInput;
|
nsCSSScanner scanner(aInput, aLineNumber);
|
||||||
const nsAString* input = &aInput;
|
|
||||||
if (sNestingEnabled &&
|
|
||||||
mozilla::css::FlattenBasicCSSNesting(aInput, flattenedInput)) {
|
|
||||||
input = &flattenedInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCSSScanner scanner(*input, aLineNumber);
|
|
||||||
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
|
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
|
||||||
InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
|
InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
|
||||||
|
|
||||||
|
|
@ -18997,8 +18987,6 @@ nsCSSParser::Startup()
|
||||||
"layout.css.legacy-negation-pseudo.enabled");
|
"layout.css.legacy-negation-pseudo.enabled");
|
||||||
Preferences::AddBoolVarCache(&sCascadeLayersEnabled,
|
Preferences::AddBoolVarCache(&sCascadeLayersEnabled,
|
||||||
"layout.css.cascade-layers.enabled");
|
"layout.css.cascade-layers.enabled");
|
||||||
Preferences::AddBoolVarCache(&sNestingEnabled,
|
|
||||||
"layout.css.nesting.enabled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
|
nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,6 @@ support-files = file_animations_with_disabled_properties.html
|
||||||
[test_attribute_selector_eof_behavior.html]
|
[test_attribute_selector_eof_behavior.html]
|
||||||
[test_aspect_ratio_property.html]
|
[test_aspect_ratio_property.html]
|
||||||
[test_background_blend_mode.html]
|
[test_background_blend_mode.html]
|
||||||
[test_basic_nesting_flattening.html]
|
|
||||||
[test_nesting_flattening_parser_edges.html]
|
|
||||||
[test_nesting_flattening_recovery.html]
|
|
||||||
[test_box_size_keywords.html]
|
[test_box_size_keywords.html]
|
||||||
[test_bug73586.html]
|
[test_bug73586.html]
|
||||||
[test_css_math_functions.html]
|
[test_css_math_functions.html]
|
||||||
|
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test for Basic CSS Nesting Flattening</title>
|
|
||||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="scope" class="scope order">
|
|
||||||
<span id="desc" class="desc"></span>
|
|
||||||
</div>
|
|
||||||
<div id="button" class="button active"></div>
|
|
||||||
<pre id="standalone-log"></pre>
|
|
||||||
<script>
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function colorOf(win, element, property) {
|
|
||||||
return win.getComputedStyle(element).getPropertyValue(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendTestSheet() {
|
|
||||||
var style = document.createElement("style");
|
|
||||||
style.textContent =
|
|
||||||
".scope {\n" +
|
|
||||||
" color: rgb(1, 2, 3);\n" +
|
|
||||||
" .desc {\n" +
|
|
||||||
" color: rgb(4, 5, 6);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" span {\n" +
|
|
||||||
" border-left-style: solid;\n" +
|
|
||||||
" border-left-color: rgb(19, 20, 21);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" span:first-child, span:last-child {\n" +
|
|
||||||
" border-bottom-style: solid;\n" +
|
|
||||||
" border-bottom-color: rgb(22, 23, 24);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" background-color: rgb(7, 8, 9);\n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
".button {\n" +
|
|
||||||
" &.active {\n" +
|
|
||||||
" color: rgb(10, 11, 12);\n" +
|
|
||||||
" }\n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
".order {\n" +
|
|
||||||
" border-top-style: solid;\n" +
|
|
||||||
" @media all {\n" +
|
|
||||||
" & {\n" +
|
|
||||||
" border-top-color: rgb(13, 14, 15);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" }\n" +
|
|
||||||
" border-right-style: solid;\n" +
|
|
||||||
" border-right-color: rgb(16, 17, 18);\n" +
|
|
||||||
"}\n";
|
|
||||||
document.head.appendChild(style);
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runChecks(style, report) {
|
|
||||||
var scope = document.getElementById("scope");
|
|
||||||
var desc = document.getElementById("desc");
|
|
||||||
var button = document.getElementById("button");
|
|
||||||
|
|
||||||
report(colorOf(window, scope, "color") === "rgb(1, 2, 3)",
|
|
||||||
"outer rule declarations should apply",
|
|
||||||
colorOf(window, scope, "color"),
|
|
||||||
"rgb(1, 2, 3)");
|
|
||||||
report(colorOf(window, desc, "color") === "rgb(4, 5, 6)",
|
|
||||||
"nested descendant rule should be flattened",
|
|
||||||
colorOf(window, desc, "color"),
|
|
||||||
"rgb(4, 5, 6)");
|
|
||||||
report(colorOf(window, desc, "border-left-color") === "rgb(19, 20, 21)",
|
|
||||||
"nested type selector rule should be flattened",
|
|
||||||
colorOf(window, desc, "border-left-color"),
|
|
||||||
"rgb(19, 20, 21)");
|
|
||||||
report(colorOf(window, desc, "border-bottom-color") === "rgb(22, 23, 24)",
|
|
||||||
"nested type selector pseudos should be flattened",
|
|
||||||
colorOf(window, desc, "border-bottom-color"),
|
|
||||||
"rgb(22, 23, 24)");
|
|
||||||
report(colorOf(window, scope, "background-color") === "rgb(7, 8, 9)",
|
|
||||||
"declarations after a nested rule should preserve order",
|
|
||||||
colorOf(window, scope, "background-color"),
|
|
||||||
"rgb(7, 8, 9)");
|
|
||||||
report(colorOf(window, button, "color") === "rgb(10, 11, 12)",
|
|
||||||
"ampersand selectors should be expanded",
|
|
||||||
colorOf(window, button, "color"),
|
|
||||||
"rgb(10, 11, 12)");
|
|
||||||
report(colorOf(window, scope, "border-top-color") === "rgb(13, 14, 15)",
|
|
||||||
"nested group rules should target the parent selector",
|
|
||||||
colorOf(window, scope, "border-top-color"),
|
|
||||||
"rgb(13, 14, 15)");
|
|
||||||
report(colorOf(window, scope, "border-right-color") === "rgb(16, 17, 18)",
|
|
||||||
"declarations after nested group rules should still apply",
|
|
||||||
colorOf(window, scope, "border-right-color"),
|
|
||||||
"rgb(16, 17, 18)");
|
|
||||||
report(style.sheet.cssRules.length === 9,
|
|
||||||
"flattening should produce flat top-level rules",
|
|
||||||
String(style.sheet.cssRules.length),
|
|
||||||
"9");
|
|
||||||
}
|
|
||||||
|
|
||||||
function runStandalone() {
|
|
||||||
var log = document.getElementById("standalone-log");
|
|
||||||
var lines = [
|
|
||||||
"Standalone mode.",
|
|
||||||
"This page only demonstrates nesting if layout.css.nesting.enabled is already true in the browser.",
|
|
||||||
""
|
|
||||||
];
|
|
||||||
var style = appendTestSheet();
|
|
||||||
|
|
||||||
runChecks(style, function(pass, message, actual, expected) {
|
|
||||||
lines.push((pass ? "PASS" : "FAIL") + ": " + message);
|
|
||||||
if (!pass) {
|
|
||||||
lines.push(" expected: " + expected);
|
|
||||||
lines.push(" actual: " + actual);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log.textContent = lines.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function runMochitest() {
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
SpecialPowers.pushPrefEnv({
|
|
||||||
set: [["layout.css.nesting.enabled", true]]
|
|
||||||
}, function() {
|
|
||||||
var style = appendTestSheet();
|
|
||||||
|
|
||||||
runChecks(style, function(pass, message, actual, expected) {
|
|
||||||
is(actual, expected, message);
|
|
||||||
});
|
|
||||||
|
|
||||||
SimpleTest.finish();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof SimpleTest !== "undefined" && typeof SpecialPowers !== "undefined") {
|
|
||||||
runMochitest();
|
|
||||||
} else {
|
|
||||||
runStandalone();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test CSS Nesting Flattening Parser Edges</title>
|
|
||||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="scope" class="scope">
|
|
||||||
<span id="firstSpan" class="desc"></span>
|
|
||||||
<span id="middleSpan" class="middle"></span>
|
|
||||||
<span id="escapedSpan" class="1foo"></span>
|
|
||||||
<svg id="svgRoot" xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
||||||
<rect id="nsRect" width="10" height="10"></rect>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div id="afterMalformed" class="after-malformed"></div>
|
|
||||||
<div id="afterTarget" class="after-target"></div>
|
|
||||||
<pre id="standalone-log"></pre>
|
|
||||||
<script>
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function propOf(win, element, property) {
|
|
||||||
return win.getComputedStyle(element).getPropertyValue(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendTestSheet() {
|
|
||||||
var style = document.createElement("style");
|
|
||||||
style.textContent =
|
|
||||||
"@namespace svg url(http://www.w3.org/2000/svg);\n" +
|
|
||||||
".scope {\n" +
|
|
||||||
" --payload: { alpha: 1; beta: 2; };\n" +
|
|
||||||
" color: rgb(1, 2, 3);\n" +
|
|
||||||
" span:first-of-type, span:last-of-type {\n" +
|
|
||||||
" color: rgb(30, 31, 32);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" .\\31 foo {\n" +
|
|
||||||
" border-left-style: solid;\n" +
|
|
||||||
" border-left-color: rgb(40, 41, 42);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" svg|rect {\n" +
|
|
||||||
" stroke: rgb(50, 51, 52);\n" +
|
|
||||||
" stroke-width: 1px;\n" +
|
|
||||||
" }\n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
".after-malformed {\n" +
|
|
||||||
" color: rgb(60, 61, 62);\n" +
|
|
||||||
" : {\n" +
|
|
||||||
" color: rgb(70, 71, 72);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" background-color: rgb(80, 81, 82);\n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
".after-target {\n" +
|
|
||||||
" color: rgb(90, 91, 92);\n" +
|
|
||||||
"}\n";
|
|
||||||
document.head.appendChild(style);
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runChecks(style, report) {
|
|
||||||
var scope = document.getElementById("scope");
|
|
||||||
var firstSpan = document.getElementById("firstSpan");
|
|
||||||
var middleSpan = document.getElementById("middleSpan");
|
|
||||||
var escapedSpan = document.getElementById("escapedSpan");
|
|
||||||
var rect = document.getElementById("nsRect");
|
|
||||||
var afterMalformed = document.getElementById("afterMalformed");
|
|
||||||
var afterTarget = document.getElementById("afterTarget");
|
|
||||||
|
|
||||||
var payload = propOf(window, scope, "--payload");
|
|
||||||
report(payload.indexOf("alpha") !== -1 && payload.indexOf("beta") !== -1,
|
|
||||||
"custom property payload should survive flattening",
|
|
||||||
payload,
|
|
||||||
"contains alpha and beta");
|
|
||||||
|
|
||||||
report(propOf(window, scope, "color") === "rgb(1, 2, 3)",
|
|
||||||
"base declaration should apply",
|
|
||||||
propOf(window, scope, "color"),
|
|
||||||
"rgb(1, 2, 3)");
|
|
||||||
report(propOf(window, firstSpan, "color") === "rgb(30, 31, 32)",
|
|
||||||
"nested pseudo selector should style first span",
|
|
||||||
propOf(window, firstSpan, "color"),
|
|
||||||
"rgb(30, 31, 32)");
|
|
||||||
report(propOf(window, escapedSpan, "color") === "rgb(30, 31, 32)",
|
|
||||||
"nested pseudo selector should style last span",
|
|
||||||
propOf(window, escapedSpan, "color"),
|
|
||||||
"rgb(30, 31, 32)");
|
|
||||||
report(propOf(window, middleSpan, "color") === "rgb(1, 2, 3)",
|
|
||||||
"middle span should keep inherited scope color",
|
|
||||||
propOf(window, middleSpan, "color"),
|
|
||||||
"rgb(1, 2, 3)");
|
|
||||||
|
|
||||||
report(propOf(window, escapedSpan, "border-left-color") === "rgb(40, 41, 42)",
|
|
||||||
"escaped class selector should match",
|
|
||||||
propOf(window, escapedSpan, "border-left-color"),
|
|
||||||
"rgb(40, 41, 42)");
|
|
||||||
|
|
||||||
report(propOf(window, rect, "stroke") === "rgb(50, 51, 52)",
|
|
||||||
"namespace selector should match nested SVG rect",
|
|
||||||
propOf(window, rect, "stroke"),
|
|
||||||
"rgb(50, 51, 52)");
|
|
||||||
|
|
||||||
report(propOf(window, afterMalformed, "color") === "rgb(60, 61, 62)",
|
|
||||||
"declaration before malformed nested selector should apply",
|
|
||||||
propOf(window, afterMalformed, "color"),
|
|
||||||
"rgb(60, 61, 62)");
|
|
||||||
report(propOf(window, afterMalformed, "background-color") === "rgb(80, 81, 82)",
|
|
||||||
"declaration after malformed nested selector should still apply",
|
|
||||||
propOf(window, afterMalformed, "background-color"),
|
|
||||||
"rgb(80, 81, 82)");
|
|
||||||
report(propOf(window, afterTarget, "color") === "rgb(90, 91, 92)",
|
|
||||||
"subsequent top-level rules should still parse",
|
|
||||||
propOf(window, afterTarget, "color"),
|
|
||||||
"rgb(90, 91, 92)");
|
|
||||||
}
|
|
||||||
|
|
||||||
function runStandalone() {
|
|
||||||
var log = document.getElementById("standalone-log");
|
|
||||||
var lines = [
|
|
||||||
"Standalone mode.",
|
|
||||||
"This page only demonstrates nesting if layout.css.nesting.enabled is already true in the browser.",
|
|
||||||
""
|
|
||||||
];
|
|
||||||
var style = appendTestSheet();
|
|
||||||
|
|
||||||
runChecks(style, function(pass, message, actual, expected) {
|
|
||||||
lines.push((pass ? "PASS" : "FAIL") + ": " + message);
|
|
||||||
if (!pass) {
|
|
||||||
lines.push(" expected: " + expected);
|
|
||||||
lines.push(" actual: " + actual);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log.textContent = lines.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function runMochitest() {
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
SpecialPowers.pushPrefEnv({
|
|
||||||
set: [["layout.css.nesting.enabled", true]]
|
|
||||||
}, function() {
|
|
||||||
var style = appendTestSheet();
|
|
||||||
|
|
||||||
runChecks(style, function(pass, message, actual, expected) {
|
|
||||||
ok(pass, message + " (expected: " + expected + ", actual: " + actual + ")");
|
|
||||||
});
|
|
||||||
|
|
||||||
SimpleTest.finish();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof SimpleTest !== "undefined" && typeof SpecialPowers !== "undefined") {
|
|
||||||
runMochitest();
|
|
||||||
} else {
|
|
||||||
runStandalone();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test CSS Nesting Flattening Recovery Paths</title>
|
|
||||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="recBase" class="rec-base"><span id="recBaseChild"></span></div>
|
|
||||||
<div id="recMalformed" class="rec-malformed"></div>
|
|
||||||
<div id="recGroup" class="rec-group"></div>
|
|
||||||
<div id="recTail" class="rec-tail"></div>
|
|
||||||
<pre id="standalone-log"></pre>
|
|
||||||
<script>
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function propOf(win, element, property) {
|
|
||||||
return win.getComputedStyle(element).getPropertyValue(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendTestSheet() {
|
|
||||||
var style = document.createElement("style");
|
|
||||||
style.textContent =
|
|
||||||
".rec-base {\n" +
|
|
||||||
" color: rgb(1, 2, 3);\n" +
|
|
||||||
" |* {\n" +
|
|
||||||
" color: rgb(4, 5, 6);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" background-color: rgb(7, 8, 9);\n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
".rec-malformed {\n" +
|
|
||||||
" color: rgb(20, 21, 22);\n" +
|
|
||||||
" : {\n" +
|
|
||||||
" color: rgb(23, 24, 25);\n" +
|
|
||||||
" }\n" +
|
|
||||||
" border-top-style: solid;\n" +
|
|
||||||
" border-top-color: rgb(26, 27, 28);\n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
".rec-group {\n" +
|
|
||||||
" color: rgb(32, 33, 34);\n" +
|
|
||||||
" @counter-style rec-counter {\n" +
|
|
||||||
" system: fixed;\n" +
|
|
||||||
" symbols: a;\n" +
|
|
||||||
" }\n" +
|
|
||||||
" background-color: rgb(35, 36, 37);\n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
".rec-tail {\n" +
|
|
||||||
" color: rgb(50, 51, 52);\n" +
|
|
||||||
"}\n";
|
|
||||||
document.head.appendChild(style);
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runChecks(style, report) {
|
|
||||||
var recBase = document.getElementById("recBase");
|
|
||||||
var recBaseChild = document.getElementById("recBaseChild");
|
|
||||||
var recMalformed = document.getElementById("recMalformed");
|
|
||||||
var recGroup = document.getElementById("recGroup");
|
|
||||||
var recTail = document.getElementById("recTail");
|
|
||||||
|
|
||||||
report(propOf(window, recBase, "color") === "rgb(1, 2, 3)",
|
|
||||||
"unsupported nested selector should not wipe earlier declarations",
|
|
||||||
propOf(window, recBase, "color"),
|
|
||||||
"rgb(1, 2, 3)");
|
|
||||||
report(propOf(window, recBase, "background-color") === "rgb(7, 8, 9)",
|
|
||||||
"unsupported nested selector should not wipe later declarations",
|
|
||||||
propOf(window, recBase, "background-color"),
|
|
||||||
"rgb(7, 8, 9)");
|
|
||||||
report(propOf(window, recBaseChild, "color") === "rgb(1, 2, 3)",
|
|
||||||
"unsupported nested selector should not restyle descendants unexpectedly",
|
|
||||||
propOf(window, recBaseChild, "color"),
|
|
||||||
"rgb(1, 2, 3)");
|
|
||||||
|
|
||||||
report(propOf(window, recMalformed, "color") === "rgb(20, 21, 22)",
|
|
||||||
"malformed nested selector should keep declaration before it",
|
|
||||||
propOf(window, recMalformed, "color"),
|
|
||||||
"rgb(20, 21, 22)");
|
|
||||||
report(propOf(window, recMalformed, "border-top-color") === "rgb(26, 27, 28)",
|
|
||||||
"malformed nested selector should keep declaration after it",
|
|
||||||
propOf(window, recMalformed, "border-top-color"),
|
|
||||||
"rgb(26, 27, 28)");
|
|
||||||
|
|
||||||
report(propOf(window, recGroup, "color") === "rgb(32, 33, 34)",
|
|
||||||
"nested unsupported at-rule should keep declaration before it",
|
|
||||||
propOf(window, recGroup, "color"),
|
|
||||||
"rgb(32, 33, 34)");
|
|
||||||
report(propOf(window, recGroup, "background-color") === "rgb(35, 36, 37)",
|
|
||||||
"nested unsupported at-rule should keep declaration after it",
|
|
||||||
propOf(window, recGroup, "background-color"),
|
|
||||||
"rgb(35, 36, 37)");
|
|
||||||
|
|
||||||
report(propOf(window, recTail, "color") === "rgb(50, 51, 52)",
|
|
||||||
"rules after recovery scenarios should still parse",
|
|
||||||
propOf(window, recTail, "color"),
|
|
||||||
"rgb(50, 51, 52)");
|
|
||||||
|
|
||||||
report(style.sheet.cssRules.length >= 4,
|
|
||||||
"stylesheet should remain parseable after recovery scenarios",
|
|
||||||
String(style.sheet.cssRules.length),
|
|
||||||
">= 4");
|
|
||||||
}
|
|
||||||
|
|
||||||
function runStandalone() {
|
|
||||||
var log = document.getElementById("standalone-log");
|
|
||||||
var lines = [
|
|
||||||
"Standalone mode.",
|
|
||||||
"This page only demonstrates nesting if layout.css.nesting.enabled is already true in the browser.",
|
|
||||||
""
|
|
||||||
];
|
|
||||||
var style = appendTestSheet();
|
|
||||||
|
|
||||||
runChecks(style, function(pass, message, actual, expected) {
|
|
||||||
lines.push((pass ? "PASS" : "FAIL") + ": " + message);
|
|
||||||
if (!pass) {
|
|
||||||
lines.push(" expected: " + expected);
|
|
||||||
lines.push(" actual: " + actual);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log.textContent = lines.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function runMochitest() {
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
SpecialPowers.pushPrefEnv({
|
|
||||||
set: [["layout.css.nesting.enabled", true]]
|
|
||||||
}, function() {
|
|
||||||
var style = appendTestSheet();
|
|
||||||
|
|
||||||
runChecks(style, function(pass, message, actual, expected) {
|
|
||||||
ok(pass, message + " (expected: " + expected + ", actual: " + actual + ")");
|
|
||||||
});
|
|
||||||
|
|
||||||
SimpleTest.finish();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof SimpleTest !== "undefined" && typeof SpecialPowers !== "undefined") {
|
|
||||||
runMochitest();
|
|
||||||
} else {
|
|
||||||
runStandalone();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue