Compare commits
182
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05441188be | ||
|
|
25a2d31be7 | ||
|
|
58a4e0d545 | ||
|
|
ad06b4ce5e | ||
|
|
be3827b088 | ||
|
|
a136aaf502 | ||
|
|
40d91a8eae | ||
|
|
5b81a81ac4 | ||
|
|
328ddb2927 | ||
|
|
06b9622302 | ||
|
|
1218649202 | ||
|
|
cbea8f5a53 | ||
|
|
f42e644486 | ||
|
|
a40490b946 | ||
|
|
ad98693f3d | ||
|
|
7b4aaca975 | ||
|
|
400e59a96d | ||
|
|
09276b72a6 | ||
|
|
ec18304362 | ||
|
|
4cf411f393 | ||
|
|
a0f135b94f | ||
|
|
3be679effc | ||
|
|
2d91c80555 | ||
|
|
e5326c1d74 | ||
|
|
5ddccdc7f6 | ||
|
|
8dc41b293a | ||
|
|
f02956a324 | ||
|
|
e3646af419 | ||
|
|
ee1f2e31a1 | ||
|
|
dcb90d6f92 | ||
|
|
f9cdc54e09 | ||
|
|
28be7ae6e0 | ||
|
|
463e7ec3c6 | ||
|
|
531eeaabaa | ||
|
|
af92afbd6c | ||
|
|
5005a47921 | ||
|
|
72d4ca2b35 | ||
|
|
b8951dbc2a | ||
|
|
20c92e63c5 | ||
|
|
ea785c3bb0 | ||
|
|
7abc11b3d1 | ||
|
|
2a4c4687c6 | ||
|
|
de3970d9b4 | ||
|
|
4da8c1fe4c | ||
|
|
8230f9b359 | ||
|
|
310fec9ad5 | ||
|
|
5f2040153b | ||
|
|
30a774eb08 | ||
|
|
e8ee09b41f | ||
|
|
5fc626f0fc | ||
|
|
c87c0e440b | ||
|
|
39b34ed8c5 | ||
|
|
e6b4a84e7e | ||
|
|
f7dfc278c4 | ||
|
|
13349f484c | ||
|
|
bd2670e006 | ||
|
|
e5bebae2c7 | ||
|
|
d93df7216d | ||
|
|
3d0a6899ce | ||
|
|
fdde68855b | ||
|
|
2d038e6c1a | ||
|
|
c10f8a567b | ||
|
|
dcfa35e1ff | ||
|
|
de1a7f5fe1 | ||
|
|
2b0935adf4 | ||
|
|
cbdc5fa85f | ||
|
|
2a06c40a09 | ||
|
|
f499e3dd36 | ||
|
|
34d88ac937 | ||
|
|
48d21fa2bf | ||
|
|
c91c69272b | ||
|
|
91f4fa744e | ||
|
|
c2abd4aaa6 | ||
|
|
617ad84f96 | ||
|
|
33b6bfa1b9 | ||
|
|
4e03afd191 | ||
|
|
3512b73bc3 | ||
|
|
d0ac45cb08 | ||
|
|
ab3d950bd3 | ||
|
|
44ddd79356 | ||
|
|
054ddf661f | ||
|
|
da466a3bdd | ||
|
|
42f0c2d78f | ||
|
|
5853784a31 | ||
|
|
5b20295620 | ||
|
|
68e151c2a3 | ||
|
|
dd764a70a2 | ||
|
|
12defe0d12 | ||
|
|
8868dd5785 | ||
|
|
e787d585a4 | ||
|
|
5eb7518ce2 | ||
|
|
5be93da111 | ||
|
|
39a89b708e | ||
|
|
69ad4f1674 | ||
|
|
59ae55a0e2 | ||
|
|
6eb551332e | ||
|
|
96be1bb7a0 | ||
|
|
4dc3b6db93 | ||
|
|
a1da555b51 | ||
|
|
c1abc2eb3e | ||
|
|
f0acda0bf4 | ||
|
|
16212905de | ||
|
|
2eb5e91b62 | ||
|
|
fa6b67c8ac | ||
|
|
6a117bd9d1 | ||
|
|
714c709ad0 | ||
|
|
eafc230ff2 | ||
|
|
523a9aa2de | ||
|
|
ec92c29029 | ||
|
|
4a9d702bc1 | ||
|
|
f363ec1838 | ||
|
|
ffb3a95eb9 | ||
|
|
63094adef4 | ||
|
|
1f8dcf74c8 | ||
|
|
f20c549f66 | ||
|
|
b62cccda62 | ||
|
|
43f3b3c7ee | ||
|
|
a328464912 | ||
|
|
977ddd9e81 | ||
|
|
67717762f9 | ||
|
|
a33f6857fd | ||
|
|
5c7c192df1 | ||
|
|
0186e78221 | ||
|
|
e9e858348b | ||
|
|
631bb15b05 | ||
|
|
e7d6301c75 | ||
|
|
22001fbca3 | ||
|
|
22e3028d56 | ||
|
|
9498ce3f1e | ||
|
|
172c7b6f25 | ||
|
|
750ef67831 | ||
|
|
c19a39c4a5 | ||
|
|
d6bfeeaf1a | ||
|
|
e7ea20c073 | ||
|
|
a7e01b3f77 | ||
|
|
c3a541c55a | ||
|
|
41efdfd488 | ||
|
|
7d41d2a47c | ||
|
|
ec951c592a | ||
|
|
829fd431e2 | ||
|
|
9b7d25d1fd | ||
|
|
1ab3e82a4d | ||
|
|
b07102b821 | ||
|
|
3adf3917ee | ||
|
|
bbaf41e032 | ||
|
|
618c017249 | ||
|
|
3cf7c94995 | ||
|
|
2ca5d21147 | ||
|
|
266cc371f7 | ||
|
|
fba218cff4 | ||
|
|
f33ccb5ce3 | ||
|
|
8ca5438db9 | ||
|
|
8b6414ea7c | ||
|
|
1c09cd7816 | ||
|
|
07b47c8472 | ||
|
|
950a608e01 | ||
|
|
c7f08c7e53 | ||
|
|
4f3857c1ea | ||
|
|
60b4b1ad47 | ||
|
|
8262184873 | ||
|
|
e9abd8b022 | ||
|
|
fe29e0430c | ||
|
|
589967f3df | ||
|
|
9db275d300 | ||
|
|
b6d0a261d2 | ||
|
|
dad0daf389 | ||
|
|
b58e2b9312 | ||
|
|
b8f458a819 | ||
|
|
39321f4796 | ||
|
|
d8de738239 | ||
|
|
40407bb784 | ||
|
|
b7b1918a36 | ||
|
|
aa69d5ec99 | ||
|
|
6e4bb257a4 | ||
|
|
a30f439a9c | ||
|
|
0ccaab55ca | ||
|
|
e61621692c | ||
|
|
9a8cb2e22e | ||
|
|
eb487c2ca1 | ||
|
|
5c0a35a358 | ||
|
|
a468f81a5e | ||
|
|
d1b46d7347 |
No files matched your search
@@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Clobber for Python 3 migration
|
||||
Clobber for Idle GC backout
|
||||
@@ -43,8 +43,6 @@ gyp_vars.update({
|
||||
'arm_neon': 0,
|
||||
'arm_neon_optional': 1,
|
||||
|
||||
'moz_webrtc_omx': 0,
|
||||
'moz_webrtc_mediacodec': 0,
|
||||
|
||||
# Turn off multi monitor screen share
|
||||
'multi_monitor_screenshare%' : 0,
|
||||
|
||||
@@ -344,7 +344,7 @@ def split_triplet(triplet):
|
||||
if cpu.endswith('86') or (cpu.startswith('i') and '86' in cpu):
|
||||
canonical_cpu = 'x86'
|
||||
endianness = 'little'
|
||||
elif cpu in ('x86_64', 'ia64'):
|
||||
elif cpu == 'x86_64':
|
||||
canonical_cpu = cpu
|
||||
endianness = 'little'
|
||||
elif cpu in ('s390', 's390x'):
|
||||
|
||||
@@ -152,6 +152,7 @@ def old_configure_options(*options):
|
||||
'--enable-jitspew',
|
||||
'--enable-js-lto',
|
||||
'--enable-leak-sanitizer',
|
||||
'--enable-libaom',
|
||||
'--enable-libjpeg-turbo',
|
||||
'--enable-libproxy',
|
||||
'--enable-llvm-hacks',
|
||||
|
||||
@@ -61,6 +61,44 @@ set_config('HAVE_YASM', have_yasm)
|
||||
# Until the YASM variable is not necessary in old-configure.
|
||||
add_old_configure_assignment('YASM', have_yasm)
|
||||
|
||||
# nasm detection
|
||||
# ==============================================================
|
||||
nasm = check_prog('NASM', ['nasm'], allow_missing=True)
|
||||
|
||||
@depends_if(nasm)
|
||||
@checking('nasm version')
|
||||
def nasm_version(nasm):
|
||||
version = check_cmd_output(
|
||||
nasm, '-v',
|
||||
onerror=lambda: die('Failed to get nasm version.')
|
||||
).splitlines()[0].split()[2]
|
||||
return Version(version)
|
||||
|
||||
@depends(nasm, nasm_version)
|
||||
def have_nasm(nasm, version):
|
||||
if nasm and version >= Version('2.14'):
|
||||
return True
|
||||
|
||||
set_config('HAVE_NASM', have_nasm)
|
||||
|
||||
@depends(have_nasm, target)
|
||||
def nasm_asflags(have_nasm, target):
|
||||
if have_nasm:
|
||||
asflags = {
|
||||
('OSX', 'x86'): '-f macho32',
|
||||
('OSX', 'x86_64'): '-f macho64',
|
||||
('WINNT', 'x86'): '-f win32',
|
||||
('WINNT', 'x86_64'): '-f win64',
|
||||
}.get((target.os, target.cpu), None)
|
||||
if asflags is None:
|
||||
if target.cpu == 'x86':
|
||||
asflags = '-f elf32'
|
||||
elif target.cpu == 'x86_64':
|
||||
asflags = '-f elf64'
|
||||
return asflags
|
||||
|
||||
set_config('NASM_ASFLAGS', nasm_asflags)
|
||||
|
||||
# MacOS deployment target version
|
||||
# ==============================================================
|
||||
# This needs to happen before any compilation test is done.
|
||||
|
||||
@@ -290,11 +290,6 @@ fi
|
||||
if [ -n "$LD_LIBRARY_PATH_64" ]; then
|
||||
LD_LIBRARY_PATH_64=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH_64:+":$LD_LIBRARY_PATH_64"}
|
||||
fi
|
||||
#
|
||||
#
|
||||
## Set SHLIB_PATH for HPUX
|
||||
SHLIB_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${SHLIB_PATH:+":$SHLIB_PATH"}
|
||||
#
|
||||
## Set LIBPATH for AIX
|
||||
LIBPATH=${MOZ_DIST_BIN}:${MRE_HOME}${LIBPATH:+":$LIBPATH"}
|
||||
#
|
||||
@@ -339,7 +334,6 @@ then
|
||||
fi
|
||||
echo "DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH"
|
||||
echo " LIBRARY_PATH=$LIBRARY_PATH"
|
||||
echo " SHLIB_PATH=$SHLIB_PATH"
|
||||
echo " LIBPATH=$LIBPATH"
|
||||
echo " ADDON_PATH=$ADDON_PATH"
|
||||
echo " MOZ_PROGRAM=$MOZ_PROGRAM"
|
||||
@@ -350,7 +344,7 @@ then
|
||||
fi
|
||||
#
|
||||
export MOZILLA_FIVE_HOME LD_LIBRARY_PATH
|
||||
export SHLIB_PATH LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH
|
||||
export LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH
|
||||
|
||||
if [ $moz_debug -eq 1 ]
|
||||
then
|
||||
|
||||
Vendored
+4
-1
@@ -29,7 +29,10 @@ if CONFIG['MOZ_WEBM_ENCODER']:
|
||||
external_dirs += ['media/libvpx']
|
||||
|
||||
if CONFIG['MOZ_AV1']:
|
||||
external_dirs += ['media/libaom']
|
||||
if CONFIG['AV1_USE_AOM']:
|
||||
external_dirs += ['media/libaom']
|
||||
else:
|
||||
external_dirs += ['media/libdav1d']
|
||||
|
||||
external_dirs += ['media/libpng']
|
||||
|
||||
|
||||
@@ -448,7 +448,6 @@ gtk/gtkx.h
|
||||
gtk/gtkunixprint.h
|
||||
HIToolbox/HIToolbox.h
|
||||
hlink.h
|
||||
ia64/sys/inline.h
|
||||
Icons.h
|
||||
iconv.h
|
||||
ieeefp.h
|
||||
|
||||
@@ -1004,6 +1004,7 @@ GK_ATOM(parameter, "parameter")
|
||||
GK_ATOM(parent, "parent")
|
||||
GK_ATOM(parentfocused, "parentfocused")
|
||||
GK_ATOM(parsetype, "parsetype")
|
||||
GK_ATOM(part, "part")
|
||||
GK_ATOM(password, "password")
|
||||
GK_ATOM(pattern, "pattern")
|
||||
GK_ATOM(patternSeparator, "pattern-separator")
|
||||
|
||||
@@ -26,7 +26,6 @@ support-files =
|
||||
image_yellow75.png
|
||||
imagebitmap_bug1239300.js
|
||||
imagebitmap_bug1239752.js
|
||||
imagebitmap_extensions.html
|
||||
imagebitmap_on_worker.js
|
||||
imagebitmap_structuredclone.js
|
||||
imagebitmap_structuredclone_iframe.html
|
||||
|
||||
@@ -63,6 +63,7 @@ EncodingUtils::IsAsciiCompatible(const nsACString& aPreferredName)
|
||||
aPreferredName.LowerCaseEqualsLiteral("utf-16be") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("utf-16le") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("replacement") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("iso-2022-jp") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("hz-gb-2312") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("utf-7") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("x-imap4-modified-utf7"));
|
||||
|
||||
@@ -652,7 +652,7 @@ nsresult nsGeolocationService::Init()
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
|
||||
if (Preferences::GetBool("geo.provider.use_corelocation", true)) {
|
||||
mProvider = new CoreLocationLocationProvider();
|
||||
}
|
||||
|
||||
@@ -105,8 +105,6 @@ gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
|
||||
* Given an array of input channels, downmix to aOutputChannelCount, and copy
|
||||
* the results to the channel buffers in aOutputChannels. Don't call this with
|
||||
* input count <= output count.
|
||||
* Each of aOutputChannels must be non-null.
|
||||
* aChannelArray elements may be null.
|
||||
*/
|
||||
template<typename T>
|
||||
void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
@@ -121,11 +119,7 @@ void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
if (inputChannelCount > 6) {
|
||||
// Just drop the unknown channels.
|
||||
for (uint32_t o = 0; o < aOutputChannelCount; ++o) {
|
||||
if (inputChannels[o]) {
|
||||
PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
|
||||
} else {
|
||||
std::fill_n(aOutputChannels[o], aDuration, static_cast<T>(0));
|
||||
}
|
||||
PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -144,19 +138,13 @@ void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
// want an input channel to contribute to nothing
|
||||
T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1] = {0};
|
||||
for (uint32_t c = 0; c < inputChannelCount; ++c) {
|
||||
if (inputChannels[c]) {
|
||||
outputChannels[m.mInputDestination[c]] +=
|
||||
m.mInputCoefficient[c] *
|
||||
(static_cast<const T*>(inputChannels[c]))[s];
|
||||
}
|
||||
outputChannels[m.mInputDestination[c]] +=
|
||||
m.mInputCoefficient[c]*(static_cast<const T*>(inputChannels[c]))[s];
|
||||
}
|
||||
// Utilize the fact that in every layout, C is the third channel.
|
||||
if (m.mCExtraDestination != IGNORE) {
|
||||
if (inputChannels[SURROUND_C]) {
|
||||
outputChannels[m.mCExtraDestination] +=
|
||||
m.mInputCoefficient[SURROUND_C] *
|
||||
(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
|
||||
}
|
||||
outputChannels[m.mCExtraDestination] +=
|
||||
m.mInputCoefficient[SURROUND_C]*(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
|
||||
}
|
||||
|
||||
for (uint32_t c = 0; c < aOutputChannelCount; ++c) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <speex/speex_resampler.h>
|
||||
#include <string.h>
|
||||
#include <cmath>
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
/*
|
||||
* Parts derived from MythTV AudioConvert Class
|
||||
@@ -262,8 +263,15 @@ AudioConverter::ResampleAudio(void* aOut, const void* aIn, size_t aFrames)
|
||||
if (!mResampler) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t outframes = ResampleRecipientFrames(aFrames);
|
||||
uint32_t inframes = aFrames;
|
||||
uint32_t outframes;
|
||||
if (!ResampleRecipientFrames(aFrames, &outframes)) {
|
||||
return 0;
|
||||
}
|
||||
CheckedUint32 inframesChecked(aFrames);
|
||||
if (!inframesChecked.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t inframes = inframesChecked.value();
|
||||
|
||||
int error;
|
||||
if (mOut.Format() == AudioConfig::FORMAT_FLT) {
|
||||
@@ -288,7 +296,7 @@ AudioConverter::ResampleAudio(void* aOut, const void* aIn, size_t aFrames)
|
||||
mResampler = nullptr;
|
||||
return 0;
|
||||
}
|
||||
MOZ_ASSERT(inframes == aFrames, "Some frames will be dropped");
|
||||
MOZ_ASSERT(static_cast<size_t>(inframes) == aFrames, "Some frames will be dropped");
|
||||
return outframes;
|
||||
}
|
||||
|
||||
@@ -373,17 +381,27 @@ AudioConverter::UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const
|
||||
return aFrames;
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioConverter::ResampleRecipientFrames(size_t aFrames) const
|
||||
{
|
||||
bool AudioConverter::ResampleRecipientFrames(size_t aFrames,
|
||||
uint32_t* aOutFrames) const {
|
||||
if (!aFrames && mIn.Rate() != mOut.Rate()) {
|
||||
if (!mResampler) {
|
||||
return 0;
|
||||
*aOutFrames = 0;
|
||||
return true;
|
||||
}
|
||||
// We drain by pushing in get_input_latency() samples of 0
|
||||
aFrames = speex_resampler_get_input_latency(mResampler);
|
||||
}
|
||||
return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1;
|
||||
CheckedInt<uint64_t> numerator = CheckedInt<uint64_t>(aFrames) * mOut.Rate();
|
||||
if (!numerator.isValid()) {
|
||||
return false;
|
||||
}
|
||||
CheckedUint32 outFrames(numerator.value() / mIn.Rate());
|
||||
outFrames += 1u;
|
||||
if (!outFrames.isValid()) {
|
||||
return false;
|
||||
}
|
||||
*aOutFrames = outFrames.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define AudioConverter_h
|
||||
|
||||
#include "MediaInfo.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
// Forward declaration
|
||||
typedef struct SpeexResamplerState_ SpeexResamplerState;
|
||||
@@ -163,9 +164,15 @@ public:
|
||||
AlignedBuffer<Value>* outputBuffer = &temp1;
|
||||
AlignedBuffer<Value> temp2;
|
||||
if (!frames || mOut.Rate() > mIn.Rate()) {
|
||||
uint32_t resampledFrames;
|
||||
// We are upsampling or about to drain, we can't work in place.
|
||||
// Allocate another temporary buffer where the upsampling will occur.
|
||||
if (!temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) {
|
||||
if (!ResampleRecipientFrames(frames, &resampledFrames)) {
|
||||
return AudioDataBuffer<Format, Value>(std::move(temp2));
|
||||
}
|
||||
CheckedInt<size_t> outputSamples =
|
||||
CheckedInt<size_t>(resampledFrames) * mOut.Channels();
|
||||
if (!outputSamples.isValid() || !temp2.SetLength(outputSamples.value())) {
|
||||
return AudioDataBuffer<Format, Value>(Move(temp2));
|
||||
}
|
||||
outputBuffer = &temp2;
|
||||
@@ -229,7 +236,7 @@ private:
|
||||
// Resampler context.
|
||||
SpeexResamplerState* mResampler;
|
||||
size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames);
|
||||
size_t ResampleRecipientFrames(size_t aFrames) const;
|
||||
bool ResampleRecipientFrames(size_t aFrames, uint32_t* aOutFrames) const;
|
||||
void RecreateResampler();
|
||||
size_t DrainResampler(void* aOut);
|
||||
};
|
||||
|
||||
@@ -121,19 +121,13 @@ AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
|
||||
}
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, static_cast<const AudioDataValue*>(nullptr));
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<AudioDataValue>());
|
||||
for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
|
||||
AudioDataValue* ptr =
|
||||
PointerForOffsetInChannel(buf.Elements(), outBufferLength,
|
||||
aOutputChannels, channel, offsetSamples);
|
||||
if (channelData[channel]) {
|
||||
PodCopy(
|
||||
ptr,
|
||||
reinterpret_cast<const AudioDataValue*>(channelData[channel]),
|
||||
frames);
|
||||
} else {
|
||||
PodZero(ptr, frames);
|
||||
}
|
||||
PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
|
||||
frames);
|
||||
}
|
||||
MOZ_ASSERT(channelData.Length() == aOutputChannels);
|
||||
} else if (channelData.Length() > aOutputChannels) {
|
||||
|
||||
@@ -61,22 +61,15 @@ template <typename SrcT, typename DestT>
|
||||
static void
|
||||
InterleaveAndConvertBuffer(const SrcT* const* aSourceChannels,
|
||||
uint32_t aLength, float aVolume,
|
||||
uint32_t aChannelCount,
|
||||
uint32_t aChannels,
|
||||
DestT* aOutput)
|
||||
{
|
||||
for (size_t channel = 0; channel < aChannelCount; ++channel) {
|
||||
DestT* output = aOutput;
|
||||
if (aSourceChannels[channel]) {
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
output += aChannelCount;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
*output = static_cast<DestT>(0);
|
||||
output += aChannelCount;
|
||||
}
|
||||
DestT* output = aOutput;
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
for (size_t channel = 0; channel < aChannels; ++channel) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
++output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +93,7 @@ DeinterleaveAndConvertBuffer(const SrcT* aSourceBuffer,
|
||||
class SilentChannel
|
||||
{
|
||||
public:
|
||||
static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
|
||||
static const int AUDIO_PROCESSING_FRAMES = 4096; /* > 10ms of 48KHz audio */
|
||||
static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES];
|
||||
// We take advantage of the fact that zero in float and zero in int have the
|
||||
// same all-zeros bit layout.
|
||||
@@ -415,7 +408,7 @@ void WriteChunk(AudioChunk& aChunk,
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix. Note that this might actually make channelData have more
|
||||
// than aOutputChannels temporarily.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, static_cast<const SrcT*>(nullptr));
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<SrcT>());
|
||||
}
|
||||
if (channelData.Length() > aOutputChannels) {
|
||||
// Down-mix.
|
||||
|
||||
@@ -111,7 +111,7 @@ private:
|
||||
DECL_MEDIA_PREF("media.ffvpx.enabled", PDMFFVPXEnabled, bool, true);
|
||||
#endif
|
||||
#ifdef MOZ_AV1
|
||||
DECL_MEDIA_PREF("media.av1.enabled", AV1Enabled, bool, false);
|
||||
DECL_MEDIA_PREF("media.av1.enabled", AV1Enabled, bool, true);
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
DECL_MEDIA_PREF("media.wmf.enabled", PDMWMFEnabled, bool, true);
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
#include "TheoraDecoder.h"
|
||||
|
||||
#ifdef MOZ_AV1
|
||||
#ifdef AV1_USE_AOM
|
||||
#include "AOMDecoder.h"
|
||||
#else
|
||||
#include "Dav1dDecoder.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
@@ -30,7 +34,11 @@ AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType,
|
||||
TheoraDecoder::IsTheora(aMimeType);
|
||||
#ifdef MOZ_AV1
|
||||
if (MediaPrefs::AV1Enabled()) {
|
||||
#ifdef AV1_USE_AOM
|
||||
supports |= AOMDecoder::IsAV1(aMimeType);
|
||||
#else
|
||||
supports |= Dav1dDecoder::IsAV1(aMimeType);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
MOZ_LOG(sPDMLog, LogLevel::Debug, ("Agnostic decoder %s requested type",
|
||||
@@ -47,10 +55,17 @@ AgnosticDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
|
||||
m = new VPXDecoder(aParams);
|
||||
}
|
||||
#ifdef MOZ_AV1
|
||||
#ifdef AV1_USE_AOM
|
||||
else if (AOMDecoder::IsAV1(aParams.mConfig.mMimeType) &&
|
||||
MediaPrefs::AV1Enabled()) {
|
||||
m = new AOMDecoder(aParams);
|
||||
}
|
||||
#else
|
||||
else if (Dav1dDecoder::IsAV1(aParams.mConfig.mMimeType) &&
|
||||
MediaPrefs::AV1Enabled()) {
|
||||
m = new Dav1dDecoder(aParams);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
else if (TheoraDecoder::IsTheora(aParams.mConfig.mMimeType)) {
|
||||
m = new TheoraDecoder(aParams);
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/* -*- 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 "Dav1dDecoder.h"
|
||||
#include "MediaResult.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "dav1d/dav1d.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "nsError.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef LOG
|
||||
#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("Dav1dDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
using namespace layers;
|
||||
|
||||
Dav1dDecoder::Dav1dDecoder(const CreateDecoderParams& aParams)
|
||||
: mImageContainer(aParams.mImageContainer)
|
||||
, mTaskQueue(aParams.mTaskQueue)
|
||||
, mCallback(aParams.mCallback)
|
||||
, mIsFlushing(false)
|
||||
, mDecoder(nullptr)
|
||||
, mInfo(aParams.VideoConfig())
|
||||
{
|
||||
}
|
||||
|
||||
Dav1dDecoder::~Dav1dDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Shutdown()
|
||||
{
|
||||
if (mDecoder) {
|
||||
dav1d_close(&mDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::InitPromise>
|
||||
Dav1dDecoder::Init()
|
||||
{
|
||||
int decodeThreads = 2;
|
||||
if (mInfo.mDisplay.width >= 2048) {
|
||||
decodeThreads = 8;
|
||||
} else if (mInfo.mDisplay.width >= 1024) {
|
||||
decodeThreads = 4;
|
||||
}
|
||||
decodeThreads = std::min(decodeThreads, PR_GetNumberOfProcessors());
|
||||
|
||||
Dav1dSettings settings;
|
||||
dav1d_default_settings(&settings);
|
||||
settings.n_threads = decodeThreads;
|
||||
settings.logger.callback = nullptr;
|
||||
|
||||
if (dav1d_open(&mDecoder, &settings) < 0) {
|
||||
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
||||
}
|
||||
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mIsFlushing = true;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () {
|
||||
if (mDecoder) {
|
||||
dav1d_flush(mDecoder);
|
||||
}
|
||||
});
|
||||
SyncRunnable::DispatchToThread(mTaskQueue, r);
|
||||
mIsFlushing = false;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPlaneSize(int aWidth, int aHeight, size_t* aSize)
|
||||
{
|
||||
CheckedInt<size_t> size = aWidth;
|
||||
size *= aHeight;
|
||||
if (!size.isValid()) {
|
||||
return false;
|
||||
}
|
||||
*aSize = size.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
DownshiftPlane(uint8_t* aDst, int aDstStride, const uint8_t* aSrc,
|
||||
ptrdiff_t aSrcStride, int aWidth, int aHeight, int aShift)
|
||||
{
|
||||
for (int y = 0; y < aHeight; y++) {
|
||||
const uint16_t* src =
|
||||
reinterpret_cast<const uint16_t*>(aSrc + y * aSrcStride);
|
||||
uint8_t* dst = aDst + y * aDstStride;
|
||||
for (int x = 0; x < aWidth; x++) {
|
||||
dst[x] = (src[x] >> aShift) & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MediaResult
|
||||
Dav1dDecoder::OutputPicture(const Dav1dPicture& aPicture)
|
||||
{
|
||||
const int width = aPicture.p.w;
|
||||
const int height = aPicture.p.h;
|
||||
if (width <= 0 || height <= 0) {
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d returned invalid AV1 picture size"));
|
||||
}
|
||||
|
||||
int chromaWidth = width;
|
||||
int chromaHeight = height;
|
||||
switch (aPicture.p.layout) {
|
||||
case DAV1D_PIXEL_LAYOUT_I420:
|
||||
chromaWidth = (width + 1) >> 1;
|
||||
chromaHeight = (height + 1) >> 1;
|
||||
break;
|
||||
case DAV1D_PIXEL_LAYOUT_I422:
|
||||
chromaWidth = (width + 1) >> 1;
|
||||
chromaHeight = height;
|
||||
break;
|
||||
case DAV1D_PIXEL_LAYOUT_I444:
|
||||
break;
|
||||
default:
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d returned unsupported AV1 pixel layout: %d",
|
||||
int(aPicture.p.layout)));
|
||||
}
|
||||
|
||||
const int planeWidths[3] = { width, chromaWidth, chromaWidth };
|
||||
const int planeHeights[3] = { height, chromaHeight, chromaHeight };
|
||||
const ptrdiff_t srcStrides[3] = {
|
||||
aPicture.stride[0],
|
||||
aPicture.stride[1],
|
||||
aPicture.stride[1]
|
||||
};
|
||||
uint8_t* planeData[3] = {
|
||||
static_cast<uint8_t*>(aPicture.data[0]),
|
||||
static_cast<uint8_t*>(aPicture.data[1]),
|
||||
static_cast<uint8_t*>(aPicture.data[2])
|
||||
};
|
||||
uint32_t planeStrides[3] = { 0, 0, 0 };
|
||||
UniquePtr<uint8_t[]> downshifted[3];
|
||||
|
||||
if (aPicture.p.bpc > 8) {
|
||||
const int shift = aPicture.p.bpc - 8;
|
||||
for (int plane = 0; plane < 3; plane++) {
|
||||
size_t planeSize;
|
||||
if (!GetPlaneSize(planeWidths[plane], planeHeights[plane], &planeSize)) {
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("AV1 downshift plane size overflow"));
|
||||
}
|
||||
downshifted[plane] = MakeUniqueFallible<uint8_t[]>(planeSize);
|
||||
if (!downshifted[plane]) {
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("Couldn't allocate AV1 conversion buffer"));
|
||||
}
|
||||
DownshiftPlane(downshifted[plane].get(), planeWidths[plane],
|
||||
planeData[plane], srcStrides[plane],
|
||||
planeWidths[plane], planeHeights[plane], shift);
|
||||
planeData[plane] = downshifted[plane].get();
|
||||
planeStrides[plane] = planeWidths[plane];
|
||||
}
|
||||
} else {
|
||||
for (int plane = 0; plane < 3; plane++) {
|
||||
CheckedInt<uint32_t> stride(srcStrides[plane]);
|
||||
if (srcStrides[plane] < 0 || !stride.isValid()) {
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d returned invalid AV1 stride"));
|
||||
}
|
||||
planeStrides[plane] = stride.value();
|
||||
}
|
||||
}
|
||||
|
||||
VideoData::YCbCrBuffer b;
|
||||
for (int plane = 0; plane < 3; plane++) {
|
||||
b.mPlanes[plane].mData = planeData[plane];
|
||||
b.mPlanes[plane].mStride = planeStrides[plane];
|
||||
b.mPlanes[plane].mHeight = planeHeights[plane];
|
||||
b.mPlanes[plane].mWidth = planeWidths[plane];
|
||||
b.mPlanes[plane].mOffset = 0;
|
||||
b.mPlanes[plane].mSkip = 0;
|
||||
}
|
||||
|
||||
if (aPicture.seq_hdr) {
|
||||
switch (aPicture.seq_hdr->mtrx) {
|
||||
case DAV1D_MC_BT601:
|
||||
case DAV1D_MC_BT470BG:
|
||||
b.mYUVColorSpace = YUVColorSpace::BT601;
|
||||
break;
|
||||
case DAV1D_MC_BT709:
|
||||
b.mYUVColorSpace = YUVColorSpace::BT709;
|
||||
break;
|
||||
case DAV1D_MC_IDENTITY:
|
||||
b.mYUVColorSpace = YUVColorSpace::IDENTITY;
|
||||
break;
|
||||
default:
|
||||
LOG("Unhandled colorspace %d", aPicture.seq_hdr->mtrx);
|
||||
break;
|
||||
}
|
||||
b.mColorRange = aPicture.seq_hdr->color_range ? ColorRange::FULL
|
||||
: ColorRange::LIMITED;
|
||||
}
|
||||
|
||||
const int64_t time =
|
||||
aPicture.m.timestamp == INT64_MIN ? 0 : aPicture.m.timestamp;
|
||||
const int64_t duration = aPicture.m.duration;
|
||||
const int64_t offset = aPicture.m.offset;
|
||||
const bool keyframe =
|
||||
aPicture.frame_hdr &&
|
||||
aPicture.frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY;
|
||||
|
||||
RefPtr<VideoData> v =
|
||||
VideoData::CreateAndCopyData(mInfo,
|
||||
mImageContainer,
|
||||
offset,
|
||||
time,
|
||||
duration,
|
||||
b,
|
||||
keyframe,
|
||||
time,
|
||||
mInfo.ScaledImageRect(width, height));
|
||||
|
||||
if (!v) {
|
||||
LOG("Image allocation error source %dx%d display %ux%u picture %ux%u",
|
||||
width, height, mInfo.mDisplay.width, mInfo.mDisplay.height,
|
||||
mInfo.mImage.width, mInfo.mImage.height);
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||
}
|
||||
mCallback->Output(v);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaResult
|
||||
Dav1dDecoder::DrainOutput()
|
||||
{
|
||||
while (true) {
|
||||
Dav1dPicture picture;
|
||||
PodZero(&picture);
|
||||
const int res = dav1d_get_picture(mDecoder, &picture);
|
||||
if (res == DAV1D_ERR(EAGAIN)) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (res < 0) {
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d error getting AV1 picture: %d",
|
||||
res));
|
||||
}
|
||||
|
||||
MediaResult rv = OutputPicture(picture);
|
||||
dav1d_picture_unref(&picture);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MediaResult
|
||||
Dav1dDecoder::DoDecode(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
Dav1dData data;
|
||||
PodZero(&data);
|
||||
uint8_t* dst = dav1d_data_create(&data, aSample->Size());
|
||||
if (!dst) {
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("Couldn't allocate dav1d AV1 input buffer"));
|
||||
}
|
||||
memcpy(dst, aSample->Data(), aSample->Size());
|
||||
data.m.timestamp = aSample->mTime;
|
||||
data.m.duration = aSample->mDuration;
|
||||
data.m.offset = aSample->mOffset;
|
||||
data.m.size = aSample->Size();
|
||||
|
||||
while (data.sz) {
|
||||
const int res = dav1d_send_data(mDecoder, &data);
|
||||
if (res == DAV1D_ERR(EAGAIN)) {
|
||||
MediaResult rv = DrainOutput();
|
||||
if (NS_FAILED(rv)) {
|
||||
dav1d_data_unref(&data);
|
||||
return rv;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (res < 0) {
|
||||
dav1d_data_unref(&data);
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d error decoding AV1 sample: %d",
|
||||
res));
|
||||
}
|
||||
}
|
||||
|
||||
dav1d_data_unref(&data);
|
||||
return DrainOutput();
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::ProcessDecode(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
if (mIsFlushing) {
|
||||
return;
|
||||
}
|
||||
MediaResult rv = DoDecode(aSample);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error(rv);
|
||||
} else {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
|
||||
this, &Dav1dDecoder::ProcessDecode, aSample));
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::ProcessDrain()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
MediaResult rv = DrainOutput();
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error(rv);
|
||||
} else {
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mTaskQueue->Dispatch(NewRunnableMethod(this, &Dav1dDecoder::ProcessDrain));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
Dav1dDecoder::IsAV1(const nsACString& aMimeType)
|
||||
{
|
||||
return aMimeType.EqualsLiteral("video/webm; codecs=av1") ||
|
||||
aMimeType.EqualsLiteral("video/av1");
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIntSize
|
||||
Dav1dDecoder::GetFrameSize(Span<const uint8_t> aBuffer)
|
||||
{
|
||||
Dav1dSequenceHeader seqHdr;
|
||||
PodZero(&seqHdr);
|
||||
if (dav1d_parse_sequence_header(&seqHdr,
|
||||
aBuffer.Elements(),
|
||||
aBuffer.Length()) < 0) {
|
||||
return nsIntSize(0, 0);
|
||||
}
|
||||
return nsIntSize(seqHdr.max_width, seqHdr.max_height);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
#undef LOG
|
||||
@@ -0,0 +1,61 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#if !defined(Dav1dDecoder_h_)
|
||||
#define Dav1dDecoder_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/Span.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct Dav1dContext Dav1dContext;
|
||||
typedef struct Dav1dPicture Dav1dPicture;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class Dav1dDecoder : public MediaDataDecoder
|
||||
{
|
||||
public:
|
||||
explicit Dav1dDecoder(const CreateDecoderParams& aParams);
|
||||
|
||||
RefPtr<InitPromise> Init() override;
|
||||
void Input(MediaRawData* aSample) override;
|
||||
void Flush() override;
|
||||
void Drain() override;
|
||||
void Shutdown() override;
|
||||
const char* GetDescriptionName() const override
|
||||
{
|
||||
return "dav1d (AV1) video decoder";
|
||||
}
|
||||
|
||||
// Return true if aMimeType is a one of the strings used
|
||||
// by our demuxers to identify AV1 streams.
|
||||
static bool IsAV1(const nsACString& aMimeType);
|
||||
|
||||
// Return the frame dimensions from a sequence header, when one is present.
|
||||
static nsIntSize GetFrameSize(Span<const uint8_t> aBuffer);
|
||||
|
||||
private:
|
||||
~Dav1dDecoder();
|
||||
void ProcessDecode(MediaRawData* aSample);
|
||||
MediaResult DoDecode(MediaRawData* aSample);
|
||||
MediaResult DrainOutput();
|
||||
MediaResult OutputPicture(const Dav1dPicture& aPicture);
|
||||
void ProcessDrain();
|
||||
|
||||
const RefPtr<layers::ImageContainer> mImageContainer;
|
||||
const RefPtr<TaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
Atomic<bool> mIsFlushing;
|
||||
|
||||
Dav1dContext* mDecoder;
|
||||
|
||||
const VideoInfo& mInfo;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // Dav1dDecoder_h_
|
||||
@@ -55,12 +55,20 @@ if CONFIG['MOZ_FFMPEG']:
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_AV1']:
|
||||
EXPORTS += [
|
||||
'agnostic/AOMDecoder.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'agnostic/AOMDecoder.cpp',
|
||||
]
|
||||
if CONFIG['AV1_USE_AOM']:
|
||||
EXPORTS += [
|
||||
'agnostic/AOMDecoder.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'agnostic/AOMDecoder.cpp',
|
||||
]
|
||||
else:
|
||||
EXPORTS += [
|
||||
'agnostic/Dav1dDecoder.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'agnostic/Dav1dDecoder.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_APPLEMEDIA']:
|
||||
EXPORTS += [
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef MOZ_AV1
|
||||
#if defined(MOZ_AV1) && defined(AV1_USE_AOM)
|
||||
#include "AOMDecoder.h"
|
||||
#endif
|
||||
#include "MediaPrefs.h"
|
||||
@@ -136,4 +136,3 @@ WebMDecoder::GetMozDebugReaderData(nsAString& aString)
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
+1297
-1280
File diff suppressed because it is too large.
Load diff
@@ -24,6 +24,11 @@ if CONFIG['MOZ_WEBM_ENCODER']:
|
||||
'WebMWriter.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_AV1']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/media/platforms/agnostic',
|
||||
]
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_LIBVPX_CFLAGS']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
@@ -29,8 +29,7 @@ enum {
|
||||
* Abstract interface for managing audio and video devices. Each platform
|
||||
* must implement a concrete class that will map these classes and methods
|
||||
* to the appropriate backend. For example, on Desktop platforms, these will
|
||||
* correspond to equivalent webrtc (GIPS) calls, and on B2G they will map to
|
||||
* a Gonk interface.
|
||||
* correspond to equivalent webrtc (GIPS) calls.
|
||||
*/
|
||||
class MediaEngineVideoSource;
|
||||
class MediaEngineAudioSource;
|
||||
@@ -302,7 +301,6 @@ public:
|
||||
|
||||
/* If implementation of MediaEngineSource supports TakePhoto(), the picture
|
||||
* should be return via aCallback object. Otherwise, it returns NS_ERROR_NOT_IMPLEMENTED.
|
||||
* Currently, only Gonk MediaEngineSource implementation supports it.
|
||||
*/
|
||||
virtual nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) = 0;
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ bool MediaEngineCameraVideoSource::AppendToTrack(SourceMediaStream* aSource,
|
||||
return aSource->AppendToTrack(aID, &(segment));
|
||||
}
|
||||
|
||||
// Sub-classes (B2G or desktop) should overload one of both of these two methods
|
||||
// to provide capabilities
|
||||
// Subclasses should overload one or both of these methods to provide
|
||||
// capabilities.
|
||||
size_t
|
||||
MediaEngineCameraVideoSource::NumCapabilities() const
|
||||
{
|
||||
@@ -324,21 +324,6 @@ MediaEngineCameraVideoSource::SetName(nsString aName)
|
||||
bool hasFacingMode = false;
|
||||
VideoFacingModeEnum facingMode = VideoFacingModeEnum::User;
|
||||
|
||||
// Set facing mode based on device name.
|
||||
#if defined(ANDROID)
|
||||
// Names are generated. Example: "Camera 0, Facing back, Orientation 90"
|
||||
//
|
||||
// See media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/
|
||||
// webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
|
||||
|
||||
if (aName.Find(NS_LITERAL_STRING("Facing back")) != kNotFound) {
|
||||
hasFacingMode = true;
|
||||
facingMode = VideoFacingModeEnum::Environment;
|
||||
} else if (aName.Find(NS_LITERAL_STRING("Facing front")) != kNotFound) {
|
||||
hasFacingMode = true;
|
||||
facingMode = VideoFacingModeEnum::User;
|
||||
}
|
||||
#endif // ANDROID
|
||||
#ifdef XP_MACOSX
|
||||
// Kludge to test user-facing cameras on OSX.
|
||||
if (aName.Find(NS_LITERAL_STRING("Face")) != -1) {
|
||||
|
||||
@@ -108,7 +108,7 @@ protected:
|
||||
nsTArray<PrincipalHandle> mPrincipalHandles; // Directly mapped to mSources.
|
||||
RefPtr<layers::Image> mImage;
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
int mWidth, mHeight; // protected with mMonitor on Gonk due to different threading
|
||||
int mWidth, mHeight; // protected with mMonitor
|
||||
// end of data protected by mMonitor
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#ifdef FF
|
||||
#undef FF
|
||||
#endif
|
||||
#include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
|
||||
#include "SingleRwFifo.h"
|
||||
|
||||
#define CHANNELS 1
|
||||
#define ENCODING "L16"
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "SingleRwFifo.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static int
|
||||
UpdatePos(int pos, int capacity)
|
||||
{
|
||||
return (pos + 1) % capacity;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace subtle {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
inline void
|
||||
MemoryBarrier()
|
||||
{
|
||||
::MemoryBarrier();
|
||||
}
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
inline void
|
||||
MemoryBarrier()
|
||||
{
|
||||
__sync_synchronize();
|
||||
}
|
||||
#else
|
||||
#error Add an implementation of MemoryBarrier() for this platform!
|
||||
#endif
|
||||
|
||||
} // namespace subtle
|
||||
|
||||
SingleRwFifo::SingleRwFifo(int capacity)
|
||||
: capacity_(capacity)
|
||||
, size_(0)
|
||||
, read_pos_(0)
|
||||
, write_pos_(0)
|
||||
{
|
||||
queue_.reset(new int8_t*[capacity_]);
|
||||
}
|
||||
|
||||
SingleRwFifo::~SingleRwFifo()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SingleRwFifo::Push(int8_t* mem)
|
||||
{
|
||||
assert(mem);
|
||||
|
||||
// Ensure that there is space for the new data in the FIFO. There is only one
|
||||
// writer, so the other thread is guaranteed only to decrease the size.
|
||||
const int free_slots = capacity() - size();
|
||||
if (free_slots <= 0) {
|
||||
// Size can be queried outside of Push. The caller is expected to ensure
|
||||
// that Push will be successful before calling it.
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
queue_[write_pos_] = mem;
|
||||
// Ensure that |size_| is updated after the stored pointer is visible.
|
||||
subtle::MemoryBarrier();
|
||||
++size_;
|
||||
write_pos_ = UpdatePos(write_pos_, capacity());
|
||||
}
|
||||
|
||||
int8_t*
|
||||
SingleRwFifo::Pop()
|
||||
{
|
||||
int8_t* ret_val = nullptr;
|
||||
if (size() <= 0) {
|
||||
// Size can be queried outside of Pop. The caller is expected to ensure
|
||||
// that Pop will be successful before calling it.
|
||||
assert(false);
|
||||
return ret_val;
|
||||
}
|
||||
ret_val = queue_[read_pos_];
|
||||
// Ensure that |size_| is updated after the stored pointer is read.
|
||||
subtle::MemoryBarrier();
|
||||
--size_;
|
||||
read_pos_ = UpdatePos(read_pos_, capacity());
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
+5
-8
@@ -8,8 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_DEVICE_ANDROID_SINGLE_RW_FIFO_H_
|
||||
#define WEBRTC_MODULES_AUDIO_DEVICE_ANDROID_SINGLE_RW_FIFO_H_
|
||||
#ifndef DOM_MEDIA_WEBRTC_SINGLE_RW_FIFO_H_
|
||||
#define DOM_MEDIA_WEBRTC_SINGLE_RW_FIFO_H_
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/system_wrappers/interface/atomic32.h"
|
||||
@@ -17,10 +17,9 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Implements a lock-free FIFO losely based on
|
||||
// Implements a lock-free FIFO loosely based on
|
||||
// http://src.chromium.org/viewvc/chrome/trunk/src/media/base/audio_fifo.cc
|
||||
// Note that this class assumes there is one producer (writer) and one
|
||||
// consumer (reader) thread.
|
||||
// This class assumes there is one producer and one consumer thread.
|
||||
class SingleRwFifo {
|
||||
public:
|
||||
explicit SingleRwFifo(int capacity);
|
||||
@@ -29,8 +28,6 @@ class SingleRwFifo {
|
||||
void Push(int8_t* mem);
|
||||
int8_t* Pop();
|
||||
|
||||
void Clear();
|
||||
|
||||
int size() { return size_.Value(); }
|
||||
int capacity() const { return capacity_; }
|
||||
|
||||
@@ -46,4 +43,4 @@ class SingleRwFifo {
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_DEVICE_ANDROID_SINGLE_RW_FIFO_H_
|
||||
#endif // DOM_MEDIA_WEBRTC_SINGLE_RW_FIFO_H_
|
||||
@@ -38,6 +38,7 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
# MediaEngineWebRTC.cpp needs to be built separately.
|
||||
SOURCES += [
|
||||
'MediaEngineWebRTC.cpp',
|
||||
'SingleRwFifo.cpp',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
@@ -68,8 +69,7 @@ include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
# Suppress some GCC/clang warnings being treated as errors:
|
||||
# - about attributes on forward declarations for types that are already
|
||||
# defined, which complains about important MOZ_EXPORT attributes for
|
||||
# android API types
|
||||
# defined, which complains about important MOZ_EXPORT attributes.
|
||||
if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-error=attributes',
|
||||
|
||||
@@ -293,6 +293,12 @@ SpeechSynthesis::ForceEnd()
|
||||
}
|
||||
}
|
||||
|
||||
void SpeechSynthesis::DisconnectFromOwner() {
|
||||
Pause();
|
||||
Cancel();
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeechSynthesis::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
|
||||
@@ -58,6 +58,8 @@ public:
|
||||
void GetVoices(nsTArray< RefPtr<SpeechSynthesisVoice> >& aResult);
|
||||
|
||||
void ForceEnd();
|
||||
|
||||
void DisconnectFromOwner() override;
|
||||
|
||||
IMPL_EVENT_HANDLER(voiceschanged)
|
||||
|
||||
|
||||
@@ -325,12 +325,12 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpeechService* aService,
|
||||
(XRE_IsContentProcess()) ? "child" : "parent"));
|
||||
|
||||
bool found = false;
|
||||
VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
|
||||
RefPtr<VoiceData> retval = mUriVoiceMap.GetWeak(aUri, &found);
|
||||
|
||||
if(NS_WARN_IF(!(found))) {
|
||||
if(NS_WARN_IF(!found)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if(NS_WARN_IF(!(aService == retval->mService))) {
|
||||
if(NS_WARN_IF(aService != retval->mService)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
|
||||
if (NS_FAILED(ConnectInternal(aAddressInfo.addr(), aAddressInfo.port()))) {
|
||||
if (NS_FAILED(ConnectInternal(aSocket, aAddressInfo.addr(), aAddressInfo.port()))) {
|
||||
SendInternalError(aReturnThread, __LINE__);
|
||||
return;
|
||||
}
|
||||
@@ -320,13 +320,15 @@ UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocketParent::ConnectInternal(const nsCString& aHost, const uint16_t& aPort)
|
||||
UDPSocketParent::ConnectInternal(const nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
const nsCString& aHost,
|
||||
const uint16_t& aPort)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort));
|
||||
|
||||
if (!mSocket) {
|
||||
if (!aSocket) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
@@ -340,7 +342,7 @@ UDPSocketParent::ConnectInternal(const nsCString& aHost, const uint16_t& aPort)
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
|
||||
rv = mSocket->Connect(&addr);
|
||||
rv = aSocket->Connect(&addr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ private:
|
||||
const bool& aAddressReuse, const bool& aLoopback,
|
||||
const uint32_t& recvBufferSize,
|
||||
const uint32_t& sendBufferSize);
|
||||
nsresult ConnectInternal(const nsCString& aHost, const uint16_t& aPort);
|
||||
nsresult ConnectInternal(const nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
const nsCString& aHost,
|
||||
const uint16_t& aPort);
|
||||
void FireInternalError(uint32_t aLineNo);
|
||||
void SendInternalError(nsIEventTarget *aThread,
|
||||
uint32_t aLineNo);
|
||||
|
||||
@@ -194,7 +194,7 @@ static void FreeStringArray(uint32_t variants, char ** array)
|
||||
|
||||
static bool CanLoadPlugin(char16ptr_t aBinaryPath)
|
||||
{
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64)
|
||||
#if defined(_M_IX86) || defined(_M_X64)
|
||||
bool canLoad = false;
|
||||
|
||||
HANDLE file = CreateFileW(aBinaryPath, GENERIC_READ,
|
||||
@@ -214,8 +214,6 @@ static bool CanLoadPlugin(char16ptr_t aBinaryPath)
|
||||
canLoad = (arch == IMAGE_FILE_MACHINE_I386);
|
||||
#elif defined(_M_X64)
|
||||
canLoad = (arch == IMAGE_FILE_MACHINE_AMD64);
|
||||
#elif defined(_M_IA64)
|
||||
canLoad = (arch == IMAGE_FILE_MACHINE_IA64);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ enum CSSStyleSheetParsingMode {
|
||||
"agent"
|
||||
};
|
||||
|
||||
[Constructor]
|
||||
interface CSSStyleSheet : StyleSheet {
|
||||
[Pure]
|
||||
readonly attribute CSSRule? ownerRule;
|
||||
|
||||
@@ -823,16 +823,37 @@ nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// The approach in WalkAllShadowRootHostRules seems reasonable on the surface and reminds me of what is done with mScopedRoot elsewhere. But something important is missing, either here or in the code that would normally use it.
|
||||
|
||||
void
|
||||
nsBindingManager::WalkAllShadowRootHostRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||
ElementDependentRuleProcessorData* aData)
|
||||
{
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||
WalkAllRules(aFunc, aData, true);
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
||||
}
|
||||
{
|
||||
if (!mBoundContentSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool oldOnlyMatchHostPseudo = aData->mTreeMatchContext.mOnlyMatchHostPseudo;
|
||||
nsIContent* oldScopedRoot = aData->mTreeMatchContext.mScopedRoot;
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||
|
||||
for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsIContent* boundContent = iter.Get()->GetKey();
|
||||
ShadowRoot* shadowRoot = boundContent->GetShadowRoot();
|
||||
if (!shadowRoot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsXBLBinding* binding = shadowRoot->GetAssociatedBinding();
|
||||
if (!binding) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aData->mTreeMatchContext.mScopedRoot = boundContent;
|
||||
binding->WalkRules(aFunc, aData);
|
||||
}
|
||||
|
||||
aData->mTreeMatchContext.mScopedRoot = oldScopedRoot;
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = oldOnlyMatchHostPseudo;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
|
||||
|
||||
@@ -7,8 +7,6 @@ UNIFIED_SOURCES += [
|
||||
'CharDistribution.cpp',
|
||||
'JpCntx.cpp',
|
||||
'nsCharSetProber.cpp',
|
||||
'nsEscCharsetProber.cpp',
|
||||
'nsEscSM.cpp',
|
||||
'nsEUCJPProber.cpp',
|
||||
'nsLatin1Prober.cpp',
|
||||
'nsMBCSGroupProber.cpp',
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; 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 "nsEscCharsetProber.h"
|
||||
#include "nsUniversalDetector.h"
|
||||
|
||||
nsEscCharSetProber::nsEscCharSetProber()
|
||||
{
|
||||
mCodingSM = new nsCodingStateMachine(&ISO2022JPSMModel);
|
||||
mState = eDetecting;
|
||||
mDetectedCharset = nullptr;
|
||||
}
|
||||
|
||||
nsEscCharSetProber::~nsEscCharSetProber(void)
|
||||
{
|
||||
}
|
||||
|
||||
void nsEscCharSetProber::Reset(void)
|
||||
{
|
||||
mState = eDetecting;
|
||||
mCodingSM->Reset();
|
||||
mDetectedCharset = nullptr;
|
||||
}
|
||||
|
||||
nsProbingState nsEscCharSetProber::HandleData(const char* aBuf, uint32_t aLen)
|
||||
{
|
||||
nsSMState codingState;
|
||||
uint32_t i;
|
||||
|
||||
for ( i = 0; i < aLen && mState == eDetecting; i++)
|
||||
{
|
||||
codingState = mCodingSM->NextState(aBuf[i]);
|
||||
if (codingState == eItsMe)
|
||||
{
|
||||
mState = eFoundIt;
|
||||
mDetectedCharset = mCodingSM->GetCodingStateMachine();
|
||||
return mState;
|
||||
}
|
||||
}
|
||||
|
||||
return mState;
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; 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 nsEscCharSetProber_h__
|
||||
#define nsEscCharSetProber_h__
|
||||
|
||||
#include "nsCharSetProber.h"
|
||||
#include "nsCodingStateMachine.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsEscCharSetProber: public nsCharSetProber {
|
||||
public:
|
||||
nsEscCharSetProber();
|
||||
virtual ~nsEscCharSetProber(void);
|
||||
nsProbingState HandleData(const char* aBuf, uint32_t aLen);
|
||||
const char* GetCharSetName() {return mDetectedCharset;}
|
||||
nsProbingState GetState(void) {return mState;}
|
||||
void Reset(void);
|
||||
float GetConfidence(void){return (float)0.99;}
|
||||
|
||||
protected:
|
||||
void GetDistribution(uint32_t aCharLen, const char* aStr);
|
||||
|
||||
nsAutoPtr<nsCodingStateMachine> mCodingSM;
|
||||
nsProbingState mState;
|
||||
const char * mDetectedCharset;
|
||||
};
|
||||
|
||||
#endif /* nsEscCharSetProber_h__ */
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; 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 "nsCodingStateMachine.h"
|
||||
|
||||
static const uint32_t ISO2022JP_cls [ 256 / 8 ] = {
|
||||
PCK4BITS(2,0,0,0,0,0,0,0), // 00 - 07
|
||||
PCK4BITS(0,0,0,0,0,0,2,2), // 08 - 0f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 10 - 17
|
||||
PCK4BITS(0,0,0,1,0,0,0,0), // 18 - 1f
|
||||
PCK4BITS(0,0,0,0,7,0,0,0), // 20 - 27
|
||||
PCK4BITS(3,0,0,0,0,0,0,0), // 28 - 2f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 30 - 37
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 38 - 3f
|
||||
PCK4BITS(6,0,4,0,8,0,0,0), // 40 - 47
|
||||
PCK4BITS(0,9,5,0,0,0,0,0), // 48 - 4f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 50 - 57
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 58 - 5f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 60 - 67
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 68 - 6f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 70 - 77
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 78 - 7f
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 80 - 87
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 88 - 8f
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 90 - 97
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 98 - 9f
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // a0 - a7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // a8 - af
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // b0 - b7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // b8 - bf
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // c0 - c7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // c8 - cf
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // d0 - d7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // d8 - df
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // e0 - e7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // e8 - ef
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // f0 - f7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2) // f8 - ff
|
||||
};
|
||||
|
||||
|
||||
static const uint32_t ISO2022JP_st [ 9] = {
|
||||
PCK4BITS(eStart, 3,eError,eStart,eStart,eStart,eStart,eStart),//00-07
|
||||
PCK4BITS(eStart,eStart,eError,eError,eError,eError,eError,eError),//08-0f
|
||||
PCK4BITS(eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe),//10-17
|
||||
PCK4BITS(eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eError),//18-1f
|
||||
PCK4BITS(eError, 5,eError,eError,eError, 4,eError,eError),//20-27
|
||||
PCK4BITS(eError,eError,eError, 6,eItsMe,eError,eItsMe,eError),//28-2f
|
||||
PCK4BITS(eError,eError,eError,eError,eError,eError,eItsMe,eItsMe),//30-37
|
||||
PCK4BITS(eError,eError,eError,eItsMe,eError,eError,eError,eError),//38-3f
|
||||
PCK4BITS(eError,eError,eError,eError,eItsMe,eError,eStart,eStart) //40-47
|
||||
};
|
||||
|
||||
static const uint32_t ISO2022JPCharLenTable[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
const SMModel ISO2022JPSMModel = {
|
||||
{eIdxSft4bits, eSftMsk4bits, eBitSft4bits, eUnitMsk4bits, ISO2022JP_cls },
|
||||
10,
|
||||
{eIdxSft4bits, eSftMsk4bits, eBitSft4bits, eUnitMsk4bits, ISO2022JP_st },
|
||||
CHAR_LEN_TABLE(ISO2022JPCharLenTable),
|
||||
"ISO-2022-JP",
|
||||
};
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "nsUniversalDetector.h"
|
||||
|
||||
#include "nsMBCSGroupProber.h"
|
||||
#include "nsEscCharsetProber.h"
|
||||
#include "nsLatin1Prober.h"
|
||||
|
||||
nsUniversalDetector::nsUniversalDetector()
|
||||
@@ -16,7 +15,6 @@ nsUniversalDetector::nsUniversalDetector()
|
||||
mDone = false;
|
||||
mBestGuess = -1; //illegal value as signal
|
||||
mInTag = false;
|
||||
mEscCharSetProber = nullptr;
|
||||
|
||||
mStart = true;
|
||||
mDetectedCharset = nullptr;
|
||||
@@ -33,8 +31,6 @@ nsUniversalDetector::~nsUniversalDetector()
|
||||
{
|
||||
for (int32_t i = 0; i < NUM_OF_CHARSET_PROBERS; i++)
|
||||
delete mCharSetProbers[i];
|
||||
|
||||
delete mEscCharSetProber;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -50,9 +46,6 @@ nsUniversalDetector::Reset()
|
||||
mInputState = ePureAscii;
|
||||
mLastChar = '\0';
|
||||
|
||||
if (mEscCharSetProber)
|
||||
mEscCharSetProber->Reset();
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < NUM_OF_CHARSET_PROBERS; i++)
|
||||
if (mCharSetProbers[i])
|
||||
@@ -117,12 +110,6 @@ nsresult nsUniversalDetector::HandleData(const char* aBuf, uint32_t aLen)
|
||||
//adjust state
|
||||
mInputState = eHighbyte;
|
||||
|
||||
//kill mEscCharSetProber if it is active
|
||||
if (mEscCharSetProber) {
|
||||
delete mEscCharSetProber;
|
||||
mEscCharSetProber = nullptr;
|
||||
}
|
||||
|
||||
//start multibyte and singlebyte charset prober
|
||||
if (nullptr == mCharSetProbers[0])
|
||||
{
|
||||
@@ -140,12 +127,6 @@ nsresult nsUniversalDetector::HandleData(const char* aBuf, uint32_t aLen)
|
||||
}
|
||||
else
|
||||
{
|
||||
//ok, just pure ascii so far
|
||||
if ((ePureAscii == mInputState) && (aBuf[i] == '\033'))
|
||||
{
|
||||
//found escape character
|
||||
mInputState = eEscAscii;
|
||||
}
|
||||
mLastChar = aBuf[i];
|
||||
}
|
||||
}
|
||||
@@ -153,19 +134,6 @@ nsresult nsUniversalDetector::HandleData(const char* aBuf, uint32_t aLen)
|
||||
nsProbingState st;
|
||||
switch (mInputState)
|
||||
{
|
||||
case eEscAscii:
|
||||
if (nullptr == mEscCharSetProber) {
|
||||
mEscCharSetProber = new nsEscCharSetProber();
|
||||
if (nullptr == mEscCharSetProber)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
st = mEscCharSetProber->HandleData(aBuf, aLen);
|
||||
if (st == eFoundIt)
|
||||
{
|
||||
mDone = true;
|
||||
mDetectedCharset = mEscCharSetProber->GetCharSetName();
|
||||
}
|
||||
break;
|
||||
case eHighbyte:
|
||||
for (i = 0; i < NUM_OF_CHARSET_PROBERS; i++)
|
||||
{
|
||||
@@ -231,8 +199,6 @@ void nsUniversalDetector::DataEnd()
|
||||
Report(mCharSetProbers[maxProber]->GetCharSetName());
|
||||
}
|
||||
break;
|
||||
case eEscAscii:
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ class nsCharSetProber;
|
||||
|
||||
typedef enum {
|
||||
ePureAscii = 0,
|
||||
eEscAscii = 1,
|
||||
eHighbyte = 2
|
||||
eHighbyte = 1
|
||||
} nsInputState;
|
||||
|
||||
class nsUniversalDetector {
|
||||
@@ -37,7 +36,6 @@ protected:
|
||||
uint32_t mLanguageFilter;
|
||||
|
||||
nsCharSetProber *mCharSetProbers[NUM_OF_CHARSET_PROBERS];
|
||||
nsCharSetProber *mEscCharSetProber;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#ifndef CAIRO_DEPRECATED_H
|
||||
#define CAIRO_DEPRECATED_H
|
||||
|
||||
#define CAIRO_FONT_TYPE_ATSUI CAIRO_FONT_TYPE_QUARTZ
|
||||
|
||||
/* Obsolete functions. These definitions exist to coerce the compiler
|
||||
* into providing a little bit of guidance with its error
|
||||
* messages. The idea is to help users port their old code without
|
||||
@@ -59,8 +57,6 @@
|
||||
#define cairo_ps_surface_set_dpi cairo_ps_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
|
||||
#define cairo_pdf_surface_set_dpi cairo_pdf_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
|
||||
#define cairo_svg_surface_set_dpi cairo_svg_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
|
||||
#define cairo_atsui_font_face_create_for_atsu_font_id cairo_atsui_font_face_create_for_atsu_font_id_REPLACED_BY_cairo_quartz_font_face_create_for_atsu_font_id
|
||||
|
||||
#define cairo_current_path cairo_current_path_DEPRECATED_BY_cairo_copy_path
|
||||
#define cairo_current_path_flat cairo_current_path_flat_DEPRECATED_BY_cairo_copy_path_flat
|
||||
#define cairo_get_path cairo_get_path_DEPRECATED_BY_cairo_copy_path
|
||||
|
||||
@@ -767,45 +767,3 @@ _cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
|
||||
|
||||
return ffont->ctFont;
|
||||
}
|
||||
|
||||
#if !defined(__LP64__) && !TARGET_OS_IPHONE
|
||||
/*
|
||||
* compat with old ATSUI backend
|
||||
*/
|
||||
|
||||
/**
|
||||
* cairo_quartz_font_face_create_for_atsu_font_id
|
||||
* @font_id: an ATSUFontID for the font.
|
||||
*
|
||||
* Creates a new font for the Quartz font backend based on an
|
||||
* #ATSUFontID. This font can then be used with
|
||||
* cairo_set_font_face() or cairo_scaled_font_create().
|
||||
*
|
||||
* Return value: a newly created #cairo_font_face_t. Free with
|
||||
* cairo_font_face_destroy() when you are done using it.
|
||||
*
|
||||
* Since: 1.6
|
||||
**/
|
||||
cairo_font_face_t *
|
||||
cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
|
||||
{
|
||||
ATSFontRef atsFont = FMGetATSFontRefFromFont (font_id);
|
||||
CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
|
||||
cairo_font_face_t *ff;
|
||||
|
||||
ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
|
||||
|
||||
CGFontRelease (cgFont);
|
||||
|
||||
return ff;
|
||||
}
|
||||
|
||||
/* This is the old name for the above function, exported for compat purposes */
|
||||
cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
|
||||
|
||||
cairo_font_face_t *
|
||||
cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
|
||||
{
|
||||
return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
|
||||
}
|
||||
#endif
|
||||
@@ -63,11 +63,6 @@ cairo_quartz_surface_get_image (cairo_surface_t *surface);
|
||||
cairo_public cairo_font_face_t *
|
||||
cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
|
||||
|
||||
#if !defined(__LP64__) && !TARGET_OS_IPHONE
|
||||
cairo_public cairo_font_face_t *
|
||||
cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
|
||||
#endif
|
||||
|
||||
#endif /* CAIRO_HAS_QUARTZ_FONT */
|
||||
|
||||
CAIRO_END_DECLS
|
||||
|
||||
@@ -213,8 +213,6 @@
|
||||
#define cairo_qpainter_surface_create_with_qpixmap _moz_cairo_qpainter_surface_create_with_qpixmap
|
||||
#define cairo_qpainter_surface_get_image _moz_cairo_qpainter_surface_get_image
|
||||
#define cairo_qpainter_surface_get_qimage _moz_cairo_qpainter_surface_get_qimage
|
||||
#define cairo_qpainter_surface_get_qpainter _moz_cairo_qpainter_surface_get_qpainter
|
||||
#define cairo_quartz_font_face_create_for_atsu_font_id _moz_cairo_quartz_font_face_create_for_atsu_font_id
|
||||
#define cairo_quartz_font_face_create_for_cgfont _moz_cairo_quartz_font_face_create_for_cgfont
|
||||
#define cairo_quartz_image_surface_create _moz_cairo_quartz_image_surface_create
|
||||
#define cairo_quartz_image_surface_get_image _moz_cairo_quartz_image_surface_get_image
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#include "harfbuzz/hb.h"
|
||||
#include "harfbuzz/hb-ot.h"
|
||||
#include "graphite2/Font.h"
|
||||
@@ -884,7 +888,7 @@ gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID)
|
||||
if (!SetupCairoFont(aDrawTarget)) {
|
||||
return 0;
|
||||
}
|
||||
#if !defined(XP_MACOSX) || (defined(XP_MACOSX) && !(defined(__ppc__) || defined(__ppc64__)))
|
||||
#if !defined(XP_MACOSX) || (defined(XP_MACOSX) && (defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6))
|
||||
if (ProvidesGlyphWidths()) {
|
||||
return GetGlyphWidth(*aDrawTarget, aGID) / 65536.0;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include "unicode/unorm.h"
|
||||
#include "unicode/utext.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
static const UNormalizer2 * sNormalizer = nullptr;
|
||||
|
||||
#include <algorithm>
|
||||
@@ -327,7 +331,7 @@ gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const
|
||||
glyph = mNumLongHMetrics - 1;
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX) && (defined(__ppc__) || defined(__ppc64__))
|
||||
#if defined(XP_MACOSX) && !(defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6))
|
||||
if (MOZ_UNLIKELY(mFont->mSpacingKludge && glyph == mFont->GetSpaceGlyph())) {
|
||||
// This is one of the fonts where the spacing went awry,
|
||||
// invariably on an nbsp character that escaped into the run.
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include "cairo-quartz.h"
|
||||
|
||||
#include "nsCocoaFeatures.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
@@ -474,25 +476,11 @@ gfxMacFont::InitMetrics()
|
||||
// If we are dealing with a weird font (Apple is notorious), we could have
|
||||
// the situation where we wind up with space == maxAdvance due to a 10.4 bug.
|
||||
// In that case, kludge a reasonable upper limit (TenFourFox issue 355).
|
||||
//fprintf(stderr, "glyph: %i sB=%s width=%f avg=%f max=%f cgf=%f mAS=%f\n", glyphID, (IsSyntheticBold() ? "Y" : "n"), mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.maxAdvance, cgConvFactor, mAdjustedSize);
|
||||
if (MOZ_UNLIKELY(!hasX && mMetrics.spaceWidth >= mMetrics.maxAdvance)) {
|
||||
mMetrics.spaceWidth = std::min((float)mMetrics.emDescent * 1.4f,
|
||||
(float)mMetrics.maxAdvance * 0.4f);
|
||||
mSpacingKludge = true;
|
||||
}
|
||||
//fprintf(stderr, "glyph: %i sB=%s ......%f avg=%f max=%f cgf=%f mAS=%f\n", glyphID, (IsSyntheticBold() ? "Y" : "n"), mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.maxAdvance, cgConvFactor, mAdjustedSize);
|
||||
|
||||
|
||||
#if 0
|
||||
fprintf (stderr, "Font: %p (%s) size: %f\n", this,
|
||||
NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
|
||||
// fprintf (stderr, " fbounds.origin.x %f y %f size.width %f height %f\n", fbounds.origin.x, fbounds.origin.y, fbounds.size.width, fbounds.size.height);
|
||||
fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
|
||||
fprintf (stderr, " maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
|
||||
fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
|
||||
fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f capHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight, mMetrics.capHeight);
|
||||
fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
@@ -527,7 +515,10 @@ gfxMacFont::CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont,
|
||||
CGFloat aSize,
|
||||
CTFontDescriptorRef aFontDesc)
|
||||
{
|
||||
CFDictionaryRef variations = ::CGFontCopyVariations(aCGFont);
|
||||
// Avoid calling potentially buggy variation APIs on pre-Sierra macOS
|
||||
// versions (see bug 1331683)
|
||||
CFDictionaryRef variations = nsCocoaFeatures::OnSierraOrLater() ?
|
||||
::CGFontCopyVariations(aCGFont) : 0;
|
||||
CTFontRef ctFont;
|
||||
if (variations) {
|
||||
CFDictionaryRef varAttr =
|
||||
|
||||
@@ -42,7 +42,10 @@ public:
|
||||
// with embedded color bitmaps (Apple Color Emoji), as Core Text renders
|
||||
// the glyphs with non-linear scaling at small pixel sizes.
|
||||
virtual bool ProvidesGlyphWidths() const override {
|
||||
return mVariationFont ||
|
||||
return
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
mVariationFont ||
|
||||
#endif
|
||||
mFontEntry->HasFontTable(TRUETYPE_TAG('s','b','i','x'));
|
||||
}
|
||||
|
||||
|
||||
+12
-40
@@ -147,7 +147,6 @@ gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
|
||||
: gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
|
||||
, mUserData(aParams->mUserData)
|
||||
, mFontGroup(aFontGroup)
|
||||
, mReleasedFontGroup(false)
|
||||
, mShapingState(eShapingState_Normal)
|
||||
{
|
||||
NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
|
||||
@@ -184,30 +183,18 @@ gfxTextRun::~gfxTextRun()
|
||||
mFlags = 0xFFFFFFFF;
|
||||
#endif
|
||||
|
||||
// The cached ellipsis textrun (if any) in a fontgroup will have already
|
||||
// been told to release its reference to the group, so we mustn't do that
|
||||
// again here.
|
||||
if (!mReleasedFontGroup) {
|
||||
#ifdef DEBUG
|
||||
gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
|
||||
if (tp) {
|
||||
tp->current.textrunDestr++;
|
||||
}
|
||||
#endif
|
||||
NS_RELEASE(mFontGroup);
|
||||
gfxTextPerfMetrics* tp = mFontGroup->GetTextPerfMetrics();
|
||||
if (tp) {
|
||||
tp->current.textrunDestr++;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_RELEASE(mFontGroup);
|
||||
|
||||
MOZ_COUNT_DTOR(gfxTextRun);
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::ReleaseFontGroup()
|
||||
{
|
||||
NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
|
||||
NS_RELEASE(mFontGroup);
|
||||
mReleasedFontGroup = true;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxTextRun::SetPotentialLineBreaks(Range aRange, const uint8_t* aBreakBefore)
|
||||
{
|
||||
@@ -2541,17 +2528,11 @@ gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
|
||||
}
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
LazyReferenceDrawTargetGetter& aRefDrawTargetGetter)
|
||||
{
|
||||
already_AddRefed<gfxTextRun>
|
||||
gfxFontGroup::MakeEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
DrawTarget* aRefDrawTarget) {
|
||||
MOZ_ASSERT(!(aFlags & ~TEXT_ORIENT_MASK),
|
||||
"flags here should only be used to specify orientation");
|
||||
if (mCachedEllipsisTextRun &&
|
||||
(mCachedEllipsisTextRun->GetFlags() & TEXT_ORIENT_MASK) == aFlags &&
|
||||
mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
|
||||
return mCachedEllipsisTextRun.get();
|
||||
}
|
||||
|
||||
// Use a Unicode ellipsis if the font supports it,
|
||||
// otherwise use three ASCII periods as fallback.
|
||||
@@ -2562,20 +2543,11 @@ gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
: nsDependentString(kASCIIPeriodsChar,
|
||||
ArrayLength(kASCIIPeriodsChar) - 1);
|
||||
|
||||
RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
|
||||
Parameters params = {
|
||||
refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
|
||||
aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
|
||||
};
|
||||
mCachedEllipsisTextRun =
|
||||
MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms,
|
||||
aFlags | TEXT_IS_PERSISTENT, nullptr);
|
||||
if (!mCachedEllipsisTextRun) {
|
||||
return nullptr;
|
||||
}
|
||||
// don't let the presence of a cached ellipsis textrun prolong the
|
||||
// fontgroup's life
|
||||
mCachedEllipsisTextRun->ReleaseFontGroup();
|
||||
return mCachedEllipsisTextRun.get();
|
||||
return MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms,
|
||||
aFlags | TEXT_IS_PERSISTENT, nullptr);
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFont>
|
||||
|
||||
+5
-18
@@ -755,8 +755,6 @@ private:
|
||||
bool mSkipDrawing; // true if the font group we used had a user font
|
||||
// download that's in progress, so we should hide text
|
||||
// until the download completes (or timeout fires)
|
||||
bool mReleasedFontGroup; // we already called NS_RELEASE on
|
||||
// mFontGroup, so don't do it again
|
||||
|
||||
// shaping state for handling variant fallback features
|
||||
// such as subscript/superscript variant glyphs
|
||||
@@ -897,7 +895,6 @@ public:
|
||||
mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
|
||||
mSkipDrawing = false;
|
||||
mHyphenWidth = -1;
|
||||
mCachedEllipsisTextRun = nullptr;
|
||||
}
|
||||
|
||||
// If there is a user font set, check to see whether the font list or any
|
||||
@@ -911,17 +908,11 @@ public:
|
||||
return mSkipDrawing;
|
||||
}
|
||||
|
||||
class LazyReferenceDrawTargetGetter {
|
||||
public:
|
||||
virtual already_AddRefed<DrawTarget> GetRefDrawTarget() = 0;
|
||||
};
|
||||
// The gfxFontGroup keeps ownership of this textrun.
|
||||
// It is only guaranteed to exist until the next call to GetEllipsisTextRun
|
||||
// (which might use a different appUnitsPerDev value or flags) for the font
|
||||
// group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
|
||||
// Get it/use it/forget it :) - don't keep a reference that might go stale.
|
||||
gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
LazyReferenceDrawTargetGetter& aRefDrawTargetGetter);
|
||||
// Make a textrun for the ellipsis character (with fallback to "..." if
|
||||
// ellipsis is not supported by the font).
|
||||
already_AddRefed<gfxTextRun> MakeEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
|
||||
uint32_t aFlags,
|
||||
DrawTarget* aRefDrawTarget);
|
||||
|
||||
protected:
|
||||
// search through pref fonts for a character, return nullptr if no matching pref font
|
||||
@@ -1093,10 +1084,6 @@ protected:
|
||||
|
||||
gfxTextPerfMetrics *mTextPerf;
|
||||
|
||||
// Cache a textrun representing an ellipsis (useful for CSS text-overflow)
|
||||
// at a specific appUnitsPerDevPixel size and orientation
|
||||
RefPtr<gfxTextRun> mCachedEllipsisTextRun;
|
||||
|
||||
// cache the most recent pref font to avoid general pref font lookup
|
||||
RefPtr<gfxFontFamily> mLastPrefFamily;
|
||||
RefPtr<gfxFont> mLastPrefFont;
|
||||
|
||||
@@ -446,7 +446,7 @@ for (k = 0; k < 2; k++) {
|
||||
while ((c = fgetc(f)) != '\n' && c != EOF);
|
||||
/* issue warning if not a comment */
|
||||
if (buf[0] != '%') {
|
||||
fprintf(stderr, "Warning: skipping too long pattern (more than %lu chars)\n", sizeof(buf));
|
||||
fprintf(stderr, "Warning: skipping too long pattern (more than %zu chars)\n", sizeof(buf));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -47,10 +47,12 @@
|
||||
#include "builtin/AtomicsObject.h"
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "builtin/Promise.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsnum.h"
|
||||
@@ -58,7 +60,9 @@
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "js/Class.h"
|
||||
#include "vm/BigIntType.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
#include "vm/Time.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
@@ -121,6 +125,68 @@ GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle<TypedArrayObject*> view,
|
||||
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
|
||||
CompareExchange(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidate,
|
||||
SharedMem<void*> viewData, uint32_t offset, bool* badArrayType = nullptr)
|
||||
@@ -191,6 +257,20 @@ js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp)
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
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;
|
||||
if (!ToInt32(cx, oldv, &oldCandidate))
|
||||
return false;
|
||||
@@ -259,6 +339,22 @@ js::atomics_load(JSContext* cx, unsigned argc, Value* vp)
|
||||
r.setNumber(v);
|
||||
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:
|
||||
return ReportBadArrayType(cx);
|
||||
}
|
||||
@@ -337,6 +433,25 @@ ExchangeOrStore(JSContext* cx, unsigned argc, Value* vp)
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
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;
|
||||
if (!ToInteger(cx, valv, &integerValue))
|
||||
return false;
|
||||
@@ -380,6 +495,24 @@ AtomicsBinop(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValue valv
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
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;
|
||||
if (!ToInt32(cx, valv, &numberValue))
|
||||
return false;
|
||||
@@ -434,6 +567,7 @@ class PerformAdd
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAddSeqCst)
|
||||
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
|
||||
@@ -448,6 +582,7 @@ class PerformSub
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchSubSeqCst)
|
||||
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
|
||||
@@ -462,6 +597,7 @@ class PerformAnd
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAndSeqCst)
|
||||
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
|
||||
@@ -476,6 +612,7 @@ class PerformOr
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchOrSeqCst)
|
||||
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
|
||||
@@ -490,6 +627,7 @@ class PerformXor
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchXorSeqCst)
|
||||
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
|
||||
@@ -693,11 +831,13 @@ js::atomics_cmpxchg_asm_callout(wasm::Instance* instance, int32_t vt, int32_t of
|
||||
|
||||
namespace js {
|
||||
|
||||
class AtomicsWaitAsyncTask;
|
||||
|
||||
// Represents one waiting worker.
|
||||
//
|
||||
// The type is declared opaque in SharedArrayObject.h. Instances of
|
||||
// js::FutexWaiter are stack-allocated and linked onto a list across a
|
||||
// call to FutexRuntime::wait().
|
||||
// js::FutexWaiter are linked onto a list across a call to FutexRuntime::wait()
|
||||
// or an async wait task.
|
||||
//
|
||||
// The 'waiters' field of the SharedArrayRawBuffer points to the highest
|
||||
// priority waiter in the list, and lower priority nodes are linked through
|
||||
@@ -711,14 +851,34 @@ class FutexWaiter
|
||||
public:
|
||||
FutexWaiter(uint32_t offset, JSRuntime* rt)
|
||||
: offset(offset),
|
||||
kind(Sync),
|
||||
rt(rt),
|
||||
asyncTask(nullptr),
|
||||
lower_pri(nullptr),
|
||||
back(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t offset; // int32 element index within the SharedArrayBuffer
|
||||
FutexWaiter(uint32_t offset, AtomicsWaitAsyncTask* asyncTask)
|
||||
: 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
|
||||
AtomicsWaitAsyncTask* asyncTask; // The async waiter task, if any
|
||||
FutexWaiter* lower_pri; // Lower priority nodes in circular doubly-linked list of waiters
|
||||
FutexWaiter* back; // Other direction
|
||||
};
|
||||
@@ -742,8 +902,195 @@ class AutoLockFutexAPI
|
||||
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
|
||||
|
||||
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
|
||||
js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -759,26 +1106,24 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
||||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
if (view->type() != Scalar::Int32)
|
||||
if (!IsWaitableTypedArray(view->type()))
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
int32_t value;
|
||||
if (!ToInt32(cx, valv, &value))
|
||||
return false;
|
||||
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
||||
if (!timeoutv.isUndefined()) {
|
||||
double timeout_ms;
|
||||
if (!ToNumber(cx, timeoutv, &timeout_ms))
|
||||
uint64_t value = 0;
|
||||
if (view->type() == Scalar::BigInt64) {
|
||||
if (!BigIntToRawBits(cx, view->type(), valv, &value))
|
||||
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));
|
||||
}
|
||||
} 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;
|
||||
|
||||
if (!rt->fx.canWait())
|
||||
return ReportCannotWait(cx);
|
||||
@@ -787,8 +1132,7 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
// and it provides the necessary memory fence.
|
||||
AutoLockFutexAPI lock;
|
||||
|
||||
SharedMem<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset;
|
||||
if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
|
||||
if (!WaitValueMatches(view, offset, value)) {
|
||||
r.setString(cx->names().futexNotEqual);
|
||||
return true;
|
||||
}
|
||||
@@ -796,16 +1140,8 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
|
||||
SharedArrayRawBuffer* sarb = sab->rawBufferObject();
|
||||
|
||||
FutexWaiter w(offset, rt);
|
||||
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);
|
||||
}
|
||||
FutexWaiter w(GetWaiterByteOffset(view, offset), rt);
|
||||
AddWaiter(sarb, &w);
|
||||
|
||||
FutexRuntime::WaitResult result = FutexRuntime::FutexOK;
|
||||
bool retval = rt->fx.wait(cx, lock.unique(), timeout, &result);
|
||||
@@ -820,17 +1156,98 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
RemoveWaiter(sarb, &w);
|
||||
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
|
||||
js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -843,11 +1260,12 @@ js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
||||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
if (view->type() != Scalar::Int32)
|
||||
if (!IsWaitableTypedArray(view->type()))
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
offset = GetWaiterByteOffset(view, offset);
|
||||
double count;
|
||||
if (countv.isUndefined()) {
|
||||
count = mozilla::PositiveInfinity<double>();
|
||||
@@ -870,9 +1288,9 @@ js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||
do {
|
||||
FutexWaiter* c = iter;
|
||||
iter = iter->lower_pri;
|
||||
if (c->offset != offset || !c->rt->fx.isWaiting())
|
||||
if (c->offset != offset || !c->isWaiting())
|
||||
continue;
|
||||
c->rt->fx.notify(FutexRuntime::NotifyExplicit);
|
||||
c->notify();
|
||||
++woken;
|
||||
--count;
|
||||
} while (count > 0 && iter != waiters);
|
||||
@@ -1106,6 +1524,7 @@ const JSFunctionSpec AtomicsMethods[] = {
|
||||
JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor),
|
||||
JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree),
|
||||
JS_FN("wait", atomics_wait, 4,0),
|
||||
JS_FN("waitAsync", atomics_waitAsync, 4,0),
|
||||
JS_FN("notify", atomics_notify, 3,0),
|
||||
JS_FN("wake", atomics_notify, 3,0), //Legacy name
|
||||
JS_FS_END
|
||||
|
||||
@@ -35,6 +35,7 @@ class AtomicsObject : public JSObject
|
||||
[[nodiscard]] bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_wait(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_waitAsync(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_notify(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/* asm.js callouts */
|
||||
|
||||
@@ -0,0 +1,636 @@
|
||||
/* -*- 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/FinalizationRegistryObject.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/WeakRefObject.h"
|
||||
#include "gc/Nursery.h"
|
||||
#include "gc/Tracer.h"
|
||||
#include "vm/EqualityOperations.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/Symbol.h"
|
||||
|
||||
#include "jswrapper.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
enum class WeakCellKind {
|
||||
Empty,
|
||||
Object,
|
||||
Symbol
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
NormalizeWeakObject(JSObject* obj)
|
||||
{
|
||||
if (JSObject* unwrapped = CheckedUnwrap(obj, /* stopAtWindowProxy = */ false))
|
||||
return unwrapped;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPrototypeFromFinalizationRegistryConstructor(JSContext* cx, HandleObject newTarget,
|
||||
MutableHandleObject proto)
|
||||
{
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, proto))
|
||||
return false;
|
||||
if (proto)
|
||||
return true;
|
||||
|
||||
RootedObject realmObject(cx, CheckedUnwrap(newTarget, /* stopAtWindowProxy = */ false));
|
||||
if (!realmObject)
|
||||
return false;
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, realmObject);
|
||||
Rooted<GlobalObject*> global(cx, &realmObject->global());
|
||||
if (!GlobalObject::ensureConstructor(cx, global, JSProto_FinalizationRegistry))
|
||||
return false;
|
||||
proto.set(&global->getPrototype(JSProto_FinalizationRegistry).toObject());
|
||||
}
|
||||
|
||||
return cx->compartment()->wrap(cx, proto);
|
||||
}
|
||||
|
||||
struct WeakCell {
|
||||
WeakCellKind kind;
|
||||
WeakRef<JSObject*> object;
|
||||
WeakRef<JS::Symbol*> symbol;
|
||||
bool enabled;
|
||||
|
||||
WeakCell()
|
||||
: kind(WeakCellKind::Empty)
|
||||
, object(nullptr)
|
||||
, symbol(nullptr)
|
||||
, enabled(true)
|
||||
{}
|
||||
|
||||
explicit WeakCell(JSObject* obj, bool enabled)
|
||||
: kind(WeakCellKind::Object)
|
||||
, object(obj)
|
||||
, symbol(nullptr)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
explicit WeakCell(JS::Symbol* sym, bool enabled)
|
||||
: kind(WeakCellKind::Symbol)
|
||||
, object(nullptr)
|
||||
, symbol(sym)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
void clear() {
|
||||
kind = WeakCellKind::Empty;
|
||||
object = nullptr;
|
||||
symbol = nullptr;
|
||||
}
|
||||
|
||||
bool isEmpty() const { return kind == WeakCellKind::Empty; }
|
||||
|
||||
bool isDead() const {
|
||||
if (kind == WeakCellKind::Object)
|
||||
return !object.unbarrieredGet();
|
||||
if (kind == WeakCellKind::Symbol)
|
||||
return !symbol.unbarrieredGet();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sameValue(HandleValue value) const {
|
||||
if (kind == WeakCellKind::Object)
|
||||
return value.isObject() && object.get() == NormalizeWeakObject(&value.toObject());
|
||||
if (kind == WeakCellKind::Symbol)
|
||||
return value.isSymbol() && symbol.get() == value.toSymbol();
|
||||
return false;
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc, const char* name) {
|
||||
if (kind == WeakCellKind::Object) {
|
||||
JSObject* target = object.unbarrieredGet();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (!enabled) {
|
||||
// Do nothing
|
||||
} else if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
return;
|
||||
TraceWeakEdge(trc, &object, name);
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (enabled && target) {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
return;
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void traceIfZoneIsCollecting(JSTracer* trc, const char* name) {
|
||||
if (kind == WeakCellKind::Object) {
|
||||
JSObject* target = object.unbarrieredGet();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (!enabled) {
|
||||
// Do nothing
|
||||
} else if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else if (target->asTenured().zone()->isCollecting()) {
|
||||
TraceWeakEdge(trc, &object, name);
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (enabled && target && target->asTenured().zone()->isCollecting())
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FinalizationRecord {
|
||||
WeakCell target;
|
||||
JS::Heap<Value> heldValue;
|
||||
WeakCell unregisterToken;
|
||||
bool active;
|
||||
bool queued;
|
||||
bool enabled;
|
||||
|
||||
FinalizationRecord(HandleValue targetValue, HandleValue heldValue,
|
||||
HandleValue unregisterTokenValue, bool enabled)
|
||||
: heldValue(heldValue),
|
||||
active(true),
|
||||
queued(false),
|
||||
enabled(enabled)
|
||||
{
|
||||
if (targetValue.isObject())
|
||||
target = WeakCell(NormalizeWeakObject(&targetValue.toObject()), enabled);
|
||||
else
|
||||
target = WeakCell(targetValue.toSymbol(), enabled);
|
||||
|
||||
if (unregisterTokenValue.isObject())
|
||||
unregisterToken = WeakCell(NormalizeWeakObject(&unregisterTokenValue.toObject()), enabled);
|
||||
else if (unregisterTokenValue.isSymbol())
|
||||
unregisterToken = WeakCell(unregisterTokenValue.toSymbol(), enabled);
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
if (active || queued)
|
||||
JS::TraceEdge(trc, &heldValue, "FinalizationRegistry held value");
|
||||
|
||||
if (active) {
|
||||
target.trace(trc, "FinalizationRegistry target");
|
||||
if (!unregisterToken.isEmpty())
|
||||
unregisterToken.trace(trc, "FinalizationRegistry unregister token");
|
||||
}
|
||||
}
|
||||
|
||||
void traceWeakEdgesForCollectedZones(JSTracer* trc) {
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
target.traceIfZoneIsCollecting(trc, "FinalizationRegistry target");
|
||||
if (!unregisterToken.isEmpty())
|
||||
unregisterToken.traceIfZoneIsCollecting(trc,
|
||||
"FinalizationRegistry unregister token");
|
||||
}
|
||||
|
||||
bool targetIsDead() const {
|
||||
return active && target.isDead();
|
||||
}
|
||||
|
||||
bool matchesToken(HandleValue token) const {
|
||||
return active && !queued && !unregisterToken.isEmpty() &&
|
||||
unregisterToken.sameValue(token);
|
||||
}
|
||||
|
||||
void queueForCleanup() {
|
||||
MOZ_ASSERT(active);
|
||||
MOZ_ASSERT(!queued);
|
||||
active = false;
|
||||
queued = true;
|
||||
target.clear();
|
||||
unregisterToken.clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
active = false;
|
||||
queued = false;
|
||||
target.clear();
|
||||
unregisterToken.clear();
|
||||
heldValue.setUndefined();
|
||||
}
|
||||
};
|
||||
|
||||
using FinalizationRecordVector = Vector<FinalizationRecord*, 0, SystemAllocPolicy>;
|
||||
|
||||
struct FinalizationRegistryObject::Data {
|
||||
JS::Heap<JSObject*> cleanupCallback;
|
||||
JS::Heap<JSObject*> cleanupJob;
|
||||
FinalizationRecordVector records;
|
||||
FinalizationRecordVector cleanupQueue;
|
||||
bool queuedForCleanup;
|
||||
bool enabled;
|
||||
|
||||
explicit Data(JSObject* cleanupCallback, bool enabled)
|
||||
: cleanupCallback(cleanupCallback),
|
||||
cleanupJob(nullptr),
|
||||
records(SystemAllocPolicy()),
|
||||
cleanupQueue(SystemAllocPolicy()),
|
||||
queuedForCleanup(false),
|
||||
enabled(enabled)
|
||||
{}
|
||||
|
||||
~Data() {
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
js_delete(records[i]);
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
if (enabled) {
|
||||
JS::TraceEdge(trc, &cleanupCallback, "FinalizationRegistry cleanup callback");
|
||||
if (cleanupJob.unbarrieredGet())
|
||||
JS::TraceEdge(trc, &cleanupJob, "FinalizationRegistry cleanup job");
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
records[i]->trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
bool appendRecord(FinalizationRecord* record) {
|
||||
return records.append(record);
|
||||
}
|
||||
|
||||
bool appendCleanupRecord(FinalizationRecord* record) {
|
||||
return cleanupQueue.append(record);
|
||||
}
|
||||
|
||||
void compactRecords() {
|
||||
for (size_t i = 0; i < records.length();) {
|
||||
FinalizationRecord* record = records[i];
|
||||
if (!record->active && !record->queued) {
|
||||
js_delete(record);
|
||||
records.erase(records.begin() + i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void unregister(HandleValue token, bool* removed) {
|
||||
*removed = false;
|
||||
for (size_t i = 0; i < records.length(); i++) {
|
||||
FinalizationRecord* record = records[i];
|
||||
if (record->matchesToken(token)) {
|
||||
record->clear();
|
||||
*removed = true;
|
||||
}
|
||||
}
|
||||
compactRecords();
|
||||
}
|
||||
};
|
||||
|
||||
static FinalizationRegistryObject::Data*
|
||||
GetData(JSObject* obj)
|
||||
{
|
||||
return obj->as<FinalizationRegistryObject>().getData();
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsFinalizationRegistry(HandleValue v)
|
||||
{
|
||||
return v.isObject() && v.toObject().is<FinalizationRegistryObject>();
|
||||
}
|
||||
|
||||
static bool
|
||||
FinalizationRegistry_register_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsFinalizationRegistry(args.thisv()));
|
||||
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_TARGET);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue target(cx, args[0]);
|
||||
RootedValue heldValue(cx, args.get(1));
|
||||
|
||||
bool same;
|
||||
if (!SameValue(cx, target, heldValue, &same))
|
||||
return false;
|
||||
if (same) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_HELD_VALUE);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue unregisterToken(cx, args.get(2));
|
||||
if (!CanBeHeldWeakly(unregisterToken)) {
|
||||
if (!unregisterToken.isUndefined()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_TOKEN);
|
||||
return false;
|
||||
}
|
||||
unregisterToken.setUndefined();
|
||||
}
|
||||
|
||||
FinalizationRecord* record = cx->new_<FinalizationRecord>(target, heldValue, unregisterToken, cx->options().weakRefs());
|
||||
if (!record)
|
||||
return false;
|
||||
|
||||
auto* data = GetData(&args.thisv().toObject());
|
||||
if (!data->appendRecord(record)) {
|
||||
js_delete(record);
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
FinalizationRegistry_unregister_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsFinalizationRegistry(args.thisv()));
|
||||
|
||||
RootedValue unregisterToken(cx, args.get(0));
|
||||
if (!CanBeHeldWeakly(unregisterToken)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_TOKEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool removed = false;
|
||||
auto* data = GetData(&args.thisv().toObject());
|
||||
data->unregister(unregisterToken, &removed);
|
||||
|
||||
args.rval().setBoolean(removed);
|
||||
return true;
|
||||
}
|
||||
|
||||
const JSPropertySpec FinalizationRegistryObject::properties[] = {
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec FinalizationRegistryObject::methods[] = {
|
||||
JS_FN("register", FinalizationRegistryObject::register_, 2, 0),
|
||||
JS_FN("unregister", FinalizationRegistryObject::unregister, 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
InitFinalizationRegistryClass(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, FinalizationRegistryObject::construct,
|
||||
ClassName(JSProto_FinalizationRegistry, cx), 1));
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
if (defineMembers) {
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, FinalizationRegistryObject::properties,
|
||||
FinalizationRegistryObject::methods)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!DefineToStringTag(cx, proto, cx->names().FinalizationRegistry))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_FinalizationRegistry, ctor, proto))
|
||||
return nullptr;
|
||||
return proto;
|
||||
}
|
||||
|
||||
/* static */ FinalizationRegistryObject*
|
||||
FinalizationRegistryObject::create(JSContext* cx, HandleObject cleanupCallback,
|
||||
HandleObject proto /* = nullptr */)
|
||||
{
|
||||
Rooted<FinalizationRegistryObject*> obj(cx,
|
||||
NewObjectWithClassProto<FinalizationRegistryObject>(cx, proto));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
Data* data = cx->new_<Data>(cleanupCallback, cx->options().weakRefs());
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
obj->setPrivate(data);
|
||||
|
||||
RootedAtom funName(cx, cx->names().empty);
|
||||
RootedFunction cleanupJob(cx, NewNativeFunction(cx, FinalizationRegistryObject::cleanupJob, 0,
|
||||
funName, gc::AllocKind::FUNCTION_EXTENDED,
|
||||
GenericObject));
|
||||
if (!cleanupJob)
|
||||
return nullptr;
|
||||
|
||||
cleanupJob->setExtendedSlot(0, ObjectValue(*obj));
|
||||
data->cleanupJob = cleanupJob;
|
||||
|
||||
if (!cx->zone()->finalizationRegistries.append(WeakRef<JSObject*>(obj))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!ThrowIfNotConstructing(cx, args, "FinalizationRegistry"))
|
||||
return false;
|
||||
|
||||
RootedValue cleanupCallbackValue(cx, args.get(0));
|
||||
RootedObject cleanupCallback(cx, ValueToCallable(cx, cleanupCallbackValue, 0, NO_CONSTRUCT));
|
||||
if (!cleanupCallback)
|
||||
return false;
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromFinalizationRegistryConstructor(cx, newTarget, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<FinalizationRegistryObject*> obj(cx,
|
||||
FinalizationRegistryObject::create(cx, cleanupCallback, proto));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::register_(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsFinalizationRegistry, FinalizationRegistry_register_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::unregister(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsFinalizationRegistry, FinalizationRegistry_unregister_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::cleanupJob(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedFunction job(cx, &args.callee().as<JSFunction>());
|
||||
Rooted<FinalizationRegistryObject*> registry(cx,
|
||||
&job->getExtendedSlot(0).toObject().as<FinalizationRegistryObject>());
|
||||
Data* data = registry->getData();
|
||||
if (!data) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
data->queuedForCleanup = false;
|
||||
|
||||
RootedValue callback(cx, ObjectValue(*data->cleanupCallback.get()));
|
||||
RootedValue heldValue(cx);
|
||||
RootedValue ignored(cx);
|
||||
RootedValue undefined(cx, UndefinedValue());
|
||||
|
||||
while (data->cleanupQueue.length() > 0) {
|
||||
FinalizationRecord* record = data->cleanupQueue[0];
|
||||
data->cleanupQueue.erase(data->cleanupQueue.begin());
|
||||
|
||||
if (!record->queued) {
|
||||
data->compactRecords();
|
||||
continue;
|
||||
}
|
||||
|
||||
heldValue.set(record->heldValue.get());
|
||||
record->clear();
|
||||
data->compactRecords();
|
||||
|
||||
if (!Call(cx, callback, undefined, heldValue, &ignored))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FinalizationRegistryObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
if (Data* data = GetData(obj))
|
||||
data->trace(trc);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FinalizationRegistryObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
if (Data* data = GetData(obj))
|
||||
fop->delete_(data);
|
||||
}
|
||||
|
||||
void
|
||||
FinalizationRegistryObject::traceWeakEdgesForCollectedZones(JSTracer* trc)
|
||||
{
|
||||
Data* data = getData();
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < data->records.length(); i++)
|
||||
data->records[i]->traceWeakEdgesForCollectedZones(trc);
|
||||
}
|
||||
|
||||
void
|
||||
FinalizationRegistryObject::sweepAfterGC(JSRuntime* rt)
|
||||
{
|
||||
Data* data = getData();
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
bool needsCleanupJob = false;
|
||||
for (size_t i = 0; i < data->records.length(); i++) {
|
||||
FinalizationRecord* record = data->records[i];
|
||||
if (!record->targetIsDead())
|
||||
continue;
|
||||
|
||||
record->queueForCleanup();
|
||||
if (!data->appendCleanupRecord(record)) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
oomUnsafe.crash("queueing FinalizationRegistry cleanup record");
|
||||
}
|
||||
needsCleanupJob = true;
|
||||
}
|
||||
|
||||
if (needsCleanupJob && !data->queuedForCleanup) {
|
||||
JSContext* cx = rt->contextFromMainThread();
|
||||
RootedObject cleanupJob(cx, data->cleanupJob.unbarrieredGet());
|
||||
if (!rt->enqueueFinalizationRegistryCleanupJob(cx, cleanupJob)) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
oomUnsafe.crash("queueing FinalizationRegistry cleanup job");
|
||||
}
|
||||
data->queuedForCleanup = true;
|
||||
}
|
||||
|
||||
data->compactRecords();
|
||||
}
|
||||
|
||||
static const ClassOps FinalizationRegistryObjectClassOps = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
FinalizationRegistryObject::finalize,
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
FinalizationRegistryObject::trace
|
||||
};
|
||||
|
||||
const Class FinalizationRegistryObject::class_ = {
|
||||
"FinalizationRegistry",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_FinalizationRegistry) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&FinalizationRegistryObjectClassOps
|
||||
};
|
||||
|
||||
/* static */ JSObject*
|
||||
FinalizationRegistryObject::initClass(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return ::InitFinalizationRegistryClass(cx, obj, true);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::InitFinalizationRegistryClass(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return FinalizationRegistryObject::initClass(cx, obj);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::InitBareFinalizationRegistryCtor(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return ::InitFinalizationRegistryClass(cx, obj, false);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* -*- 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_FinalizationRegistryObject_h
|
||||
#define builtin_FinalizationRegistryObject_h
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class FinalizationRegistryObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
struct Data;
|
||||
|
||||
static const Class class_;
|
||||
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static FinalizationRegistryObject* create(JSContext* cx, HandleObject cleanupCallback,
|
||||
HandleObject proto = nullptr);
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
void traceWeakEdgesForCollectedZones(JSTracer* trc);
|
||||
[[nodiscard]] static bool construct(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool register_(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool unregister(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool cleanupJob(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
void sweepAfterGC(JSRuntime* rt);
|
||||
|
||||
Data* getData() const {
|
||||
return static_cast<Data*>(getPrivate());
|
||||
}
|
||||
|
||||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
};
|
||||
|
||||
extern JSObject*
|
||||
InitFinalizationRegistryClass(JSContext* cx, HandleObject obj);
|
||||
|
||||
extern JSObject*
|
||||
InitBareFinalizationRegistryCtor(JSContext* cx, HandleObject obj);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_FinalizationRegistryObject_h */
|
||||
@@ -55,6 +55,49 @@ 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.
|
||||
var elements;
|
||||
if (callFunction(std_Map_has, groups, key)) {
|
||||
elements = callFunction(std_Map_get, groups, key);
|
||||
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 };
|
||||
|
||||
function MapIteratorNext() {
|
||||
|
||||
@@ -332,9 +332,15 @@ const JSPropertySpec MapObject::staticProperties[] = {
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec MapObject::staticMethods[] = {
|
||||
JS_SELF_HOSTED_FN("groupBy", "MapGroupBy", 2, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSProtoKey key, Native construct,
|
||||
const JSPropertySpec* properties, const JSFunctionSpec* methods,
|
||||
const JSFunctionSpec* staticMethods,
|
||||
const JSPropertySpec* staticProperties)
|
||||
{
|
||||
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
||||
@@ -343,8 +349,13 @@ InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSPro
|
||||
|
||||
Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(key, cx), 0));
|
||||
if (!ctor ||
|
||||
!JS_DefineProperties(cx, ctor, staticProperties) ||
|
||||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||
!JS_DefineProperties(cx, ctor, staticProperties))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (staticMethods && !JS_DefineFunctions(cx, ctor, staticMethods))
|
||||
return nullptr;
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||
!GlobalObject::initBuiltinConstructor(cx, global, key, ctor, proto))
|
||||
{
|
||||
@@ -359,7 +370,7 @@ MapObject::initClass(JSContext* cx, JSObject* obj)
|
||||
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
||||
RootedObject proto(cx,
|
||||
InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods,
|
||||
staticProperties));
|
||||
staticMethods, staticProperties));
|
||||
if (proto) {
|
||||
// Define the "entries" method.
|
||||
JSFunction* fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0);
|
||||
@@ -1084,7 +1095,7 @@ SetObject::initClass(JSContext* cx, JSObject* obj)
|
||||
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
||||
RootedObject proto(cx,
|
||||
InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods,
|
||||
staticProperties));
|
||||
nullptr, staticProperties));
|
||||
if (proto) {
|
||||
// Define the "values" method.
|
||||
JSFunction* fun = JS_DefineFunction(cx, proto, "values", values, 0, 0);
|
||||
|
||||
@@ -110,7 +110,9 @@ class MapObject : public NativeObject {
|
||||
[[nodiscard]] static bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
|
||||
JS::MutableHandle<GCVector<JS::Value>> entries);
|
||||
[[nodiscard]] static bool entries(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool get(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);
|
||||
|
||||
// Publicly exposed Map calls for JSAPI access (webidl maplike/setlike
|
||||
@@ -137,6 +139,7 @@ class MapObject : public NativeObject {
|
||||
|
||||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
static const JSFunctionSpec staticMethods[];
|
||||
static const JSPropertySpec staticProperties[];
|
||||
ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
|
||||
static ValueMap& extract(HandleObject o);
|
||||
@@ -153,10 +156,8 @@ class MapObject : public NativeObject {
|
||||
[[nodiscard]] static bool size_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool size(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool get_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool get(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool has_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool set_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool set(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool delete_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool delete_(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool keys_impl(JSContext* cx, const CallArgs& args);
|
||||
|
||||
@@ -240,12 +240,16 @@ function ObjectGroupBy(items, callbackfn) {
|
||||
// Steps 5-8.
|
||||
for (var value of allowContentIter(items)) {
|
||||
// Step 6.a.
|
||||
var key = callContentFunction(callbackfn, undefined, value, k);
|
||||
if (k >= MAX_NUMERIC_INDEX)
|
||||
ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
|
||||
|
||||
// Step 6.b.
|
||||
var key = callContentFunction(callbackfn, undefined, value, k);
|
||||
|
||||
// Step 6.c.
|
||||
key = ToPropertyKey(key);
|
||||
|
||||
// Steps 6.c-d.
|
||||
// Steps 6.d-e.
|
||||
var elements = groups[key];
|
||||
if (elements === undefined) {
|
||||
_DefineDataProperty(groups, key, [value]);
|
||||
@@ -253,7 +257,7 @@ function ObjectGroupBy(items, callbackfn) {
|
||||
callFunction(std_Array_push, elements, value);
|
||||
}
|
||||
|
||||
// Step 6.e.
|
||||
// Step 6.f.
|
||||
k++;
|
||||
}
|
||||
|
||||
|
||||
@@ -3722,6 +3722,48 @@ Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
|
||||
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.
|
||||
*/
|
||||
@@ -5251,6 +5293,7 @@ static const JSFunctionSpec promise_static_methods[] = {
|
||||
JS_FN("race", Promise_static_race, 1, 0),
|
||||
JS_FN("reject", Promise_reject, 1, 0),
|
||||
JS_FN("resolve", Promise_static_resolve, 1, 0),
|
||||
JS_FN("withResolvers", Promise_static_withResolvers, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
||||
@@ -654,6 +654,82 @@ function String_repeat(count) {
|
||||
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.
|
||||
function String_iterator() {
|
||||
RequireObjectCoercible(this);
|
||||
|
||||
@@ -41,10 +41,21 @@ 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) {
|
||||
var buffer = ViewedArrayBufferIfReified(tarray);
|
||||
if (IsDetachedBuffer(buffer))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
ThrowIfTypedArrayOutOfBounds(tarray);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -67,6 +78,7 @@ function IsTypedArrayEnsuringArrayBuffer(arg) {
|
||||
if (IsObject(arg) && IsPossiblyWrappedTypedArray(arg)) {
|
||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(arg))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(arg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -89,6 +101,7 @@ function ValidateTypedArray(obj, error) {
|
||||
if (IsPossiblyWrappedTypedArray(obj)) {
|
||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(obj))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(obj);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1130,12 +1143,18 @@ function TypedArraySet(overloaded, offset = 0) {
|
||||
// Steps 9-10.
|
||||
var targetBuffer = GetAttachedArrayBuffer(target);
|
||||
|
||||
ThrowIfTypedArrayOutOfBounds(target);
|
||||
|
||||
// Step 11.
|
||||
var targetLength = TypedArrayLength(target);
|
||||
|
||||
// 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 SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer);
|
||||
}
|
||||
@@ -1472,6 +1491,8 @@ function TypedArraySubarray(begin, end) {
|
||||
"TypedArraySubarray");
|
||||
}
|
||||
|
||||
GetAttachedArrayBuffer(obj);
|
||||
|
||||
// Steps 4-6.
|
||||
var buffer = TypedArrayBuffer(obj);
|
||||
var srcLength = TypedArrayLength(obj);
|
||||
@@ -1952,7 +1973,10 @@ function ArrayBufferSlice(start, end) {
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
|
||||
// Steps 19-21.
|
||||
ArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
|
||||
var currentLen = ArrayBufferByteLength(O);
|
||||
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.
|
||||
return new_;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/WeakRefObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
@@ -21,21 +22,80 @@ IsWeakMap(HandleValue v)
|
||||
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
|
||||
WeakMap_has_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
if (!args.get(0).isObject()) {
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (map->has(key)) {
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
||||
if (args.get(0).isObject()) {
|
||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (map->has(key)) {
|
||||
args.rval().setBoolean(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,16 +115,27 @@ WeakMap_get_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
if (!args.get(0).isObject()) {
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
args.rval().set(ptr->value());
|
||||
return true;
|
||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
||||
if (args.get(0).isObject()) {
|
||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
args.rval().set(ptr->value());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,17 +155,29 @@ WeakMap_delete_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
if (!args.get(0).isObject()) {
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
map->remove(ptr);
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
||||
if (args.get(0).isObject()) {
|
||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
map->remove(ptr);
|
||||
args.rval().setBoolean(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,22 +209,13 @@ TryPreserveReflector(JSContext* cx, HandleObject obj)
|
||||
return true;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
HandleObject key, HandleValue value)
|
||||
static bool
|
||||
SetWeakMapObjectEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
HandleObject key, HandleValue value)
|
||||
{
|
||||
if (!EnsureWeakMapData(cx, mapObj))
|
||||
return false;
|
||||
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.
|
||||
if (!TryPreserveReflector(cx, key))
|
||||
@@ -162,13 +236,28 @@ SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
WeakMap_set_impl(JSContext* cx, const CallArgs& args)
|
||||
static bool
|
||||
SetWeakMapSymbolEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
Handle<JS::Symbol*> key, HandleValue value)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
if (!EnsureWeakMapData(cx, mapObj))
|
||||
return false;
|
||||
SymbolValueMap* map = mapObj->getSymbolMap();
|
||||
|
||||
if (!args.get(0).isObject()) {
|
||||
UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
|
||||
MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
|
||||
if (!map->put(key, value)) {
|
||||
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)
|
||||
return false;
|
||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
|
||||
@@ -176,11 +265,24 @@ WeakMap_set_impl(JSContext* cx, const CallArgs& args)
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject key(cx, &args[0].toObject());
|
||||
if (key.isObject()) {
|
||||
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<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
|
||||
|
||||
if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
|
||||
if (!SetWeakMapEntryInternal(cx, map, args.get(0), args.get(1)))
|
||||
return false;
|
||||
args.rval().set(args.thisv());
|
||||
return true;
|
||||
@@ -193,6 +295,13 @@ js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp)
|
||||
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_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
|
||||
{
|
||||
@@ -205,11 +314,11 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||
RootedObject arr(cx, NewDenseEmptyArray(cx));
|
||||
if (!arr)
|
||||
return false;
|
||||
ObjectValueMap* map = obj->as<WeakMapObject>().getMap();
|
||||
if (map) {
|
||||
WeakMapObject::Data* data = obj->as<WeakMapObject>().getData();
|
||||
if (data) {
|
||||
// Prevent GC from mutating the weakmap while iterating.
|
||||
AutoSuppressGC suppress(cx);
|
||||
for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
|
||||
for (ObjectValueMap::Base::Range r = data->objectMap.all(); !r.empty(); r.popFront()) {
|
||||
JS::ExposeObjectToActiveJS(r.front().key());
|
||||
RootedObject key(cx, r.front().key());
|
||||
if (!cx->compartment()->wrap(cx, &key))
|
||||
@@ -217,6 +326,14 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||
if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
|
||||
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);
|
||||
return true;
|
||||
@@ -225,21 +342,23 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||
static void
|
||||
WeakMap_mark(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
|
||||
map->trace(trc);
|
||||
if (WeakMapObject::Data* data = obj->as<WeakMapObject>().getData()) {
|
||||
data->objectMap.trace(trc);
|
||||
data->symbolMap.trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
WeakMap_finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(fop->maybeOffMainThread());
|
||||
if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
|
||||
if (WeakMapObject::Data* data = obj->as<WeakMapObject>().getData()) {
|
||||
#ifdef DEBUG
|
||||
map->~ObjectValueMap();
|
||||
memset(static_cast<void*>(map), 0xdc, sizeof(*map));
|
||||
fop->free_(map);
|
||||
data->~Data();
|
||||
memset(static_cast<void*>(data), 0xdc, sizeof(*data));
|
||||
fop->free_(data);
|
||||
#else
|
||||
fop->delete_(map);
|
||||
fop->delete_(data);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -282,7 +401,8 @@ JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, key, val);
|
||||
Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
|
||||
return SetWeakMapEntryInternal(cx, rootedMap, key, val);
|
||||
RootedValue keyValue(cx, ObjectValue(*key));
|
||||
return SetWeakMapEntryInternal(cx, rootedMap, keyValue, val);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -386,4 +506,3 @@ js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return InitWeakMapClass(cx, obj, false);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ class WeakMapObject : public NativeObject
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
ObjectValueMap* getMap() { return static_cast<ObjectValueMap*>(getPrivate()); }
|
||||
struct Data;
|
||||
|
||||
Data* getData() { return static_cast<Data*>(getPrivate()); }
|
||||
ObjectValueMap* getMap();
|
||||
SymbolValueMap* getSymbolMap();
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
#include "gc/Nursery.h"
|
||||
#include "gc/Tracer.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Symbol.h"
|
||||
|
||||
#include "jswrapper.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
@@ -31,17 +33,64 @@ IsWeakRef(HandleValue v)
|
||||
return v.isObject() && v.toObject().is<WeakRefObject>();
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPrototypeFromWeakRefConstructor(JSContext* cx, HandleObject newTarget,
|
||||
MutableHandleObject proto)
|
||||
{
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, proto))
|
||||
return false;
|
||||
if (proto)
|
||||
return true;
|
||||
|
||||
RootedObject realmObject(cx, CheckedUnwrap(newTarget, /* stopAtWindowProxy = */ false));
|
||||
if (!realmObject)
|
||||
return false;
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, realmObject);
|
||||
Rooted<GlobalObject*> global(cx, &realmObject->global());
|
||||
if (!GlobalObject::ensureConstructor(cx, global, JSProto_WeakRef))
|
||||
return false;
|
||||
proto.set(&global->getPrototype(JSProto_WeakRef).toObject());
|
||||
}
|
||||
|
||||
return cx->compartment()->wrap(cx, proto);
|
||||
}
|
||||
|
||||
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
|
||||
if (!data) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data->isObject()) {
|
||||
JSObject* target = data->objectTarget.get();
|
||||
if (target) {
|
||||
RootedValue kept(cx, ObjectValue(*target));
|
||||
if (!cx->runtime()->addWeakRefKeptObject(cx, kept))
|
||||
return false;
|
||||
args.rval().set(kept);
|
||||
} else {
|
||||
args.rval().setUndefined();
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(data->isSymbol());
|
||||
JS::Symbol* target = data->symbolTarget.get();
|
||||
if (target) {
|
||||
RootedValue kept(cx, SymbolValue(target));
|
||||
if (!cx->runtime()->addWeakRefKeptObject(cx, kept))
|
||||
return false;
|
||||
args.rval().set(kept);
|
||||
} else {
|
||||
args.rval().setUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -83,13 +132,18 @@ InitWeakRefClass(JSContext* cx, HandleObject obj, bool defineMembers)
|
||||
}
|
||||
|
||||
/* static */ WeakRefObject*
|
||||
WeakRefObject::create(JSContext* cx, HandleObject target, HandleObject proto /* = nullptr */)
|
||||
WeakRefObject::create(JSContext* cx, HandleValue 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());
|
||||
Referent* data;
|
||||
if (target.isObject())
|
||||
data = cx->new_<Referent>(&target.toObject(), cx->options().weakRefs());
|
||||
else
|
||||
data = cx->new_<Referent>(target.toSymbol(), cx->options().weakRefs());
|
||||
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
@@ -97,6 +151,18 @@ WeakRefObject::create(JSContext* cx, HandleObject target, HandleObject proto /*
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CanBeHeldWeakly(HandleValue target)
|
||||
{
|
||||
if (target.isObject())
|
||||
return true;
|
||||
|
||||
if (target.isSymbol())
|
||||
return target.toSymbol()->code() != JS::SymbolCode::InSymbolRegistry;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -105,28 +171,25 @@ WeakRefObject::construct(JSContext* cx, unsigned argc, Value* 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());
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_WEAKREF_TARGET);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject target(cx, &args[0].toObject());
|
||||
RootedValue target(cx, args[0]);
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromWeakRefConstructor(cx, newTarget, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<WeakRefObject*> obj(cx, WeakRefObject::create(cx, target, proto));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
if (!cx->runtime()->addWeakRefKeptObject(cx, target))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
@@ -142,19 +205,25 @@ WeakRefObject::deref(JSContext* cx, unsigned argc, Value* vp)
|
||||
WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
if (Referent* data = GetReferent(obj)) {
|
||||
JSObject* target = data->target.unbarrieredGet();
|
||||
if (!target)
|
||||
return;
|
||||
if (data->isObject()) {
|
||||
JSObject* target = data->objectTarget.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");
|
||||
if (!data->enabled) {
|
||||
// Pref disabled, don't do anything.
|
||||
} else if (IsInsideNursery(target)) {
|
||||
// Weak edges must be tenured; trace strongly while referent is in the nursery.
|
||||
TraceManuallyBarrieredEdge(trc, data->objectTarget.unsafeGet(),
|
||||
"WeakRef nursery referent");
|
||||
} else {
|
||||
TraceWeakEdge(trc, &data->objectTarget, "WeakRef referent");
|
||||
}
|
||||
} else {
|
||||
TraceWeakEdge(trc, &data->target, "WeakRef referent");
|
||||
MOZ_ASSERT(data->isSymbol());
|
||||
JS::Symbol* target = data->symbolTarget.unbarrieredGet();
|
||||
if (data->enabled && target)
|
||||
TraceWeakEdge(trc, &data->symbolTarget, "WeakRef symbol referent");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +231,6 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
/* static */ void
|
||||
WeakRefObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(!fop->maybeOffMainThread());
|
||||
if (Referent* data = GetReferent(obj))
|
||||
fop->delete_(data);
|
||||
}
|
||||
|
||||
@@ -11,20 +11,44 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
bool CanBeHeldWeakly(HandleValue target);
|
||||
|
||||
class WeakRefObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
struct Referent {
|
||||
enum class Kind {
|
||||
Object,
|
||||
Symbol
|
||||
};
|
||||
|
||||
explicit Referent(JSObject* obj, bool enabled)
|
||||
: target(obj), enabled(enabled) {}
|
||||
WeakRef<JSObject*> target;
|
||||
: kind(Kind::Object)
|
||||
, objectTarget(obj)
|
||||
, symbolTarget(nullptr)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
explicit Referent(JS::Symbol* sym, bool enabled)
|
||||
: kind(Kind::Symbol)
|
||||
, objectTarget(nullptr)
|
||||
, symbolTarget(sym)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
bool isObject() const { return kind == Kind::Object; }
|
||||
bool isSymbol() const { return kind == Kind::Symbol; }
|
||||
|
||||
Kind kind;
|
||||
WeakRef<JSObject*> objectTarget;
|
||||
WeakRef<JS::Symbol*> symbolTarget;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static const Class class_;
|
||||
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static WeakRefObject* create(JSContext* cx, HandleObject target, HandleObject proto = nullptr);
|
||||
static WeakRefObject* create(JSContext* cx, HandleValue target, HandleObject proto = nullptr);
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
@@ -37,7 +61,8 @@ class WeakRefObject : public NativeObject
|
||||
|
||||
WeakRef<JSObject*>& target() {
|
||||
MOZ_ASSERT(getData());
|
||||
return getData()->target;
|
||||
MOZ_ASSERT(getData()->isObject());
|
||||
return getData()->objectTarget;
|
||||
}
|
||||
|
||||
static const JSPropertySpec properties[];
|
||||
|
||||
@@ -32,7 +32,7 @@ function WeakSet_add(value) {
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
|
||||
|
||||
// Step 5.
|
||||
if (!IsObject(value))
|
||||
if (!CanBeHeldWeakly(value))
|
||||
ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, value));
|
||||
|
||||
// Steps 7-8.
|
||||
@@ -55,7 +55,7 @@ function WeakSet_delete(value) {
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
|
||||
|
||||
// Step 5.
|
||||
if (!IsObject(value))
|
||||
if (!CanBeHeldWeakly(value))
|
||||
return false;
|
||||
|
||||
// Steps 7-8.
|
||||
@@ -75,7 +75,7 @@ function WeakSet_has(value) {
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
|
||||
|
||||
// Step 6.
|
||||
if (!IsObject(value))
|
||||
if (!CanBeHeldWeakly(value))
|
||||
return false;
|
||||
|
||||
// Steps 7-8.
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "builtin/MapObject.h"
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "builtin/WeakMapObject.h"
|
||||
#include "builtin/WeakRefObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
@@ -108,7 +109,6 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
if (optimized) {
|
||||
RootedValue keyVal(cx);
|
||||
RootedObject keyObject(cx);
|
||||
RootedValue placeholder(cx, BooleanValue(true));
|
||||
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
|
||||
RootedArrayObject array(cx, &iterable.toObject().as<ArrayObject>());
|
||||
@@ -116,7 +116,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
keyVal.set(array->getDenseElement(index));
|
||||
MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
|
||||
|
||||
if (keyVal.isPrimitive()) {
|
||||
if (!CanBeHeldWeakly(keyVal)) {
|
||||
UniqueChars bytes =
|
||||
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr);
|
||||
if (!bytes)
|
||||
@@ -126,8 +126,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
keyObject = &keyVal.toObject();
|
||||
if (!SetWeakMapEntry(cx, map, keyObject, placeholder))
|
||||
if (!SetWeakMapEntryValue(cx, map, keyVal, placeholder))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -939,6 +939,8 @@ class GCRuntime
|
||||
IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
|
||||
template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
|
||||
void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
|
||||
template <class ZoneIterT> void traceFinalizationRegistryWeakRefs();
|
||||
void traceFinalizationRegistryWeakRefsInCurrentGroup();
|
||||
template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
|
||||
void markBufferedGrayRoots(JS::Zone* zone);
|
||||
void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
|
||||
@@ -951,6 +953,7 @@ class GCRuntime
|
||||
void getNextZoneGroup();
|
||||
void endMarkingZoneGroup();
|
||||
void beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock);
|
||||
void sweepFinalizationRegistries();
|
||||
bool shouldReleaseObservedTypes();
|
||||
void endSweepingZoneGroup();
|
||||
IncrementalProgress performSweepActions(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
|
||||
@@ -977,7 +980,7 @@ class GCRuntime
|
||||
void sweepTypesAfterCompacting(Zone* zone);
|
||||
void sweepZoneAfterCompacting(Zone* zone);
|
||||
[[nodiscard]] bool relocateArenas(Zone* zone, JS::gcreason::Reason reason,
|
||||
Arena*& relocatedListOut, SliceBudget& sliceBudget);
|
||||
Arena*& relocatedListOut, SliceBudget& sliceBudget);
|
||||
void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
|
||||
void updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, size_t bgTaskCount);
|
||||
void updateAllCellPointers(MovingTracer* trc, Zone* zone);
|
||||
|
||||
+10
-10
@@ -416,13 +416,13 @@ MapMemoryAt(void* desired, size_t length, int prot = PROT_READ | PROT_WRITE,
|
||||
int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0)
|
||||
|
||||
// Solaris manages 64-bit address space in a different manner from every other
|
||||
// AMD64 operating system, but fortunately the fix is the same one
|
||||
// required for every operating system on 64-bit SPARC, Itanium, and ARM.
|
||||
// AMD64 operating system, but fortunately the fix is the same one
|
||||
// required for every operating system on 64-bit SPARC and ARM.
|
||||
// Most people's intuition failed them here and they thought this couldn't
|
||||
// possibly be correct on AMD64, but for Solaris/illumos it is.
|
||||
|
||||
{
|
||||
#if defined(__ia64__) || defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) || (defined(__sparc__) && defined(__arch64__) && (defined(__NetBSD__) || defined(__linux__)))
|
||||
#if defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) || (defined(__sparc__) && defined(__arch64__) && (defined(__NetBSD__) || defined(__linux__)))
|
||||
MOZ_ASSERT((0xffff800000000000ULL & (uintptr_t(desired) + length - 1)) == 0);
|
||||
#endif
|
||||
void* region = mmap(desired, length, prot, flags, fd, offset);
|
||||
@@ -445,14 +445,14 @@ static inline void*
|
||||
MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE,
|
||||
int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0)
|
||||
{
|
||||
#if defined(__ia64__) || (defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__))
|
||||
#if defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__)
|
||||
/*
|
||||
* The JS engine assumes that all allocated pointers have their high 17 bits clear,
|
||||
* which ia64's mmap doesn't support directly. However, we can emulate it by passing
|
||||
* mmap an "addr" parameter with those bits clear. The mmap will return that address,
|
||||
* or the nearest available memory above that address, providing a near-guarantee
|
||||
* that those bits are clear. If they are not, we return nullptr below to indicate
|
||||
* out-of-memory.
|
||||
* which mmap on this platform doesn't support directly. However, we can emulate it
|
||||
* by passing mmap an "addr" parameter with those bits clear. The mmap will return
|
||||
* that address, or the nearest available memory above that address, providing a
|
||||
* near-guarantee that those bits are clear. If they are not, we return nullptr below
|
||||
* to indicate out-of-memory.
|
||||
*
|
||||
* The addr is chosen as 0x0000070000000000, which still allows about 120TB of virtual
|
||||
* address space.
|
||||
@@ -479,7 +479,7 @@ MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE,
|
||||
* different due to the different mmap behavior.
|
||||
*
|
||||
* TODO: Merge with the above code block if this implementation works for
|
||||
* ia64 and sparc64.
|
||||
* sparc64.
|
||||
*/
|
||||
const uintptr_t start = UINT64_C(0x0000070000000000);
|
||||
const uintptr_t end = UINT64_C(0x0000800000000000);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "ds/SplayTree.h"
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/FindSCCs.h"
|
||||
#include "gc/GCRuntime.h"
|
||||
#include "js/GCHashTable.h"
|
||||
@@ -325,6 +326,10 @@ struct Zone : public JS::shadow::Zone,
|
||||
using WeakEdges = js::Vector<js::gc::TenuredCell**, 0, js::SystemAllocPolicy>;
|
||||
WeakEdges gcWeakRefs;
|
||||
|
||||
// FinalizationRegistry objects with weakly-held target cells in this zone.
|
||||
using FinalizationRegistryVector = js::Vector<js::WeakRef<JSObject*>, 0, js::SystemAllocPolicy>;
|
||||
FinalizationRegistryVector finalizationRegistries;
|
||||
|
||||
// List of non-ephemeron weak containers to sweep during beginSweepingZoneGroup.
|
||||
mozilla::LinkedList<WeakCache<void*>> weakCaches_;
|
||||
void registerWeakCache(WeakCache<void*>* cachep) {
|
||||
|
||||
@@ -1470,6 +1470,12 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_
|
||||
res.isNumber() &&
|
||||
!TypedArrayGetElemStubExists(stub, obj))
|
||||
{
|
||||
if (obj->is<TypedArrayObject>() &&
|
||||
obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cx->runtime()->jitSupportsFloatingPoint &&
|
||||
(TypedThingRequiresFloatingPoint(obj) || rhs.isDouble()))
|
||||
{
|
||||
@@ -2160,6 +2166,8 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
Register obj = masm.extractObject(R0, ExtractTemp0);
|
||||
masm.loadPtr(Address(ICStubReg, ICGetElem_TypedArray::offsetOfShape()), scratchReg);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
|
||||
if (layout_ == Layout_TypedArray)
|
||||
GuardResizableOrGrowableTypedArray(masm, obj, scratchReg, &failure);
|
||||
|
||||
// Ensure the index is an integer.
|
||||
if (cx->runtime()->jitSupportsFloatingPoint) {
|
||||
@@ -2631,6 +2639,9 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||
bool expectOutOfBounds;
|
||||
double idx = index.toNumber();
|
||||
if (obj->is<TypedArrayObject>()) {
|
||||
if (obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
||||
return true;
|
||||
|
||||
expectOutOfBounds = (idx < 0 || idx >= double(obj->as<TypedArrayObject>().length()));
|
||||
} else {
|
||||
// Typed objects throw on out of bounds accesses. Don't attach
|
||||
@@ -3221,6 +3232,8 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
Register obj = masm.extractObject(R0, ExtractTemp0);
|
||||
masm.loadPtr(Address(ICStubReg, ICSetElem_TypedArray::offsetOfShape()), scratchReg);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
|
||||
if (layout_ == Layout_TypedArray)
|
||||
GuardResizableOrGrowableTypedArray(masm, obj, scratchReg, &failure);
|
||||
|
||||
// Ensure the index is an integer.
|
||||
if (cx->runtime()->jitSupportsFloatingPoint) {
|
||||
|
||||
@@ -8889,9 +8889,17 @@ CodeGenerator::branchIfNotEmptyObjectElements(Register obj, Label* target)
|
||||
Address(obj, NativeObject::offsetOfElements()),
|
||||
ImmPtr(js::emptyObjectElements),
|
||||
&emptyObj);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
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,
|
||||
Address(obj, NativeObject::offsetOfElements()),
|
||||
ImmPtr(js::emptyObjectElementsSharedResizableOrGrowable),
|
||||
target);
|
||||
masm.bind(&emptyObj);
|
||||
}
|
||||
|
||||
@@ -537,8 +537,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||
Label* ifDoesntEmulateUndefined,
|
||||
Register scratch, OutOfLineTestObject* ool);
|
||||
|
||||
// Branch to target unless obj has an emptyObjectElements or emptyObjectElementsShared
|
||||
// elements pointer.
|
||||
// Branch to target unless obj has one of the empty elements pointers.
|
||||
void branchIfNotEmptyObjectElements(Register obj, Label* target);
|
||||
|
||||
void emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType,
|
||||
|
||||
@@ -1245,6 +1245,7 @@ GenerateTypedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAtta
|
||||
masm.branchPtr(Assembler::AboveOrEqual, tmpReg,
|
||||
ImmPtr(&TypedArrayObject::classes[Scalar::MaxTypedArrayViewType]),
|
||||
failures);
|
||||
GuardResizableOrGrowableTypedArray(masm, object, tmpReg, failures);
|
||||
|
||||
// Load length.
|
||||
masm.loadTypedOrValue(Address(object, TypedArrayObject::lengthOffset()), output);
|
||||
@@ -1644,6 +1645,9 @@ GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript
|
||||
if (!JSID_IS_ATOM(id, cx->names().length))
|
||||
return true;
|
||||
|
||||
if (obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
||||
return true;
|
||||
|
||||
if (hasTypedArrayLengthStub(obj))
|
||||
return true;
|
||||
|
||||
@@ -4038,6 +4042,12 @@ GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& i
|
||||
if (!obj->is<TypedArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
return false;
|
||||
|
||||
if (obj->is<TypedArrayObject>() &&
|
||||
obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(idval.isInt32() || idval.isString());
|
||||
|
||||
// Don't emit a stub if the access is out of bounds. We make to make
|
||||
@@ -4092,6 +4102,9 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
|
||||
// Decide to what type index the stub should be optimized
|
||||
Register tmpReg = output.scratchReg().gpr();
|
||||
MOZ_ASSERT(tmpReg != InvalidReg);
|
||||
if (array->is<TypedArrayObject>())
|
||||
GuardResizableOrGrowableTypedArray(masm, object, tmpReg, &failures);
|
||||
|
||||
Register indexReg = tmpReg;
|
||||
if (idval.isString()) {
|
||||
MOZ_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
|
||||
@@ -4575,6 +4588,7 @@ GenerateSetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::Stub
|
||||
if (!shape)
|
||||
return false;
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
|
||||
GuardResizableOrGrowableTypedArray(masm, object, temp, &failures);
|
||||
|
||||
// Ensure the index is an int32.
|
||||
Register indexReg;
|
||||
@@ -4661,6 +4675,9 @@ SetPropertyIC::tryAttachTypedArrayElement(JSContext* cx, HandleScript outerScrip
|
||||
if (!IsTypedArrayElementSetInlineable(obj, idval, val))
|
||||
return true;
|
||||
|
||||
if (obj->as<TypedArrayObject>().hasResizableOrGrowableBuffer())
|
||||
return true;
|
||||
|
||||
*emitted = true;
|
||||
|
||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||
|
||||
+1
-1
@@ -1928,7 +1928,7 @@ LAllocation::toRegister() const
|
||||
# endif
|
||||
# include "jit/mips-shared/LIR-mips-shared.h"
|
||||
#elif defined(JS_CODEGEN_LOONGARCH64)
|
||||
# include "jit/mips64/LIR-mips64.h"
|
||||
# include "jit/loongarch64/LIR-loongarch64.h"
|
||||
# include "jit/mips-shared/LIR-mips-shared.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/LIR-none.h"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
# include "jit/arm64/LOpcodes-arm64.h"
|
||||
#elif defined(JS_CODEGEN_LOONGARCH64)
|
||||
# include "jit/mips64/LOpcodes-mips64.h"
|
||||
# include "jit/loongarch64/LOpcodes-loongarch64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/LOpcodes-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
|
||||
@@ -365,13 +365,9 @@ IonBuilder::inlineNativeGetter(CallInfo& callInfo, JSFunction* target)
|
||||
|
||||
// Try to optimize typed array lengths.
|
||||
if (TypedArrayObject::isOriginalLengthGetter(native)) {
|
||||
Scalar::Type type = thisTypes->getTypedArrayType(constraints());
|
||||
if (type == Scalar::MaxTypedArrayViewType)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MInstruction* length = addTypedArrayLength(thisArg);
|
||||
current->push(length);
|
||||
return InliningStatus_Inlined;
|
||||
// RAB/GSAB views can have dynamic length or temporarily become
|
||||
// out-of-bounds. Let the property IC/VM path handle the getter.
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Try to optimize RegExp getters.
|
||||
@@ -2480,21 +2476,10 @@ IsTypedArrayObject(CompilerConstraintList* constraints, MDefinition* def)
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlinePossiblyWrappedTypedArrayLength(CallInfo& callInfo)
|
||||
{
|
||||
MOZ_ASSERT(!callInfo.constructing());
|
||||
MOZ_ASSERT(callInfo.argc() == 1);
|
||||
if (callInfo.getArg(0)->type() != MIRType::Object)
|
||||
return InliningStatus_NotInlined;
|
||||
if (getInlineReturnType() != MIRType::Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
(void) callInfo;
|
||||
|
||||
if (!IsTypedArrayObject(constraints(), callInfo.getArg(0)))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MInstruction* length = addTypedArrayLength(callInfo.getArg(0));
|
||||
current->push(length);
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
return InliningStatus_Inlined;
|
||||
// RAB/GSAB views require dynamic length semantics.
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
@@ -3251,45 +3236,14 @@ bool
|
||||
IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayType,
|
||||
bool* requiresTagCheck, AtomicCheckResult checkResult)
|
||||
{
|
||||
if (!JitSupportsAtomics())
|
||||
return false;
|
||||
(void) callInfo;
|
||||
(void) arrayType;
|
||||
(void) requiresTagCheck;
|
||||
(void) checkResult;
|
||||
|
||||
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;
|
||||
}
|
||||
// Atomics bounds checks through addTypedArrayLengthAndData assume stable
|
||||
// SharedArrayBuffer lengths. Avoid this path now that growable SAB exists.
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+8
-18
@@ -5505,25 +5505,15 @@ jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints,
|
||||
MDefinition* obj, MDefinition* id,
|
||||
Scalar::Type* arrayType)
|
||||
{
|
||||
if (obj->mightBeType(MIRType::String))
|
||||
return false;
|
||||
(void) constraints;
|
||||
(void) obj;
|
||||
(void) id;
|
||||
(void) arrayType;
|
||||
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
# include "jit/arm64/MoveEmitter-arm64.h"
|
||||
#elif defined(JS_CODEGEN_LOONGARCH64)
|
||||
# include "jit/mips64/MoveEmitter-mips64.h"
|
||||
# include "jit/loongarch64/MoveEmitter-loongarch64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/MoveEmitter-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
|
||||
@@ -3631,6 +3631,17 @@ CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Labe
|
||||
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
|
||||
LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result)
|
||||
{
|
||||
|
||||
@@ -2307,6 +2307,10 @@ CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register
|
||||
void
|
||||
CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure);
|
||||
|
||||
void
|
||||
GuardResizableOrGrowableTypedArray(MacroAssembler& masm, Register obj, Register scratch,
|
||||
Label* failure);
|
||||
|
||||
[[nodiscard]] bool
|
||||
DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
|
||||
MutableHandleValue result);
|
||||
|
||||
@@ -320,7 +320,6 @@ size_t AssemblerLOONGARCH64::bytesNeeded() const {
|
||||
|
||||
// write a blob of binary into the instruction stream
|
||||
BufferOffset AssemblerLOONGARCH64::writeInst(uint32_t x, uint32_t* dest) {
|
||||
MOZ_ASSERT(hasCreator());
|
||||
if (dest == nullptr) {
|
||||
return m_buffer.putInt(x);
|
||||
}
|
||||
|
||||
@@ -212,6 +212,8 @@ static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
static constexpr Register WasmIonExitRegCallee = t0;
|
||||
static constexpr Register WasmIonExitRegE0 = a0;
|
||||
static constexpr Register WasmIonExitRegE1 = a1;
|
||||
static constexpr Register WasmIonExitRegReturnData = JSReturnReg_Data;
|
||||
static constexpr Register WasmIonExitRegReturnType = JSReturnReg_Type;
|
||||
static constexpr Register WasmIonExitRegD0 = a0;
|
||||
static constexpr Register WasmIonExitRegD1 = a1;
|
||||
static constexpr Register WasmIonExitRegD2 = t0;
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
#include "jit/loongarch64/CodeGenerator-loongarch64.h"
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "jit/JitCompartment.h"
|
||||
#include "jit/JitFrames.h"
|
||||
#include "jit/MIR.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
@@ -99,6 +103,255 @@ CodeGeneratorLoongArch64::emitTableSwitchDispatch(MTableSwitch* mir, Register in
|
||||
masm.jump(base);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitOutOfLineBailout(OutOfLineBailout* ool)
|
||||
{
|
||||
masm.push(ImmWord(ool->snapshot()->snapshotOffset()));
|
||||
masm.jump(&deoptLabel_);
|
||||
}
|
||||
|
||||
FrameSizeClass
|
||||
FrameSizeClass::FromDepth(uint32_t frameDepth)
|
||||
{
|
||||
return FrameSizeClass::None();
|
||||
}
|
||||
|
||||
FrameSizeClass
|
||||
FrameSizeClass::ClassLimit()
|
||||
{
|
||||
return FrameSizeClass(0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
FrameSizeClass::frameSize() const
|
||||
{
|
||||
MOZ_CRASH("LoongArch64 does not use frame size classes");
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorLoongArch64::ToValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getOperand(pos)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorLoongArch64::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getDef(0)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorLoongArch64::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getTemp(pos)));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitBox(LBox* box)
|
||||
{
|
||||
const LAllocation* in = box->getOperand(0);
|
||||
const LDefinition* result = box->getDef(0);
|
||||
|
||||
if (IsFloatingPointType(box->type())) {
|
||||
FloatRegister reg = ToFloatRegister(in);
|
||||
if (box->type() == MIRType::Float32) {
|
||||
masm.convertFloat32ToDouble(reg, ScratchDoubleReg);
|
||||
reg = ScratchDoubleReg;
|
||||
}
|
||||
masm.moveFromDouble(reg, ToRegister(result));
|
||||
} else {
|
||||
masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitUnbox(LUnbox* unbox)
|
||||
{
|
||||
MUnbox* mir = unbox->mir();
|
||||
|
||||
if (mir->fallible()) {
|
||||
const ValueOperand value = ToValue(unbox, LUnbox::Input);
|
||||
masm.splitTag(value, SecondScratchReg);
|
||||
bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(MIRTypeToTag(mir->type())),
|
||||
unbox->snapshot());
|
||||
}
|
||||
|
||||
LAllocation* input = unbox->getOperand(LUnbox::Input);
|
||||
Register result = ToRegister(unbox->output());
|
||||
if (input->isRegister()) {
|
||||
Register inputReg = ToRegister(input);
|
||||
switch (mir->type()) {
|
||||
case MIRType::Int32:
|
||||
masm.unboxInt32(inputReg, result);
|
||||
break;
|
||||
case MIRType::Boolean:
|
||||
masm.unboxBoolean(inputReg, result);
|
||||
break;
|
||||
case MIRType::Object:
|
||||
masm.unboxObject(inputReg, result);
|
||||
break;
|
||||
case MIRType::String:
|
||||
masm.unboxString(inputReg, result);
|
||||
break;
|
||||
case MIRType::Symbol:
|
||||
masm.unboxSymbol(inputReg, result);
|
||||
break;
|
||||
case MIRType::BigInt:
|
||||
masm.unboxBigInt(inputReg, result);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Given MIRType cannot be unboxed.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Address inputAddr = ToAddress(input);
|
||||
switch (mir->type()) {
|
||||
case MIRType::Int32:
|
||||
masm.unboxInt32(inputAddr, result);
|
||||
break;
|
||||
case MIRType::Boolean:
|
||||
masm.unboxBoolean(inputAddr, result);
|
||||
break;
|
||||
case MIRType::Object:
|
||||
masm.unboxObject(inputAddr, result);
|
||||
break;
|
||||
case MIRType::String:
|
||||
masm.unboxString(inputAddr, result);
|
||||
break;
|
||||
case MIRType::Symbol:
|
||||
masm.unboxSymbol(inputAddr, result);
|
||||
break;
|
||||
case MIRType::BigInt:
|
||||
masm.unboxBigInt(inputAddr, result);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Given MIRType cannot be unboxed.");
|
||||
}
|
||||
}
|
||||
|
||||
Register
|
||||
CodeGeneratorLoongArch64::splitTagForTest(const ValueOperand& value)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
return SecondScratchReg;
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCompareB(LCompareB* lir)
|
||||
{
|
||||
MCompare* mir = lir->mir();
|
||||
|
||||
const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
|
||||
const LAllocation* rhs = lir->rhs();
|
||||
const Register output = ToRegister(lir->output());
|
||||
|
||||
MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
|
||||
if (rhs->isConstant())
|
||||
masm.moveValue(rhs->toConstant()->toJSValue(), ScratchRegister);
|
||||
else
|
||||
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
|
||||
|
||||
masm.cmpPtrSet(cond, lhs.valueReg(), ScratchRegister, output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCompareBAndBranch(LCompareBAndBranch* lir)
|
||||
{
|
||||
MCompare* mir = lir->cmpMir();
|
||||
const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
|
||||
const LAllocation* rhs = lir->rhs();
|
||||
|
||||
MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
|
||||
|
||||
if (rhs->isConstant())
|
||||
masm.moveValue(rhs->toConstant()->toJSValue(), ScratchRegister);
|
||||
else
|
||||
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
|
||||
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
emitBranch(lhs.valueReg(), ScratchRegister, cond, lir->ifTrue(), lir->ifFalse());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCompareBitwise(LCompareBitwise* lir)
|
||||
{
|
||||
MCompare* mir = lir->mir();
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
const ValueOperand lhs = ToValue(lir, LCompareBitwise::LhsInput);
|
||||
const ValueOperand rhs = ToValue(lir, LCompareBitwise::RhsInput);
|
||||
const Register output = ToRegister(lir->output());
|
||||
|
||||
MOZ_ASSERT(IsEqualityOp(mir->jsop()));
|
||||
|
||||
masm.cmpPtrSet(cond, lhs.valueReg(), rhs.valueReg(), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
|
||||
{
|
||||
MCompare* mir = lir->cmpMir();
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
const ValueOperand lhs = ToValue(lir, LCompareBitwiseAndBranch::LhsInput);
|
||||
const ValueOperand rhs = ToValue(lir, LCompareBitwiseAndBranch::RhsInput);
|
||||
|
||||
MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
|
||||
mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
|
||||
|
||||
emitBranch(lhs.valueReg(), rhs.valueReg(), cond, lir->ifTrue(), lir->ifFalse());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCompareI64(LCompareI64* lir)
|
||||
{
|
||||
MCompare* mir = lir->mir();
|
||||
MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
||||
mir->compareType() == MCompare::Compare_UInt64);
|
||||
|
||||
const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
||||
const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
||||
Register lhsReg = ToRegister64(lhs).reg;
|
||||
Register output = ToRegister(lir->output());
|
||||
Register rhsReg;
|
||||
|
||||
if (IsConstant(rhs)) {
|
||||
rhsReg = ScratchRegister;
|
||||
masm.ma_li(rhsReg, ImmWord(ToInt64(rhs)));
|
||||
} else {
|
||||
rhsReg = ToRegister64(rhs).reg;
|
||||
}
|
||||
|
||||
bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
||||
masm.cmpPtrSet(JSOpToCondition(lir->jsop(), isSigned), lhsReg, rhsReg, output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCompareI64AndBranch(LCompareI64AndBranch* lir)
|
||||
{
|
||||
MCompare* mir = lir->cmpMir();
|
||||
MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
||||
mir->compareType() == MCompare::Compare_UInt64);
|
||||
|
||||
const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
||||
const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
||||
Register lhsReg = ToRegister64(lhs).reg;
|
||||
Register rhsReg;
|
||||
|
||||
if (IsConstant(rhs)) {
|
||||
rhsReg = ScratchRegister;
|
||||
masm.ma_li(rhsReg, ImmWord(ToInt64(rhs)));
|
||||
} else {
|
||||
rhsReg = ToRegister64(rhs).reg;
|
||||
}
|
||||
|
||||
bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
||||
Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
|
||||
emitBranch(lhsReg, rhsReg, cond, lir->ifTrue(), lir->ifFalse());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCompare(LCompare* comp)
|
||||
{
|
||||
@@ -369,6 +622,246 @@ CodeGeneratorLoongArch64::visitUDivOrModI64(LUDivOrModI64* lir)
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
CodeGeneratorLoongArch64::emitWasmLoadI64(T* lir)
|
||||
{
|
||||
const MWasmLoad* mir = lir->mir();
|
||||
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||
|
||||
uint32_t offset = mir->access().offset();
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
|
||||
Register ptr = ToRegister(lir->ptr());
|
||||
|
||||
if (offset) {
|
||||
Register ptrPlusOffset = ToRegister(lir->ptrCopy());
|
||||
masm.addPtr(Imm32(offset), ptrPlusOffset);
|
||||
ptr = ptrPlusOffset;
|
||||
} else {
|
||||
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
|
||||
}
|
||||
|
||||
unsigned byteSize = mir->access().byteSize();
|
||||
bool isSigned;
|
||||
|
||||
switch (mir->access().type()) {
|
||||
case Scalar::Int8: isSigned = true; break;
|
||||
case Scalar::Uint8: isSigned = false; break;
|
||||
case Scalar::Int16: isSigned = true; break;
|
||||
case Scalar::Uint16: isSigned = false; break;
|
||||
case Scalar::Int32: isSigned = true; break;
|
||||
case Scalar::Uint32: isSigned = false; break;
|
||||
case Scalar::Int64: isSigned = true; break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
|
||||
masm.memoryBarrier(mir->access().barrierBefore());
|
||||
|
||||
if (IsUnaligned(mir->access())) {
|
||||
Register temp = ToRegister(lir->getTemp(1));
|
||||
|
||||
masm.ma_load_unaligned(mir->access(), ToOutRegister64(lir).reg,
|
||||
BaseIndex(HeapReg, ptr, TimesOne), temp,
|
||||
static_cast<LoadStoreSize>(8 * byteSize),
|
||||
isSigned ? SignExtend : ZeroExtend);
|
||||
return;
|
||||
}
|
||||
|
||||
masm.ma_load(ToOutRegister64(lir).reg, BaseIndex(HeapReg, ptr, TimesOne),
|
||||
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
||||
masm.append(mir->access(), masm.size() - 4, masm.framePushed());
|
||||
|
||||
masm.memoryBarrier(mir->access().barrierAfter());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmLoadI64(LWasmLoadI64* lir)
|
||||
{
|
||||
emitWasmLoadI64(lir);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir)
|
||||
{
|
||||
emitWasmLoadI64(lir);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
CodeGeneratorLoongArch64::emitWasmStoreI64(T* lir)
|
||||
{
|
||||
const MWasmStore* mir = lir->mir();
|
||||
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||
|
||||
uint32_t offset = mir->access().offset();
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
|
||||
Register ptr = ToRegister(lir->ptr());
|
||||
|
||||
if (offset) {
|
||||
Register ptrPlusOffset = ToRegister(lir->ptrCopy());
|
||||
masm.addPtr(Imm32(offset), ptrPlusOffset);
|
||||
ptr = ptrPlusOffset;
|
||||
} else {
|
||||
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
|
||||
}
|
||||
|
||||
unsigned byteSize = mir->access().byteSize();
|
||||
bool isSigned;
|
||||
|
||||
switch (mir->access().type()) {
|
||||
case Scalar::Int8: isSigned = true; break;
|
||||
case Scalar::Uint8: isSigned = false; break;
|
||||
case Scalar::Int16: isSigned = true; break;
|
||||
case Scalar::Uint16: isSigned = false; break;
|
||||
case Scalar::Int32: isSigned = true; break;
|
||||
case Scalar::Uint32: isSigned = false; break;
|
||||
case Scalar::Int64: isSigned = true; break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
|
||||
masm.memoryBarrier(mir->access().barrierBefore());
|
||||
|
||||
if (IsUnaligned(mir->access())) {
|
||||
Register temp = ToRegister(lir->getTemp(1));
|
||||
|
||||
masm.ma_store_unaligned(mir->access(), ToRegister64(lir->value()).reg,
|
||||
BaseIndex(HeapReg, ptr, TimesOne), temp,
|
||||
static_cast<LoadStoreSize>(8 * byteSize),
|
||||
isSigned ? SignExtend : ZeroExtend);
|
||||
return;
|
||||
}
|
||||
|
||||
masm.ma_store(ToRegister64(lir->value()).reg, BaseIndex(HeapReg, ptr, TimesOne),
|
||||
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
||||
masm.append(mir->access(), masm.size() - 4, masm.framePushed());
|
||||
|
||||
masm.memoryBarrier(mir->access().barrierAfter());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmStoreI64(LWasmStoreI64* lir)
|
||||
{
|
||||
emitWasmStoreI64(lir);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* lir)
|
||||
{
|
||||
emitWasmStoreI64(lir);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
|
||||
{
|
||||
const MWasmLoadGlobalVar* mir = ins->mir();
|
||||
unsigned addr = mir->globalDataOffset() - WasmGlobalRegBias;
|
||||
MOZ_ASSERT(mir->type() == MIRType::Int64);
|
||||
masm.load64(Address(GlobalReg, addr), ToOutRegister64(ins));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
|
||||
{
|
||||
const MWasmStoreGlobalVar* mir = ins->mir();
|
||||
unsigned addr = mir->globalDataOffset() - WasmGlobalRegBias;
|
||||
MOZ_ASSERT(mir->value()->type() == MIRType::Int64);
|
||||
masm.store64(ToRegister64(ins->value()), Address(GlobalReg, addr));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmSelectI64(LWasmSelectI64* lir)
|
||||
{
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||
|
||||
Register cond = ToRegister(lir->condExpr());
|
||||
const LInt64Allocation falseExpr = lir->falseExpr();
|
||||
|
||||
Register64 out = ToOutRegister64(lir);
|
||||
MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, "true expr is reused for input");
|
||||
|
||||
if (falseExpr.value().isRegister()) {
|
||||
masm.as_movz(out.reg, ToRegister(falseExpr.value()), cond);
|
||||
} else {
|
||||
Label done;
|
||||
masm.ma_b(cond, cond, &done, Assembler::NonZero, ShortJump);
|
||||
masm.loadPtr(ToAddress(falseExpr.value()), out.reg);
|
||||
masm.bind(&done);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmReinterpretFromI64(LWasmReinterpretFromI64* lir)
|
||||
{
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Double);
|
||||
MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64);
|
||||
masm.as_dmtc1(ToRegister(lir->input()), ToFloatRegister(lir->output()));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmReinterpretToI64(LWasmReinterpretToI64* lir)
|
||||
{
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||
MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double);
|
||||
masm.as_dmfc1(ToRegister(lir->output()), ToFloatRegister(lir->input()));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
||||
{
|
||||
const LAllocation* input = lir->getOperand(0);
|
||||
Register output = ToRegister(lir->output());
|
||||
|
||||
if (lir->mir()->isUnsigned())
|
||||
masm.ma_dext(output, ToRegister(input), Imm32(0), Imm32(32));
|
||||
else
|
||||
masm.ma_sll(output, ToRegister(input), Imm32(0));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
|
||||
{
|
||||
const LAllocation* input = lir->getOperand(0);
|
||||
Register output = ToRegister(lir->output());
|
||||
|
||||
if (lir->mir()->bottomHalf()) {
|
||||
if (input->isMemory())
|
||||
masm.load32(ToAddress(input), output);
|
||||
else
|
||||
masm.ma_sll(output, ToRegister(input), Imm32(0));
|
||||
} else {
|
||||
MOZ_CRASH("Not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitClzI64(LClzI64* lir)
|
||||
{
|
||||
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||
Register64 output = ToOutRegister64(lir);
|
||||
masm.clz64(input, output.reg);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitCtzI64(LCtzI64* lir)
|
||||
{
|
||||
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||
Register64 output = ToOutRegister64(lir);
|
||||
masm.ctz64(input, output.reg);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitNotI64(LNotI64* lir)
|
||||
{
|
||||
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||
Register output = ToRegister(lir->output());
|
||||
|
||||
masm.cmp64Set(Assembler::Equal, input.reg, Imm32(0), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
|
||||
{
|
||||
@@ -485,6 +978,49 @@ CodeGeneratorLoongArch64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
|
||||
masm.moveFromDouble(ScratchDoubleReg, output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
|
||||
{
|
||||
Register input = ToRegister(lir->input());
|
||||
FloatRegister output = ToFloatRegister(lir->output());
|
||||
|
||||
MIRType outputType = lir->mir()->type();
|
||||
MOZ_ASSERT(outputType == MIRType::Double || outputType == MIRType::Float32);
|
||||
|
||||
if (outputType == MIRType::Double) {
|
||||
if (lir->mir()->isUnsigned())
|
||||
masm.convertUInt64ToDouble(input, output);
|
||||
else
|
||||
masm.convertInt64ToDouble(input, output);
|
||||
} else {
|
||||
if (lir->mir()->isUnsigned())
|
||||
masm.convertUInt64ToFloat32(input, output);
|
||||
else
|
||||
masm.convertInt64ToFloat32(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitTestI64AndBranch(LTestI64AndBranch* lir)
|
||||
{
|
||||
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||
MBasicBlock* ifTrue = lir->ifTrue();
|
||||
MBasicBlock* ifFalse = lir->ifFalse();
|
||||
|
||||
emitBranch(input.reg, Imm32(0), Assembler::NonZero, ifTrue, ifFalse);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
MOZ_ASSERT(ReturnFloat32Reg.encoding() == FloatRegisters::f0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.encoding() == FloatRegisters::f0);
|
||||
FloatRegister f1 = { FloatRegisters::f1, FloatRegisters::Single };
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(f1);
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorLoongArch64::visitWasmAddOffset(LWasmAddOffset* lir)
|
||||
{
|
||||
|
||||
@@ -6,28 +6,79 @@
|
||||
#ifndef jit_loongarch64_CodeGenerator_loongarch64_h
|
||||
#define jit_loongarch64_CodeGenerator_loongarch64_h
|
||||
|
||||
#define CodeGeneratorSpecific CodeGeneratorMIPS64Specific
|
||||
#include "jit/mips64/CodeGenerator-mips64.h"
|
||||
#undef CodeGeneratorSpecific
|
||||
#include "jit/mips-shared/CodeGenerator-mips-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class CodeGeneratorLoongArch64 : public CodeGeneratorMIPS64
|
||||
class CodeGeneratorLoongArch64 : public CodeGeneratorMIPSShared
|
||||
{
|
||||
public:
|
||||
using CodeGeneratorMIPS64::CodeGeneratorMIPS64;
|
||||
protected:
|
||||
void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
||||
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_NULL), cond, ifTrue, ifFalse);
|
||||
}
|
||||
void testUndefinedEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
||||
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), cond, ifTrue, ifFalse);
|
||||
}
|
||||
void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
||||
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), cond, ifTrue, ifFalse);
|
||||
}
|
||||
|
||||
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
||||
|
||||
template <typename T>
|
||||
void emitWasmLoadI64(T* ins);
|
||||
template <typename T>
|
||||
void emitWasmStoreI64(T* ins);
|
||||
|
||||
public:
|
||||
CodeGeneratorLoongArch64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
|
||||
: CodeGeneratorMIPSShared(gen, graph, masm)
|
||||
{ }
|
||||
|
||||
void visitCompareB(LCompareB* lir);
|
||||
void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
||||
void visitCompareBitwise(LCompareBitwise* lir);
|
||||
void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
|
||||
void visitCompare(LCompare* comp);
|
||||
void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
|
||||
void visitCompareAndBranch(LCompareAndBranch* comp);
|
||||
void visitCompareI64(LCompareI64* lir);
|
||||
void visitCompareI64AndBranch(LCompareI64AndBranch* lir);
|
||||
void visitDivI(LDivI* ins);
|
||||
void visitModI(LModI* ins);
|
||||
void visitUDivOrMod(LUDivOrMod* ins);
|
||||
void visitDivOrModI64(LDivOrModI64* lir);
|
||||
void visitUDivOrModI64(LUDivOrModI64* lir);
|
||||
void visitWasmLoadI64(LWasmLoadI64* lir);
|
||||
void visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir);
|
||||
void visitWasmStoreI64(LWasmStoreI64* ins);
|
||||
void visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* ins);
|
||||
void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
|
||||
void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
|
||||
void visitWasmSelectI64(LWasmSelectI64* ins);
|
||||
void visitWasmReinterpretFromI64(LWasmReinterpretFromI64* lir);
|
||||
void visitWasmReinterpretToI64(LWasmReinterpretToI64* lir);
|
||||
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
|
||||
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
|
||||
void visitClzI64(LClzI64* lir);
|
||||
void visitCtzI64(LCtzI64* lir);
|
||||
void visitNotI64(LNotI64* lir);
|
||||
void visitWasmTruncateToInt32(LWasmTruncateToInt32* lir);
|
||||
void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
|
||||
void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
|
||||
void visitTestI64AndBranch(LTestI64AndBranch* lir);
|
||||
void visitWasmAddOffset(LWasmAddOffset* lir);
|
||||
void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
|
||||
void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
|
||||
@@ -38,8 +89,21 @@ class CodeGeneratorLoongArch64 : public CodeGeneratorMIPS64
|
||||
void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
|
||||
void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
|
||||
|
||||
void visitOutOfLineBailout(OutOfLineBailout* ool);
|
||||
void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
|
||||
|
||||
protected:
|
||||
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
Register splitTagForTest(const ValueOperand& value);
|
||||
|
||||
public:
|
||||
void visitBox(LBox* box);
|
||||
void visitUnbox(LUnbox* unbox);
|
||||
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
};
|
||||
|
||||
typedef CodeGeneratorLoongArch64 CodeGeneratorSpecific;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/* -*- 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 jit_loongarch64_LIR_loongarch64_h
|
||||
#define jit_loongarch64_LIR_loongarch64_h
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class LUnbox : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Unbox);
|
||||
|
||||
explicit LUnbox(const LAllocation& input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
|
||||
MUnbox* mir() const {
|
||||
return mir_->toUnbox();
|
||||
}
|
||||
const char* extraName() const {
|
||||
return StringFromMIRType(mir()->type());
|
||||
}
|
||||
};
|
||||
|
||||
class LUnboxFloatingPoint : public LUnbox
|
||||
{
|
||||
MIRType type_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(UnboxFloatingPoint);
|
||||
|
||||
LUnboxFloatingPoint(const LAllocation& input, MIRType type)
|
||||
: LUnbox(input),
|
||||
type_(type)
|
||||
{ }
|
||||
|
||||
MIRType type() const {
|
||||
return type_;
|
||||
}
|
||||
};
|
||||
|
||||
class LDivOrModI64 : public LBinaryMath<1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(DivOrModI64)
|
||||
|
||||
LDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
|
||||
setOperand(0, lhs);
|
||||
setOperand(1, rhs);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LDefinition* remainder() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
MBinaryArithInstruction* mir() const {
|
||||
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||
return static_cast<MBinaryArithInstruction*>(mir_);
|
||||
}
|
||||
bool canBeDivideByZero() const {
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->canBeDivideByZero();
|
||||
return mir_->toDiv()->canBeDivideByZero();
|
||||
}
|
||||
bool canBeNegativeOverflow() const {
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->canBeNegativeDividend();
|
||||
return mir_->toDiv()->canBeNegativeOverflow();
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->trapOffset();
|
||||
return mir_->toDiv()->trapOffset();
|
||||
}
|
||||
};
|
||||
|
||||
class LUDivOrModI64 : public LBinaryMath<1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(UDivOrModI64);
|
||||
|
||||
LUDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
|
||||
setOperand(0, lhs);
|
||||
setOperand(1, rhs);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LDefinition* remainder() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
const char* extraName() const {
|
||||
return mir()->isTruncated() ? "Truncated" : nullptr;
|
||||
}
|
||||
|
||||
MBinaryArithInstruction* mir() const {
|
||||
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||
return static_cast<MBinaryArithInstruction*>(mir_);
|
||||
}
|
||||
|
||||
bool canBeDivideByZero() const {
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->canBeDivideByZero();
|
||||
return mir_->toDiv()->canBeDivideByZero();
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->trapOffset();
|
||||
return mir_->toDiv()->trapOffset();
|
||||
}
|
||||
};
|
||||
|
||||
class LWasmTruncateToInt64 : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(WasmTruncateToInt64);
|
||||
|
||||
explicit LWasmTruncateToInt64(const LAllocation& in) {
|
||||
setOperand(0, in);
|
||||
}
|
||||
|
||||
MWasmTruncateToInt64* mir() const {
|
||||
return mir_->toWasmTruncateToInt64();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_loongarch64_LIR_loongarch64_h */
|
||||
@@ -0,0 +1,23 @@
|
||||
/* -*- 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 jit_loongarch64_LOpcodes_loongarch64_h__
|
||||
#define jit_loongarch64_LOpcodes_loongarch64_h__
|
||||
|
||||
#include "jit/shared/LOpcodes-shared.h"
|
||||
|
||||
#define LIR_CPU_OPCODE_LIST(_) \
|
||||
_(ModMaskI) \
|
||||
_(DivOrModI64) \
|
||||
_(UDivOrMod) \
|
||||
_(UDivOrModI64) \
|
||||
_(WasmUnalignedLoad) \
|
||||
_(WasmUnalignedStore) \
|
||||
_(WasmUnalignedLoadI64) \
|
||||
_(WasmUnalignedStoreI64) \
|
||||
_(WasmTruncateToInt64) \
|
||||
_(Int64ToFloatingPoint)
|
||||
|
||||
#endif // jit_loongarch64_LOpcodes_loongarch64_h__
|
||||
@@ -2395,7 +2395,7 @@ void MacroAssemblerLOONGARCH64::wasmLoadImpl(const wasm::MemoryAccessDesc& acces
|
||||
Register ptrScratch,
|
||||
AnyRegister output, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2442,7 +2442,7 @@ void MacroAssemblerLOONGARCH64::wasmStoreImpl(const wasm::MemoryAccessDesc& acce
|
||||
Register memoryBase, Register ptr,
|
||||
Register ptrScratch, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2488,7 +2488,7 @@ void MacroAssemblerLOONGARCH64Compat::wasmLoadI64Impl(
|
||||
const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
||||
Register ptrScratch, Register64 output, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2534,7 +2534,7 @@ void MacroAssemblerLOONGARCH64Compat::wasmStoreI64Impl(
|
||||
const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase,
|
||||
Register ptr, Register ptrScratch, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2774,12 +2774,13 @@ CodeOffset MacroAssembler::nopPatchableToNearJump() {
|
||||
}
|
||||
|
||||
void MacroAssembler::patchNopToNearJump(uint8_t* jump, uint8_t* target) {
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->encode() == NopInst);
|
||||
new (jump) InstJump(op_b, JOffImm26(target - jump));
|
||||
}
|
||||
|
||||
void MacroAssembler::patchNearJumpToNop(uint8_t* jump) {
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstJump>());
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->extractBitField(31, 26) ==
|
||||
(uint32_t)op_b >> 26);
|
||||
new (jump) InstNOP();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/* -*- 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 "jit/loongarch64/MoveEmitter-loongarch64.h"
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
void
|
||||
MoveEmitterLoongArch64::breakCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slotId)
|
||||
{
|
||||
switch (type) {
|
||||
case MoveOp::FLOAT32:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchFloat32Reg;
|
||||
masm.loadFloat32(getAdjustedAddress(to), temp);
|
||||
masm.storeFloat32(temp, cycleSlot(slotId));
|
||||
} else {
|
||||
masm.storeFloat32(to.floatReg(), cycleSlot(slotId));
|
||||
}
|
||||
break;
|
||||
case MoveOp::DOUBLE:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchDoubleReg;
|
||||
masm.loadDouble(getAdjustedAddress(to), temp);
|
||||
masm.storeDouble(temp, cycleSlot(slotId));
|
||||
} else {
|
||||
masm.storeDouble(to.floatReg(), cycleSlot(slotId));
|
||||
}
|
||||
break;
|
||||
case MoveOp::INT32:
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.load32(getAdjustedAddress(to), temp);
|
||||
masm.store32(temp, cycleSlot(0));
|
||||
} else {
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.store32(to.reg(), cycleSlot(0));
|
||||
}
|
||||
break;
|
||||
case MoveOp::GENERAL:
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.loadPtr(getAdjustedAddress(to), temp);
|
||||
masm.storePtr(temp, cycleSlot(0));
|
||||
} else {
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.storePtr(to.reg(), cycleSlot(0));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected move type");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MoveEmitterLoongArch64::completeCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slotId)
|
||||
{
|
||||
switch (type) {
|
||||
case MoveOp::FLOAT32:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchFloat32Reg;
|
||||
masm.loadFloat32(cycleSlot(slotId), temp);
|
||||
masm.storeFloat32(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
masm.loadFloat32(cycleSlot(slotId), to.floatReg());
|
||||
}
|
||||
break;
|
||||
case MoveOp::DOUBLE:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchDoubleReg;
|
||||
masm.loadDouble(cycleSlot(slotId), temp);
|
||||
masm.storeDouble(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
masm.loadDouble(cycleSlot(slotId), to.floatReg());
|
||||
}
|
||||
break;
|
||||
case MoveOp::INT32:
|
||||
MOZ_ASSERT(slotId == 0);
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.load32(cycleSlot(0), temp);
|
||||
masm.store32(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.load32(cycleSlot(0), to.reg());
|
||||
}
|
||||
break;
|
||||
case MoveOp::GENERAL:
|
||||
MOZ_ASSERT(slotId == 0);
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.loadPtr(cycleSlot(0), temp);
|
||||
masm.storePtr(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.loadPtr(cycleSlot(0), to.reg());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected move type");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MoveEmitterLoongArch64::emitDoubleMove(const MoveOperand& from, const MoveOperand& to)
|
||||
{
|
||||
MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchDoubleReg);
|
||||
MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchDoubleReg);
|
||||
|
||||
if (from.isFloatReg()) {
|
||||
if (to.isFloatReg()) {
|
||||
masm.moveDouble(from.floatReg(), to.floatReg());
|
||||
} else if (to.isGeneralReg()) {
|
||||
masm.moveFromDouble(from.floatReg(), to.reg());
|
||||
} else {
|
||||
MOZ_ASSERT(to.isMemory());
|
||||
masm.storeDouble(from.floatReg(), getAdjustedAddress(to));
|
||||
}
|
||||
} else if (to.isFloatReg()) {
|
||||
if (from.isMemory())
|
||||
masm.loadDouble(getAdjustedAddress(from), to.floatReg());
|
||||
else
|
||||
masm.moveToDouble(from.reg(), to.floatReg());
|
||||
} else {
|
||||
MOZ_ASSERT(from.isMemory());
|
||||
MOZ_ASSERT(to.isMemory());
|
||||
masm.loadDouble(getAdjustedAddress(from), ScratchDoubleReg);
|
||||
masm.storeDouble(ScratchDoubleReg, getAdjustedAddress(to));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* -*- 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 jit_loongarch64_MoveEmitter_loongarch64_h
|
||||
#define jit_loongarch64_MoveEmitter_loongarch64_h
|
||||
|
||||
#include "jit/mips-shared/MoveEmitter-mips-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class MoveEmitterLoongArch64 : public MoveEmitterMIPSShared
|
||||
{
|
||||
void emitDoubleMove(const MoveOperand& from, const MoveOperand& to);
|
||||
void breakCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slot);
|
||||
void completeCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slot);
|
||||
|
||||
public:
|
||||
MoveEmitterLoongArch64(MacroAssembler& masm)
|
||||
: MoveEmitterMIPSShared(masm)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef MoveEmitterLoongArch64 MoveEmitter;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_loongarch64_MoveEmitter_loongarch64_h */
|
||||
@@ -81,6 +81,10 @@ MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty array
|
||||
MSG_DEF(JSMSG_UNEXPECTED_TYPE, 2, JSEXN_TYPEERR, "{0} is {1}")
|
||||
MSG_DEF(JSMSG_MISSING_FUN_ARG, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
|
||||
MSG_DEF(JSMSG_NOT_NONNULL_OBJECT, 1, JSEXN_TYPEERR, "{0} is not a non-null object")
|
||||
MSG_DEF(JSMSG_NOT_WEAKREF_TARGET, 0, JSEXN_TYPEERR, "WeakRef target must be an object or a non-registered symbol")
|
||||
MSG_DEF(JSMSG_BAD_FINALIZATION_REGISTRY_TARGET, 0, JSEXN_TYPEERR, "FinalizationRegistry target must be an object or a non-registered symbol")
|
||||
MSG_DEF(JSMSG_BAD_FINALIZATION_REGISTRY_HELD_VALUE, 0, JSEXN_TYPEERR, "FinalizationRegistry held value must not be the target")
|
||||
MSG_DEF(JSMSG_BAD_FINALIZATION_REGISTRY_TOKEN, 0, JSEXN_TYPEERR, "FinalizationRegistry unregister token must be an object or a non-registered symbol")
|
||||
MSG_DEF(JSMSG_SET_NON_OBJECT_RECEIVER, 1, JSEXN_TYPEERR, "can't assign to properties of {0}: not an object")
|
||||
MSG_DEF(JSMSG_INVALID_DESCRIPTOR, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified")
|
||||
MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 1, JSEXN_TYPEERR, "{0}: Object is not extensible")
|
||||
@@ -548,6 +552,8 @@ MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array")
|
||||
|
||||
// Typed array
|
||||
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_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")
|
||||
@@ -555,12 +561,15 @@ 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_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_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_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}")
|
||||
|
||||
// Shared array buffer
|
||||
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_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")
|
||||
|
||||
@@ -301,7 +301,7 @@ void unmapPages(void* p, size_t size) { }
|
||||
void*
|
||||
mapMemoryAt(void* desired, size_t length)
|
||||
{
|
||||
#if defined(__ia64__) || defined(__aarch64__) || \
|
||||
#if defined(__aarch64__) || \
|
||||
(defined(__sparc__) && defined(__arch64__) && (defined(__NetBSD__) || defined(__linux__)))
|
||||
MOZ_RELEASE_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0);
|
||||
#endif
|
||||
@@ -324,7 +324,7 @@ mapMemory(size_t length)
|
||||
int fd = -1;
|
||||
off_t offset = 0;
|
||||
// The test code must be aligned with the implementation in gc/Memory.cpp.
|
||||
#if defined(__ia64__) || (defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__))
|
||||
#if defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__)
|
||||
void* region = mmap((void*)0x0000070000000000, length, prot, flags, fd, offset);
|
||||
if (region == MAP_FAILED)
|
||||
return nullptr;
|
||||
|
||||
@@ -21,6 +21,64 @@ struct MyHeap
|
||||
|
||||
BEGIN_TEST(testGCWeakRef)
|
||||
{
|
||||
CHECK(cx->options().weakRefs());
|
||||
cx->options().setWeakRefs(false);
|
||||
CHECK(cx->options().weakRefs());
|
||||
|
||||
JS::RootedValue v(cx);
|
||||
EXEC("var weakRefTarget = { x: 42 };\n"
|
||||
"var weakRef = new WeakRef(weakRefTarget);\n"
|
||||
"weakRefTarget = null;\n");
|
||||
|
||||
// The constructor keeps the target alive until the host clears kept
|
||||
// objects. This must remain true even after callers try the old disabled
|
||||
// option path above.
|
||||
JS_GC(cx);
|
||||
JS_GC(cx);
|
||||
EVAL("weakRef.deref()", &v);
|
||||
CHECK(v.isObject());
|
||||
|
||||
JS::ClearWeakRefKeptObjects(cx);
|
||||
v = JS::UndefinedValue();
|
||||
JS_GC(cx);
|
||||
JS_GC(cx);
|
||||
EVAL("weakRef.deref()", &v);
|
||||
CHECK(v.isUndefined());
|
||||
|
||||
EXEC("var weakRefSymbol = Symbol('weak-ref-symbol');\n"
|
||||
"var symbolRef = new WeakRef(weakRefSymbol);\n"
|
||||
"if (symbolRef.deref() !== weakRefSymbol)\n"
|
||||
" throw new Error('WeakRef must accept unique symbols');\n"
|
||||
"var registeredSymbolRejected = false;\n"
|
||||
"try {\n"
|
||||
" new WeakRef(Symbol.for('weak-ref-symbol'));\n"
|
||||
"} catch (e) {\n"
|
||||
" registeredSymbolRejected = e instanceof TypeError;\n"
|
||||
"}\n"
|
||||
"if (!registeredSymbolRejected)\n"
|
||||
" throw new Error('WeakRef must reject registered symbols');\n");
|
||||
|
||||
EXEC("var keptTarget = { y: 7 };\n"
|
||||
"var keptRef = new WeakRef(keptTarget);\n");
|
||||
JS::ClearWeakRefKeptObjects(cx);
|
||||
EXEC("var keptResult = keptRef.deref();\n"
|
||||
"if (keptResult !== keptTarget)\n"
|
||||
" throw new Error('WeakRef deref must return the target');\n"
|
||||
"keptTarget = null;\n"
|
||||
"keptResult = null;\n");
|
||||
|
||||
JS_GC(cx);
|
||||
JS_GC(cx);
|
||||
EVAL("keptRef.deref()", &v);
|
||||
CHECK(v.isObject());
|
||||
|
||||
JS::ClearWeakRefKeptObjects(cx);
|
||||
v = JS::UndefinedValue();
|
||||
JS_GC(cx);
|
||||
JS_GC(cx);
|
||||
EVAL("keptRef.deref()", &v);
|
||||
CHECK(v.isUndefined());
|
||||
|
||||
// Create an object and add a property to it so that we can read the
|
||||
// property back later to verify that object internals are not garbage.
|
||||
JS::RootedObject obj(cx, JS_NewPlainObject(cx));
|
||||
@@ -38,7 +96,6 @@ BEGIN_TEST(testGCWeakRef)
|
||||
// references.
|
||||
CHECK(heap.get().weak.unbarrieredGet() != nullptr);
|
||||
obj = heap.get().weak;
|
||||
JS::RootedValue v(cx);
|
||||
CHECK(JS_GetProperty(cx, obj, "x", &v));
|
||||
CHECK(v.isInt32());
|
||||
CHECK(v.toInt32() == 42);
|
||||
@@ -61,4 +118,3 @@ BEGIN_TEST(testGCWeakRef)
|
||||
return true;
|
||||
}
|
||||
END_TEST(testGCWeakRef)
|
||||
|
||||
Loaded 100 of 1264 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user