Merge inbound to m-c.

This commit is contained in:
Ryan VanderMeulen 2014-04-24 13:19:53 -04:00
commit 7843a5e1a7
237 changed files with 10567 additions and 2215 deletions

View File

@ -35,7 +35,7 @@ gyp_vars = {
'arm_neon_optional': 1,
'moz_widget_toolkit_gonk': 0,
'moz_omx_encoder': 0,
'moz_webrtc_omx': 0,
# (for vp8) chromium sets to 0 also
'use_temporal_layers': 0,
@ -62,8 +62,8 @@ elif os == 'Android':
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
gyp_vars['build_with_gonk'] = 1
gyp_vars['moz_widget_toolkit_gonk'] = 1
if CONFIG['MOZ_OMX_ENCODER']:
gyp_vars['moz_omx_encoder'] = 1
if int(CONFIG['ANDROID_VERSION']) >= 18:
gyp_vars['moz_webrtc_omx'] = 1
else:
gyp_vars.update(
gtest_target_type='executable',

View File

@ -7435,6 +7435,18 @@ if test -n "$MOZ_VISUAL_EVENT_TRACER"; then
AC_DEFINE(MOZ_VISUAL_EVENT_TRACER)
fi
dnl ========================================================
dnl = Enable TaskTracer
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(tasktracer,
[ --enable-tasktracer Set compile flags necessary for using TaskTracer],
MOZ_TASK_TRACER=1,
MOZ_TASK_TRACER= )
if test "$MOZ_WIDGET_TOOLKIT" = "gonk" -a -n "$MOZ_TASK_TRACER"; then
AC_DEFINE(MOZ_TASK_TRACER)
AC_SUBST(MOZ_TASK_TRACER)
fi
dnl ========================================================
dnl Turn on reflow counting
dnl ========================================================
@ -8897,9 +8909,7 @@ fi # COMPILE_ENVIRONMENT
dnl Set various defines and substitutions
dnl ========================================================
if test "$OS_ARCH" = "Darwin"; then
AC_DEFINE(XP_UNIX)
elif test "$OS_ARCH" != "WINNT"; then
if test "$OS_ARCH" != "WINNT"; then
AC_DEFINE(XP_UNIX)
fi

View File

@ -1234,6 +1234,16 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
newEditor = mEditor; // just pretend that we have a new editor!
}
// Get the current value of the textfield from the content.
// Note that if we've created a new editor, mEditor is null at this stage,
// so we will get the real value from the content.
nsAutoString defaultValue;
if (aValue) {
defaultValue = *aValue;
} else {
GetValue(defaultValue, true);
}
if (!mEditorInitialized) {
// Now initialize the editor.
//
@ -1254,7 +1264,8 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
// already does the relevant security checks.
AutoNoJSAPI nojsapi;
rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags);
rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags,
defaultValue);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1335,16 +1346,6 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
newEditor->SetFlags(editorFlags);
}
// Get the current value of the textfield from the content.
// Note that if we've created a new editor, mEditor is null at this stage,
// so we will get the real value from the content.
nsAutoString defaultValue;
if (aValue) {
defaultValue = *aValue;
} else {
GetValue(defaultValue, true);
}
if (shouldInitializeEditor) {
// Hold on to the newly created editor
preDestroyer.Swap(mEditor);
@ -1780,18 +1781,7 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
#endif
nsAutoString currentValue;
if (!mEditorInitialized && IsSingleLineTextControl()) {
// Grab the current value directly from the text node to make sure that we
// deal with stale data correctly.
NS_ASSERTION(mRootNode, "We should have a root node here");
nsIContent *textContent = mRootNode->GetFirstChild();
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(textContent);
if (textNode) {
textNode->GetData(currentValue);
}
} else {
mBoundFrame->GetText(currentValue);
}
mBoundFrame->GetText(currentValue);
nsWeakFrame weakFrame(mBoundFrame);

View File

@ -393,6 +393,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
[test_bug787134.html]
[test_bug797113.html]
[test_bug803677.html]
[test_bug821307.html]
[test_bug827126.html]
[test_bug827426.html]
[test_bug838582.html]

View File

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=821307
-->
<head>
<title>Test for Bug 821307</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821307">Mozilla Bug 821307</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<input id='dummy'></input>
<input type="password" id='input' value='11111111111111111' style="width:40em; font-size:40px;"></input>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var dummy = document.getElementById('dummy');
dummy.focus();
is(document.activeElement, dummy, "Check dummy element is now focused");
var input = document.getElementById('input');
var rect = input.getBoundingClientRect();
synthesizeMouse(input, 100, rect.height/2, {});
is(document.activeElement, input, "Check input element is now focused");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@ -14,7 +14,8 @@ namespace mozilla {
typedef void(*MixerFunc)(AudioDataValue* aMixedBuffer,
AudioSampleFormat aFormat,
uint32_t aChannels,
uint32_t aFrames);
uint32_t aFrames,
uint32_t aSampleRate);
/**
* This class mixes multiple streams of audio together to output a single audio
@ -34,7 +35,8 @@ public:
AudioMixer(MixerFunc aCallback)
: mCallback(aCallback),
mFrames(0),
mChannels(0)
mChannels(0),
mSampleRate(0)
{ }
/* Get the data from the mixer. This is supposed to be called when all the
@ -43,21 +45,27 @@ public:
mCallback(mMixedAudio.Elements(),
AudioSampleTypeToFormat<AudioDataValue>::Format,
mChannels,
mFrames);
mFrames,
mSampleRate);
PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
mChannels = mFrames = 0;
mSampleRate = mChannels = mFrames = 0;
}
/* Add a buffer to the mix. aSamples is interleaved. */
void Mix(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames) {
void Mix(AudioDataValue* aSamples,
uint32_t aChannels,
uint32_t aFrames,
uint32_t aSampleRate) {
if (!mFrames && !mChannels) {
mFrames = aFrames;
mChannels = aChannels;
mSampleRate = aSampleRate;
EnsureCapacityAndSilence();
}
MOZ_ASSERT(aFrames == mFrames);
MOZ_ASSERT(aChannels == mChannels);
MOZ_ASSERT(aSampleRate == mSampleRate);
for (uint32_t i = 0; i < aFrames * aChannels; i++) {
mMixedAudio[i] += aSamples[i];
@ -77,6 +85,8 @@ private:
uint32_t mFrames;
/* Number of channels for this mixing block. */
uint32_t mChannels;
/* Sample rate the of the mixed data. */
uint32_t mSampleRate;
/* Buffer containing the mixed audio data. */
nsTArray<AudioDataValue> mMixedAudio;
};

View File

@ -216,7 +216,7 @@ AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer)
aOutput->Write(buf.Elements(), GetDuration(), &(mChunks[mChunks.Length() - 1].mTimeStamp));
if (aMixer) {
aMixer->Mix(buf.Elements(), outputChannels, GetDuration());
aMixer->Mix(buf.Elements(), outputChannels, GetDuration(), aOutput->GetRate());
}
aOutput->Start();
}

View File

@ -2466,7 +2466,10 @@ void MediaDecoderStateMachine::AdvanceFrame()
mReader->VideoQueue().PushFront(currentFrame.forget());
}
StartBuffering();
ScheduleStateMachine();
// Don't go straight back to the state machine loop since that might
// cause us to start decoding again and we could flip-flop between
// decoding and quick-buffering.
ScheduleStateMachine(USECS_PER_S);
return;
}
}

View File

@ -340,7 +340,7 @@ MediaStreamGraphImpl::GetAudioPosition(MediaStream* aStream)
return mCurrentTime;
}
return aStream->mAudioOutputStreams[0].mAudioPlaybackStartTime +
TicksToTimeRoundDown(IdealAudioRate(),
TicksToTimeRoundDown(mSampleRate,
positionInFrames);
}
@ -586,7 +586,8 @@ MediaStreamGraphImpl::UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream
static void AudioMixerCallback(AudioDataValue* aMixedBuffer,
AudioSampleFormat aFormat,
uint32_t aChannels,
uint32_t aFrames)
uint32_t aFrames,
uint32_t aSampleRate)
{
// Need an api to register mixer callbacks, bug 989921
#ifdef MOZ_WEBRTC
@ -594,7 +595,7 @@ static void AudioMixerCallback(AudioDataValue* aMixedBuffer,
// XXX need Observer base class and registration API
if (gFarendObserver) {
gFarendObserver->InsertFarEnd(aMixedBuffer, aFrames, false,
IdealAudioRate(), aChannels, aFormat);
aSampleRate, aChannels, aFormat);
}
}
#endif
@ -847,7 +848,7 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTim
// XXX for now, allocate stereo output. But we need to fix this to
// match the system's ideal channel configuration.
// NOTE: we presume this is either fast or async-under-the-covers
audioOutputStream->mStream->Init(2, IdealAudioRate(),
audioOutputStream->mStream->Init(2, mSampleRate,
AudioChannel::Normal,
AudioStream::LowLatency);
audioOutputStream->mTrackID = tracks->GetID();
@ -879,7 +880,7 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
// the rounding between {Graph,Stream}Time and track ticks is not dependant
// on the absolute value of the {Graph,Stream}Time, and so that number of
// ticks to play is the same for each cycle.
TrackTicks ticksNeeded = TimeToTicksRoundDown(IdealAudioRate(), aTo) - TimeToTicksRoundDown(IdealAudioRate(), aFrom);
TrackTicks ticksNeeded = TimeToTicksRoundDown(mSampleRate, aTo) - TimeToTicksRoundDown(mSampleRate, aFrom);
if (aStream->mAudioOutputStreams.IsEmpty()) {
return 0;
@ -897,7 +898,7 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
StreamBuffer::Track* track = aStream->mBuffer.FindTrack(audioOutput.mTrackID);
AudioSegment* audio = track->Get<AudioSegment>();
AudioSegment output;
MOZ_ASSERT(track->GetRate() == IdealAudioRate());
MOZ_ASSERT(track->GetRate() == mSampleRate);
// offset and audioOutput.mLastTickWritten can differ by at most one sample,
// because of the rounding issue. We track that to ensure we don't skip a
@ -933,7 +934,7 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
if (end >= aTo) {
toWrite = ticksNeeded;
} else {
toWrite = TimeToTicksRoundDown(IdealAudioRate(), end - aFrom);
toWrite = TimeToTicksRoundDown(mSampleRate, end - aFrom);
}
if (blocked) {
@ -1284,23 +1285,8 @@ MediaStreamGraphImpl::RunThread()
UpdateStreamOrder();
}
TrackRate sampleRate;
// Find the sampling rate that we need to use for non-realtime graphs.
if (!mRealtime) {
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
AudioNodeStream* n = mStreams[i]->AsAudioNodeStream();
if (n) {
// We know that the rest of the streams will run at the same rate.
sampleRate = n->SampleRate();
break;
}
}
} else {
sampleRate = IdealAudioRate();
}
GraphTime endBlockingDecisions =
RoundUpToNextAudioBlock(sampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
RoundUpToNextAudioBlock(mSampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
bool ensureNextIteration = false;
// Grab pending stream input.
@ -1919,7 +1905,8 @@ MediaStream::EnsureTrack(TrackID aTrackId, TrackRate aSampleRate)
nsAutoPtr<MediaSegment> segment(new AudioSegment());
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyQueuedTrackChanges(Graph(), aTrackId, IdealAudioRate(), 0,
l->NotifyQueuedTrackChanges(Graph(), aTrackId,
GraphImpl()->AudioSampleRate(), 0,
MediaStreamListener::TRACK_EVENT_CREATED,
*segment);
}
@ -2269,7 +2256,7 @@ SourceMediaStream::AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
data->mInputRate = aRate;
// We resample all audio input tracks to the sample rate of the audio mixer.
data->mOutputRate = aSegment->GetType() == MediaSegment::AUDIO ?
IdealAudioRate() : aRate;
GraphImpl()->AudioSampleRate() : aRate;
data->mStart = aStart;
data->mCommands = TRACK_CREATE;
data->mData = aSegment;
@ -2283,7 +2270,7 @@ void
SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment)
{
if (aSegment->GetType() != MediaSegment::AUDIO ||
aTrackData->mInputRate == IdealAudioRate()) {
aTrackData->mInputRate == GraphImpl()->AudioSampleRate()) {
return;
}
AudioSegment* segment = static_cast<AudioSegment*>(aSegment);
@ -2291,7 +2278,7 @@ SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSe
int channels = segment->ChannelCount();
SpeexResamplerState* state = speex_resampler_init(channels,
aTrackData->mInputRate,
IdealAudioRate(),
GraphImpl()->AudioSampleRate(),
SPEEX_RESAMPLER_QUALITY_DEFAULT,
nullptr);
if (state) {
@ -2639,7 +2626,7 @@ ProcessedMediaStream::DestroyImpl()
*/
static const int32_t INITIAL_CURRENT_TIME = 1;
MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate)
: mCurrentTime(INITIAL_CURRENT_TIME)
, mStateComputedTime(INITIAL_CURRENT_TIME)
, mProcessingGraphUpdateIndex(0)
@ -2648,6 +2635,7 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
, mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
, mWaitState(WAITSTATE_RUNNING)
, mEndTime(GRAPH_TIME_MAX)
, mSampleRate(aSampleRate)
, mNeedAnotherIteration(false)
, mForceShutDown(false)
, mPostedRunInStableStateEvent(false)
@ -2714,22 +2702,22 @@ MediaStreamGraph::GetInstance()
nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
}
gGraph = new MediaStreamGraphImpl(true);
AudioStream::InitPreferredSampleRate();
gGraph = new MediaStreamGraphImpl(true, AudioStream::PreferredSampleRate());
STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
AudioStream::InitPreferredSampleRate();
}
return gGraph;
}
MediaStreamGraph*
MediaStreamGraph::CreateNonRealtimeInstance()
MediaStreamGraph::CreateNonRealtimeInstance(TrackRate aSampleRate)
{
NS_ASSERTION(NS_IsMainThread(), "Main thread only");
MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false, aSampleRate);
return graph;
}

View File

@ -790,7 +790,8 @@ public:
TrackID mID;
// Sample rate of the input data.
TrackRate mInputRate;
// Sample rate of the output data, always equal to IdealAudioRate()
// Sample rate of the output data, always equal to the sample rate of the
// graph.
TrackRate mOutputRate;
// Resampler if the rate of the input track does not match the
// MediaStreamGraph's.
@ -1078,9 +1079,6 @@ protected:
bool mInCycle;
};
// Returns ideal audio rate for processing.
inline TrackRate IdealAudioRate() { return AudioStream::PreferredSampleRate(); }
/**
* Initially, at least, we will have a singleton MediaStreamGraph per
* process. Each OfflineAudioContext object creates its own MediaStreamGraph
@ -1089,13 +1087,13 @@ inline TrackRate IdealAudioRate() { return AudioStream::PreferredSampleRate(); }
class MediaStreamGraph {
public:
// We ensure that the graph current time advances in multiples of
// IdealAudioBlockSize()/IdealAudioRate(). A stream that never blocks
// and has a track with the ideal audio rate will produce audio in
// multiples of the block size.
// IdealAudioBlockSize()/AudioStream::PreferredSampleRate(). A stream that
// never blocks and has a track with the ideal audio rate will produce audio
// in multiples of the block size.
// Main thread only
static MediaStreamGraph* GetInstance();
static MediaStreamGraph* CreateNonRealtimeInstance();
static MediaStreamGraph* CreateNonRealtimeInstance(TrackRate aSampleRate);
// Idempotent
static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);

View File

@ -120,7 +120,7 @@ public:
* output. Those objects currently only support audio, and are used to
* implement OfflineAudioContext. They do not support MediaStream inputs.
*/
explicit MediaStreamGraphImpl(bool aRealtime);
explicit MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate);
/**
* Unregisters memory reporting and deletes this instance. This should be
@ -392,6 +392,8 @@ public:
*/
void ResumeAllAudioOutputs();
TrackRate AudioSampleRate() { return mSampleRate; }
// Data members
/**
@ -531,6 +533,13 @@ public:
* The graph should stop processing at or after this time.
*/
GraphTime mEndTime;
/**
* Sample rate at which this graph runs. For real time graphs, this is
* the rate of the audio mixer. For offline graphs, this is the rate specified
* at construction.
*/
TrackRate mSampleRate;
/**
* True when another iteration of the control loop is required.
*/

View File

