mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-10 17:24:29 +00:00
Merge inbound to m-c.
This commit is contained in:
commit
7843a5e1a7
@ -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',
|
||||
|
16
configure.in
16
configure.in
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]
|
||||
|
41
content/html/content/test/test_bug821307.html
Normal file
41
content/html/content/test/test_bug821307.html
Normal 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>
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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']
|
||||
|
@ -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']
|
@ -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/.
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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]
|
||||
|
40
dom/events/test/test_bug704423.html
Normal file
40
dom/events/test/test_bug704423.html
Normal 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>
|
@ -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();
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
makeAllAppsLaunchable();
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
|
||||
var mmListener = {
|
||||
receiveMessage: function(aMessage) {
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -101,6 +101,8 @@ AsyncCompositionManager::ResolveRefLayers()
|
||||
if (!mLayerManager->GetRoot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mReadyForCompose = true;
|
||||
WalkTheTree<Resolve>(mLayerManager->GetRoot(),
|
||||
mReadyForCompose,
|
||||
mTargetConfig);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,6 +268,8 @@ public:
|
||||
|
||||
virtual bool IsSymbolFont();
|
||||
|
||||
virtual bool HasFontTable(uint32_t aTableTag);
|
||||
|
||||
inline bool HasGraphiteTables() {
|
||||
if (!mCheckedForGraphiteTables) {
|
||||
CheckForGraphiteTables();
|
||||
|
@ -9,6 +9,7 @@ EXPORTS += [
|
||||
'nsIUnicodeDecoder.h',
|
||||
'nsIUnicodeEncoder.h',
|
||||
'nsUConvCID.h',
|
||||
'nsUCSupport.h',
|
||||
'uconvutil.h',
|
||||
]
|
||||
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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]
|
||||
|
@ -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(',', '&')
|
||||
{
|
||||
}
|
@ -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___ */
|
@ -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 \
|
||||
|
@ -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('/', '+')
|
||||
{
|
||||
}
|
@ -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___ */
|
@ -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(',', '&')
|
||||
{
|
||||
}
|
@ -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___ */
|
@ -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;
|
||||
}
|
@ -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___ */
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
69
js/src/jit-test/lib/jitopts.js
Normal file
69
js/src/jit-test/lib/jitopts.js
Normal 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
|
||||
};
|
24
js/src/jit-test/tests/debug/Debugger-debuggees-22.js
Normal file
24
js/src/jit-test/tests/debug/Debugger-debuggees-22.js
Normal 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);
|
||||
} + ")();");
|
107
js/src/jit-test/tests/debug/Debugger-debuggees-23.js
Normal file
107
js/src/jit-test/tests/debug/Debugger-debuggees-23.js
Normal 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);
|
||||
} + ")();");
|
||||
})();
|
55
js/src/jit-test/tests/debug/Debugger-debuggees-24.js
Normal file
55
js/src/jit-test/tests/debug/Debugger-debuggees-24.js
Normal 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(); } + ")();");
|
||||
|
48
js/src/jit-test/tests/debug/Debugger-debuggees-25.js
Normal file
48
js/src/jit-test/tests/debug/Debugger-debuggees-25.js
Normal 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(); } + ")();");
|
||||
|
34
js/src/jit-test/tests/debug/Debugger-debuggees-26.js
Normal file
34
js/src/jit-test/tests/debug/Debugger-debuggees-26.js
Normal 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);
|
||||
}
|
||||
});
|
47
js/src/jit-test/tests/debug/Environment-getVariable-13.js
Normal file
47
js/src/jit-test/tests/debug/Environment-getVariable-13.js
Normal 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);
|
||||
} + ")();");
|
||||
});
|
34
js/src/jit-test/tests/debug/Frame-eval-19.js
Normal file
34
js/src/jit-test/tests/debug/Frame-eval-19.js
Normal 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);
|
||||
} + ")();");
|
||||
});
|
46
js/src/jit-test/tests/debug/Frame-eval-20.js
Normal file
46
js/src/jit-test/tests/debug/Frame-eval-20.js
Normal 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);
|
||||
});
|
33
js/src/jit-test/tests/debug/Frame-eval-21.js
Normal file
33
js/src/jit-test/tests/debug/Frame-eval-21.js
Normal 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"); } + ")();");
|
||||
});
|
32
js/src/jit-test/tests/debug/Frame-eval-22.js
Normal file
32
js/src/jit-test/tests/debug/Frame-eval-22.js
Normal 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);
|
||||
} + ")();");
|
||||
});
|
37
js/src/jit-test/tests/debug/Frame-eval-23.js
Normal file
37
js/src/jit-test/tests/debug/Frame-eval-23.js
Normal 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);
|
||||
} + ")();");
|
||||
});
|
45
js/src/jit-test/tests/debug/Frame-implementation-01.js
Normal file
45
js/src/jit-test/tests/debug/Frame-implementation-01.js
Normal 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);
|
||||
});
|
51
js/src/jit-test/tests/debug/Frame-implementation-02.js
Normal file
51
js/src/jit-test/tests/debug/Frame-implementation-02.js
Normal 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);
|
||||
});
|
42
js/src/jit-test/tests/debug/optimized-out-01.js
Normal file
42
js/src/jit-test/tests/debug/optimized-out-01.js
Normal 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);
|
||||
} + ")();");
|
||||
});
|
93
js/src/jit-test/tests/debug/resumption-08.js
Normal file
93
js/src/jit-test/tests/debug/resumption-08.js
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
27
js/src/jit-test/tests/ion/array-splice.js
Normal file
27
js/src/jit-test/tests/ion/array-splice.js
Normal 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();
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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!");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
709
js/src/jit/BaselineDebugModeOSR.cpp
Normal file
709
js/src/jit/BaselineDebugModeOSR.cpp
Normal 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;
|
||||
}
|
106
js/src/jit/BaselineDebugModeOSR.h
Normal file
106
js/src/jit/BaselineDebugModeOSR.h
Normal 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
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user