@ -11,7 +11,7 @@ using mozilla::AudioSampleFormat;
/* In this test, the different audio stream and channels are always created to
* cancel each other. */
void MixingDone(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames)
void MixingDone(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames, uint32_t aSampleRate)
{
bool silent = true;
for (uint32_t i = 0; i < aChannels * aFrames; i++) {
@ -67,6 +67,7 @@ void FillBuffer(AudioDataValue* aBuffer, uint32_t aLength, AudioDataValue aValue
int main(int argc, char* argv[]) {
const uint32_t CHANNEL_LENGTH = 256;
const uint32_t AUDIO_RATE = 44100;
AudioDataValue a[CHANNEL_LENGTH * 2];
AudioDataValue b[CHANNEL_LENGTH * 2];
FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
@ -81,8 +82,8 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "Test AudioMixer constant buffer length.\n");
while (iterations--) {
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
}
}
@ -96,22 +97,22 @@ int main(int argc, char* argv[]) {
FillBuffer(a + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
FillBuffer(b + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
mixer.Mix(a, 2, CHANNEL_LENGTH / 2);
mixer.Mix(b, 2, CHANNEL_LENGTH / 2);
mixer.Mix(a, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
mixer.FinishMixing();
FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
FillBuffer(a + CHANNEL_LENGTH, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
FillBuffer(b + CHANNEL_LENGTH, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
FillBuffer(a, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
FillBuffer(a + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
FillBuffer(b + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
mixer.Mix(a, 2, CHANNEL_LENGTH / 2);
mixer.Mix(b, 2, CHANNEL_LENGTH / 2);
mixer.Mix(a, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
mixer.FinishMixing();
}
@ -122,14 +123,14 @@ int main(int argc, char* argv[]) {
mozilla::AudioMixer mixer(MixingDone);
fprintf(stderr, "Test AudioMixer variable channel count.\n");
mixer.Mix(a, 1, CHANNEL_LENGTH);
mixer.Mix(b, 1, CHANNEL_LENGTH);
mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 1, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
mixer.Mix(a, 1, CHANNEL_LENGTH);
mixer.Mix(b, 1, CHANNEL_LENGTH);
mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 1, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
mixer.Mix(a, 1, CHANNEL_LENGTH);
mixer.Mix(b, 1, CHANNEL_LENGTH);
mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 1, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
}
@ -137,16 +138,16 @@ int main(int argc, char* argv[]) {
mozilla::AudioMixer mixer(MixingDone);
fprintf(stderr, "Test AudioMixer variable stream count.\n");
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
mixer.FinishMixing();
}

View File

@ -418,6 +418,8 @@ OMXVideoEncoder::GetCodecConfig(nsTArray<uint8_t>* aOutputBuf)
return AppendDecoderConfig(aOutputBuf, nullptr) == OK ? NS_OK : NS_ERROR_FAILURE;
}
// MediaCodec::setParameters() is available only after API level 18.
#if ANDROID_VERSION >= 18
nsresult
OMXVideoEncoder::SetBitrate(int32_t aKbps)
{
@ -427,6 +429,7 @@ OMXVideoEncoder::SetBitrate(int32_t aKbps)
MOZ_ASSERT(result == OK);
return result == OK ? NS_OK : NS_ERROR_FAILURE;
}
#endif
nsresult
OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,

View File

@ -267,8 +267,10 @@ public:
nsresult Encode(const mozilla::layers::Image* aImage, int aWidth, int aHeight,
int64_t aTimestamp, int aInputFlags = 0);
#if ANDROID_VERSION >= 18
/** Set encoding bitrate (in kbps). */
nsresult SetBitrate(int32_t aKbps);
#endif
/**
* Get current AVC codec config blob. The output format depends on the

View File

@ -83,7 +83,7 @@ function handleRequest(request, response)
}
var samples = [];
for (var i = 0; i < 100000; ++i) {
for (var i = 0; i < 1000000; ++i) {
samples.push(0);
}
var bytes = buildWave(samples, 44100).join("");
@ -137,10 +137,10 @@ function handleRequest(request, response)
return;
}
// Write the first 120K of the Wave file. We know the cache size is set to
// Write the first 1.2M of the Wave file. We know the cache size is set to
// 100K so this will fill the cache and and cause a "suspend" event on
// the loading element.
out.write(bytes, 120000);
out.write(bytes, 1200000);
response.processAsync();
// Now wait for the message to cancel this response

View File

@ -244,7 +244,7 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
, mExtraCurrentTimeUpdatedSinceLastStableState(false)
{
MediaStreamGraph* graph = aIsOffline ?
MediaStreamGraph::CreateNonRealtimeInstance() :
MediaStreamGraph::CreateNonRealtimeInstance(aSampleRate) :
MediaStreamGraph::GetInstance();
AudioNodeEngine* engine = aIsOffline ?
new OfflineDestinationNodeEngine(this, aNumberOfChannels,

View File

@ -5,4 +5,9 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
PARALLEL_DIRS += ['src']
TEST_DIRS += ['tests']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']

View File

@ -1,13 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
MOCHITEST_MANIFESTS += ['mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
DIRS += ['signed']

View File

@ -1,5 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.

View File

@ -347,6 +347,8 @@ URL::GetHostname(nsString& aHostname) const
void
URL::SetHostname(const nsAString& aHostname)
{
// nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname
// The return code is silently ignored
mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname));
}

View File

@ -1,17 +1,16 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=887364
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 887364</title>
<title>Test URL API</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887364">Mozilla Bug 887364</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=991471">Mozilla Bug 991471</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=996055">Mozilla Bug 996055</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe name="x" id="x"></iframe>
@ -275,5 +274,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887364
}
</script>
<script>
/** Test for Bug 991471 **/
var url = new URL("http://localhost/");
url.hostname = "";
url.username = "tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt";
url.hostname = "www.mozilla.org";
url.username = "";
url.hostname = "www.mozilla.org";
is(url.href, "http://www.mozilla.org/", "No parsing error with empty host");
</script>
<script>
/** Test for Bug 996055 **/
var url = new URL("http://localhost/");
url.hostname = "";
is(url.href, "http://localhost/", "Empty hostname is ignored");
</script>
</body>
</html>

View File

@ -114,6 +114,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #CRASH_DUMP, RANDOM # b2g(bu
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
[test_bug698929.html]
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
[test_bug704423.html]
[test_bug741666.html]
skip-if = toolkit == 'android'
[test_bug742376.html]

View File

@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=704423
-->
<head>
<title>Test for Bug 704423</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=704423">Mozilla Bug 704423</a>
<p id="display"></p>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 704423 **/
function doTest()
{
function handler(aEvent) {
aEvent.preventDefault();
ok(aEvent.defaultPrevented,
"mousemove event should be cancelable");
}
window.addEventListener("mousemove", handler, true);
synthesizeMouseAtCenter(document.body, { type: "mousemove" });
window.removeEventListener("mousemove", handler, true);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(doTest);
</script>
</pre>
</body>
</html>

View File

@ -1,35 +1,21 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* DOMApplicationRegistry._isLaunchable() sometimes returns false right after
* installation on Mac, perhaps because of a race condition between
* WebappsInstaller and nsIMacWebAppUtils::pathForAppWithIdentifier().
* That causes methods like mgmt.getAll() to exclude the app from their results,
* even though the app is registered and installed.
*
* To work around this problem, set DOMApplicationRegistry.allAppsLaunchable
* to true, which makes _isLaunchable() return true for all registered apps.
*/
function makeAllAppsLaunchable() {
var Webapps = {};
Components.utils.import("resource://gre/modules/Webapps.jsm", Webapps);
var originalValue = Webapps.DOMApplicationRegistry.allAppsLaunchable;
Webapps.DOMApplicationRegistry.allAppsLaunchable = true;
// Clean up after ourselves once tests are done so the test page is unloaded.
window.addEventListener("unload", function restoreAllAppsLaunchable(event) {
if (event.target == window.document) {
window.removeEventListener("unload", restoreAllAppsLaunchable, false);
Webapps.DOMApplicationRegistry.allAppsLaunchable = originalValue;
}
}, false);
}
function runAll(steps) {
SimpleTest.waitForExplicitFinish();
makeAllAppsLaunchable();
/**
* On Mac, apps aren't considered launchable right after they've been
* installed because the OS takes some time to detect them (so
* nsIMacWebAppUtils::pathForAppWithIdentifier() returns null).
* That causes methods like mgmt.getAll() to exclude the app from their
* results, even though the app is installed and is in the registry.
* See the tests under toolkit/webapps for a viable solution.
*
* To work around this problem, set allAppsLaunchable to true, which makes
* all apps considered as launchable.
*/
SpecialPowers.setAllAppsLaunchable(true);
// Clone the array so we don't modify the original.
steps = steps.concat();

View File

@ -20,7 +20,7 @@
SimpleTest.waitForExplicitFinish();
makeAllAppsLaunchable();
SpecialPowers.setAllAppsLaunchable(true);
var mmListener = {
receiveMessage: function(aMessage) {

View File

@ -22,7 +22,7 @@ const Cu = Components.utils;
SimpleTest.waitForExplicitFinish();
makeAllAppsLaunchable();
SpecialPowers.setAllAppsLaunchable(true);
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PopupNotifications.jsm");

View File

@ -10,6 +10,11 @@
#include "WifiUtils.h"
#include "nsCxPusher.h"
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
using namespace mozilla::tasktracer;
#endif
#define NS_WIFIPROXYSERVICE_CID \
{ 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} }
@ -63,6 +68,12 @@ public:
nsAutoString event;
gWpaSupplicant->WaitForEvent(event, mInterface);
if (!event.IsEmpty()) {
#ifdef MOZ_TASK_TRACER
// Make wifi initialization events to be the source events of TaskTracer,
// and originate the rest correlation tasks from here.
AutoSourceEvent taskTracerEvent(SourceEventType::WIFI);
AddLabel("%s %s", mInterface.get(), NS_ConvertUTF16toUTF8(event).get());
#endif
nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event, mInterface);
NS_DispatchToMainThread(runnable);
}

View File

@ -448,7 +448,7 @@ nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
NS_ENSURE_SUCCESS(rv, rv);
rv = editor->Init(domDoc, nullptr /* root content */,
nullptr, mEditorFlags);
nullptr, mEditorFlags, EmptyString());
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection> selection;

View File

@ -21,7 +21,7 @@ interface nsIEditActionListener;
interface nsIInlineSpellChecker;
interface nsITransferable;
[scriptable, uuid(753b38d1-ee03-4e58-a650-1076ccccdb7f)]
[scriptable, uuid(65523eab-db1f-44aa-893e-dfe57ad306f0)]
interface nsIEditor : nsISupports
{
@ -56,7 +56,8 @@ interface nsIEditor : nsISupports
[noscript] void init(in nsIDOMDocument doc,
in nsIContent aRoot,
in nsISelectionController aSelCon,
in unsigned long aFlags);
in unsigned long aFlags,
in AString initialValue);
void setAttributeOrEquivalent(in nsIDOMElement element,
in AString sourceAttrName,

View File

@ -6,10 +6,9 @@
#ifndef nsEditRules_h__
#define nsEditRules_h__
// FB45AC36-E8F1-44ae-8FB7-466E1BE119B0
#define NS_IEDITRULES_IID \
{ 0x2cc50d11, 0x9909, 0x433f, \
{ 0xb6, 0xfb, 0x4c, 0xf2, 0x56, 0xe5, 0xe5, 0x71 } }
{ 0x3836386d, 0x806a, 0x488d, \
{ 0x8b, 0xab, 0xaf, 0x42, 0xbb, 0x4c, 0x90, 0x66 } }
#include "nsEditor.h"
@ -43,6 +42,7 @@ public:
//NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
NS_IMETHOD Init(nsPlaintextEditor *aEditor)=0;
NS_IMETHOD SetInitialValue(const nsAString& aValue) = 0;
NS_IMETHOD DetachEditor()=0;
NS_IMETHOD BeforeEdit(EditAction action,
nsIEditor::EDirection aDirection) = 0;

View File

@ -204,7 +204,9 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor)
NS_IMETHODIMP
nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags)
nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
nsISelectionController *aSelCon, uint32_t aFlags,
const nsAString& aValue)
{
NS_PRECONDITION(aDoc, "bad arg");
if (!aDoc)

View File

@ -225,10 +225,12 @@ NS_IMETHODIMP
nsHTMLEditor::Init(nsIDOMDocument *aDoc,
nsIContent *aRoot,
nsISelectionController *aSelCon,
uint32_t aFlags)
uint32_t aFlags,
const nsAString& aInitialValue)
{
NS_PRECONDITION(aDoc && !aSelCon, "bad arg");
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
MOZ_ASSERT(aInitialValue.IsEmpty(), "Non-empty initial values not supported");
nsresult result = NS_OK, rulesRes = NS_OK;
@ -238,7 +240,7 @@ nsHTMLEditor::Init(nsIDOMDocument *aDoc,
nsAutoEditInitRulesTrigger rulesTrigger(static_cast<nsPlaintextEditor*>(this), rulesRes);
// Init the plaintext editor
result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags);
result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags, aInitialValue);
if (NS_FAILED(result)) { return result; }
// Init mutation observer
@ -3227,12 +3229,22 @@ nsHTMLEditor::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aFirstNewContent,
int32_t aIndexInContainer)
{
ContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer);
DoContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer,
eAppended);
}
void
nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aChild, int32_t aIndexInContainer)
{
DoContentInserted(aDocument, aContainer, aChild, aIndexInContainer,
eInserted);
}
void
nsHTMLEditor::DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
nsIContent* aChild, int32_t aIndexInContainer,
InsertedOrAppended aInsertedOrAppended)
{
if (!aChild) {
return;
@ -3257,8 +3269,17 @@ nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
// Update spellcheck for only the newly-inserted node (bug 743819)
if (mInlineSpellChecker) {
nsRefPtr<nsRange> range = new nsRange(aChild);
int32_t endIndex = aIndexInContainer + 1;
if (aInsertedOrAppended == eAppended) {
// Count all the appended nodes
nsIContent* sibling = aChild->GetNextSibling();
while (sibling) {
endIndex++;
sibling = sibling->GetNextSibling();
}
}
nsresult res = range->Set(aContainer, aIndexInContainer,
aContainer, aIndexInContainer + 1);
aContainer, endIndex);
if (NS_SUCCEEDED(res)) {
mInlineSpellChecker->SpellCheckRange(range);
}

View File

@ -249,7 +249,9 @@ public:
nsresult EndUpdateViewBatch();
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags);
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
nsISelectionController *aSelCon, uint32_t aFlags,
const nsAString& aValue);
NS_IMETHOD PreDestroy(bool aDestroyingFrames);
/** Internal, static version */
@ -950,7 +952,10 @@ private:
nsIAtom* aProperty,
const nsAString* aAttribute,
const nsAString* aValue);
typedef enum { eInserted, eAppended } InsertedOrAppended;
void DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
nsIContent* aChild, int32_t aIndexInContainer,
InsertedOrAppended aInsertedOrAppended);
};
#endif //nsHTMLEditor_h__

View File

@ -115,7 +115,8 @@ NS_INTERFACE_MAP_END_INHERITING(nsEditor)
NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
nsIContent *aRoot,
nsISelectionController *aSelCon,
uint32_t aFlags)
uint32_t aFlags,
const nsAString& aInitialValue)
{
NS_PRECONDITION(aDoc, "bad arg");
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
@ -126,13 +127,12 @@ NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
mRules = nullptr;
}
if (1)
{
// block to scope nsAutoEditInitRulesTrigger
nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
// Init the base editor
res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags);
res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags, aInitialValue);
}
// check the "single line editor newline handling"
@ -140,6 +140,13 @@ NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
GetDefaultEditorPrefs(mNewlineHandling, mCaretStyle);
NS_ENSURE_SUCCESS(rulesRes, rulesRes);
// mRules may not have been initialized yet, when this is called via
// nsHTMLEditor::Init.
if (mRules) {
mRules->SetInitialValue(aInitialValue);
}
return res;
}

View File

@ -73,7 +73,9 @@ public:
bool aSuppressTransaction);
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags);
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
nsISelectionController *aSelCon, uint32_t aFlags,
const nsAString& aValue);
NS_IMETHOD GetDocumentIsEmpty(bool *aDocumentIsEmpty);
NS_IMETHOD GetIsDocumentEditable(bool *aIsDocumentEditable);

View File

@ -135,6 +135,15 @@ nsTextEditRules::Init(nsPlaintextEditor *aEditor)
return res;
}
NS_IMETHODIMP
nsTextEditRules::SetInitialValue(const nsAString& aValue)
{
if (IsPasswordEditor()) {
mPasswordText = aValue;
}
return NS_OK;
}
NS_IMETHODIMP
nsTextEditRules::DetachEditor()
{

View File

@ -47,6 +47,7 @@ public:
// nsIEditRules methods
NS_IMETHOD Init(nsPlaintextEditor *aEditor);
NS_IMETHOD SetInitialValue(const nsAString& aValue);
NS_IMETHOD DetachEditor();
NS_IMETHOD BeforeEdit(EditAction action,
nsIEditor::EDirection aDirection);

View File

@ -111,7 +111,7 @@ FillRectWithMask(DrawTarget* aDT,
Matrix inverseMask = *aMaskTransform;
inverseMask.Invert();
Matrix transform = inverseMask * oldTransform;
Matrix transform = oldTransform * inverseMask;
if (aSurfaceTransform) {
transform = transform * (*aSurfaceTransform);
}

View File

@ -101,6 +101,8 @@ AsyncCompositionManager::ResolveRefLayers()
if (!mLayerManager->GetRoot()) {
return;
}
mReadyForCompose = true;
WalkTheTree<Resolve>(mLayerManager->GetRoot(),
mReadyForCompose,
mTargetConfig);

View File

@ -800,11 +800,17 @@ gfxFontEntry::DisconnectSVG()
}
}
bool
gfxFontEntry::HasFontTable(uint32_t aTableTag)
{
AutoTable table(this, aTableTag);
return table && hb_blob_get_length(table) > 0;
}
void
gfxFontEntry::CheckForGraphiteTables()
{
AutoTable silfTable(this, TRUETYPE_TAG('S','i','l','f'));
mHasGraphiteTables = silfTable && hb_blob_get_length(silfTable) > 0;
mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
}
/* static */ size_t
@ -4165,13 +4171,25 @@ gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
// Abs because of negative xHeight seen in Kokonor (Tibetan) font
aMetrics.xHeight = Abs(aMetrics.xHeight);
}
// this should always be present
if (len >= offsetof(OS2Table, yStrikeoutPosition) + sizeof(int16_t)) {
// this should always be present in any valid OS/2 of any version
if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
SET_SIGNED(subscriptOffset, os2->ySubscriptYOffset);
SET_SIGNED(superscriptOffset, os2->ySuperscriptYOffset);
SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
// for fonts with USE_TYPO_METRICS set in the fsSelection field,
// and for all OpenType math fonts (having a 'MATH' table),
// let the OS/2 sTypo* metrics override those from the hhea table
// (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
const uint16_t kUseTypoMetricsMask = 1 << 7;
if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask) ||
mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) {
SET_SIGNED(maxAscent, os2->sTypoAscender);
SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
SET_SIGNED(externalLeading, os2->sTypoLineGap);
}
}
}

View File

@ -268,6 +268,8 @@ public:
virtual bool IsSymbolFont();
virtual bool HasFontTable(uint32_t aTableTag);
inline bool HasGraphiteTables() {
if (!mCheckedForGraphiteTables) {
CheckForGraphiteTables();

View File

@ -9,6 +9,7 @@ EXPORTS += [
'nsIUnicodeDecoder.h',
'nsIUnicodeEncoder.h',
'nsUConvCID.h',
'nsUCSupport.h',
'uconvutil.h',
]

View File

@ -111,7 +111,6 @@ UNIFIED_SOURCES += [
'../ucvlatin/nsMacIcelandicToUnicode.cpp',
'../ucvlatin/nsMacRomanianToUnicode.cpp',
'../ucvlatin/nsMacTurkishToUnicode.cpp',
'../ucvlatin/nsMUTF7ToUnicode.cpp',
'../ucvlatin/nsT61ToUnicode.cpp',
'../ucvlatin/nsTCVN5712ToUnicode.cpp',
'../ucvlatin/nsTIS620ToUnicode.cpp',
@ -161,18 +160,15 @@ UNIFIED_SOURCES += [
'../ucvlatin/nsUnicodeToMacIcelandic.cpp',
'../ucvlatin/nsUnicodeToMacRomanian.cpp',
'../ucvlatin/nsUnicodeToMacTurkish.cpp',
'../ucvlatin/nsUnicodeToMUTF7.cpp',
'../ucvlatin/nsUnicodeToT61.cpp',
'../ucvlatin/nsUnicodeToTCVN5712.cpp',
'../ucvlatin/nsUnicodeToTIS620.cpp',
'../ucvlatin/nsUnicodeToUserDefined.cpp',
'../ucvlatin/nsUnicodeToUTF16.cpp',
'../ucvlatin/nsUnicodeToUTF7.cpp',
'../ucvlatin/nsUnicodeToVISCII.cpp',
'../ucvlatin/nsUnicodeToVPS.cpp',
'../ucvlatin/nsUserDefinedToUnicode.cpp',
'../ucvlatin/nsUTF16ToUnicode.cpp',
'../ucvlatin/nsUTF7ToUnicode.cpp',
'../ucvlatin/nsVISCIIToUnicode.cpp',
'../ucvlatin/nsVPSToUnicode.cpp',
]

View File

@ -74,8 +74,6 @@
#include "nsTCVN5712ToUnicode.h"
#include "nsVISCIIToUnicode.h"
#include "nsVPSToUnicode.h"
#include "nsUTF7ToUnicode.h"
#include "nsMUTF7ToUnicode.h"
#include "nsUTF16ToUnicode.h"
#include "nsT61ToUnicode.h"
#include "nsUserDefinedToUnicode.h"
@ -123,8 +121,6 @@
#include "nsUnicodeToTCVN5712.h"
#include "nsUnicodeToVISCII.h"
#include "nsUnicodeToVPS.h"
#include "nsUnicodeToUTF7.h"
#include "nsUnicodeToMUTF7.h"
#include "nsUnicodeToUTF16.h"
#include "nsUnicodeToT61.h"
#include "nsUnicodeToUserDefined.h"
@ -253,8 +249,6 @@ NS_UCONV_REG_UNREG("armscii-8", NS_ARMSCII8TOUNICODE_CID, NS_UNICODETOARMSCII8_C
NS_UCONV_REG_UNREG("x-viet-tcvn5712", NS_TCVN5712TOUNICODE_CID, NS_UNICODETOTCVN5712_CID)
NS_UCONV_REG_UNREG("VISCII", NS_VISCIITOUNICODE_CID, NS_UNICODETOVISCII_CID)
NS_UCONV_REG_UNREG("x-viet-vps", NS_VPSTOUNICODE_CID, NS_UNICODETOVPS_CID)
NS_UCONV_REG_UNREG("UTF-7", NS_UTF7TOUNICODE_CID, NS_UNICODETOUTF7_CID)
NS_UCONV_REG_UNREG("x-imap4-modified-utf7", NS_MUTF7TOUNICODE_CID, NS_UNICODETOMUTF7_CID)
NS_UCONV_REG_UNREG("UTF-16", NS_UTF16TOUNICODE_CID, NS_UNICODETOUTF16_CID)
NS_UCONV_REG_UNREG("UTF-16BE", NS_UTF16BETOUNICODE_CID, NS_UNICODETOUTF16BE_CID)
NS_UCONV_REG_UNREG("UTF-16LE", NS_UTF16LETOUNICODE_CID, NS_UNICODETOUTF16LE_CID)
@ -313,13 +307,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF8ToUnicode)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsReplacementToUnicode)
// ucvlatin
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF7ToUnicode)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMUTF7ToUnicode)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF16ToUnicode)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF16BEToUnicode)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF16LEToUnicode)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF7)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToMUTF7)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF16BE)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF16LE)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF16)
@ -530,8 +520,6 @@ NS_DEFINE_NAMED_CID(NS_ARMSCII8TOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_TCVN5712TOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_VISCIITOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_VPSTOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_UTF7TOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_MUTF7TOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_UTF16TOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_UTF16BETOUNICODE_CID);
NS_DEFINE_NAMED_CID(NS_UTF16LETOUNICODE_CID);
@ -587,8 +575,6 @@ NS_DEFINE_NAMED_CID(NS_UNICODETOARMSCII8_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOTCVN5712_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOVISCII_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOVPS_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOUTF7_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOMUTF7_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16BE_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16LE_CID);
NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16_CID);
@ -701,8 +687,6 @@ static const mozilla::Module::CIDEntry kUConvCIDs[] = {
{ &kNS_TCVN5712TOUNICODE_CID, false, nullptr, nsTCVN5712ToUnicodeConstructor },
{ &kNS_VISCIITOUNICODE_CID, false, nullptr, nsVISCIIToUnicodeConstructor },
{ &kNS_VPSTOUNICODE_CID, false, nullptr, nsVPSToUnicodeConstructor },
{ &kNS_UTF7TOUNICODE_CID, false, nullptr, nsUTF7ToUnicodeConstructor },
{ &kNS_MUTF7TOUNICODE_CID, false, nullptr, nsMUTF7ToUnicodeConstructor },
{ &kNS_UTF16TOUNICODE_CID, false, nullptr, nsUTF16ToUnicodeConstructor },
{ &kNS_UTF16BETOUNICODE_CID, false, nullptr, nsUTF16BEToUnicodeConstructor },
{ &kNS_UTF16LETOUNICODE_CID, false, nullptr, nsUTF16LEToUnicodeConstructor },
@ -758,8 +742,6 @@ static const mozilla::Module::CIDEntry kUConvCIDs[] = {
{ &kNS_UNICODETOTCVN5712_CID, false, nullptr, nsUnicodeToTCVN5712Constructor },
{ &kNS_UNICODETOVISCII_CID, false, nullptr, nsUnicodeToVISCIIConstructor },
{ &kNS_UNICODETOVPS_CID, false, nullptr, nsUnicodeToVPSConstructor },
{ &kNS_UNICODETOUTF7_CID, false, nullptr, nsUnicodeToUTF7Constructor },
{ &kNS_UNICODETOMUTF7_CID, false, nullptr, nsUnicodeToMUTF7Constructor },
{ &kNS_UNICODETOUTF16BE_CID, false, nullptr, nsUnicodeToUTF16BEConstructor },
{ &kNS_UNICODETOUTF16LE_CID, false, nullptr, nsUnicodeToUTF16LEConstructor },
{ &kNS_UNICODETOUTF16_CID, false, nullptr, nsUnicodeToUTF16Constructor },
@ -874,8 +856,6 @@ static const mozilla::Module::ContractIDEntry kUConvContracts[] = {
{ NS_UNICODEDECODER_CONTRACTID_BASE "x-viet-tcvn5712", &kNS_TCVN5712TOUNICODE_CID },
{ NS_UNICODEDECODER_CONTRACTID_BASE "VISCII", &kNS_VISCIITOUNICODE_CID },
{ NS_UNICODEDECODER_CONTRACTID_BASE "x-viet-vps", &kNS_VPSTOUNICODE_CID },
{ NS_UNICODEDECODER_CONTRACTID_BASE "UTF-7", &kNS_UTF7TOUNICODE_CID },
{ NS_UNICODEDECODER_CONTRACTID_BASE "x-imap4-modified-utf7", &kNS_MUTF7TOUNICODE_CID },
{ NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16", &kNS_UTF16TOUNICODE_CID },
{ NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16BE", &kNS_UTF16BETOUNICODE_CID },
{ NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16LE", &kNS_UTF16LETOUNICODE_CID },
@ -931,8 +911,6 @@ static const mozilla::Module::ContractIDEntry kUConvContracts[] = {
{ NS_UNICODEENCODER_CONTRACTID_BASE "x-viet-tcvn5712", &kNS_UNICODETOTCVN5712_CID },
{ NS_UNICODEENCODER_CONTRACTID_BASE "VISCII", &kNS_UNICODETOVISCII_CID },
{ NS_UNICODEENCODER_CONTRACTID_BASE "x-viet-vps", &kNS_UNICODETOVPS_CID },
{ NS_UNICODEENCODER_CONTRACTID_BASE "UTF-7", &kNS_UNICODETOUTF7_CID },
{ NS_UNICODEENCODER_CONTRACTID_BASE "x-imap4-modified-utf7", &kNS_UNICODETOMUTF7_CID },
{ NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16BE", &kNS_UNICODETOUTF16BE_CID },
{ NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16LE", &kNS_UNICODETOUTF16LE_CID },
{ NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16", &kNS_UNICODETOUTF16_CID },

View File

@ -707,78 +707,6 @@ nsresult testUTF8Decoder()
}
}
/**
* Test the M-UTF-7 decoder.
*/
nsresult testMUTF7Decoder()
{
char * testName = "T107";
printf("\n[%s] Unicode <- MUTF7\n", testName);
// create converter
CREATE_DECODER("x-imap4-modified-utf7");
// test data
char src[] = {"\x50\x51\x52\x53&AAAAAAAA-&-&AAA-"};
char16_t exp[] = {0x0050,0x0051,0x0052,0x0053,0x0000,0x0000,0x0000,'&',0x0000};
// test converter - normal operation
res = testDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
// reset converter
if (NS_SUCCEEDED(res)) res = resetDecoder(dec, testName);
// test converter - stress test
if (NS_SUCCEEDED(res))
res = testStressDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
// release converter
NS_RELEASE(dec);
if (NS_FAILED(res)) {
return res;
} else {
printf("Test Passed.\n");
return NS_OK;
}
}
/**
* Test the UTF-7 decoder.
*/
nsresult testUTF7Decoder()
{
char * testName = "T108";
printf("\n[%s] Unicode <- UTF7\n", testName);
// create converter
CREATE_DECODER("utf-7");
// test data
char src[] = {"+ADwAIQ-DOC"};
char16_t exp[] = {'<','!','D','O','C'};
// test converter - normal operation
res = testDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
// reset converter
if (NS_SUCCEEDED(res)) res = resetDecoder(dec, testName);
// test converter - stress test
if (NS_SUCCEEDED(res))
res = testStressDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
// release converter
NS_RELEASE(dec);
if (NS_FAILED(res)) {
return res;
} else {
printf("Test Passed.\n");
return NS_OK;
}
}
//----------------------------------------------------------------------
// Encoders testing functions
@ -942,80 +870,6 @@ nsresult testISO2022JPEncoder()
}
}
/**
* Test the M-UTF-7 encoder.
*/
nsresult testMUTF7Encoder()
{
char * testName = "T205";
printf("\n[%s] Unicode -> MUTF-7\n", testName);
// create converter
CREATE_ENCODER("x-imap4-modified-utf7");
enc->SetOutputErrorBehavior(enc->kOnError_Replace, nullptr, 0x00cc);
// test data
char16_t src[] = {0x0050,0x0051,0x0052,0x0053,0x0000,0x0000,0x0000,'&',0x0000};
char exp[] = {"\x50\x51\x52\x53&AAAAAAAA-&-&AAA-"};
// test converter - easy test
res = testEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
// reset converter
if (NS_SUCCEEDED(res)) res = resetEncoder(enc, testName);
// test converter - stress test
if (NS_SUCCEEDED(res))
res = testStressEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
// release converter
NS_RELEASE(enc);
if (NS_FAILED(res)) {
return res;
} else {
printf("Test Passed.\n");
return NS_OK;
}
}
/**
* Test the UTF-7 encoder.
*/
nsresult testUTF7Encoder()
{
char * testName = "T206";
printf("\n[%s] Unicode -> UTF-7\n", testName);
// create converter
CREATE_ENCODER("utf-7");
enc->SetOutputErrorBehavior(enc->kOnError_Replace, nullptr, 0x00cc);
// test data
char16_t src[] = {'e','t','i','r','a',0x0a};
char exp[] = {"etira\x0a"};
// test converter - easy test
res = testEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
// reset converter
if (NS_SUCCEEDED(res)) res = resetEncoder(enc, testName);
// test converter - stress test
if (NS_SUCCEEDED(res))
res = testStressEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
// release converter
NS_RELEASE(enc);
if (NS_FAILED(res)) {
return res;
} else {
printf("Test Passed.\n");
return NS_OK;
}
}
nsresult testPlatformCharset()
{
nsIPlatformCharset *cinfo;

View File

@ -59,8 +59,6 @@ var encoderList = [
"x-viet-tcvn5712",
"VISCII",
"x-viet-vps",
"UTF-7",
"x-imap4-modified-utf7",
"UTF-16",
"UTF-16BE",
"UTF-16LE",
@ -145,8 +143,6 @@ var decoderList = [
"x-viet-tcvn5712",
"VISCII",
"x-viet-vps",
"UTF-7",
"x-imap4-modified-utf7",
"UTF-16",
"UTF-16BE",
"UTF-16LE",

View File

@ -1,14 +0,0 @@
// Tests conversion from UTF-7 to Unicode. The conversion should fail!
load('CharsetConversionTests.js');
const inString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
const expectedString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
"unicode-1-1-utf-7", "csunicode11utf7" ];
function run_test() {
testDecodeAliases();
}

View File

@ -1,14 +0,0 @@
// Tests conversion from UTF-7 to Unicode.
load('CharsetConversionTests.js');
const inString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
const expectedString = "\u2C62-\u2132\u22A5\u2229 \u0287\u0279oddns \u01DD\u028D s\u0131\u0265\u0287 p\u0250\u01DD\u0279 u\u0250\u0254 no\u028E \u025FI";
const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
"unicode-1-1-utf-7", "csunicode11utf7" ];
function run_test() {
testDecodeAliasesInternal();
}

View File

@ -1,14 +0,0 @@
// Tests conversion from Unicode to UTF-7. The conversion should fail!
load('CharsetConversionTests.js');
const inString = "\u2C62-\u2132\u22A5\u2229 \u0287\u0279oddns \u01DD\u028D s\u0131\u0265\u0287 p\u0250\u01DD\u0279 u\u0250\u0254 no\u028E \u025FI";
const expectedString = "?-??? ??oddns ?? s??? p??? u?? no? ?I";
const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
"unicode-1-1-utf-7", "csunicode11utf7" ];
function run_test() {
testEncodeAliases();
}

View File

@ -1,14 +0,0 @@
// Tests conversion from Unicode to UTF-7.
load('CharsetConversionTests.js');
const inString = "\u2C62-\u2132\u22A5\u2229 \u0287\u0279oddns \u01DD\u028D s\u0131\u0265\u0287 p\u0250\u01DD\u0279 u\u0250\u0254 no\u028E \u025FI";
const expectedString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
"unicode-1-1-utf-7", "csunicode11utf7" ];
function run_test() {
testEncodeAliasesInternal();
}

View File

@ -76,8 +76,6 @@ support-files =
[test_decode_gb18030.js]
[test_decode_gbk.js]
[test_decode_tcvn5712.js]
[test_decode_utf-7_internal.js]
[test_decode_utf-7.js]
[test_decode_viscii.js]
[test_decode_vps.js]
[test_decode_x_mac_arabic.js]
@ -131,8 +129,6 @@ support-files =
[test_encode_armscii.js]
[test_encode_gbk.js]
[test_encode_tcvn5712.js]
[test_encode_utf-7_internal.js]
[test_encode_utf-7.js]
[test_encode_viscii.js]
[test_encode_vps.js]
[test_encode_x_mac_arabic.js]

View File

@ -1,14 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsMUTF7ToUnicode.h"
//----------------------------------------------------------------------
// Class nsMUTF7ToUnicode [implementation]
nsMUTF7ToUnicode::nsMUTF7ToUnicode()
: nsBasicUTF7Decoder(',', '&')
{
}

View File

@ -1,31 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsMUTF7ToUnicode_h___
#define nsMUTF7ToUnicode_h___
#include "nsUTF7ToUnicode.h"
//----------------------------------------------------------------------
// Class nsMUTF7ToUnicode [declaration]
/**
* A character set converter from Modified UTF7 to Unicode.
*
* @created 18/May/1999
* @author Catalin Rotaru [CATA]
*/
class nsMUTF7ToUnicode : public nsBasicUTF7Decoder
{
public:
/**
* Class constructor.
*/
nsMUTF7ToUnicode();
};
#endif /* nsMUTF7ToUnicode_h___ */

View File

@ -189,16 +189,6 @@
#define NS_VPSTOUNICODE_CID \
{ 0x6394eeb0, 0xfc3d, 0x11d2, {0xb3, 0xb8, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
// Class ID for our UTF7ToUnicode charset converter
// {77CFAAF1-1CF4-11d3-8AAF-00600811A836}
#define NS_UTF7TOUNICODE_CID \
{ 0x77cfaaf1, 0x1cf4, 0x11d3, {0x8a, 0xaf, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
// Class ID for our MUTF7ToUnicode charset converter
// {B57F97C1-0D70-11d3-8AAE-00600811A836}
#define NS_MUTF7TOUNICODE_CID \
{ 0xb57f97c1, 0xd70, 0x11d3, {0x8a, 0xae, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
// Class ID for our UnicodeToISO88592 charset converter
// {7B8556A6-EC79-11d2-8AAC-00600811A836}
#define NS_UNICODETOISO88592_CID \
@ -380,16 +370,6 @@
#define NS_UNICODETOVPS_CID \
{ 0x6394eec0, 0xfc3d, 0x11d2, {0xb3, 0xb8, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
// Class ID for our UnicodeToUTF7 charset converter
// {77CFAAF2-1CF4-11d3-8AAF-00600811A836}
#define NS_UNICODETOUTF7_CID \
{ 0x77cfaaf2, 0x1cf4, 0x11d3, {0x8a, 0xaf, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
// Class ID for our UnicodeToMUTF7 charset converter
// {B57F97C2-0D70-11d3-8AAE-00600811A836}
#define NS_UNICODETOMUTF7_CID \
{ 0xb57f97c2, 0xd70, 0x11d3, {0x8a, 0xae, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
// Class ID for our CP1255ToUnicode charset converter
// {BA6151A1-1DFA-11d3-B3BF-00805F8A6670}
#define NS_CP1255TOUNICODE_CID \

View File

@ -1,228 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsUTF7ToUnicode.h"
#define ENC_DIRECT 0
#define ENC_BASE64 1
//----------------------------------------------------------------------
// Class nsBasicUTF7Decoder [implementation]
nsBasicUTF7Decoder::nsBasicUTF7Decoder(char aLastChar, char aEscChar)
: nsBufferDecoderSupport(1)
{
mLastChar = aLastChar;
mEscChar = aEscChar;
Reset();
}
nsresult nsBasicUTF7Decoder::DecodeDirect(
const char * aSrc,
int32_t * aSrcLength,
char16_t * aDest,
int32_t * aDestLength)
{
const char * srcEnd = aSrc + *aSrcLength;
const char * src = aSrc;
char16_t * destEnd = aDest + *aDestLength;
char16_t * dest = aDest;
nsresult res = NS_OK;
char ch;
while (src < srcEnd) {
ch = *src;
// stop when we meet other chars or end of direct encoded seq.
// if (!(DirectEncodable(ch)) || (ch == mEscChar)) {
// but we are decoding; so we should be lax; pass everything until escchar
if (ch == mEscChar) {
res = NS_ERROR_UDEC_ILLEGALINPUT;
break;
}
if (dest >= destEnd) {
res = NS_OK_UDEC_MOREOUTPUT;
break;
} else {
*dest++ = ch;
src++;
}
}
*aSrcLength = src - aSrc;
*aDestLength = dest - aDest;
return res;
}
nsresult nsBasicUTF7Decoder::DecodeBase64(
const char * aSrc,
int32_t * aSrcLength,
char16_t * aDest,
int32_t * aDestLength)
{
const char * srcEnd = aSrc + *aSrcLength;
const char * src = aSrc;
char16_t * destEnd = aDest + *aDestLength;
char16_t * dest = aDest;
nsresult res = NS_OK;
char ch;
uint32_t value;
while (src < srcEnd) {
ch = *src;
// stop when we meet other chars or end of direct encoded seq.
value = CharToValue(ch);
if (value > 0xff) {
res = NS_ERROR_UDEC_ILLEGALINPUT;
break;
}
switch (mEncStep) {
case 0:
mEncBits = value << 10;
break;
case 1:
mEncBits += value << 4;
break;
case 2:
if (dest >= destEnd) {
res = NS_OK_UDEC_MOREOUTPUT;
break;
}
mEncBits += value >> 2;
*(dest++) = (char16_t) mEncBits;
mEncBits = (value & 0x03) << 14;
break;
case 3:
mEncBits += value << 8;
break;
case 4:
mEncBits += value << 2;
break;
case 5:
if (dest >= destEnd) {
res = NS_OK_UDEC_MOREOUTPUT;
break;
}
mEncBits += value >> 4;
*(dest++) = (char16_t) mEncBits;
mEncBits = (value & 0x0f) << 12;
break;
case 6:
mEncBits += value << 6;
break;
case 7:
if (dest >= destEnd) {
res = NS_OK_UDEC_MOREOUTPUT;
break;
}
mEncBits += value;
*(dest++) = (char16_t) mEncBits;
mEncBits = 0;
break;
}
if (res != NS_OK) break;
src++;
(++mEncStep)%=8;
}
*aSrcLength = src - aSrc;
*aDestLength = dest - aDest;
return res;
}
uint32_t nsBasicUTF7Decoder::CharToValue(char aChar) {
if ((aChar>='A')&&(aChar<='Z'))
return (uint8_t)(aChar-'A');
else if ((aChar>='a')&&(aChar<='z'))
return (uint8_t)(26+aChar-'a');
else if ((aChar>='0')&&(aChar<='9'))
return (uint8_t)(26+26+aChar-'0');
else if (aChar=='+')
return (uint8_t)(26+26+10);
else if (aChar==mLastChar)
return (uint8_t)(26+26+10+1);
else
return 0xffff;
}
//----------------------------------------------------------------------
// Subclassing of nsBufferDecoderSupport class [implementation]
NS_IMETHODIMP nsBasicUTF7Decoder::ConvertNoBuff(const char * aSrc,
int32_t * aSrcLength,
char16_t * aDest,
int32_t * aDestLength)
{
const char * srcEnd = aSrc + *aSrcLength;
const char * src = aSrc;
char16_t * destEnd = aDest + *aDestLength;
char16_t * dest = aDest;
int32_t bcr,bcw;
nsresult res = NS_OK;
while (src < srcEnd) {
// fist, attept to decode in the current mode
bcr = srcEnd - src;
bcw = destEnd - dest;
if (mEncoding == ENC_DIRECT)
res = DecodeDirect(src, &bcr, dest, &bcw);
else if ((mFreshBase64) && (*src == '-')) {
*dest = mEscChar;
bcr = 0;
bcw = 1;
res = NS_ERROR_UDEC_ILLEGALINPUT;
} else {
mFreshBase64 = false;
res = DecodeBase64(src, &bcr, dest, &bcw);
}
src += bcr;
dest += bcw;
// if an illegal char was encountered, test if it is an escape seq.
if (res == NS_ERROR_UDEC_ILLEGALINPUT) {
if (mEncoding == ENC_DIRECT) {
if (*src == mEscChar) {
mEncoding = ENC_BASE64;
mFreshBase64 = true;
mEncBits = 0;
mEncStep = 0;
src++;
res = NS_OK;
} else break;
} else {
mEncoding = ENC_DIRECT;
res = NS_OK;
// absorbe end of escape sequence
if (*src == '-') src++;
}
} else if (res != NS_OK) break;
}
*aSrcLength = src - aSrc;
*aDestLength = dest - aDest;
return res;
}
NS_IMETHODIMP nsBasicUTF7Decoder::Reset()
{
mEncoding = ENC_DIRECT;
mEncBits = 0;
mEncStep = 0;
return nsBufferDecoderSupport::Reset();
}
//----------------------------------------------------------------------
// Class nsUTF7ToUnicode [implementation]
nsUTF7ToUnicode::nsUTF7ToUnicode()
: nsBasicUTF7Decoder('/', '+')
{
}

View File

@ -1,72 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsUTF7ToUnicode_h___
#define nsUTF7ToUnicode_h___
#include "nsUCSupport.h"
//----------------------------------------------------------------------
// Class nsBasicUTF7Decoder [declaration]
/**
* Basic class for a character set converter from UTF-7 to Unicode.
*
* @created 03/Jun/1999
* @author Catalin Rotaru [CATA]
*/
class nsBasicUTF7Decoder : public nsBufferDecoderSupport
{
public:
/**
* Class constructor.
*/
nsBasicUTF7Decoder(char aLastChar, char aEscChar);
protected:
int32_t mEncoding; // current encoding
uint32_t mEncBits;
int32_t mEncStep;
char mLastChar;
char mEscChar;
bool mFreshBase64;
nsresult DecodeDirect(const char * aSrc, int32_t * aSrcLength,
char16_t * aDest, int32_t * aDestLength);
nsresult DecodeBase64(const char * aSrc, int32_t * aSrcLength,
char16_t * aDest, int32_t * aDestLength);
uint32_t CharToValue(char aChar);
//--------------------------------------------------------------------
// Subclassing of nsBufferDecoderSupport class [declaration]
NS_IMETHOD ConvertNoBuff(const char * aSrc, int32_t * aSrcLength,
char16_t * aDest, int32_t * aDestLength);
NS_IMETHOD Reset();
};
//----------------------------------------------------------------------
// Class nsUTF7ToUnicode [declaration]
/**
* A character set converter from Modified UTF7 to Unicode.
*
* @created 18/May/1999
* @author Catalin Rotaru [CATA]
*/
class nsUTF7ToUnicode : public nsBasicUTF7Decoder
{
public:
/**
* Class constructor.
*/
nsUTF7ToUnicode();
};
#endif /* nsUTF7ToUnicode_h___ */

View File

@ -1,14 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsUnicodeToMUTF7.h"
//----------------------------------------------------------------------
// Class nsUnicodeToMUTF7 [implementation]
nsUnicodeToMUTF7::nsUnicodeToMUTF7()
: nsBasicUTF7Encoder(',', '&')
{
}

View File

@ -1,31 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsUnicodeToMUTF7_h___
#define nsUnicodeToMUTF7_h___
#include "nsUnicodeToUTF7.h"
//----------------------------------------------------------------------
// Class nsUnicodeToMUTF7 [declaration]
/**
* A character set converter from Unicode to Modified UTF-7.
*
* @created 18/May/1999
* @author Catalin Rotaru [CATA]
*/
class nsUnicodeToMUTF7 : public nsBasicUTF7Encoder
{
public:
/**
* Class constructor.
*/
nsUnicodeToMUTF7();
};
#endif /* nsUnicodeToMUTF7_h___ */

View File

@ -1,298 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsUnicodeToUTF7.h"
#include <string.h>
//----------------------------------------------------------------------
// Global functions and data [declaration]
#define ENC_DIRECT 0
#define ENC_BASE64 1
//----------------------------------------------------------------------
// Class nsBasicUTF7Encoder [implementation]
nsBasicUTF7Encoder::nsBasicUTF7Encoder(char aLastChar, char aEscChar)
: nsEncoderSupport(5)
{
mLastChar = aLastChar;
mEscChar = aEscChar;
Reset();
}
nsresult nsBasicUTF7Encoder::ShiftEncoding(int32_t aEncoding,
char * aDest,
int32_t * aDestLength)
{
if (aEncoding == mEncoding) {
*aDestLength = 0;
return NS_OK;
}
nsresult res = NS_OK;
char * dest = aDest;
char * destEnd = aDest + *aDestLength;
if (mEncStep != 0) {
if (dest >= destEnd) return NS_OK_UENC_MOREOUTPUT;
*(dest++)=ValueToChar(mEncBits);
mEncStep = 0;
mEncBits = 0;
}
if (dest >= destEnd) {
res = NS_OK_UENC_MOREOUTPUT;
} else {
switch (aEncoding) {
case 0:
*(dest++) = '-';
mEncStep = 0;
mEncBits = 0;
break;
case 1:
*(dest++) = mEscChar;
break;
}
mEncoding = aEncoding;
}
*aDestLength = dest - aDest;
return res;
}
nsresult nsBasicUTF7Encoder::EncodeDirect(
const char16_t * aSrc,
int32_t * aSrcLength,
char * aDest,
int32_t * aDestLength)
{
nsresult res = NS_OK;
const char16_t * src = aSrc;
const char16_t * srcEnd = aSrc + *aSrcLength;
char * dest = aDest;
char * destEnd = aDest + *aDestLength;
char16_t ch;
while (src < srcEnd) {
ch = *src;
// stop when we reach Unicode chars
if (!DirectEncodable(ch)) break;
if (ch == mEscChar) {
// special case for the escape char
if (destEnd - dest < 1) {
res = NS_OK_UENC_MOREOUTPUT;
break;
} else {
*dest++ = (char)ch;
*dest++ = (char)'-';
src++;
}
} else {
//classic direct encoding
if (dest >= destEnd) {
res = NS_OK_UENC_MOREOUTPUT;
break;
} else {
*dest++ = (char)ch;
src++;
}
}
}
*aSrcLength = src - aSrc;
*aDestLength = dest - aDest;
return res;
}
nsresult nsBasicUTF7Encoder::EncodeBase64(
const char16_t * aSrc,
int32_t * aSrcLength,
char * aDest,
int32_t * aDestLength)
{
nsresult res = NS_OK;
const char16_t * src = aSrc;
const char16_t * srcEnd = aSrc + *aSrcLength;
char * dest = aDest;
char * destEnd = aDest + *aDestLength;
char16_t ch;
uint32_t value;
while (src < srcEnd) {
ch = *src;
// stop when we reach printable US-ASCII chars
if (DirectEncodable(ch)) break;
switch (mEncStep) {
case 0:
if (destEnd - dest < 2) {
res = NS_OK_UENC_MOREOUTPUT;
break;
}
value=ch>>10;
*(dest++)=ValueToChar(value);
value=(ch>>4)&0x3f;
*(dest++)=ValueToChar(value);
mEncBits=(ch&0x0f)<<2;
break;
case 1:
if (destEnd - dest < 3) {
res = NS_OK_UENC_MOREOUTPUT;
break;
}
value=mEncBits+(ch>>14);
*(dest++)=ValueToChar(value);
value=(ch>>8)&0x3f;
*(dest++)=ValueToChar(value);
value=(ch>>2)&0x3f;
*(dest++)=ValueToChar(value);
mEncBits=(ch&0x03)<<4;
break;
case 2:
if (destEnd - dest < 3) {
res = NS_OK_UENC_MOREOUTPUT;
break;
}
value=mEncBits+(ch>>12);
*(dest++)=ValueToChar(value);
value=(ch>>6)&0x3f;
*(dest++)=ValueToChar(value);
value=ch&0x3f;
*(dest++)=ValueToChar(value);
mEncBits=0;
break;
}
if (res != NS_OK) break;
src++;
(++mEncStep)%=3;
}
*aSrcLength = src - aSrc;
*aDestLength = dest - aDest;
return res;
}
char nsBasicUTF7Encoder::ValueToChar(uint32_t aValue) {
if (aValue < 26)
return (char)('A'+aValue);
else if (aValue < 26 + 26)
return (char)('a' + aValue - 26);
else if (aValue < 26 + 26 + 10)
return (char)('0' + aValue - 26 - 26);
else if (aValue == 26 + 26 + 10)
return '+';
else if (aValue == 26 + 26 + 10 + 1)
return mLastChar;
else
return -1;
}
bool nsBasicUTF7Encoder::DirectEncodable(char16_t aChar) {
// spec says: printable US-ASCII chars
if ((aChar >= 0x20) && (aChar <= 0x7e)) return true;
else return false;
}
//----------------------------------------------------------------------
// Subclassing of nsEncoderSupport class [implementation]
NS_IMETHODIMP nsBasicUTF7Encoder::ConvertNoBuffNoErr(
const char16_t * aSrc,
int32_t * aSrcLength,
char * aDest,
int32_t * aDestLength)
{
nsresult res = NS_OK;
const char16_t * src = aSrc;
const char16_t * srcEnd = aSrc + *aSrcLength;
char * dest = aDest;
char * destEnd = aDest + *aDestLength;
int32_t bcr,bcw;
char16_t ch;
int32_t enc;
while (src < srcEnd) {
// find the encoding for the next char
ch = *src;
if (DirectEncodable(ch))
enc = ENC_DIRECT;
else
enc = ENC_BASE64;
// if necessary, shift into the required encoding
bcw = destEnd - dest;
res = ShiftEncoding(enc, dest, &bcw);
dest += bcw;
if (res != NS_OK) break;
// now encode (as much as you can)
bcr = srcEnd - src;
bcw = destEnd - dest;
if (enc == ENC_DIRECT)
res = EncodeDirect(src, &bcr, dest, &bcw);
else
res = EncodeBase64(src, &bcr, dest, &bcw);
src += bcr;
dest += bcw;
if (res != NS_OK) break;
}
*aSrcLength = src - aSrc;
*aDestLength = dest - aDest;
return res;
}
NS_IMETHODIMP nsBasicUTF7Encoder::FinishNoBuff(char * aDest,
int32_t * aDestLength)
{
return ShiftEncoding(ENC_DIRECT, aDest, aDestLength);
}
NS_IMETHODIMP nsBasicUTF7Encoder::Reset()
{
mEncoding = ENC_DIRECT;
mEncBits = 0;
mEncStep = 0;
return nsEncoderSupport::Reset();
}
//----------------------------------------------------------------------
// Class nsUnicodeToUTF7 [implementation]
nsUnicodeToUTF7::nsUnicodeToUTF7()
: nsBasicUTF7Encoder('/', '+')
{
}
bool nsUnicodeToUTF7::DirectEncodable(char16_t aChar) {
if ((aChar >= 'A') && (aChar <= 'Z')) return true;
else if ((aChar >= 'a') && (aChar <= 'z')) return true;
else if ((aChar >= '0') && (aChar <= '9')) return true;
else if ((aChar >= 39) && (aChar <= 41)) return true;
else if ((aChar >= 44) && (aChar <= 47)) return true;
else if (aChar == 58) return true;
else if (aChar == 63) return true;
else if (aChar == ' ') return true;
else if (aChar == 9) return true;
else if (aChar == 13) return true;
else if (aChar == 10) return true;
else if (aChar == 60) return true; // '<'
else if (aChar == 33) return true; // '!'
else if (aChar == 34) return true; // '"'
else if (aChar == 62) return true; // '>'
else if (aChar == 61) return true; // '='
else if (aChar == 59) return true; // ';'
else if (aChar == 91) return true; // '['
else if (aChar == 93) return true; // ']'
else return false;
}

View File

@ -1,78 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsUnicodeToUTF7_h___
#define nsUnicodeToUTF7_h___
#include "nsUCSupport.h"
//----------------------------------------------------------------------
// Class nsBasicUTF7Encoder [declaration]
/**
* Basic class for a character set converter from Unicode to UTF-7.
*
* @created 03/Jun/1999
* @author Catalin Rotaru [CATA]
*/
class nsBasicUTF7Encoder : public nsEncoderSupport
{
public:
/**
* Class constructor.
*/
nsBasicUTF7Encoder(char aLastChar, char aEscChar);
protected:
int32_t mEncoding; // current encoding
uint32_t mEncBits;
int32_t mEncStep;
char mLastChar;
char mEscChar;
nsresult ShiftEncoding(int32_t aEncoding, char * aDest,
int32_t * aDestLength);
nsresult EncodeDirect(const char16_t * aSrc, int32_t * aSrcLength,
char * aDest, int32_t * aDestLength);
nsresult EncodeBase64(const char16_t * aSrc, int32_t * aSrcLength,
char * aDest, int32_t * aDestLength);
char ValueToChar(uint32_t aValue);
virtual bool DirectEncodable(char16_t aChar);
//--------------------------------------------------------------------
// Subclassing of nsEncoderSupport class [declaration]
NS_IMETHOD ConvertNoBuffNoErr(const char16_t * aSrc, int32_t * aSrcLength,
char * aDest, int32_t * aDestLength);
NS_IMETHOD FinishNoBuff(char * aDest, int32_t * aDestLength);
NS_IMETHOD Reset();
};
//----------------------------------------------------------------------
// Class nsUnicodeToUTF7 [declaration]
/**
* A character set converter from Unicode to UTF-7.
*
* @created 03/Jun/1999
* @author Catalin Rotaru [CATA]
*/
class nsUnicodeToUTF7 : public nsBasicUTF7Encoder
{
public:
/**
* Class constructor.
*/
nsUnicodeToUTF7();
protected:
virtual bool DirectEncodable(char16_t aChar);
};
#endif /* nsUnicodeToUTF7_h___ */

View File

@ -31,6 +31,9 @@
#ifdef ANDROID
#include "base/message_pump_android.h"
#endif
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#endif
#include "MessagePump.h"
@ -277,6 +280,11 @@ void MessageLoop::PostNonNestableDelayedTask(
void MessageLoop::PostIdleTask(
const tracked_objects::Location& from_here, Task* task) {
DCHECK(current() == this);
#ifdef MOZ_TASK_TRACER
task = mozilla::tasktracer::CreateTracedTask(task);
#endif
task->SetBirthPlace(from_here);
PendingTask pending_task(task, false);
deferred_non_nestable_work_queue_.push(pending_task);
@ -286,6 +294,11 @@ void MessageLoop::PostIdleTask(
void MessageLoop::PostTask_Helper(
const tracked_objects::Location& from_here, Task* task, int delay_ms,
bool nestable) {
#ifdef MOZ_TASK_TRACER
task = mozilla::tasktracer::CreateTracedTask(task);
#endif
task->SetBirthPlace(from_here);
PendingTask pending_task(task, nestable);

View File

@ -11,6 +11,10 @@
#include "GeckoProfiler.h"
#include "mozilla/IOInterposer.h"
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#endif
namespace base {
// This task is used to trigger the message loop to exit.
@ -172,6 +176,10 @@ void Thread::ThreadMain() {
mozilla::IOInterposer::UnregisterCurrentThread();
profiler_unregister_thread();
#ifdef MOZ_TASK_TRACER
mozilla::tasktracer::FreeTraceInfo();
#endif
// We can't receive messages anymore.
message_loop_ = NULL;
thread_id_ = 0;

View File

@ -32,6 +32,11 @@
#include "chrome/common/ipc_message_utils.h"
#include "mozilla/ipc/ProtocolUtils.h"
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracerImpl.h"
using namespace mozilla::tasktracer;
#endif
namespace IPC {
// IPC channels on Windows use named pipes (CreateNamedPipe()) with
@ -594,6 +599,14 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
DLOG(INFO) << "received message on channel @" << this <<
" with type " << m.type();
#endif
#ifdef MOZ_TASK_TRACER
AutoSaveCurTraceInfo saveCurTraceInfo;
SetCurTraceInfo(m.header()->source_event_id,
m.header()->parent_task_id,
m.header()->source_event_type);
#endif
if (m.routing_id() == MSG_ROUTING_NONE &&
m.type() == HELLO_MESSAGE_TYPE) {
// The Hello message contains only the process id.
@ -687,6 +700,11 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
msg->set_fd_cookie(++last_pending_fd_id_);
#endif
}
#ifdef MOZ_TASK_TRACER
GetCurTraceInfo(&msg->header()->source_event_id,
&msg->header()->parent_task_id,
&msg->header()->source_event_type);
#endif
size_t amt_to_write = msg->size() - message_send_bytes_written_;
DCHECK(amt_to_write != 0);

View File

@ -10,6 +10,13 @@
#if defined(OS_POSIX)
#include "chrome/common/file_descriptor_set_posix.h"
#endif
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#endif
#ifdef MOZ_TASK_TRACER
using namespace mozilla::tasktracer;
#endif
namespace IPC {
@ -23,6 +30,11 @@ Message::Message()
header()->routing = header()->type = header()->flags = 0;
#if defined(OS_POSIX)
header()->num_fds = 0;
#endif
#ifdef MOZ_TASK_TRACER
header()->source_event_id = 0;
header()->parent_task_id = 0;
header()->source_event_type = SourceEventType::UNKNOWN;
#endif
InitLoggingVariables();
}
@ -43,6 +55,11 @@ Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority,
header()->seqno = 0;
#if defined(OS_MACOSX)
header()->cookie = 0;
#endif
#ifdef MOZ_TASK_TRACER
header()->source_event_id = 0;
header()->parent_task_id = 0;
header()->source_event_type = SourceEventType::UNKNOWN;
#endif
InitLoggingVariables(name);
}
@ -56,6 +73,11 @@ Message::Message(const Message& other) : Pickle(other) {
#if defined(OS_POSIX)
file_descriptor_set_ = other.file_descriptor_set_;
#endif
#ifdef MOZ_TASK_TRACER
header()->source_event_id = other.header()->source_event_id;
header()->parent_task_id = other.header()->parent_task_id;
header()->source_event_type = other.header()->source_event_type;
#endif
}
void Message::InitLoggingVariables(const char* const name) {
@ -72,6 +94,11 @@ Message& Message::operator=(const Message& other) {
InitLoggingVariables(other.name_);
#if defined(OS_POSIX)
file_descriptor_set_ = other.file_descriptor_set_;
#endif
#ifdef MOZ_TASK_TRACER
header()->source_event_id = other.header()->source_event_id;
header()->parent_task_id = other.header()->parent_task_id;
header()->source_event_type = other.header()->source_event_type;
#endif
return *this;
}

View File

@ -10,6 +10,10 @@
#include "base/basictypes.h"
#include "base/pickle.h"
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#endif
#ifndef NDEBUG
#define IPC_MESSAGE_LOG_ENABLED
#endif
@ -336,6 +340,11 @@ class Message : public Pickle {
uint32_t interrupt_local_stack_depth;
// Sequence number
int32_t seqno;
#ifdef MOZ_TASK_TRACER
uint64_t source_event_id;
uint64_t parent_task_id;
mozilla::tasktracer::SourceEventType source_event_type;
#endif
};
Header* header() {

View File

@ -9,6 +9,11 @@
#include "nsXULAppAPI.h"
#include <fcntl.h>
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
using namespace mozilla::tasktracer;
#endif
static const size_t MAX_READ_SIZE = 1 << 16;
namespace mozilla {
@ -673,6 +678,12 @@ UnixSocketImpl::OnSocketCanReceiveWithoutBlocking()
return;
}
#ifdef MOZ_TASK_TRACER
// Make unix socket creation events to be the source events of TaskTracer,
// and originate the rest correlation tasks from here.
AutoSourceEvent taskTracerEvent(SourceEventType::UNIXSOCKET);
#endif
incoming->mSize = ret;
nsRefPtr<SocketReceiveRunnable> r =
new SocketReceiveRunnable(this, incoming.forget());

View File

@ -237,6 +237,7 @@ typedef enum JSWhyMagic
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
JS_ION_ERROR, /* error while running Ion code */
JS_ION_BAILOUT, /* status code to signal EnterIon will OSR into Interpret */
JS_OPTIMIZED_OUT, /* optimized out slot */
JS_GENERIC_MAGIC /* for local use */
} JSWhyMagic;
@ -1278,6 +1279,16 @@ IsPoisonedValue(const Value &v)
return false;
}
inline bool
IsOptimizedPlaceholderMagicValue(const Value &v)
{
if (v.isMagic()) {
MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
return true;
}
return false;
}
/************************************************************************/
static inline Value

View File

@ -0,0 +1,69 @@
// These predicates are for tests that require a particular set of JIT options.
// Check if toggles match. Useful for tests that shouldn't be run if a
// different set of JIT toggles are set, since TBPL runs each jit-test
// multiple times with a variety of flags.
function jitTogglesMatch(opts) {
var currentOpts = getJitCompilerOptions();
for (var k in opts) {
if (k.indexOf(".enable") > 0 && opts[k] != currentOpts[k])
return false;
}
return true;
}
// Run fn under a particular set of JIT options.
function withJitOptions(opts, fn) {
var oldOpts = getJitCompilerOptions();
for (var k in opts)
setJitCompilerOption(k, opts[k]);
try {
fn();
} finally {
for (var k in oldOpts)
setJitCompilerOption(k, oldOpts[k]);
}
}
// N.B. Ion opts *must come before* baseline opts because there's some kind of
// "undo eager compilation" logic. If we don't set the baseline usecount
// *after* the Ion usecount we end up setting the baseline usecount to be the
// default if we hit the "undo eager compilation" logic.
var Opts_BaselineEager =
{
'ion.enable': 1,
'baseline.enable': 1,
'baseline.usecount.trigger': 0,
'parallel-compilation.enable': 1
};
// Checking for parallel compilation being off is often helpful if the test
// requires a function be Ion compiled. Each individual test will usually
// finish before the Ion compilation thread has a chance to attach the
// compiled code.
var Opts_IonEagerNoParallelCompilation =
{
'ion.enable': 1,
'ion.usecount.trigger': 0,
'baseline.enable': 1,
'baseline.usecount.trigger': 0,
'parallel-compilation.enable': 0,
};
var Opts_Ion2NoParallelCompilation =
{
'ion.enable': 1,
'ion.usecount.trigger': 2,
'baseline.enable': 1,
'baseline.usecount.trigger': 1,
'parallel-compilation.enable': 0
};
var Opts_NoJits =
{
'ion.enable': 0,
'ion.usecount.trigger': 0,
'baseline.usecount.trigger': 0,
'baseline.enable': 0,
'parallel-compilation.enable': 0
};

View File

@ -0,0 +1,24 @@
// Adding a debuggee allowed with scripts on stack.
var g = newGlobal();
g.dbg = new Debugger;
g.eval("" + function f(d) {
g(d);
if (d)
assertEq(dbg.hasDebuggee(this), true);
});
g.eval("" + function g(d) {
if (!d)
return;
dbg.addDebuggee(this);
});
g.eval("(" + function test() {
f(false);
f(false);
f(true);
f(true);
} + ")();");

View File

@ -0,0 +1,107 @@
// Adding a debuggee allowed with scripts on stack from stranger places.
// Test CCW.
(function testCCW() {
var g = newGlobal();
var dbg = new Debugger;
g.dbg = dbg;
g.GLOBAL = g;
g.turnOnDebugger = function () {
dbg.addDebuggee(g);
};
g.eval("" + function f(d) {
turnOnDebugger();
assertEq(dbg.hasDebuggee(GLOBAL), true);
});
g.eval("(" + function test() {
f(false);
f(false);
f(true);
f(true);
} + ")();");
})();
// Test getter.
(function testGetter() {
var g = newGlobal();
g.dbg = new Debugger;
g.GLOBAL = g;
g.eval("" + function f(obj) {
obj.foo;
assertEq(dbg.hasDebuggee(GLOBAL), true);
});
g.eval("(" + function test() {
f({ get foo() { dbg.addDebuggee(GLOBAL); } });
} + ")();");
})();
// Test setter.
(function testSetter() {
var g = newGlobal();
g.dbg = new Debugger;
g.GLOBAL = g;
g.eval("" + function f(obj) {
obj.foo = 42;
assertEq(dbg.hasDebuggee(GLOBAL), true);
});
g.eval("(" + function test() {
f({ set foo(v) { dbg.addDebuggee(GLOBAL); } });
} + ")();");
})();
// Test toString.
(function testToString() {
var g = newGlobal();
g.dbg = new Debugger;
g.GLOBAL = g;
g.eval("" + function f(obj) {
obj + "";
assertEq(dbg.hasDebuggee(GLOBAL), true);
});
g.eval("(" + function test() {
f({ toString: function () { dbg.addDebuggee(GLOBAL); }});
} + ")();");
})();
// Test valueOf.
(function testValueOf() {
var g = newGlobal();
g.dbg = new Debugger;
g.GLOBAL = g;
g.eval("" + function f(obj) {
obj + "";
assertEq(dbg.hasDebuggee(GLOBAL), true);
});
g.eval("(" + function test() {
f({ valueOf: function () { dbg.addDebuggee(GLOBAL); }});
} + ")();");
})();
// Test proxy trap.
(function testProxyTrap() {
var g = newGlobal();
g.dbg = new Debugger;
g.GLOBAL = g;
g.eval("" + function f(proxy) {
proxy["foo"];
assertEq(dbg.hasDebuggee(GLOBAL), true);
});
g.eval("(" + function test() {
var handler = { get: function () { dbg.addDebuggee(GLOBAL); } };
var proxy = new Proxy({}, handler);
f(proxy);
} + ")();");
})();

View File

@ -0,0 +1,55 @@
// Turning debugger on for a particular global with on-stack scripts shouldn't
// make other globals' scripts observable.
var g1 = newGlobal();
var g2 = newGlobal();
var g3 = newGlobal();
g1.eval("" + function f() {
var name = "f";
g();
return name;
});
g2.eval("" + function g() {
var name = "g";
h();
return name;
});
g3.eval("" + function h() {
var name = "h";
toggle();
return name;
});
g1.g = g2.g;
g2.h = g3.h;
function name(f) {
return f.environment.getVariable("name");
}
var dbg = new Debugger;
g3.toggle = function () {
var frame;
// Only f should be visible.
dbg.addDebuggee(g1);
frame = dbg.getNewestFrame();
assertEq(name(frame), "f");
// Now h should also be visible.
dbg.addDebuggee(g3);
frame = dbg.getNewestFrame();
assertEq(name(frame), "h");
assertEq(name(frame.older), "f");
// Finally everything should be visible.
dbg.addDebuggee(g2);
frame = dbg.getNewestFrame();
assertEq(name(frame), "h");
assertEq(name(frame.older), "g");
assertEq(name(frame.older.older), "f");
};
g1.eval("(" + function () { f(); } + ")();");

View File

@ -0,0 +1,48 @@
// Turning debugger off global at a time.
var g1 = newGlobal();
var g2 = newGlobal();
var g3 = newGlobal();
g1.eval("" + function f() {
var name = "f";
g();
return name;
});
g2.eval("" + function g() {
var name = "g";
h();
return name;
});
g3.eval("" + function h() {
var name = "h";
toggle();
return name;
});
g1.g = g2.g;
g2.h = g3.h;
function name(f) {
return f.environment.getVariable("name");
}
var dbg = new Debugger;
g3.toggle = function () {
var frame;
// Add all globals.
dbg.addDebuggee(g1);
dbg.addDebuggee(g3);
dbg.addDebuggee(g2);
// Remove one at a time.
dbg.removeDebuggee(g3);
assertEq(name(dbg.getNewestFrame()), "g");
dbg.removeDebuggee(g2);
assertEq(name(dbg.getNewestFrame()), "f");
dbg.removeDebuggee(g1);
};
g1.eval("(" + function () { f(); } + ")();");

View File

@ -0,0 +1,34 @@
// Ion can bail in-place when throwing exceptions with debug mode toggled on.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
quit();
withJitOptions(Opts_Ion2NoParallelCompilation, function () {
var g = newGlobal();
var dbg = new Debugger;
g.toggle = function toggle(x, d) {
if (d) {
dbg.addDebuggee(g);
var frame = dbg.getNewestFrame().older;
assertEq(frame.callee.name, "f");
assertEq(frame.implementation, "ion");
throw 42;
}
};
g.eval("" + function f(x, d) { g(x, d); });
g.eval("" + function g(x, d) { toggle(x, d); });
try {
g.eval("(" + function test() {
for (var i = 0; i < 5; i++)
f(42, false);
f(42, true);
} + ")();");
} catch (exc) {
assertEq(exc, 42);
}
});

View File

@ -0,0 +1,47 @@
// Tests that we can use debug scopes with Ion frames.
//
// Unfortunately these tests are brittle. They depend on opaque JIT heuristics
// kicking in.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
quit(0);
withJitOptions(Opts_Ion2NoParallelCompilation, function () {
var g = newGlobal();
var dbg = new Debugger;
// Note that this *depends* on CCW scripted functions being opaque to Ion
// optimization and not deoptimizing the frames below the call to toggle.
g.toggle = function toggle(d) {
if (d) {
dbg.addDebuggee(g);
var frame = dbg.getNewestFrame();
assertEq(frame.implementation, "ion");
// g is heavyweight but its call object is optimized out, because its
// arguments and locals are unaliased.
//
// Calling frame.environment here should make a fake debug scope that
// gets things directly from the frame. Calling frame.arguments doesn't
// go through the scope object and reads directly off the frame. Assert
// that the two are equal.
assertEq(frame.environment.getVariable("x"), frame.arguments[1]);
}
};
g.eval("" + function f(d, x) { g(d, x); });
g.eval("" + function g(d, x) {
for (var i = 0; i < 200; i++);
function inner() { i = 42; };
toggle(d);
// Use x so it doesn't get optimized out.
x++;
});
g.eval("(" + function test() {
for (i = 0; i < 5; i++)
f(false, 42);
f(true, 42);
} + ")();");
});

View File

@ -0,0 +1,34 @@
// Eval-in-frame of optimized frames to break out of an infinite loop.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_IonEagerNoParallelCompilation))
quit(0);
withJitOptions(Opts_IonEagerNoParallelCompilation, function () {
var g = newGlobal();
var dbg = new Debugger;
g.eval("" + function f(d) { g(d); });
g.eval("" + function g(d) { h(d); });
g.eval("" + function h(d) {
var i = 0;
while (d)
interruptIf(d && i++ == 4000);
});
setInterruptCallback(function () {
dbg.addDebuggee(g);
var frame = dbg.getNewestFrame();
if (frame.callee.name != "h" || frame.implementation != "ion")
return true;
frame.eval("d = false;");
return true;
});
g.eval("(" + function () {
for (i = 0; i < 5; i++)
f(false);
f(true);
} + ")();");
});

View File

@ -0,0 +1,46 @@
// Eval-in-frame with different type on non-youngest Ion frame.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
quit(0);
withJitOptions(Opts_Ion2NoParallelCompilation, function () {
function test(shadow) {
var g = newGlobal();
var dbg = new Debugger;
// Note that we depend on CCW scripted functions being opaque to Ion
// optimization for this test.
g.h = function h(d) {
if (d) {
dbg.addDebuggee(g);
var f = dbg.getNewestFrame().older;
assertEq(f.implementation, "ion");
assertEq(f.environment.getVariable("foo"), 42);
// EIF of a different type too.
f.eval((shadow ? "var " : "") + "foo = 'string of 42'");
g.expected = shadow ? 42 : "string of 42";
}
}
g.eval("" + function f(d) {
var foo = 42;
g(d);
return foo;
});
g.eval("" + function g(d) {
h(d);
});
g.eval("(" + function () {
for (i = 0; i < 5; i++)
f(false);
assertEq(f(true), "string of 42");
} + ")();");
}
test(false);
test(true);
});

View File

@ -0,0 +1,33 @@
// Eval-in-frame with different type on baseline frame with let-scoping
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_BaselineEager))
quit(0);
withJitOptions(Opts_BaselineEager, function () {
var g = newGlobal();
var dbg = new Debugger;
g.h = function h(d) {
if (d) {
dbg.addDebuggee(g);
var f = dbg.getNewestFrame().older;
assertEq(f.implementation, "baseline");
assertEq(f.environment.getVariable("foo"), 42);
f.eval("foo = 'string of 42'");
}
}
g.eval("" + function f(d) {
if (d) {
let foo = 42;
g(d);
return foo;
}
});
g.eval("" + function g(d) { h(d); });
g.eval("(" + function () { assertEq(f(true), "string of 42"); } + ")();");
});

View File

@ -0,0 +1,32 @@
// Debugger.Frame preserves Ion frame identity
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
quit();
withJitOptions(Opts_Ion2NoParallelCompilation, function () {
var g = newGlobal();
var dbg1 = new Debugger;
var dbg2 = new Debugger;
g.toggle = function toggle(x, d) {
if (d) {
dbg1.addDebuggee(g);
dbg2.addDebuggee(g);
var frame1 = dbg1.getNewestFrame();
assertEq(frame1.environment.getVariable("x"), x);
assertEq(frame1.implementation, "ion");
frame1.environment.setVariable("x", "not 42");
assertEq(dbg2.getNewestFrame().environment.getVariable("x"), "not 42");
}
};
g.eval("" + function f(x, d) { toggle(x, d); });
g.eval("(" + function test() {
for (var i = 0; i < 5; i++)
f(42, false);
f(42, true);
} + ")();");
});

View File

@ -0,0 +1,37 @@
// Debugger.Frame preserves Ion frame mutations after removing debuggee.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
quit();
withJitOptions(Opts_Ion2NoParallelCompilation, function () {
var g = newGlobal();
var dbg = new Debugger;
g.toggle = function toggle(x, d) {
if (d) {
dbg.addDebuggee(g);
var frame = dbg.getNewestFrame().older;
assertEq(frame.callee.name, "f");
assertEq(frame.environment.getVariable("x"), x);
assertEq(frame.implementation, "ion");
frame.environment.setVariable("x", "not 42");
dbg.removeDebuggee(g);
}
};
g.eval("" + function f(x, d) {
g(x, d);
if (d)
assertEq(x, "not 42");
});
g.eval("" + function g(x, d) { toggle(x, d); });
g.eval("(" + function test() {
for (var i = 0; i < 5; i++)
f(42, false);
f(42, true);
} + ")();");
});

View File

@ -0,0 +1,45 @@
// Debugger.Frames of all implementations.
load(libdir + "jitopts.js");
function testFrameImpl(jitopts, assertFrameImpl) {
if (!jitTogglesMatch(jitopts))
return;
withJitOptions(jitopts, function () {
var g = newGlobal();
var dbg = new Debugger;
g.toggle = function toggle(d) {
if (d) {
dbg.addDebuggee(g);
var frame = dbg.getNewestFrame();
// We only care about the f and g frames.
for (var i = 0; i < 2; i++) {
assertFrameImpl(frame);
frame = frame.older;
}
}
};
g.eval("" + function f(d) { g(d); });
g.eval("" + function g(d) { toggle(d); });
g.eval("(" + function test() {
for (var i = 0; i < 5; i++)
f(false);
f(true);
} + ")();");
});
}
[[Opts_BaselineEager,
function (f) { assertEq(f.implementation, "baseline"); }],
// Note that the Ion case *depends* on CCW scripted functions being opaque to
// Ion optimization and not deoptimizing the frames below the call to toggle.
[Opts_Ion2NoParallelCompilation,
function (f) { assertEq(f.implementation, "ion"); }],
[Opts_NoJits,
function (f) { assertEq(f.implementation, "interpreter"); }]].forEach(function ([opts, fn]) {
testFrameImpl(opts, fn);
});

View File

@ -0,0 +1,51 @@
// Test that Ion frames are invalidated by turning on Debugger. Invalidation
// is unobservable, but we know that Ion scripts cannot handle Debugger
// handlers, so we test for the handlers being called.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
quit();
withJitOptions(Opts_Ion2NoParallelCompilation, function () {
var g = newGlobal();
var dbg = new Debugger;
var onPopExecuted = false;
var breakpointHit = false;
g.toggle = function toggle(d) {
if (d) {
dbg.addDebuggee(g);
var frame1 = dbg.getNewestFrame();
assertEq(frame1.implementation, "ion");
frame1.onPop = function () {
onPopExecuted = true;
};
var frame2 = frame1.older;
assertEq(frame2.implementation, "ion");
// Offset of |print(42 + 42)|
var offset = frame2.script.getLineOffsets(3)[0];
frame2.script.setBreakpoint(offset, { hit: function (fr) {
assertEq(fr.implementation != "ion", true);
breakpointHit = true;
}});
}
};
g.eval("" + function f(d) {
g(d);
print(42 + 42);
});
g.eval("" + function g(d) { toggle(d); });
g.eval("(" + function test() {
for (var i = 0; i < 5; i++)
f(false);
f(true);
} + ")();");
assertEq(onPopExecuted, true);
assertEq(breakpointHit, true);
});

View File

@ -0,0 +1,42 @@
// Tests that we can reflect optimized out values.
//
// Unfortunately these tests are brittle. They depend on opaque JIT heuristics
// kicking in.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
quit(0);
withJitOptions(Opts_Ion2NoParallelCompilation, function () {
var g = newGlobal();
var dbg = new Debugger;
// Note that this *depends* on CCW scripted functions being opaque to Ion
// optimization and not deoptimizing the frames below the call to toggle.
g.toggle = function toggle(d) {
if (d) {
dbg.addDebuggee(g);
var frame = dbg.getNewestFrame();
assertEq(frame.implementation, "ion");
// x is unused and should be elided.
assertEq(frame.environment.getVariable("x").optimizedOut, true);
assertEq(frame.arguments[1].optimizedOut, true);
}
};
g.eval("" + function f(d, x) { g(d, x); });
g.eval("" + function g(d, x) {
for (var i = 0; i < 200; i++);
// Hack to prevent inlining.
function inner() { i = 42; };
toggle(d);
});
g.eval("(" + function test() {
for (i = 0; i < 5; i++)
f(false, 42);
f(true, 42);
} + ")();");
});

View File

@ -0,0 +1,93 @@
// Check whether we respect resumption values when toggling debug mode on->off
// from various points with live scripts on the stack.
var g = newGlobal();
var dbg = new Debugger;
function reset() {
dbg.onEnterFrame = undefined;
dbg.onDebuggerStatement = undefined;
dbg.addDebuggee(g);
g.eval("(" + function test() {
for (i = 0; i < 5; i++)
f(42);
} + ")();");
}
g.eval("" + function f(d) {
return g(d);
});
g.eval("" + function g(d) {
debugger;
return d;
});
function testResumptionValues(handlerSetter) {
// Test normal return.
reset();
handlerSetter(undefined);
assertEq(g.eval("(" + function test() { return f(42); } + ")();"), 42);
// Test forced return.
reset();
handlerSetter({ return: "not 42" });
assertEq(g.eval("(" + function test() { return f(42); } + ")();"), "not 42");
// Test throw.
reset();
handlerSetter({ throw: "thrown 42" });
try {
g.eval("(" + function test() { return f(42); } + ")();");;
} catch (e) {
assertEq(e, "thrown 42");
}
}
// Turn off from within the prologue.
testResumptionValues(function (resumptionVal) {
dbg.onEnterFrame = function (frame) {
if (frame.older) {
if (frame.older.older) {
dbg.removeDebuggee(g);
return resumptionVal;
}
}
};
});
// Turn off from within the epilogue.
testResumptionValues(function (resumptionVal) {
dbg.onEnterFrame = function (frame) {
if (frame.older) {
if (frame.older.older) {
frame.onPop = function () {
dbg.removeDebuggee(g);
return resumptionVal;
};
}
}
};
});
// Turn off from within debugger statement handler.
testResumptionValues(function (resumptionVal) {
dbg.onDebuggerStatement = function (frame) {
dbg.removeDebuggee(g);
return resumptionVal;
};
});
// Turn off from within debug trap handler.
testResumptionValues(function (resumptionVal) {
dbg.onEnterFrame = function (frame) {
if (frame.older) {
if (frame.older.older) {
frame.onStep = function () {
dbg.removeDebuggee(g);
return resumptionVal;
}
}
}
};
});

View File

@ -0,0 +1,27 @@
function test1() {
// splice GetElement calls are observable and should be executed even if
// the return value of splice is unused.
Object.defineProperty(Object.prototype, "0", {get: function() {
c++;
}, set: function() {}});
var arr = [,,,];
var c = 0;
for (var i=0; i<100; i++) {
arr.splice(0, 1);
arr.length = 1;
}
assertEq(c, 100);
}
test1();
function test2() {
var arr = [];
for (var i=0; i<100; i++)
arr.push(i);
for (var i=0; i<40; i++)
arr.splice(0, 2);
assertEq(arr.length, 20);
assertEq(arr[0], 80);
}
test2();

View File

@ -167,23 +167,49 @@ IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
uint32_t
jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
ResumeFromException *rfe,
const ExceptionBailoutInfo &excInfo,
BaselineBailoutInfo **bailoutInfo)
bool *overrecursed)
{
JS_ASSERT(cx->isExceptionPending());
// We can be propagating debug mode exceptions without there being an
// actual exception pending. For instance, when we return false from an
// operation callback like a timeout handler.
MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
cx->mainThread().ionTop = nullptr;
JitActivationIterator jitActivations(cx->runtime());
IonBailoutIterator iter(jitActivations, frame.frame());
JitActivation *activation = jitActivations.activation()->asJit();
*bailoutInfo = nullptr;
uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo, &excInfo);
JS_ASSERT(retval == BAILOUT_RETURN_OK ||
retval == BAILOUT_RETURN_FATAL_ERROR ||
retval == BAILOUT_RETURN_OVERRECURSED);
BaselineBailoutInfo *bailoutInfo = nullptr;
uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, &bailoutInfo, &excInfo);
JS_ASSERT((retval == BAILOUT_RETURN_OK) == (*bailoutInfo != nullptr));
if (retval == BAILOUT_RETURN_OK) {
MOZ_ASSERT(bailoutInfo);
// Overwrite the kind so HandleException after the bailout returns
// false, jumping directly to the exception tail.
if (excInfo.propagatingIonExceptionForDebugMode())
bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode;
rfe->kind = ResumeFromException::RESUME_BAILOUT;
rfe->target = cx->runtime()->jitRuntime()->getBailoutTail()->raw();
rfe->bailoutInfo = bailoutInfo;
} else {
// Bailout failed. If there was a fatal error, clear the
// exception to turn this into an uncatchable error. If the
// overrecursion check failed, continue popping all inline
// frames and have the caller report an overrecursion error.
MOZ_ASSERT(!bailoutInfo);
if (!excInfo.propagatingIonExceptionForDebugMode())
cx->clearPendingException();
if (retval == BAILOUT_RETURN_OVERRECURSED)
*overrecursed = true;
else
MOZ_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR);
}
return retval;
}

View File

@ -155,18 +155,52 @@ uint32_t Bailout(BailoutStack *sp, BaselineBailoutInfo **info);
uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
BaselineBailoutInfo **info);
struct ExceptionBailoutInfo
class ExceptionBailoutInfo
{
size_t frameNo;
jsbytecode *resumePC;
size_t numExprSlots;
size_t frameNo_;
jsbytecode *resumePC_;
size_t numExprSlots_;
public:
ExceptionBailoutInfo(size_t frameNo, jsbytecode *resumePC, size_t numExprSlots)
: frameNo_(frameNo),
resumePC_(resumePC),
numExprSlots_(numExprSlots)
{ }
ExceptionBailoutInfo()
: frameNo_(0),
resumePC_(nullptr),
numExprSlots_(0)
{ }
bool catchingException() const {
return !!resumePC_;
}
bool propagatingIonExceptionForDebugMode() const {
return !resumePC_;
}
size_t frameNo() const {
MOZ_ASSERT(catchingException());
return frameNo_;
}
jsbytecode *resumePC() const {
MOZ_ASSERT(catchingException());
return resumePC_;
}
size_t numExprSlots() const {
MOZ_ASSERT(catchingException());
return numExprSlots_;
}
};
// Called from the exception handler to enter a catch or finally block.
// Returns a BAILOUT_* error code.
uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
ResumeFromException *rfe,
const ExceptionBailoutInfo &excInfo,
BaselineBailoutInfo **bailoutInfo);
bool *overrecursed);
uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo);

View File

@ -11,7 +11,10 @@
#include "jit/CompileInfo.h"
#include "jit/IonSpewer.h"
#include "jit/Recover.h"
#include "jit/RematerializedFrame.h"
#include "vm/ArgumentsObject.h"
#include "vm/Debugger.h"
#include "vm/TraceLogging.h"
#include "jsscriptinlines.h"
@ -346,9 +349,9 @@ struct BaselineStackBuilder
JS_ASSERT(BaselineFrameReg == FramePointer);
priorOffset -= sizeof(void *);
return virtualPointerAtStackOffset(priorOffset);
#elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
// On X64 and ARM, the frame pointer save location depends on the caller of the
// the rectifier frame.
#elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
// On X64, ARM and MIPS, the frame pointer save location depends on
// the caller of the rectifier frame.
BufferPointer<IonRectifierFrameLayout> priorFrame =
pointerAtStackOffset<IonRectifierFrameLayout>(priorOffset);
FrameType priorType = priorFrame->prevType();
@ -384,12 +387,12 @@ static inline void*
GetStubReturnAddress(JSContext *cx, jsbytecode *pc)
{
if (IsGetPropPC(pc))
return cx->compartment()->jitCompartment()->baselineGetPropReturnAddr();
return cx->compartment()->jitCompartment()->baselineGetPropReturnFromIonAddr();
if (IsSetPropPC(pc))
return cx->compartment()->jitCompartment()->baselineSetPropReturnAddr();
return cx->compartment()->jitCompartment()->baselineSetPropReturnFromIonAddr();
// This should be a call op of some kind, now.
JS_ASSERT(IsCallPC(pc));
return cx->compartment()->jitCompartment()->baselineCallReturnAddr();
return cx->compartment()->jitCompartment()->baselineCallReturnFromIonAddr();
}
static inline jsbytecode *
@ -484,12 +487,16 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
{
MOZ_ASSERT(script->hasBaselineScript());
// If excInfo is non-nullptr, we are bailing out to a catch or finally block
// and this is the frame where we will resume. Usually the expression stack
// should be empty in this case but there can be iterators on the stack.
// Are we catching an exception?
bool catchingException = excInfo && excInfo->catchingException();
// If we are catching an exception, we are bailing out to a catch or
// finally block and this is the frame where we will resume. Usually the
// expression stack should be empty in this case but there can be
// iterators on the stack.
uint32_t exprStackSlots;
if (excInfo)
exprStackSlots = excInfo->numExprSlots;
if (catchingException)
exprStackSlots = excInfo->numExprSlots();
else
exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun));
@ -588,7 +595,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
if (fun && fun->isHeavyweight())
flags |= BaselineFrame::HAS_CALL_OBJ;
} else {
JS_ASSERT(v.isUndefined());
JS_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
// Get scope chain from function or script.
if (fun) {
@ -617,7 +624,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
// If script maybe has an arguments object, the third slot will hold it.
if (script->argumentsHasVarBinding()) {
v = iter.read();
JS_ASSERT(v.isObject() || v.isUndefined());
JS_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
if (v.isObject())
argsObj = &v.toObject().as<ArgumentsObject>();
}
@ -680,8 +687,8 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
// Get the pc. If we are handling an exception, resume at the pc of the
// catch or finally block.
jsbytecode *pc = excInfo ? excInfo->resumePC : script->offsetToPC(iter.pcOffset());
bool resumeAfter = excInfo ? false : iter.resumeAfter();
jsbytecode *pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
bool resumeAfter = catchingException ? false : iter.resumeAfter();
JSOp op = JSOp(*pc);
@ -774,16 +781,30 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
for (uint32_t i = pushedSlots; i < exprStackSlots; i++) {
Value v;
// If coming from an invalidation bailout, and this is the topmost
// value, and a value override has been specified, don't read from the
// iterator. Otherwise, we risk using a garbage value.
if (!iter.moreFrames() && i == exprStackSlots - 1 &&
cx->runtime()->hasIonReturnOverride())
{
// If coming from an invalidation bailout, and this is the topmost
// value, and a value override has been specified, don't read from the
// iterator. Otherwise, we risk using a garbage value.
JS_ASSERT(invalidate);
iter.skip();
IonSpew(IonSpew_BaselineBailouts, " [Return Override]");
v = cx->runtime()->takeIonReturnOverride();
} else if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
// If we are in the middle of propagating an exception from Ion by
// bailing to baseline due to debug mode, we might not have all
// the stack if we are at the newest frame.
//
// For instance, if calling |f()| pushed an Ion frame which threw,
// the snapshot expects the return value to be pushed, but it's
// possible nothing was pushed before we threw. Iterators might
// still be on the stack, so we can't just drop the stack.
MOZ_ASSERT(cx->compartment()->debugMode());
if (iter.moreFrames())
v = iter.read();
else
v = MagicValue(JS_OPTIMIZED_OUT);
} else {
v = iter.read();
}
@ -856,7 +877,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
// If this was the last inline frame, or we are bailing out to a catch or
// finally block in this frame, then unpacking is almost done.
if (!iter.moreFrames() || excInfo) {
if (!iter.moreFrames() || catchingException) {
// Last frame, so PC for call to next frame is set to nullptr.
*callPC = nullptr;
@ -1320,8 +1341,21 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
iter.script()->filename(), iter.script()->lineno(), (void *) iter.ionScript(),
(int) prevFrameType);
if (excInfo)
IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
bool catchingException;
bool propagatingExceptionForDebugMode;
if (excInfo) {
catchingException = excInfo->catchingException();
propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode();
if (catchingException)
IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
if (propagatingExceptionForDebugMode)
IonSpew(IonSpew_BaselineBailouts, "Resuming in-place for debug mode");
} else {
catchingException = false;
propagatingExceptionForDebugMode = false;
}
IonSpew(IonSpew_BaselineBailouts, " Reading from snapshot offset %u size %u",
iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
@ -1376,13 +1410,17 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
// If we are bailing out to a catch or finally block in this frame,
// pass excInfo to InitFromBailout and don't unpack any other frames.
bool handleException = (excInfo && excInfo->frameNo == frameNo);
bool handleException = (catchingException && excInfo->frameNo() == frameNo);
// We also need to pass excInfo if we're bailing out in place for
// debug mode.
bool passExcInfo = handleException || propagatingExceptionForDebugMode;
jsbytecode *callPC = nullptr;
RootedFunction nextCallee(cx, nullptr);
if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
snapIter, invalidate, builder, startFrameFormals,
&nextCallee, &callPC, handleException ? excInfo : nullptr))
&nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
{
return BAILOUT_RETURN_FATAL_ERROR;
}
@ -1500,6 +1538,39 @@ HandleBaselineInfoBailout(JSContext *cx, JSScript *outerScript, JSScript *innerS
return Invalidate(cx, outerScript);
}
static bool
CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth,
BaselineFrame *frame)
{
RematerializedFrame *rematFrame = act->lookupRematerializedFrame(fp, inlineDepth);
// We might not have rematerialized a frame if the user never requested a
// Debugger.Frame for it.
if (!rematFrame)
return true;
MOZ_ASSERT(rematFrame->script() == frame->script());
MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
frame->setScopeChain(rematFrame->scopeChain());
frame->thisValue() = rematFrame->thisValue();
for (unsigned i = 0; i < frame->numActualArgs(); i++)
frame->argv()[i] = rematFrame->argv()[i];
for (size_t i = 0; i < frame->script()->nfixed(); i++)
*frame->valueSlot(i) = rematFrame->locals()[i];
IonSpew(IonSpew_BaselineBailouts,
" Copied from rematerialized frame at (%p,%u)",
fp, inlineDepth);
if (cx->compartment()->debugMode())
return Debugger::handleIonBailout(cx, rematFrame, frame);
return true;
}
uint32_t
jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
{
@ -1539,6 +1610,7 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
JS_ASSERT(cx->currentlyRunningInJit());
JitFrameIterator iter(cx);
uint8_t *outerFp = nullptr;
uint32_t frameno = 0;
while (frameno < numFrames) {
@ -1572,8 +1644,10 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
if (frameno == 0)
innerScript = frame->script();
if (frameno == numFrames - 1)
if (frameno == numFrames - 1) {
outerScript = frame->script();
outerFp = iter.fp();
}
frameno++;
}
@ -1581,8 +1655,33 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
++iter;
}
JS_ASSERT(innerScript);
JS_ASSERT(outerScript);
MOZ_ASSERT(innerScript);
MOZ_ASSERT(outerScript);
MOZ_ASSERT(outerFp);
// If we rematerialized Ion frames due to debug mode toggling, copy their
// values into the baseline frame. We need to do this even when debug mode
// is off, as we should respect the mutations made while debug mode was
// on.
JitActivation *act = cx->mainThread().activation()->asJit();
if (act->hasRematerializedFrame(outerFp)) {
JitFrameIterator iter(cx);
size_t inlineDepth = numFrames;
while (inlineDepth > 0) {
if (iter.isBaselineJS() &&
!CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
iter.baselineFrame()))
{
return false;
}
++iter;
}
// After copying from all the rematerialized frames, remove them from
// the table to keep the table up to date.
act->removeRematerializedFrame(outerFp);
}
IonSpew(IonSpew_BaselineBailouts,
" Restored outerScript=(%s:%u,%u) innerScript=(%s:%u,%u) (bailoutKind=%u)",
outerScript->filename(), outerScript->lineno(), outerScript->getUseCount(),
@ -1608,6 +1707,10 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
return false;
break;
case Bailout_IonExceptionDebugMode:
// Return false to resume in HandleException with reconstructed
// baseline frame.
return false;
default:
MOZ_ASSUME_UNREACHABLE("Unknown bailout kind!");
}

View File

@ -25,7 +25,7 @@
using namespace js;
using namespace js::jit;
BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, HandleScript script)
BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script)
: BaselineCompilerSpecific(cx, alloc, script),
modifiesArguments_(false)
{
@ -70,7 +70,7 @@ MethodStatus
BaselineCompiler::compile()
{
IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
script->filename(), script->lineno(), script.get());
script->filename(), script->lineno(), script);
IonSpew(IonSpew_Codegen, "# Emitting baseline code for script %s:%d",
script->filename(), script->lineno());
@ -121,7 +121,8 @@ BaselineCompiler::compile()
if (script->functionNonDelazifying()) {
RootedFunction fun(cx, script->functionNonDelazifying());
if (fun->isHeavyweight()) {
templateScope = CallObject::createTemplateObject(cx, script, gc::TenuredHeap);
RootedScript scriptRoot(cx, script);
templateScope = CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap);
if (!templateScope)
return Method_Error;
@ -174,13 +175,17 @@ BaselineCompiler::compile()
return Method_Error;
prologueOffset_.fixup(&masm);
epilogueOffset_.fixup(&masm);
spsPushToggleOffset_.fixup(&masm);
postDebugPrologueOffset_.fixup(&masm);
// Note: There is an extra entry in the bytecode type map for the search hint, see below.
size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset_.offset(),
epilogueOffset_.offset(),
spsPushToggleOffset_.offset(),
postDebugPrologueOffset_.offset(),
icEntries_.length(),
pcMappingIndexEntries.length(),
pcEntries.length(),
@ -389,6 +394,10 @@ BaselineCompiler::emitPrologue()
bool
BaselineCompiler::emitEpilogue()
{
// Record the offset of the epilogue, so we can do early return from
// Debugger handlers during on-stack recompile.
epilogueOffset_ = masm.currentOffset();
masm.bind(&return_);
#ifdef JS_TRACE_LOGGING
@ -433,6 +442,8 @@ BaselineCompiler::emitOutOfLinePostBarrierSlot()
// On ARM, save the link register before calling. It contains the return
// address. The |masm.ret()| later will pop this into |pc| to return.
masm.push(lr);
#elif defined(JS_CODEGEN_MIPS)
masm.push(ra);
#endif
masm.setupUnalignedABICall(2, scratch);
@ -447,9 +458,9 @@ BaselineCompiler::emitOutOfLinePostBarrierSlot()
#endif // JSGC_GENERATIONAL
bool
BaselineCompiler::emitIC(ICStub *stub, bool isForOp)
BaselineCompiler::emitIC(ICStub *stub, ICEntry::Kind kind)
{
ICEntry *entry = allocateICEntry(stub, isForOp);
ICEntry *entry = allocateICEntry(stub, kind);
if (!entry)
return false;
@ -527,27 +538,32 @@ static const VMFunction DebugPrologueInfo = FunctionInfo<DebugPrologueFn>(jit::D
bool
BaselineCompiler::emitDebugPrologue()
{
if (!debugMode_)
return true;
if (debugMode_) {
// Load pointer to BaselineFrame in R0.
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
// Load pointer to BaselineFrame in R0.
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
prepareVMCall();
pushArg(ImmPtr(pc));
pushArg(R0.scratchReg());
if (!callVM(DebugPrologueInfo))
return false;
prepareVMCall();
pushArg(ImmPtr(pc));
pushArg(R0.scratchReg());
if (!callVM(DebugPrologueInfo))
return false;
// Fix up the fake ICEntry appended by callVM for on-stack recompilation.
icEntries_.back().setForDebugPrologue();
// If the stub returns |true|, we have to return the value stored in the
// frame's return value slot.
Label done;
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
{
masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
masm.jump(&return_);
// If the stub returns |true|, we have to return the value stored in the
// frame's return value slot.
Label done;
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
{
masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
masm.jump(&return_);
}
masm.bind(&done);
}
masm.bind(&done);
postDebugPrologueOffset_ = masm.currentOffset();
return true;
}
@ -721,7 +737,7 @@ BaselineCompiler::emitDebugTrap()
#endif
// Add an IC entry for the return offset -> pc mapping.
ICEntry icEntry(script->pcToOffset(pc), false);
ICEntry icEntry(script->pcToOffset(pc), ICEntry::Kind_DebugTrap);
icEntry.setReturnOffset(masm.currentOffset());
if (!icEntries_.append(icEntry))
return false;
@ -2102,7 +2118,6 @@ BaselineCompiler::emit_JSOP_SETALIASEDVAR()
frame.syncStack(0);
Register temp = R1.scratchReg();
Nursery &nursery = cx->runtime()->gcNursery;
Label skipBarrier;
masm.branchTestObject(Assembler::NotEqual, R0, &skipBarrier);
masm.branchPtrInNurseryRange(objReg, temp, &skipBarrier);
@ -2422,7 +2437,6 @@ BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get)
Register reg = R2.scratchReg();
masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg);
Nursery &nursery = cx->runtime()->gcNursery;
Label skipBarrier;
masm.branchPtrInNurseryRange(reg, temp, &skipBarrier);
@ -2743,6 +2757,9 @@ static const VMFunction OnDebuggerStatementInfo =
bool
BaselineCompiler::emit_JSOP_DEBUGGER()
{
if (!debugMode_)
return true;
prepareVMCall();
pushArg(ImmPtr(pc));
@ -2785,6 +2802,9 @@ BaselineCompiler::emitReturn()
if (!callVM(DebugEpilogueInfo))
return false;
// Fix up the fake ICEntry appended by callVM for on-stack recompilation.
icEntries_.back().setForDebugEpilogue();
masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
}

View File

@ -14,8 +14,12 @@
# include "jit/x86/BaselineCompiler-x86.h"
#elif defined(JS_CODEGEN_X64)
# include "jit/x64/BaselineCompiler-x64.h"
#else
#elif defined(JS_CODEGEN_ARM)
# include "jit/arm/BaselineCompiler-arm.h"
#elif defined(JS_CODEGEN_MIPS)
# include "jit/mips/BaselineCompiler-mips.h"
#else
# error "Unknown architecture!"
#endif
namespace js {
@ -173,6 +177,14 @@ class BaselineCompiler : public BaselineCompilerSpecific
// Native code offset right before the scope chain is initialized.
CodeOffsetLabel prologueOffset_;
// Native code offset right before the frame is popped and the method
// returned from.
CodeOffsetLabel epilogueOffset_;
// Native code offset right after debug prologue and epilogue, or
// equivalent positions when debug mode is off.
CodeOffsetLabel postDebugPrologueOffset_;
// Whether any on stack arguments are modified.
bool modifiesArguments_;
@ -188,7 +200,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
}
public:
BaselineCompiler(JSContext *cx, TempAllocator &alloc, HandleScript script);
BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script);
bool init();
MethodStatus compile();
@ -201,12 +213,12 @@ class BaselineCompiler : public BaselineCompilerSpecific
#ifdef JSGC_GENERATIONAL
bool emitOutOfLinePostBarrierSlot();
#endif
bool emitIC(ICStub *stub, bool isForOp);
bool emitIC(ICStub *stub, ICEntry::Kind kind);
bool emitOpIC(ICStub *stub) {
return emitIC(stub, true);
return emitIC(stub, ICEntry::Kind_Op);
}
bool emitNonOpIC(ICStub *stub) {
return emitIC(stub, false);
return emitIC(stub, ICEntry::Kind_NonOp);
}
bool emitStackCheck(bool earlyCheck=false);

View File

@ -0,0 +1,709 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/BaselineDebugModeOSR.h"
#include "mozilla/DebugOnly.h"
#include "jit/IonLinker.h"
#include "jit/IonFrames-inl.h"
#include "vm/Stack-inl.h"
using namespace mozilla;
using namespace js;
using namespace js::jit;
struct DebugModeOSREntry
{
JSScript *script;
BaselineScript *oldBaselineScript;
BaselineDebugModeOSRInfo *recompInfo;
uint32_t pcOffset;
ICEntry::Kind frameKind;
// Used for sanity asserts in debug builds.
DebugOnly<ICStub *> stub;
DebugModeOSREntry(JSScript *script)
: script(script),
oldBaselineScript(script->baselineScript()),
recompInfo(nullptr),
pcOffset(uint32_t(-1)),
frameKind(ICEntry::Kind_NonOp),
stub(nullptr)
{ }
DebugModeOSREntry(JSScript *script, const ICEntry &icEntry)
: script(script),
oldBaselineScript(script->baselineScript()),
recompInfo(nullptr),
pcOffset(icEntry.pcOffset()),
frameKind(icEntry.kind()),
stub(nullptr)
{
#ifdef DEBUG
MOZ_ASSERT(pcOffset == icEntry.pcOffset());
MOZ_ASSERT(frameKind == icEntry.kind());
// Assert that if we have a NonOp ICEntry, that there are no unsynced
// slots, since such a recompile could have only been triggered from
// either an interrupt check or a debug trap handler.
//
// If triggered from an interrupt check, the stack should be fully
// synced.
//
// If triggered from a debug trap handler, we must be recompiling for
// toggling debug mode on->off, in which case the old baseline script
// should have fully synced stack at every bytecode.
if (frameKind == ICEntry::Kind_NonOp) {
PCMappingSlotInfo slotInfo;
jsbytecode *pc = script->offsetToPC(pcOffset);
oldBaselineScript->nativeCodeForPC(script, pc, &slotInfo);
MOZ_ASSERT(slotInfo.numUnsynced() == 0);
}
#endif
}
DebugModeOSREntry(DebugModeOSREntry &&other)
: script(other.script),
oldBaselineScript(other.oldBaselineScript),
recompInfo(other.recompInfo ? other.takeRecompInfo() : nullptr),
pcOffset(other.pcOffset),
frameKind(other.frameKind),
stub(other.stub)
{ }
~DebugModeOSREntry() {
// Note that this is nulled out when the recompInfo is taken by the
// frame. The frame then has the responsibility of freeing the
// recompInfo.
js_delete(recompInfo);
}
bool needsRecompileInfo() const {
return (frameKind == ICEntry::Kind_CallVM ||
frameKind == ICEntry::Kind_DebugTrap ||
frameKind == ICEntry::Kind_DebugPrologue ||
frameKind == ICEntry::Kind_DebugEpilogue);
}
BaselineDebugModeOSRInfo *takeRecompInfo() {
MOZ_ASSERT(recompInfo);
BaselineDebugModeOSRInfo *tmp = recompInfo;
recompInfo = nullptr;
return tmp;
}
bool allocateRecompileInfo(JSContext *cx) {
MOZ_ASSERT(needsRecompileInfo());
// If we are returning to a frame which needs a continuation fixer,
// allocate the recompile info up front so that the patching function
// is infallible.
jsbytecode *pc = script->offsetToPC(pcOffset);
// XXX: Work around compiler error disallowing using bitfields
// with the template magic of new_.
ICEntry::Kind kind = frameKind;
recompInfo = cx->new_<BaselineDebugModeOSRInfo>(pc, kind);
return !!recompInfo;
}
};
typedef js::Vector<DebugModeOSREntry> DebugModeOSREntryVector;
static bool
CollectOnStackScripts(JSContext *cx, const JitActivationIterator &activation,
DebugModeOSREntryVector &entries)
{
DebugOnly<ICStub *> prevFrameStubPtr = nullptr;
bool needsRecompileHandler = false;
for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
switch (iter.type()) {
case JitFrame_BaselineJS: {
JSScript *script = iter.script();
uint8_t *retAddr = iter.returnAddressToFp();
ICEntry &entry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
if (!entries.append(DebugModeOSREntry(script, entry)))
return false;
if (entries.back().needsRecompileInfo()) {
if (!entries.back().allocateRecompileInfo(cx))
return false;
needsRecompileHandler |= true;
}
entries.back().stub = prevFrameStubPtr;
prevFrameStubPtr = nullptr;
break;
}
case JitFrame_BaselineStub:
prevFrameStubPtr =
reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp())->maybeStubPtr();
break;
case JitFrame_IonJS: {
JSScript *script = iter.script();
if (!entries.append(DebugModeOSREntry(script)))
return false;
for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) {
if (!entries.append(DebugModeOSREntry(inlineIter.script())))
return false;
}
break;
}
default:;
}
}
// Initialize the on-stack recompile handler, which may fail, so that
// patching the stack is infallible.
if (needsRecompileHandler) {
JitRuntime *rt = cx->runtime()->jitRuntime();
if (!rt->getBaselineDebugModeOSRHandlerAddress(cx, true))
return false;
}
return true;
}
static inline uint8_t *
GetStubReturnFromStubAddress(JSContext *cx, jsbytecode *pc)
{
JitCompartment *comp = cx->compartment()->jitCompartment();
void *addr;
if (IsGetPropPC(pc)) {
addr = comp->baselineGetPropReturnFromStubAddr();
} else if (IsSetPropPC(pc)) {
addr = comp->baselineSetPropReturnFromStubAddr();
} else {
JS_ASSERT(IsCallPC(pc));
addr = comp->baselineCallReturnFromStubAddr();
}
return reinterpret_cast<uint8_t *>(addr);
}
static const char *
ICEntryKindToString(ICEntry::Kind kind)
{
switch (kind) {
case ICEntry::Kind_Op:
return "IC";
case ICEntry::Kind_NonOp:
return "non-op IC";
case ICEntry::Kind_CallVM:
return "callVM";
case ICEntry::Kind_DebugTrap:
return "debug trap";
case ICEntry::Kind_DebugPrologue:
return "debug prologue";
case ICEntry::Kind_DebugEpilogue:
return "debug epilogue";
default:
MOZ_ASSUME_UNREACHABLE("bad ICEntry kind");
}
}
static void
SpewPatchBaselineFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
JSScript *script, ICEntry::Kind frameKind, jsbytecode *pc)
{
IonSpew(IonSpew_BaselineDebugModeOSR,
"Patch return %#016llx -> %#016llx to BaselineJS (%s:%d) from %s at %s",
uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress),
script->filename(), script->lineno(),
ICEntryKindToString(frameKind), js_CodeName[(JSOp)*pc]);
}
static void
SpewPatchStubFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
ICStub *oldStub, ICStub *newStub)
{
IonSpew(IonSpew_BaselineDebugModeOSR,
"Patch return %#016llx -> %#016llx",
uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress));
IonSpew(IonSpew_BaselineDebugModeOSR,
"Patch stub %#016llx -> %#016llx to BaselineStub",
uintptr_t(oldStub), uintptr_t(newStub));
}
static void
PatchBaselineFramesForDebugMode(JSContext *cx, const JitActivationIterator &activation,
DebugModeOSREntryVector &entries, size_t *start)
{
//
// Recompile Patching Overview
//
// When toggling debug mode with live baseline scripts on the stack, we
// could have entered the VM via the following ways from the baseline
// script.
//
// Off to On:
// A. From a "can call" stub.
// B. From a VM call (interrupt handler, debugger statement handler).
//
// On to Off:
// - All the ways above.
// C. From the debug trap handler.
// D. From the debug prologue.
// E. From the debug epilogue.
//
// In general, we patch the return address from the VM call to return to a
// "continuation fixer" to fix up machine state (registers and stack
// state). Specifics on what need to be done are documented below.
//
IonCommonFrameLayout *prev = nullptr;
size_t entryIndex = *start;
DebugOnly<bool> expectedDebugMode = cx->compartment()->debugMode();
for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
switch (iter.type()) {
case JitFrame_BaselineJS: {
JSScript *script = entries[entryIndex].script;
uint32_t pcOffset = entries[entryIndex].pcOffset;
jsbytecode *pc = script->offsetToPC(pcOffset);
MOZ_ASSERT(script == iter.script());
MOZ_ASSERT(pcOffset < script->length());
MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
BaselineScript *bl = script->baselineScript();
ICEntry::Kind kind = entries[entryIndex].frameKind;
if (kind == ICEntry::Kind_Op) {
// Case A above.
//
// Patching this case needs to patch both the stub frame and
// the baseline frame. The stub frame is patched below. For
// the baseline frame here, we resume right after the IC
// returns.
//
// Since we're using the IC-specific k-fixer, we can resume
// directly to the IC resume address.
uint8_t *retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset));
SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc);
prev->setReturnAddress(retAddr);
entryIndex++;
break;
}
bool popFrameReg;
// The RecompileInfo must already be allocated so that this
// function may be infallible.
BaselineDebugModeOSRInfo *recompInfo = entries[entryIndex].takeRecompInfo();
switch (kind) {
case ICEntry::Kind_CallVM:
// Case B above.
//
// Patching returns from an interrupt handler or the debugger
// statement handler is similar in that we can resume at the
// next op.
pc += GetBytecodeLength(pc);
recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
popFrameReg = true;
break;
case ICEntry::Kind_DebugTrap:
// Case C above.
//
// Debug traps are emitted before each op, so we resume at the
// same op. Calling debug trap handlers is done via a toggled
// call to a thunk (DebugTrapHandler) that takes care tearing
// down its own stub frame so we don't need to worry about
// popping the frame reg.
recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
popFrameReg = false;
break;
case ICEntry::Kind_DebugPrologue:
// Case D above.
//
// We patch a jump directly to the right place in the prologue
// after popping the frame reg and checking for forced return.
recompInfo->resumeAddr = bl->postDebugPrologueAddr();
popFrameReg = true;
break;
default:
// Case E above.
//
// We patch a jump directly to the epilogue after popping the
// frame reg and checking for forced return.
MOZ_ASSERT(kind == ICEntry::Kind_DebugEpilogue);
recompInfo->resumeAddr = bl->epilogueEntryAddr();
popFrameReg = true;
break;
}
SpewPatchBaselineFrame(prev->returnAddress(), recompInfo->resumeAddr,
script, kind, recompInfo->pc);
// The recompile handler must already be created so that this
// function may be infallible.
JitRuntime *rt = cx->runtime()->jitRuntime();
void *handlerAddr = rt->getBaselineDebugModeOSRHandlerAddress(cx, popFrameReg);
MOZ_ASSERT(handlerAddr);
prev->setReturnAddress(reinterpret_cast<uint8_t *>(handlerAddr));
iter.baselineFrame()->setDebugModeOSRInfo(recompInfo);
entryIndex++;
break;
}
case JitFrame_BaselineStub: {
JSScript *script = entries[entryIndex].script;
IonBaselineStubFrameLayout *layout =
reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp());
MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
// Patch baseline stub frames for case A above.
//
// We need to patch the stub frame return address to go to the
// k-fixer that is at the end of fallback stubs of all such
// can-call ICs. These k-fixers share code with bailout-from-Ion
// fixers, but in this case we are returning from VM and not
// Ion. See e.g., JitCompartment::baselineCallReturnFromStubAddr()
//
// Subtlety here: the debug trap handler of case C above pushes a
// stub frame with a null stub pointer. This handler will exist
// across recompiling the script, so we don't patch anything for
// such stub frames. We will return to that handler, which takes
// care of cleaning up the stub frame.
//
// Note that for stub pointers that are already on the C stack
// (i.e. fallback calls), we need to check for recompilation using
// DebugModeOSRVolatileStub.
if (layout->maybeStubPtr()) {
MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
uint32_t pcOffset = entries[entryIndex].pcOffset;
uint8_t *retAddr = GetStubReturnFromStubAddress(cx, script->offsetToPC(pcOffset));
// Get the fallback stub for the IC in the recompiled
// script. The fallback stub is guaranteed to exist.
ICEntry &entry = script->baselineScript()->icEntryFromPCOffset(pcOffset);
ICStub *newStub = entry.fallbackStub();
SpewPatchStubFrame(prev->returnAddress(), retAddr, layout->maybeStubPtr(), newStub);
prev->setReturnAddress(retAddr);
layout->setStubPtr(newStub);
}
break;
}
case JitFrame_IonJS:
// Nothing to patch.
entryIndex++;
for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
entryIndex++;
break;
default:;
}
prev = iter.current();
}
*start = entryIndex;
}
static bool
RecompileBaselineScriptForDebugMode(JSContext *cx, JSScript *script)
{
BaselineScript *oldBaselineScript = script->baselineScript();
// If a script is on the stack multiple times, it may have already
// been recompiled.
bool expectedDebugMode = cx->compartment()->debugMode();
if (oldBaselineScript->debugMode() == expectedDebugMode)
return true;
IonSpew(IonSpew_BaselineDebugModeOSR, "Recompiling (%s:%d) for debug mode %s",
script->filename(), script->lineno(), expectedDebugMode ? "ON" : "OFF");
if (script->hasIonScript())
Invalidate(cx, script, /* resetUses = */ false);
script->setBaselineScript(cx, nullptr);
MethodStatus status = BaselineCompile(cx, script);
if (status != Method_Compiled) {
// We will only fail to recompile for debug mode due to OOM. Restore
// the old baseline script in case something doesn't properly
// propagate OOM.
MOZ_ASSERT(status == Method_Error);
script->setBaselineScript(cx, oldBaselineScript);
return false;
}
// Don't destroy the old baseline script yet, since if we fail any of the
// recompiles we need to rollback all the old baseline scripts.
MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
return true;
}
static void
UndoRecompileBaselineScriptsForDebugMode(JSContext *cx,
const DebugModeOSREntryVector &entries)
{
// In case of failure, roll back the entire set of active scripts so that
// we don't have to patch return addresses on the stack.
for (size_t i = 0; i < entries.length(); i++) {
JSScript *script = entries[i].script;
BaselineScript *baselineScript = script->baselineScript();
if (baselineScript != entries[i].oldBaselineScript) {
script->setBaselineScript(cx, entries[i].oldBaselineScript);
BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), baselineScript);
}
}
}
bool
jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp)
{
AutoCompartment ac(cx, comp);
// First recompile the active scripts on the stack and patch the live
// frames.
Vector<DebugModeOSREntry> entries(cx);
for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
if (iter.activation()->compartment() == comp) {
if (!CollectOnStackScripts(cx, iter, entries))
return false;
}
}
#ifdef JSGC_GENERATIONAL
// Scripts can entrain nursery things. See note in js::ReleaseAllJITCode.
if (!entries.empty())
MinorGC(cx->runtime(), JS::gcreason::EVICT_NURSERY);
#endif
// Try to recompile all the scripts. If we encounter an error, we need to
// roll back as if none of the compilations happened, so that we don't
// crash.
for (size_t i = 0; i < entries.length(); i++) {
JSScript *script = entries[i].script;
if (!RecompileBaselineScriptForDebugMode(cx, script)) {
UndoRecompileBaselineScriptsForDebugMode(cx, entries);
return false;
}
}
// If all recompiles succeeded, destroy the old baseline scripts and patch
// the live frames.
//
// After this point the function must be infallible.
for (size_t i = 0; i < entries.length(); i++)
BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entries[i].oldBaselineScript);
size_t processed = 0;
for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
if (iter.activation()->compartment() == comp)
PatchBaselineFramesForDebugMode(cx, iter, entries, &processed);
}
MOZ_ASSERT(processed == entries.length());
return true;
}
void
BaselineDebugModeOSRInfo::popValueInto(PCMappingSlotInfo::SlotLocation loc, Value *vp)
{
switch (loc) {
case PCMappingSlotInfo::SlotInR0:
valueR0 = vp[stackAdjust];
break;
case PCMappingSlotInfo::SlotInR1:
valueR1 = vp[stackAdjust];
break;
case PCMappingSlotInfo::SlotIgnore:
break;
default:
MOZ_ASSUME_UNREACHABLE("Bad slot location");
}
stackAdjust++;
}
static inline bool
HasForcedReturn(BaselineDebugModeOSRInfo *info, bool rv)
{
ICEntry::Kind kind = info->frameKind;
// The debug epilogue always checks its resumption value, so we don't need
// to check rv.
if (kind == ICEntry::Kind_DebugEpilogue)
return true;
// |rv| is the value in ReturnReg. If true, in the case of the prologue,
// debug trap, and debugger statement handler, it means a forced return.
if (kind == ICEntry::Kind_DebugPrologue ||
(kind == ICEntry::Kind_CallVM && JSOp(*info->pc) == JSOP_DEBUGGER))
{
return rv;
}
// N.B. The debug trap handler handles its own forced return, so no
// need to deal with it here.
return false;
}
static void
SyncBaselineDebugModeOSRInfo(BaselineFrame *frame, Value *vp, bool rv)
{
BaselineDebugModeOSRInfo *info = frame->debugModeOSRInfo();
MOZ_ASSERT(info);
MOZ_ASSERT(frame->script()->baselineScript()->containsCodeAddress(info->resumeAddr));
if (HasForcedReturn(info, rv)) {
// Load the frame's rval and overwrite the resume address to go to the
// epilogue.
MOZ_ASSERT(R0 == JSReturnOperand);
info->valueR0 = frame->returnValue();
info->resumeAddr = frame->script()->baselineScript()->epilogueEntryAddr();
return;
}
// Read stack values and make sure R0 and R1 have the right values.
unsigned numUnsynced = info->slotInfo.numUnsynced();
MOZ_ASSERT(numUnsynced <= 2);
if (numUnsynced > 0)
info->popValueInto(info->slotInfo.topSlotLocation(), vp);
if (numUnsynced > 1)
info->popValueInto(info->slotInfo.nextSlotLocation(), vp);
// Scale stackAdjust.
info->stackAdjust *= sizeof(Value);
}
static void
FinishBaselineDebugModeOSR(BaselineFrame *frame)
{
frame->deleteDebugModeOSRInfo();
}
void
BaselineFrame::deleteDebugModeOSRInfo()
{
js_delete(getDebugModeOSRInfo());
flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
}
JitCode *
JitRuntime::getBaselineDebugModeOSRHandler(JSContext *cx)
{
if (!baselineDebugModeOSRHandler_) {
AutoLockForExclusiveAccess lock(cx);
AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
uint32_t offset;
if (JitCode *code = generateBaselineDebugModeOSRHandler(cx, &offset)) {
baselineDebugModeOSRHandler_ = code;
baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset;
}
}
return baselineDebugModeOSRHandler_;
}
void *
JitRuntime::getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg)
{
if (!getBaselineDebugModeOSRHandler(cx))
return nullptr;
return (popFrameReg
? baselineDebugModeOSRHandler_->raw()
: baselineDebugModeOSRHandlerNoFrameRegPopAddr_);
}
JitCode *
JitRuntime::generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut)
{
MacroAssembler masm(cx);
GeneralRegisterSet regs(GeneralRegisterSet::All());
regs.take(BaselineFrameReg);
regs.take(ReturnReg);
Register temp = regs.takeAny();
Register syncedStackStart = regs.takeAny();
// Pop the frame reg.
masm.pop(BaselineFrameReg);
// Not all patched baseline frames are returning from a situation where
// the frame reg is already fixed up.
CodeOffsetLabel noFrameRegPopOffset = masm.currentOffset();
// Record the stack pointer for syncing.
masm.movePtr(StackPointer, syncedStackStart);
masm.push(BaselineFrameReg);
// Call a stub to fully initialize the info.
masm.setupUnalignedABICall(3, temp);
masm.loadBaselineFramePtr(BaselineFrameReg, temp);
masm.passABIArg(temp);
masm.passABIArg(syncedStackStart);
masm.passABIArg(ReturnReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, SyncBaselineDebugModeOSRInfo));
// Discard stack values depending on how many were unsynced, as we always
// have a fully synced stack in the recompile handler. See assert in
// DebugModeOSREntry constructor.
masm.pop(BaselineFrameReg);
masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), temp);
masm.addPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust)), StackPointer);
// Save real return address on the stack temporarily.
masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
masm.push(BaselineFrameReg);
masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr)));
// Call a stub to free the allocated info.
masm.setupUnalignedABICall(1, temp);
masm.loadBaselineFramePtr(BaselineFrameReg, temp);
masm.passABIArg(temp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBaselineDebugModeOSR));
// Restore saved values.
GeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
jumpRegs.take(R0);
jumpRegs.take(R1);
jumpRegs.take(BaselineFrameReg);
Register target = jumpRegs.takeAny();
masm.pop(target);
masm.pop(BaselineFrameReg);
masm.popValue(R1);
masm.popValue(R0);
masm.jump(target);
Linker linker(masm);
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
if (!code)
return nullptr;
noFrameRegPopOffset.fixup(&masm);
*noFrameRegPopOffsetOut = noFrameRegPopOffset.offset();
#ifdef JS_ION_PERF
writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler");
#endif
return code;
}

View File

@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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_BaselineDebugModeOSR_h
#define jit_BaselineDebugModeOSR_h
#ifdef JS_ION
#include "jit/BaselineFrame.h"
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
namespace js {
namespace jit {
// Note that this file and the corresponding .cpp implement debug mode
// on-stack recompilation. This is to be distinguished from ordinary
// Baseline->Ion OSR, which is used to jump into compiled loops.
//
// A volatile location due to recompilation of an on-stack baseline script
// (e.g., for debug mode toggling).
//
// It is usually used in fallback stubs which may trigger on-stack
// recompilation by calling out into the VM. Example use:
//
// DebugModeOSRVolatileStub<FallbackStubT *> stub(frame, stub_)
//
// // Call out to the VM
// // Other effectful operations like TypeScript::Monitor
//
// if (stub.invalid())
// return true;
//
// // First use of stub after VM call.
//
template <typename T>
class DebugModeOSRVolatileStub
{
T stub_;
BaselineFrame *frame_;
uint32_t pcOffset_;
public:
DebugModeOSRVolatileStub(BaselineFrame *frame, ICFallbackStub *stub)
: stub_(static_cast<T>(stub)),
frame_(frame),
pcOffset_(stub->icEntry()->pcOffset())
{ }
bool invalid() const {
ICEntry &entry = frame_->script()->baselineScript()->icEntryFromPCOffset(pcOffset_);
return stub_ != entry.fallbackStub();
}
operator const T&() const { MOZ_ASSERT(!invalid()); return stub_; }
T operator->() const { MOZ_ASSERT(!invalid()); return stub_; }
T *address() { MOZ_ASSERT(!invalid()); return &stub_; }
const T *address() const { MOZ_ASSERT(!invalid()); return &stub_; }
T &get() { MOZ_ASSERT(!invalid()); return stub_; }
const T &get() const { MOZ_ASSERT(!invalid()); return stub_; }
bool operator!=(const T &other) const { MOZ_ASSERT(!invalid()); return stub_ != other; }
bool operator==(const T &other) const { MOZ_ASSERT(!invalid()); return stub_ == other; }
};
//
// Auxiliary info to help the DebugModeOSRHandler fix up state.
//
struct BaselineDebugModeOSRInfo
{
uint8_t *resumeAddr;
jsbytecode *pc;
PCMappingSlotInfo slotInfo;
ICEntry::Kind frameKind;
// Filled in by SyncBaselineDebugModeOSRInfo.
uintptr_t stackAdjust;
Value valueR0;
Value valueR1;
BaselineDebugModeOSRInfo(jsbytecode *pc, ICEntry::Kind kind)
: resumeAddr(nullptr),
pc(pc),
slotInfo(0),
frameKind(kind),
stackAdjust(0),
valueR0(UndefinedValue()),
valueR1(UndefinedValue())
{ }
void popValueInto(PCMappingSlotInfo::SlotLocation loc, Value *vp);
};
bool
RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp);
} // namespace jit
} // namespace js
#endif // JS_ION
#endif // jit_BaselineDebugModeOSR_h

View File

@ -15,6 +15,8 @@
namespace js {
namespace jit {
struct BaselineDebugModeOSRInfo;
// The stack looks like this, fp is the frame pointer:
//
// fp+y arguments
@ -57,7 +59,11 @@ class BaselineFrame
HAS_PUSHED_SPS_FRAME = 1 << 8,
// Frame has over-recursed on an early check.
OVER_RECURSED = 1 << 9
OVER_RECURSED = 1 << 9,
// Frame has a BaselineRecompileInfo stashed in the scratch value
// slot. See PatchBaselineFramesForDebugMOde.
HAS_DEBUG_MODE_OSR_INFO = 1 << 10
};
protected: // Silence Clang warning about unused private fields.
@ -296,6 +302,24 @@ class BaselineFrame
flags_ |= OVER_RECURSED;
}
BaselineDebugModeOSRInfo *debugModeOSRInfo() {
MOZ_ASSERT(flags_ & HAS_DEBUG_MODE_OSR_INFO);
return *reinterpret_cast<BaselineDebugModeOSRInfo **>(&loScratchValue_);
}
BaselineDebugModeOSRInfo *getDebugModeOSRInfo() {
if (flags_ & HAS_DEBUG_MODE_OSR_INFO)
return debugModeOSRInfo();
return nullptr;
}
void setDebugModeOSRInfo(BaselineDebugModeOSRInfo *info) {
flags_ |= HAS_DEBUG_MODE_OSR_INFO;
*reinterpret_cast<BaselineDebugModeOSRInfo **>(&loScratchValue_) = info;
}
void deleteDebugModeOSRInfo();
void trace(JSTracer *trc, JitFrameIterator &frame);
bool isFunctionFrame() const {

View File

@ -162,15 +162,15 @@ enum StackAdjustment { AdjustStack, DontAdjustStack };
class FrameInfo
{
RootedScript script;
JSScript *script;
MacroAssembler &masm;
FixedList<StackValue> stack;
size_t spIndex;
public:
FrameInfo(JSContext *cx, HandleScript script, MacroAssembler &masm)
: script(cx, script),
FrameInfo(JSScript *script, MacroAssembler &masm)
: script(script),
masm(masm),
stack(),
spIndex(0)

View File

@ -15,6 +15,8 @@
# include "jit/x64/BaselineHelpers-x64.h"
#elif defined(JS_CODEGEN_ARM)
# include "jit/arm/BaselineHelpers-arm.h"
#elif defined(JS_CODEGEN_MIPS)
# include "jit/mips/BaselineHelpers-mips.h"
#else
# error "Unknown architecture!"
#endif

View File

@ -13,6 +13,7 @@
#include "jstypes.h"
#include "builtin/Eval.h"
#include "jit/BaselineDebugModeOSR.h"
#include "jit/BaselineHelpers.h"
#include "jit/BaselineJIT.h"
#include "jit/IonLinker.h"
@ -669,6 +670,20 @@ ICStubCompiler::leaveStubFrame(MacroAssembler &masm, bool calledIntoIon)
EmitLeaveStubFrame(masm, calledIntoIon);
}
void
ICStubCompiler::leaveStubFrameHead(MacroAssembler &masm, bool calledIntoIon)
{
JS_ASSERT(entersStubFrame_);
EmitLeaveStubFrameHead(masm, calledIntoIon);
}
void
ICStubCompiler::leaveStubFrameCommonTail(MacroAssembler &masm)
{
JS_ASSERT(entersStubFrame_);
EmitLeaveStubFrameCommonTail(masm);
}
void
ICStubCompiler::guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip)
{
@ -745,7 +760,7 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, Val
masm.branchPtr(Assembler::AboveOrEqual, valReg, ImmWord(nursery.heapEnd()), &skipBarrier);
// void PostWriteBarrier(JSRuntime *rt, JSObject *obj);
#ifdef JS_CODEGEN_ARM
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
saveRegs.add(BaselineTailCallReg);
#endif
saveRegs = GeneralRegisterSet::Intersect(saveRegs, GeneralRegisterSet::Volatile());
@ -1757,9 +1772,12 @@ ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
//
static bool
DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, HandleValue lhs,
DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub_, HandleValue lhs,
HandleValue rhs, MutableHandleValue ret)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICCompare_Fallback *> stub(frame, stub_);
jsbytecode *pc = stub->icEntry()->pc(frame->script());
JSOp op = JSOp(*pc);
@ -1816,6 +1834,10 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
ret.setBoolean(out);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
// Check to see if a new stub should be generated.
if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
// TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
@ -2489,9 +2511,12 @@ ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
# pragma optimize("g", off)
#endif
static bool
DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallback *stub,
DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallback *stub_,
HandleValue lhs, HandleValue rhs, MutableHandleValue ret)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICBinaryArith_Fallback *> stub(frame, stub_);
RootedScript script(cx, frame->script());
jsbytecode *pc = stub->icEntry()->pc(script);
JSOp op = JSOp(*pc);
@ -2571,6 +2596,10 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
MOZ_ASSUME_UNREACHABLE("Unhandled baseline arith op");
}
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (ret.isDouble())
stub->setSawDoubleResult();
@ -3048,9 +3077,12 @@ ICBinaryArith_DoubleWithInt32::Compiler::generateStubCode(MacroAssembler &masm)
# pragma optimize("g", off)
#endif
static bool
DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback *stub,
DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback *stub_,
HandleValue val, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICUnaryArith_Fallback *> stub(frame, stub_);
RootedScript script(cx, frame->script());
jsbytecode *pc = stub->icEntry()->pc(script);
JSOp op = JSOp(*pc);
@ -3072,6 +3104,10 @@ DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback
MOZ_ASSUME_UNREACHABLE("Unexpected op");
}
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (res.isDouble())
stub->setSawDoubleResult();
@ -3977,9 +4013,13 @@ TryAttachGetElemStub(JSContext *cx, JSScript *script, jsbytecode *pc, ICGetElem_
}
static bool
DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub, HandleValue lhs,
DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub_, HandleValue lhs,
HandleValue rhs, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICGetElem_Fallback *> stub(frame, stub_);
RootedScript script(cx, frame->script());
jsbytecode *pc = stub->icEntry()->pc(frame->script());
JSOp op = JSOp(*pc);
FallbackICSpew(cx, stub, "GetElem(%s)", js_CodeName[op]);
@ -4004,6 +4044,10 @@ DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub,
types::TypeScript::Monitor(cx, frame->script(), pc, res);
}
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
// Add a type monitor stub for the resulting value.
if (!stub->addMonitorStubForValue(cx, frame->script(), res))
return false;
@ -4945,9 +4989,12 @@ CanOptimizeDenseSetElem(JSContext *cx, HandleObject obj, uint32_t index,
}
static bool
DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub, Value *stack,
DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_, Value *stack,
HandleValue objv, HandleValue index, HandleValue rhs)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICSetElem_Fallback *> stub(frame, stub_);
RootedScript script(cx, frame->script());
jsbytecode *pc = stub->icEntry()->pc(script);
JSOp op = JSOp(*pc);
@ -4987,6 +5034,10 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub,
JS_ASSERT(stack[2] == objv);
stack[2] = rhs;
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
// TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
// But for now we just bail.
@ -5757,9 +5808,12 @@ TryAttachScopeNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *s
}
static bool
DoGetNameFallback(JSContext *cx, BaselineFrame *frame, ICGetName_Fallback *stub,
DoGetNameFallback(JSContext *cx, BaselineFrame *frame, ICGetName_Fallback *stub_,
HandleObject scopeChain, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICGetName_Fallback *> stub(frame, stub_);
RootedScript script(cx, frame->script());
jsbytecode *pc = stub->icEntry()->pc(script);
mozilla::DebugOnly<JSOp> op = JSOp(*pc);
@ -5779,6 +5833,10 @@ DoGetNameFallback(JSContext *cx, BaselineFrame *frame, ICGetName_Fallback *stub,
types::TypeScript::Monitor(cx, script, pc, res);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
// Add a type monitor stub for the resulting value.
if (!stub->addMonitorStubForValue(cx, script, res))
return false;
@ -5934,9 +5992,12 @@ ICBindName_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
//
static bool
DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallback *stub,
DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallback *stub_,
MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICGetIntrinsic_Fallback *> stub(frame, stub_);
RootedScript script(cx, frame->script());
jsbytecode *pc = stub->icEntry()->pc(script);
mozilla::DebugOnly<JSOp> op = JSOp(*pc);
@ -5953,6 +6014,10 @@ DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallb
types::TypeScript::Monitor(cx, script, pc, res);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
IonSpew(IonSpew_BaselineIC, " Generating GetIntrinsic optimized stub");
ICGetIntrinsic_Constant::Compiler compiler(cx, res);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
@ -6314,9 +6379,12 @@ TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc
}
static bool
DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub,
DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_,
MutableHandleValue val, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICGetProp_Fallback *> stub(frame, stub_);
jsbytecode *pc = stub->icEntry()->pc(frame->script());
JSOp op = JSOp(*pc);
FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]);
@ -6362,6 +6430,10 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub,
types::TypeScript::Monitor(cx, frame->script(), pc, res);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
// Add a type monitor stub for the resulting value.
if (!stub->addMonitorStubForValue(cx, frame->script(), res))
return false;
@ -6423,17 +6495,27 @@ ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
if (!tailCallVM(DoGetPropFallbackInfo, masm))
return false;
// What follows is bailout-only code for inlined scripted getters
// The return address pointed to by the baseline stack points here.
returnOffset_ = masm.currentOffset();
// What follows is bailout for inlined scripted getters or for on-stack
// debug mode recompile. The return address pointed to by the baseline
// stack points here.
//
// Even though the fallback frame doesn't enter a stub frame, the CallScripted
// frame that we are emulating does. Again, we lie.
#ifdef DEBUG
entersStubFrame_ = true;
#endif
leaveStubFrame(masm, true);
Label leaveStubCommon;
returnFromStubOffset_ = masm.currentOffset();
leaveStubFrameHead(masm, false);
masm.jump(&leaveStubCommon);
returnFromIonOffset_ = masm.currentOffset();
leaveStubFrameHead(masm, true);
masm.bind(&leaveStubCommon);
leaveStubFrameCommonTail(masm);
// When we get here, BaselineStubReg contains the ICGetProp_Fallback stub,
// which we can't use to enter the TypeMonitor IC, because it's a MonitoredFallbackStub
@ -6448,9 +6530,16 @@ ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
bool
ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code)
{
CodeOffsetLabel offset(returnOffset_);
offset.fixup(&masm);
cx->compartment()->jitCompartment()->initBaselineGetPropReturnAddr(code->raw() + offset.offset());
JitCompartment *comp = cx->compartment()->jitCompartment();
CodeOffsetLabel fromIon(returnFromIonOffset_);
fromIon.fixup(&masm);
comp->initBaselineGetPropReturnFromIonAddr(code->raw() + fromIon.offset());
CodeOffsetLabel fromVM(returnFromStubOffset_);
fromVM.fixup(&masm);
comp->initBaselineGetPropReturnFromStubAddr(code->raw() + fromVM.offset());
return true;
}
@ -7248,9 +7337,12 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
}
static bool
DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub, HandleValue lhs,
HandleValue rhs, MutableHandleValue res)
DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_,
HandleValue lhs, HandleValue rhs, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICSetProp_Fallback *> stub(frame, stub_);
RootedScript script(cx, frame->script());
jsbytecode *pc = stub->icEntry()->pc(script);
JSOp op = JSOp(*pc);
@ -7299,6 +7391,10 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub,
// Leave the RHS on the stack.
res.set(rhs);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (stub->numOptimizedStubs() >= ICSetProp_Fallback::MAX_OPTIMIZED_STUBS) {
// TODO: Discard all stubs in this IC and replace with generic setprop stub.
return true;
@ -7344,17 +7440,27 @@ ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
if (!tailCallVM(DoSetPropFallbackInfo, masm))
return false;
// What follows is bailout-only code for inlined scripted getters
// The return address pointed to by the baseline stack points here.
returnOffset_ = masm.currentOffset();
// What follows is bailout debug mode recompile code for inlined scripted
// getters The return address pointed to by the baseline stack points
// here.
//
// Even though the fallback frame doesn't enter a stub frame, the CallScripted
// frame that we are emulating does. Again, we lie.
#ifdef DEBUG
entersStubFrame_ = true;
#endif
leaveStubFrame(masm, true);
Label leaveStubCommon;
returnFromStubOffset_ = masm.currentOffset();
leaveStubFrameHead(masm, false);
masm.jump(&leaveStubCommon);
returnFromIonOffset_ = masm.currentOffset();
leaveStubFrameHead(masm, true);
masm.bind(&leaveStubCommon);
leaveStubFrameCommonTail(masm);
// Retrieve the stashed initial argument from the caller's frame before returning
EmitUnstowICValues(masm, 1);
@ -7366,9 +7472,16 @@ ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
bool
ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code)
{
CodeOffsetLabel offset(returnOffset_);
offset.fixup(&masm);
cx->compartment()->jitCompartment()->initBaselineSetPropReturnAddr(code->raw() + offset.offset());
JitCompartment *comp = cx->compartment()->jitCompartment();
CodeOffsetLabel fromIon(returnFromIonOffset_);
fromIon.fixup(&masm);
comp->initBaselineSetPropReturnFromIonAddr(code->raw() + fromIon.offset());
CodeOffsetLabel fromVM(returnFromStubOffset_);
fromVM.fixup(&masm);
comp->initBaselineSetPropReturnFromStubAddr(code->raw() + fromVM.offset());
return true;
}
@ -8084,9 +8197,12 @@ MaybeCloneFunctionAtCallsite(JSContext *cx, MutableHandleValue callee, HandleScr
}
static bool
DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub, uint32_t argc,
DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub_, uint32_t argc,
Value *vp, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICCall_Fallback *> stub(frame, stub_);
// Ensure vp array is rooted - we may GC in here.
AutoArrayRooter vpRoot(cx, argc + 2, vp);
@ -8138,6 +8254,10 @@ DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub, uint3
types::TypeScript::Monitor(cx, script, pc, res);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
// Attach a new TypeMonitor stub for this value.
ICTypeMonitor_Fallback *typeMonFbStub = stub->fallbackMonitorStub();
if (!typeMonFbStub->addMonitorStubForValue(cx, script, res))
@ -8382,17 +8502,35 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
leaveStubFrame(masm);
EmitReturnFromIC(masm);
// The following asmcode is only used when an Ion inlined frame bails out into
// baseline jitcode. The return address pushed onto the reconstructed baseline stack
// points here.
returnOffset_ = masm.currentOffset();
// The following asmcode is only used either when an Ion inlined frame
// bails out into baseline jitcode or we need to do on-stack script
// replacement for debug mode recompile.
Label leaveStubCommon;
returnFromStubOffset_ = masm.currentOffset();
// Load passed-in ThisV into R1 just in case it's needed. Need to do this before
// we leave the stub frame since that info will be lost.
// Current stack: [...., ThisV, ActualArgc, CalleeToken, Descriptor ]
masm.loadValue(Address(BaselineStackReg, 3 * sizeof(size_t)), R1);
leaveStubFrame(masm, true);
// Emit the coming-from-VM specific part of the stub-leaving code.
leaveStubFrameHead(masm, /* calledIntoIon = */ false);
// Jump to the common leave stub tail.
masm.jump(&leaveStubCommon);
// For Ion bailouts, the return address pushed onto the reconstructed
// baseline stack points here.
returnFromIonOffset_ = masm.currentOffset();
masm.loadValue(Address(BaselineStackReg, 3 * sizeof(size_t)), R1);
// Emit the coming-from-Ion specific part of the stub-leaving code.
leaveStubFrameHead(masm, /* calledIntoIon = */ true);
// Emit the common stub-leaving tail.
masm.bind(&leaveStubCommon);
leaveStubFrameCommonTail(masm);
// R1 and R0 are taken.
regs = availableGeneralRegs(2);
@ -8427,9 +8565,16 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
bool
ICCall_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code)
{
CodeOffsetLabel offset(returnOffset_);
offset.fixup(&masm);
cx->compartment()->jitCompartment()->initBaselineCallReturnAddr(code->raw() + offset.offset());
JitCompartment *comp = cx->compartment()->jitCompartment();
CodeOffsetLabel fromIon(returnFromIonOffset_);
fromIon.fixup(&masm);
comp->initBaselineCallReturnFromIonAddr(code->raw() + fromIon.offset());
CodeOffsetLabel fromVM(returnFromStubOffset_);
fromVM.fixup(&masm);
comp->initBaselineCallReturnFromStubAddr(code->raw() + fromVM.offset());
return true;
}
@ -9202,7 +9347,7 @@ ICTableSwitch::Compiler::getStub(ICStubSpace *space)
}
void
ICTableSwitch::fixupJumpTable(HandleScript script, BaselineScript *baseline)
ICTableSwitch::fixupJumpTable(JSScript *script, BaselineScript *baseline)
{
defaultTarget_ = baseline->nativeCodeForPC(script, (jsbytecode *) defaultTarget_);
@ -9251,9 +9396,12 @@ ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
//
static bool
DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallback *stub,
DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallback *stub_,
HandleValue iterValue, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICIteratorMore_Fallback *> stub(frame, stub_);
FallbackICSpew(cx, stub, "IteratorMore");
bool cond;
@ -9261,6 +9409,10 @@ DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallb
return false;
res.setBoolean(cond);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (iterValue.toObject().is<PropertyIteratorObject>() &&
!stub->hasStub(ICStub::IteratorMore_Native))
{
@ -9332,15 +9484,22 @@ ICIteratorMore_Native::Compiler::generateStubCode(MacroAssembler &masm)
//
static bool
DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallback *stub,
DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallback *stub_,
HandleValue iterValue, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICIteratorNext_Fallback *> stub(frame, stub_);
FallbackICSpew(cx, stub, "IteratorNext");
RootedObject iteratorObject(cx, &iterValue.toObject());
if (!IteratorNext(cx, iteratorObject, res))
return false;
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (!res.isString() && !stub->hasNonStringResult())
stub->setHasNonStringResult();

View File

@ -9,6 +9,8 @@
#ifdef JS_ION
#include "mozilla/Assertions.h"
#include "jscntxt.h"
#include "jscompartment.h"
#include "jsgc.h"
@ -206,15 +208,49 @@ class ICEntry
uint32_t returnOffset_;
// The PC of this IC's bytecode op within the JSScript.
uint32_t pcOffset_ : 31;
// Whether this IC is for a bytecode op.
uint32_t isForOp_ : 1;
uint32_t pcOffset_ : 29;
public:
ICEntry(uint32_t pcOffset, bool isForOp)
: firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset), isForOp_(isForOp)
{}
enum Kind {
// A for-op IC entry.
Kind_Op = 0,
// A non-op IC entry.
Kind_NonOp,
// A fake IC entry for returning from a callVM.
Kind_CallVM,
// A fake IC entry for returning from DebugTrapHandler.
Kind_DebugTrap,
// A fake IC entry for returning from a callVM to
// Debug{Prologue,Epilogue}.
Kind_DebugPrologue,
Kind_DebugEpilogue
};
private:
// What this IC is for.
Kind kind_ : 3;
// Set the kind and asserts that it's sane.
void setKind(Kind kind) {
kind_ = kind;
MOZ_ASSERT(this->kind() == kind);
}
public:
ICEntry(uint32_t pcOffset, Kind kind)
: firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset)
{
// The offset must fit in at least 29 bits, since we shave off 3 for
// the Kind enum.
MOZ_ASSERT(pcOffset_ == pcOffset);
JS_STATIC_ASSERT(BaselineScript::MAX_JSSCRIPT_LENGTH < 0x1fffffffu);
MOZ_ASSERT(pcOffset <= BaselineScript::MAX_JSSCRIPT_LENGTH);
setKind(kind);
}
CodeOffsetLabel returnOffset() const {
return CodeOffsetLabel(returnOffset_);
@ -240,8 +276,21 @@ class ICEntry
return script->offsetToPC(pcOffset_);
}
Kind kind() const {
// MSVC compiles enums as signed.
return (Kind)(kind_ & 0x7);
}
bool isForOp() const {
return isForOp_;
return kind() == Kind_Op;
}
void setForDebugPrologue() {
MOZ_ASSERT(kind() == Kind_CallVM);
setKind(Kind_DebugPrologue);
}
void setForDebugEpilogue() {
MOZ_ASSERT(kind() == Kind_CallVM);
setKind(Kind_DebugEpilogue);
}
bool hasStub() const {
@ -1043,6 +1092,8 @@ class ICStubCompiler
// to pc mapping to work.
void enterStubFrame(MacroAssembler &masm, Register scratch);
void leaveStubFrame(MacroAssembler &masm, bool calledIntoIon = false);
void leaveStubFrameHead(MacroAssembler &masm, bool calledIntoIon = false);
void leaveStubFrameCommonTail(MacroAssembler &masm);
// Some stubs need to emit SPS profiler updates. This emits the guarding
// jitcode for those stubs. If profiling is not enabled, jumps to the
@ -1057,9 +1108,12 @@ class ICStubCompiler
inline GeneralRegisterSet availableGeneralRegs(size_t numInputs) const {
GeneralRegisterSet regs(GeneralRegisterSet::All());
JS_ASSERT(!regs.has(BaselineStackReg));
#ifdef JS_CODEGEN_ARM
#if defined(JS_CODEGEN_ARM)
JS_ASSERT(!regs.has(BaselineTailCallReg));
regs.take(BaselineSecondScratchReg);
#elif defined(JS_CODEGEN_MIPS)
JS_ASSERT(!regs.has(BaselineTailCallReg));
JS_ASSERT(!regs.has(BaselineSecondScratchReg));
#endif
regs.take(BaselineFrameReg);
regs.take(BaselineStubReg);
@ -4041,7 +4095,8 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub
class Compiler : public ICStubCompiler {
protected:
uint32_t returnOffset_;
uint32_t returnFromIonOffset_;
uint32_t returnFromStubOffset_;
bool generateStubCode(MacroAssembler &masm);
bool postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code);
@ -4934,7 +4989,8 @@ class ICSetProp_Fallback : public ICFallbackStub
class Compiler : public ICStubCompiler {
protected:
uint32_t returnOffset_;
uint32_t returnFromIonOffset_;
uint32_t returnFromStubOffset_;
bool generateStubCode(MacroAssembler &masm);
bool postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code);
@ -5358,7 +5414,8 @@ class ICCall_Fallback : public ICMonitoredFallbackStub
class Compiler : public ICCallStubCompiler {
protected:
bool isConstructing_;
uint32_t returnOffset_;
uint32_t returnFromIonOffset_;
uint32_t returnFromStubOffset_;
bool generateStubCode(MacroAssembler &masm);
bool postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code);
@ -5752,7 +5809,7 @@ class ICTableSwitch : public ICStub
return space->allocate<ICTableSwitch>(code, table, min, length, defaultTarget);
}
void fixupJumpTable(HandleScript script, BaselineScript *baseline);
void fixupJumpTable(JSScript *script, BaselineScript *baseline);
class Compiler : public ICStubCompiler {
bool generateStubCode(MacroAssembler &masm);

View File

@ -40,15 +40,18 @@ PCMappingSlotInfo::ToSlotLocation(const StackValue *stackVal)
return SlotIgnore;
}
BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset)
BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset)
: method_(nullptr),
templateScope_(nullptr),
fallbackStubSpace_(),
prologueOffset_(prologueOffset),
epilogueOffset_(epilogueOffset),
#ifdef DEBUG
spsOn_(false),
#endif
spsPushToggleOffset_(spsPushToggleOffset),
postDebugPrologueOffset_(postDebugPrologueOffset),
flags_(0)
{ }
@ -214,7 +217,7 @@ jit::EnterBaselineAtBranch(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc)
}
MethodStatus
jit::BaselineCompile(JSContext *cx, HandleScript script)
jit::BaselineCompile(JSContext *cx, JSScript *script)
{
JS_ASSERT(!script->hasBaselineScript());
JS_ASSERT(script->canBaselineCompile());
@ -359,9 +362,9 @@ jit::CanEnterBaselineMethod(JSContext *cx, RunState &state)
};
BaselineScript *
BaselineScript::New(JSContext *cx, uint32_t prologueOffset,
uint32_t spsPushToggleOffset, size_t icEntries,
size_t pcMappingIndexEntries, size_t pcMappingSize,
BaselineScript::New(JSContext *cx, uint32_t prologueOffset, uint32_t epilogueOffset,
uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset,
size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
size_t bytecodeTypeMapEntries)
{
static const unsigned DataAlignment = sizeof(uintptr_t);
@ -388,7 +391,8 @@ BaselineScript::New(JSContext *cx, uint32_t prologueOffset,
return nullptr;
BaselineScript *script = reinterpret_cast<BaselineScript *>(buffer);
new (script) BaselineScript(prologueOffset, spsPushToggleOffset);
new (script) BaselineScript(prologueOffset, epilogueOffset,
spsPushToggleOffset, postDebugPrologueOffset);
size_t offsetCursor = paddedBaselineScriptSize;
@ -596,7 +600,7 @@ BaselineScript::icEntryFromReturnAddress(uint8_t *returnAddr)
}
void
BaselineScript::copyICEntries(HandleScript script, const ICEntry *entries, MacroAssembler &masm)
BaselineScript::copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm)
{
// Fix up the return offset in the IC entries and copy them in.
// Also write out the IC entry ptrs in any fallback stubs that were added.

View File

@ -119,12 +119,24 @@ struct BaselineScript
// Native code offset right before the scope chain is initialized.
uint32_t prologueOffset_;
// Native code offset right before the frame is popped and the method
// returned from.
uint32_t epilogueOffset_;
// The offsets for the toggledJump instructions for SPS update ICs.
#ifdef DEBUG
mozilla::DebugOnly<bool> spsOn_;
#endif
uint32_t spsPushToggleOffset_;
// Native code offsets right after the debug prologue VM call returns, or
// would have returned. This offset is recorded even when debug mode is
// off to aid on-stack debug mode recompilation.
//
// We don't need one for the debug epilogue because that always happens
// right before the epilogue, so we just use the epilogue offset.
uint32_t postDebugPrologueOffset_;
public:
enum Flag {
// Flag set by JSScript::argumentsOptimizationFailed. Similar to
@ -165,9 +177,11 @@ struct BaselineScript
public:
// Do not call directly, use BaselineScript::New. This is public for cx->new_.
BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset);
BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset);
static BaselineScript *New(JSContext *cx, uint32_t prologueOffset,
uint32_t epilogueOffset, uint32_t postDebugPrologueOffset,
uint32_t spsPushToggleOffset, size_t icEntries,
size_t pcMappingIndexEntries, size_t pcMappingSize,
size_t bytecodeTypeMapEntries);
@ -224,6 +238,20 @@ struct BaselineScript
return method_->raw() + prologueOffset_;
}
uint32_t epilogueOffset() const {
return epilogueOffset_;
}
uint8_t *epilogueEntryAddr() const {
return method_->raw() + epilogueOffset_;
}
uint32_t postDebugPrologueOffset() const {
return postDebugPrologueOffset_;
}
uint8_t *postDebugPrologueAddr() const {
return method_->raw() + postDebugPrologueOffset_;
}
ICEntry *icEntryList() {
return (ICEntry *)(reinterpret_cast<uint8_t *>(this) + icEntriesOffset_);
}
@ -257,10 +285,15 @@ struct BaselineScript
method()->togglePreBarriers(enabled);
}
bool containsCodeAddress(uint8_t *addr) const {
return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
}
ICEntry &icEntry(size_t index);
ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
ICEntry &icEntryFromPCOffset(uint32_t pcOffset);
ICEntry &icEntryForDebugModeRecompileFromPCOffset(uint32_t pcOffset);
ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry);
ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr);
ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr);
@ -270,7 +303,7 @@ struct BaselineScript
return icEntries_;
}
void copyICEntries(HandleScript script, const ICEntry *entries, MacroAssembler &masm);
void copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm);
void adoptFallbackStubs(FallbackICStubSpace *stubSpace);
PCMappingIndexEntry &pcMappingIndexEntry(size_t index);
@ -387,7 +420,7 @@ void
MarkActiveBaselineScripts(Zone *zone);
MethodStatus
BaselineCompile(JSContext *cx, HandleScript script);
BaselineCompile(JSContext *cx, JSScript *script);
} // namespace jit
} // namespace js

View File

@ -13,8 +13,12 @@
# include "jit/x86/BaselineRegisters-x86.h"
#elif defined(JS_CODEGEN_X64)
# include "jit/x64/BaselineRegisters-x64.h"
#else
#elif defined(JS_CODEGEN_ARM)
# include "jit/arm/BaselineRegisters-arm.h"
#elif defined(JS_CODEGEN_MIPS)
# include "jit/mips/BaselineRegisters-mips.h"
#else
# error "Unknown architecture!"
#endif
namespace js {

Some files were not shown because too many files have changed in this diff Show More