Merge m-c to fx-team. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-08-30 22:14:30 -04:00
commit 621dd98985
105 changed files with 767 additions and 753 deletions

View File

@ -18,7 +18,7 @@ function test() {
is(workers.length, 0);
executeSoon(() => {
evalInTab(tab, "let worker1 = new Worker('" + WORKER1_URL + "');");
evalInTab(tab, "var worker1 = new Worker('" + WORKER1_URL + "');");
});
yield waitForWorkerListChanged(tabClient);
@ -27,7 +27,7 @@ function test() {
is(workers[0].url, WORKER1_URL);
executeSoon(() => {
evalInTab(tab, "let worker2 = new Worker('" + WORKER2_URL + "');");
evalInTab(tab, "var worker2 = new Worker('" + WORKER2_URL + "');");
});
yield waitForWorkerListChanged(tabClient);

View File

@ -111,7 +111,7 @@ function runTests()
{
method: "run",
prepare: function() {
sp.editor.setText("var foobarBug636725cache = 'foo';" +
sp.editor.setText("window.foobarBug636725cache = 'foo';" +
"typeof foobarBug636725cache;");
},
then: function([, , result]) {
@ -122,7 +122,7 @@ function runTests()
{
method: "run",
prepare: function() {
sp.editor.setText("var foobarBug636725cache2 = 'foo';" +
sp.editor.setText("window.foobarBug636725cache2 = 'foo';" +
"typeof foobarBug636725cache2;");
},
then: function([, , result]) {
@ -152,4 +152,4 @@ function runTests()
"delete foobarBug636725cache2;");
sp.run().then(finish);
});
}
}

View File

@ -11637,14 +11637,30 @@ public:
return sList.getLast();
}
enum IteratorOption
{
// When we are committing fullscreen changes or preparing for
// that, we generally want to iterate all requests in the same
// window with eDocumentsWithSameRoot option.
eDocumentsWithSameRoot,
// If we are removing a document from the tree, we would only
// want to remove the requests from the given document and its
// descendants. For that case, use eInclusiveDescendants.
eInclusiveDescendants
};
class Iterator
{
public:
explicit Iterator(nsIDocument* aDoc)
explicit Iterator(nsIDocument* aDoc, IteratorOption aOption)
: mCurrent(PendingFullscreenRequestList::sList.getFirst())
, mRootShellForIteration(aDoc->GetDocShell())
{
if (mCurrent) {
mRootShell = GetRootShell(aDoc);
if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) {
mRootShellForIteration->
GetRootTreeItem(getter_AddRefs(mRootShellForIteration));
}
SkipToNextMatch();
}
}
@ -11658,16 +11674,6 @@ public:
const FullscreenRequest& Get() const { return *mCurrent; }
private:
already_AddRefed<nsIDocShellTreeItem> GetRootShell(nsIDocument* aDoc)
{
if (nsIDocShellTreeItem* shell = aDoc->GetDocShell()) {
nsCOMPtr<nsIDocShellTreeItem> rootShell;
shell->GetRootTreeItem(getter_AddRefs(rootShell));
return rootShell.forget();
}
return nullptr;
}
void DeleteAndNextInternal()
{
FullscreenRequest* thisRequest = mCurrent;
@ -11678,21 +11684,28 @@ public:
{
while (mCurrent) {
nsCOMPtr<nsIDocShellTreeItem>
rootShell = GetRootShell(mCurrent->GetDocument());
if (!rootShell) {
docShell = mCurrent->GetDocument()->GetDocShell();
if (!docShell) {
// Always automatically drop documents which has been
// detached from the doc shell.
DeleteAndNextInternal();
} else if (rootShell != mRootShell) {
mCurrent = mCurrent->getNext();
} else {
break;
while (docShell && docShell != mRootShellForIteration) {
docShell->GetParent(getter_AddRefs(docShell));
}
if (!docShell) {
// We've gone over the root, but haven't find the target
// ancestor, so skip this item.
mCurrent = mCurrent->getNext();
} else {
break;
}
}
}
}
FullscreenRequest* mCurrent;
nsCOMPtr<nsIDocShellTreeItem> mRootShell;
nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration;
};
private:
@ -11733,7 +11746,8 @@ nsDocument::RequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
if ((static_cast<nsGlobalWindow*>(rootWin.get())->FullScreen() &&
// The iterator being at end at the beginning indicates there is
// no pending fullscreen request which relates to this document.
PendingFullscreenRequestList::Iterator(this).AtEnd()) ||
PendingFullscreenRequestList::Iterator(
this, PendingFullscreenRequestList::eDocumentsWithSameRoot).AtEnd()) ||
nsContentUtils::GetRootDocument(this)->IsFullScreenDoc()) {
ApplyFullscreen(*aRequest);
return;
@ -11766,7 +11780,8 @@ nsDocument::RequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
{
bool handled = false;
PendingFullscreenRequestList::Iterator iter(aDoc);
PendingFullscreenRequestList::Iterator iter(
aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
while (!iter.AtEnd()) {
const FullscreenRequest& request = iter.Get();
if (request.GetDocument()->ApplyFullscreen(request)) {
@ -11780,7 +11795,8 @@ nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
static void
ClearPendingFullscreenRequests(nsIDocument* aDoc)
{
PendingFullscreenRequestList::Iterator iter(aDoc);
PendingFullscreenRequestList::Iterator iter(
aDoc, PendingFullscreenRequestList::eInclusiveDescendants);
while (!iter.AtEnd()) {
iter.DeleteAndNext();
}

View File

@ -391,8 +391,6 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case LOCAL_GL_MAX_VARYING_VECTORS:
return JS::Int32Value(mGLMaxVaryingVectors);
case LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
return JS::Int32Value(0);
case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: {
uint32_t length = mCompressedTextureFormats.Length();
JSObject* obj = dom::Uint32Array::Create(cx, this, length,

View File

@ -25,11 +25,7 @@
// For HTTP seeking, if number of bytes needing to be
// seeked forward is less than this value then a read is
// done rather than a byte range request.
//
// If we assume a 100Mbit connection, and assume reissuing an HTTP seek causes
// a delay of 200ms, then in that 200ms we could have simply read ahead 2MB. So
// setting SEEK_VS_READ_THRESHOLD to 1MB sounds reasonable.
static const int64_t SEEK_VS_READ_THRESHOLD = 1 * 1024 * 1024;
static const int64_t SEEK_VS_READ_THRESHOLD = 32*1024;
static const uint32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE = 416;

View File

@ -623,18 +623,18 @@ skip-if = toolkit == 'android' # bug 1043403
[test_eme_persistent_sessions.html]
skip-if = toolkit == 'android' # bug 1043403
[test_eme_playback.html]
skip-if = toolkit == 'android' || (os == 'win' && debug) # bug 1043403, bug 1187903
skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1187903, bug 1186406, bug 1193351
[test_eme_requestKeySystemAccess.html]
skip-if = toolkit == 'android' # bug 1043403
[test_eme_stream_capture_blocked_case1.html]
tags=msg capturestream
skip-if = toolkit == 'android' || os == 'win' # bug 1043403, bug 1140675, bug 1187903
skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1140675, bug 1187903, bug 1193351
[test_eme_stream_capture_blocked_case2.html]
tags=msg capturestream
skip-if = toolkit == 'android' || os == 'win' # bug 1043403, bug 1140675, bug 1187903
skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1140675, bug 1187903, bug 1193351
[test_eme_stream_capture_blocked_case3.html]
tags=msg capturestream
skip-if = toolkit == 'android' || os == 'win' # bug 1043403, bug 1140675, bug 1187903
skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1140675, bug 1187903, bug 1193351
[test_empty_resource.html]
[test_error_in_video_document.html]
skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634

View File

@ -40,12 +40,17 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioBuffer, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioBuffer, Release)
AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate)
uint32_t aLength, float aSampleRate,
already_AddRefed<ThreadSharedFloatArrayBufferList>
aInitialContents)
: mOwnerWindow(do_GetWeakReference(aContext->GetOwner())),
mSharedChannels(aInitialContents),
mLength(aLength),
mSampleRate(aSampleRate)
{
mJSChannels.SetCapacity(aNumberOfChannels);
MOZ_ASSERT(!mSharedChannels ||
mSharedChannels->GetChannels() == aNumberOfChannels);
mJSChannels.SetLength(aNumberOfChannels);
mozilla::HoldJSObjects(this);
}
@ -64,6 +69,8 @@ AudioBuffer::ClearJSChannels()
/* static */ already_AddRefed<AudioBuffer>
AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate,
already_AddRefed<ThreadSharedFloatArrayBufferList>
aInitialContents,
JSContext* aJSContext, ErrorResult& aRv)
{
// Note that a buffer with zero channels is permitted here for the sake of
@ -78,7 +85,12 @@ AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
}
nsRefPtr<AudioBuffer> buffer =
new AudioBuffer(aContext, aNumberOfChannels, aLength, aSampleRate);
new AudioBuffer(aContext, aNumberOfChannels, aLength, aSampleRate,
Move(aInitialContents));
if (buffer->mSharedChannels) {
return buffer.forget();
}
for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
JS::Rooted<JSObject*> array(aJSContext,
@ -87,7 +99,7 @@ AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
}
buffer->mJSChannels.AppendElement(array.get());
buffer->mJSChannels[i] = array;
}
return buffer.forget();
@ -184,15 +196,6 @@ AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
aSource.Data(), length);
}
void
AudioBuffer::SetRawChannelContents(uint32_t aChannel, float* aContents)
{
MOZ_ASSERT(!GetWrapperPreserveColor() && !mSharedChannels,
"The AudioBuffer object should not have been handed to JS or have C++ callers neuter its typed array");
JS::AutoCheckCannotGC nogc;
PodCopy(JS_GetFloat32ArrayData(mJSChannels[aChannel], nogc), aContents, mLength);
}
void
AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
JS::MutableHandle<JSObject*> aRetval,

View File

@ -33,11 +33,23 @@ class AudioContext;
class AudioBuffer final : public nsWrapperCache
{
public:
// If non-null, aInitialContents must have number of channels equal to
// aNumberOfChannels and their lengths must be at least aLength.
static already_AddRefed<AudioBuffer>
Create(AudioContext* aContext, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate,
already_AddRefed<ThreadSharedFloatArrayBufferList> aInitialContents,
JSContext* aJSContext, ErrorResult& aRv);
static already_AddRefed<AudioBuffer>
Create(AudioContext* aContext, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate,
JSContext* aJSContext, ErrorResult& aRv)
{
return Create(aContext, aNumberOfChannels, aLength, aSampleRate,
nullptr, aJSContext, aRv);
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioBuffer)
@ -91,15 +103,11 @@ public:
*/
ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext);
// This replaces the contents of the JS array for the given channel.
// This function needs to be called on an AudioBuffer which has not been
// handed off to the content yet, and right after the object has been
// initialized.
void SetRawChannelContents(uint32_t aChannel, float* aContents);
protected:
AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate);
uint32_t aLength, float aSampleRate,
already_AddRefed<ThreadSharedFloatArrayBufferList>
aInitialContents);
~AudioBuffer();
bool RestoreJSChannelData(JSContext* aJSContext);

View File

@ -32,8 +32,6 @@ static uint8_t gWebAudioOutputKey;
class OfflineDestinationNodeEngine final : public AudioNodeEngine
{
public:
typedef AutoFallibleTArray<nsAutoArrayPtr<float>, 2> InputChannels;
OfflineDestinationNodeEngine(AudioDestinationNode* aNode,
uint32_t aNumberOfChannels,
uint32_t aLength,
@ -56,65 +54,53 @@ public:
// will not go anywhere.
*aOutput = aInput;
// The output buffer is allocated lazily, on the rendering thread.
if (!mBufferAllocated) {
// The output buffer is allocated lazily, on the rendering thread, when
// non-null input is received.
if (!mBufferAllocated && !aInput.IsNull()) {
// These allocations might fail if content provides a huge number of
// channels or size, but it's OK since we'll deal with the failure
// gracefully.
if (mInputChannels.SetLength(mNumberOfChannels, fallible)) {
mBuffer = ThreadSharedFloatArrayBufferList::
Create(mNumberOfChannels, mLength, fallible);
if (mBuffer && mWriteIndex) {
// Zero leading for any null chunks that were skipped.
for (uint32_t i = 0; i < mNumberOfChannels; ++i) {
mInputChannels[i] = new (fallible) float[mLength];
if (!mInputChannels[i]) {
mInputChannels.Clear();
break;
}
float* channelData = mBuffer->GetDataForWrite(i);
PodZero(channelData, mWriteIndex);
}
}
mBufferAllocated = true;
}
// Handle the case of allocation failure in the input buffer
if (mInputChannels.IsEmpty()) {
return;
}
if (mWriteIndex >= mLength) {
NS_ASSERTION(mWriteIndex == mLength, "Overshot length");
// Don't record any more.
return;
}
// Skip copying if there is no buffer.
uint32_t outputChannelCount = mBuffer ? mNumberOfChannels : 0;
// Record our input buffer
MOZ_ASSERT(mWriteIndex < mLength, "How did this happen?");
const uint32_t duration = std::min(WEBAUDIO_BLOCK_SIZE, mLength - mWriteIndex);
const uint32_t commonChannelCount = std::min(mInputChannels.Length(),
aInput.mChannelData.Length());
// First, copy as many channels in the input as we have
for (uint32_t i = 0; i < commonChannelCount; ++i) {
if (aInput.IsNull()) {
PodZero(mInputChannels[i] + mWriteIndex, duration);
const uint32_t inputChannelCount = aInput.mChannelData.Length();
for (uint32_t i = 0; i < outputChannelCount; ++i) {
float* outputData = mBuffer->GetDataForWrite(i) + mWriteIndex;
if (aInput.IsNull() || i >= inputChannelCount) {
PodZero(outputData, duration);
} else {
const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]);
if (duration == WEBAUDIO_BLOCK_SIZE) {
// Use the optimized version of the copy with scale operation
AudioBlockCopyChannelWithScale(inputBuffer, aInput.mVolume,
mInputChannels[i] + mWriteIndex);
outputData);
} else {
if (aInput.mVolume == 1.0f) {
PodCopy(mInputChannels[i] + mWriteIndex, inputBuffer, duration);
PodCopy(outputData, inputBuffer, duration);
} else {
for (uint32_t j = 0; j < duration; ++j) {
mInputChannels[i][mWriteIndex + j] = aInput.mVolume * inputBuffer[j];
outputData[j] = aInput.mVolume * inputBuffer[j];
}
}
}
}
}
// Then, silence all of the remaining channels
for (uint32_t i = commonChannelCount; i < mInputChannels.Length(); ++i) {
PodZero(mInputChannels[i] + mWriteIndex, duration);
}
mWriteIndex += duration;
if (mWriteIndex >= mLength) {
@ -165,14 +151,11 @@ public:
// Create the input buffer
ErrorResult rv;
nsRefPtr<AudioBuffer> renderedBuffer =
AudioBuffer::Create(context, mInputChannels.Length(),
mLength, mSampleRate, cx, rv);
AudioBuffer::Create(context, mNumberOfChannels, mLength, mSampleRate,
mBuffer.forget(), cx, rv);
if (rv.Failed()) {
return;
}
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
renderedBuffer->SetRawChannelContents(i, mInputChannels[i]);
}
aNode->ResolvePromise(renderedBuffer);
@ -186,7 +169,7 @@ public:
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
{
size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
amount += mInputChannels.ShallowSizeOfExcludingThis(aMallocSizeOf);
amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
return amount;
}
@ -196,11 +179,11 @@ public:
}
private:
// The input to the destination node is recorded in the mInputChannels buffer.
// The input to the destination node is recorded in mBuffer.
// When this buffer fills up with mLength frames, the buffered input is sent
// to the main thread in order to dispatch OfflineAudioCompletionEvent.
InputChannels mInputChannels;
// An index representing the next offset in mInputChannels to be written to.
nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
// An index representing the next offset in mBuffer to be written to.
uint32_t mWriteIndex;
uint32_t mNumberOfChannels;
// How many frames the OfflineAudioContext intends to produce.

View File

@ -12,6 +12,26 @@
namespace mozilla {
already_AddRefed<ThreadSharedFloatArrayBufferList>
ThreadSharedFloatArrayBufferList::Create(uint32_t aChannelCount,
size_t aLength,
const mozilla::fallible_t&)
{
nsRefPtr<ThreadSharedFloatArrayBufferList> buffer =
new ThreadSharedFloatArrayBufferList(aChannelCount);
for (uint32_t i = 0; i < aChannelCount; ++i) {
float* channelData = js_pod_malloc<float>(aLength);
if (!channelData) {
return nullptr;
}
buffer->SetData(i, channelData, js_free, channelData);
}
return buffer.forget();
}
void
AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk)
{

View File

@ -31,12 +31,19 @@ class ThreadSharedFloatArrayBufferList final : public ThreadSharedObject
{
public:
/**
* Construct with null data.
* Construct with null channel data pointers.
*/
explicit ThreadSharedFloatArrayBufferList(uint32_t aCount)
{
mContents.SetLength(aCount);
}
/**
* Create with buffers suitable for transfer to
* JS_NewArrayBufferWithContents(). The buffer contents are uninitialized
* and so should be set using GetDataForWrite().
*/
static already_AddRefed<ThreadSharedFloatArrayBufferList>
Create(uint32_t aChannelCount, size_t aLength, const mozilla::fallible_t&);
struct Storage final
{
@ -58,7 +65,7 @@ public:
}
void* mDataToFree;
void (*mFree)(void*);
const float* mSampleData;
float* mSampleData;
};
/**
@ -69,12 +76,21 @@ public:
* This can be called on any thread.
*/
const float* GetData(uint32_t aIndex) const { return mContents[aIndex].mSampleData; }
/**
* This can be called on any thread, but only when the calling thread is the
* only owner.
*/
float* GetDataForWrite(uint32_t aIndex)
{
MOZ_ASSERT(!IsShared());
return mContents[aIndex].mSampleData;
}
/**
* Call this only during initialization, before the object is handed to
* any other thread.
*/
void SetData(uint32_t aIndex, void* aDataToFree, void (*aFreeFunc)(void*), const float* aData)
void SetData(uint32_t aIndex, void* aDataToFree, void (*aFreeFunc)(void*), float* aData)
{
Storage* s = &mContents[aIndex];
if (s->mFree) {

View File

@ -347,19 +347,9 @@ MediaDecodeTask::FinishDecode()
// Allocate the channel buffers. Note that if we end up resampling, we may
// write fewer bytes than mResampledFrames to the output buffer, in which
// case mWriteIndex will tell us how many valid samples we have.
bool memoryAllocationSuccess = true;
if (!mDecodeJob.mChannelBuffers.SetLength(channelCount, fallible)) {
memoryAllocationSuccess = false;
} else {
for (uint32_t i = 0; i < channelCount; ++i) {
mDecodeJob.mChannelBuffers[i] = new (fallible) float[resampledFrames];
if (!mDecodeJob.mChannelBuffers[i]) {
memoryAllocationSuccess = false;
break;
}
}
}
if (!memoryAllocationSuccess) {
mDecodeJob.mBuffer = ThreadSharedFloatArrayBufferList::
Create(channelCount, resampledFrames, fallible);
if (!mDecodeJob.mBuffer) {
ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
return;
}
@ -376,11 +366,12 @@ MediaDecodeTask::FinishDecode()
for (uint32_t i = 0; i < audioData->mChannels; ++i) {
uint32_t inSamples = audioData->mFrames;
uint32_t outSamples = maxOutSamples;
float* outData =
mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
WebAudioUtils::SpeexResamplerProcess(
resampler, i, &bufferData[i * audioData->mFrames], &inSamples,
mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex,
&outSamples);
outData, &outSamples);
if (i == audioData->mChannels - 1) {
mDecodeJob.mWriteIndex += outSamples;
@ -390,9 +381,10 @@ MediaDecodeTask::FinishDecode()
}
} else {
for (uint32_t i = 0; i < audioData->mChannels; ++i) {
float* outData =
mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
ConvertAudioSamples(&bufferData[i * audioData->mFrames],
mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex,
audioData->mFrames);
outData, audioData->mFrames);
if (i == audioData->mChannels - 1) {
mDecodeJob.mWriteIndex += audioData->mFrames;
@ -407,11 +399,12 @@ MediaDecodeTask::FinishDecode()
for (uint32_t i = 0; i < channelCount; ++i) {
uint32_t inSamples = inputLatency;
uint32_t outSamples = maxOutSamples;
float* outData =
mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
WebAudioUtils::SpeexResamplerProcess(
resampler, i, (AudioDataValue*)nullptr, &inSamples,
mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex,
&outSamples);
outData, &outSamples);
if (i == channelCount - 1) {
mDecodeJob.mWriteIndex += outSamples;
@ -464,17 +457,11 @@ WebAudioDecodeJob::AllocateBuffer()
// Now create the AudioBuffer
ErrorResult rv;
mOutput = AudioBuffer::Create(mContext, mChannelBuffers.Length(),
mWriteIndex, mContext->SampleRate(), cx, rv);
if (rv.Failed()) {
return false;
}
for (uint32_t i = 0; i < mChannelBuffers.Length(); ++i) {
mOutput->SetRawChannelContents(i, mChannelBuffers[i]);
}
return true;
uint32_t channelCount = mBuffer->GetChannels();
mOutput = AudioBuffer::Create(mContext, channelCount,
mWriteIndex, mContext->SampleRate(),
mBuffer.forget(), cx, rv);
return !rv.Failed();
}
void
@ -614,10 +601,7 @@ WebAudioDecodeJob::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
if (mOutput) {
amount += mOutput->SizeOfIncludingThis(aMallocSizeOf);
}
amount += mChannelBuffers.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (uint32_t i = 0; i < mChannelBuffers.Length(); ++i) {
amount += mChannelBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
}
amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
return amount;
}

View File

@ -16,6 +16,8 @@
namespace mozilla {
class ThreadSharedFloatArrayBufferList;
namespace dom {
class AudioBuffer;
class AudioContext;
@ -46,7 +48,6 @@ struct WebAudioDecodeJob final
};
typedef void (WebAudioDecodeJob::*ResultFn)(ErrorCode);
typedef nsAutoArrayPtr<float> ChannelBuffer;
void OnSuccess(ErrorCode /* ignored */);
void OnFailure(ErrorCode aErrorCode);
@ -63,7 +64,7 @@ struct WebAudioDecodeJob final
nsRefPtr<dom::DecodeSuccessCallback> mSuccessCallback;
nsRefPtr<dom::DecodeErrorCallback> mFailureCallback; // can be null
nsRefPtr<dom::AudioBuffer> mOutput;
FallibleTArray<ChannelBuffer> mChannelBuffers;
nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
private:
~WebAudioDecodeJob();

View File

@ -240,8 +240,6 @@ private:
class ScriptProcessorNodeEngine final : public AudioNodeEngine
{
public:
typedef nsAutoTArray<nsAutoArrayPtr<float>, 2> InputChannels;
ScriptProcessorNodeEngine(ScriptProcessorNode* aNode,
AudioDestinationNode* aDestination,
uint32_t aBufferSize,
@ -250,11 +248,9 @@ public:
, mSource(nullptr)
, mDestination(aDestination->Stream())
, mBufferSize(aBufferSize)
, mInputChannelCount(aNumberOfInputChannels)
, mInputWriteIndex(0)
, mSeenNonSilenceInput(false)
{
mInputChannels.SetLength(aNumberOfInputChannels);
AllocateInputBlock();
}
void SetSourceStream(AudioNodeStream* aSource)
@ -294,23 +290,34 @@ public:
if (!mIsConnected) {
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
mSharedBuffers->Reset();
mSeenNonSilenceInput = false;
mInputWriteIndex = 0;
return;
}
// First, record our input buffer
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
// The input buffer is allocated lazily when non-null input is received.
if (!aInput.IsNull() && !mInputBuffer) {
mInputBuffer = ThreadSharedFloatArrayBufferList::
Create(mInputChannelCount, mBufferSize, fallible);
if (mInputBuffer && mInputWriteIndex) {
// Zero leading for null chunks that were skipped.
for (uint32_t i = 0; i < mInputChannelCount; ++i) {
float* channelData = mInputBuffer->GetDataForWrite(i);
PodZero(channelData, mInputWriteIndex);
}
}
}
// First, record our input buffer, if its allocation succeeded.
uint32_t inputChannelCount = mInputBuffer ? mInputBuffer->GetChannels() : 0;
for (uint32_t i = 0; i < inputChannelCount; ++i) {
float* writeData = mInputBuffer->GetDataForWrite(i) + mInputWriteIndex;
if (aInput.IsNull()) {
PodZero(mInputChannels[i] + mInputWriteIndex,
aInput.GetDuration());
PodZero(writeData, aInput.GetDuration());
} else {
mSeenNonSilenceInput = true;
MOZ_ASSERT(aInput.GetDuration() == WEBAUDIO_BLOCK_SIZE, "sanity check");
MOZ_ASSERT(aInput.mChannelData.Length() == mInputChannels.Length());
MOZ_ASSERT(aInput.mChannelData.Length() == inputChannelCount);
AudioBlockCopyChannelWithScale(static_cast<const float*>(aInput.mChannelData[i]),
aInput.mVolume,
mInputChannels[i] + mInputWriteIndex);
aInput.mVolume, writeData);
}
}
mInputWriteIndex += aInput.GetDuration();
@ -323,8 +330,6 @@ public:
if (mInputWriteIndex >= mBufferSize) {
SendBuffersToMainThread(aStream);
mInputWriteIndex -= mBufferSize;
mSeenNonSilenceInput = false;
AllocateInputBlock();
}
}
@ -335,10 +340,7 @@ public:
// - mDestination (probably)
size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
amount += mSharedBuffers->SizeOfIncludingThis(aMallocSizeOf);
amount += mInputChannels.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (size_t i = 0; i < mInputChannels.Length(); i++) {
amount += mInputChannels[i].SizeOfExcludingThis(aMallocSizeOf);
}
amount += mInputBuffer->SizeOfIncludingThis(aMallocSizeOf);
return amount;
}
@ -349,15 +351,6 @@ public:
}
private:
void AllocateInputBlock()
{
for (unsigned i = 0; i < mInputChannels.Length(); ++i) {
if (!mInputChannels[i]) {
mInputChannels[i] = new float[mBufferSize];
}
}
}
void SendBuffersToMainThread(AudioNodeStream* aStream)
{
MOZ_ASSERT(!NS_IsMainThread());
@ -376,19 +369,12 @@ private:
{
public:
Command(AudioNodeStream* aStream,
InputChannels& aInputChannels,
double aPlaybackTime,
bool aNullInput)
already_AddRefed<ThreadSharedFloatArrayBufferList> aInputBuffer,
double aPlaybackTime)
: mStream(aStream)
, mInputBuffer(aInputBuffer)
, mPlaybackTime(aPlaybackTime)
, mNullInput(aNullInput)
{
mInputChannels.SetLength(aInputChannels.Length());
if (!aNullInput) {
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
mInputChannels[i] = aInputChannels[i].forget();
}
}
}
NS_IMETHOD Run() override
@ -431,22 +417,19 @@ private:
return nullptr;
}
JSContext* cx = jsapi.cx();
uint32_t inputChannelCount = aNode->ChannelCount();
// Create the input buffer
nsRefPtr<AudioBuffer> inputBuffer;
if (!mNullInput) {
if (mInputBuffer) {
ErrorResult rv;
inputBuffer =
AudioBuffer::Create(context, mInputChannels.Length(),
aNode->BufferSize(),
context->SampleRate(), cx, rv);
AudioBuffer::Create(context, inputChannelCount,
aNode->BufferSize(), context->SampleRate(),
mInputBuffer.forget(), cx, rv);
if (rv.Failed()) {
return nullptr;
}
// Put the channel data inside it
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
inputBuffer->SetRawChannelContents(i, mInputChannels[i]);
}
}
// Ask content to produce data in the output buffer
@ -457,7 +440,7 @@ private:
nsRefPtr<AudioProcessingEvent> event =
new AudioProcessingEvent(aNode, nullptr, nullptr);
event->InitEvent(inputBuffer,
mInputChannels.Length(),
inputChannelCount,
context->StreamTimeToDOMTime(mPlaybackTime));
aNode->DispatchTrustedEvent(event);
@ -478,14 +461,12 @@ private:
}
private:
nsRefPtr<AudioNodeStream> mStream;
InputChannels mInputChannels;
nsRefPtr<ThreadSharedFloatArrayBufferList> mInputBuffer;
double mPlaybackTime;
bool mNullInput;
};
NS_DispatchToMainThread(new Command(aStream, mInputChannels,
playbackTime,
!mSeenNonSilenceInput));
NS_DispatchToMainThread(new Command(aStream, mInputBuffer.forget(),
playbackTime));
}
friend class ScriptProcessorNode;
@ -493,12 +474,12 @@ private:
nsAutoPtr<SharedBuffers> mSharedBuffers;
AudioNodeStream* mSource;
AudioNodeStream* mDestination;
InputChannels mInputChannels;
nsRefPtr<ThreadSharedFloatArrayBufferList> mInputBuffer;
const uint32_t mBufferSize;
const uint32_t mInputChannelCount;
// The write index into the current input buffer
uint32_t mInputWriteIndex;
bool mIsConnected = false;
bool mSeenNonSilenceInput;
};
ScriptProcessorNode::ScriptProcessorNode(AudioContext* aContext,

View File

@ -10,9 +10,10 @@ support-files =
[test_abort.html]
skip-if = toolkit == 'android' || toolkit == 'gonk' # bug 1037287
[test_audio_capture_error.html]
skip-if = toolkit == 'gonk' # Bug 1191270
[test_call_start_from_end_handler.html]
tags=capturestream
skip-if = ((android_version == '18' || buildapp == 'b2g') && debug) # bug 967606
skip-if = (android_version == '18' && debug) || buildapp == 'b2g' # bug 967606
[test_nested_eventloop.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog)
[test_preference_enable.html]

View File

@ -1725,9 +1725,11 @@ ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
if (NS_FAILED(loadInfo.mLoadResult)) {
scriptloader::ReportLoadError(aCx, loadInfo.mURL, loadInfo.mLoadResult,
false);
aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
scriptloader::ReportLoadError(aCx, loadInfo.mLoadResult);
// Top level scripts only!
if (mIsWorkerScript) {
aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
}
return true;
}
@ -1888,25 +1890,21 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx,
return getter->GetResult();
}
void ReportLoadError(JSContext* aCx, const nsAString& aURL,
nsresult aLoadResult, bool aIsMainThread)
void ReportLoadError(JSContext* aCx, nsresult aLoadResult)
{
NS_LossyConvertUTF16toASCII url(aURL);
switch (aLoadResult) {
case NS_BINDING_ABORTED:
// Canceled, don't set an exception.
break;
case NS_ERROR_MALFORMED_URI:
JS_ReportError(aCx, "Malformed script URI: %s", url.get());
break;
case NS_ERROR_FILE_NOT_FOUND:
case NS_ERROR_NOT_AVAILABLE:
JS_ReportError(aCx, "Script file not found: %s", url.get());
Throw(aCx, NS_ERROR_DOM_NETWORK_ERR);
break;
case NS_ERROR_MALFORMED_URI:
aLoadResult = NS_ERROR_DOM_SYNTAX_ERR;
// fall through
case NS_ERROR_DOM_SECURITY_ERR:
case NS_ERROR_DOM_SYNTAX_ERR:
Throw(aCx, aLoadResult);

View File

@ -47,8 +47,7 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx,
const nsAString& aScriptURL,
nsIChannel** aChannel);
void ReportLoadError(JSContext* aCx, const nsAString& aURL,
nsresult aLoadResult, bool aIsMainThread);
void ReportLoadError(JSContext* aCx, nsresult aLoadResult);
bool LoadMainScript(JSContext* aCx, const nsAString& aScriptURL,
WorkerScriptType aWorkerScriptType);

View File

@ -4153,7 +4153,8 @@ WorkerPrivate::Constructor(JSContext* aCx,
aIsChromeWorker, InheritLoadGroup,
aWorkerType, stackLoadInfo.ptr());
if (NS_FAILED(rv)) {
scriptloader::ReportLoadError(aCx, aScriptURL, rv, !parent);
// XXXkhuey this is weird, why throw again after setting an exception?
scriptloader::ReportLoadError(aCx, rv);
aRv.Throw(rv);
return nullptr;
}

View File

@ -6,6 +6,12 @@ function callByScript() {
importScripts(['importscript.sjs']);
importScripts(['importscript.sjs']);
try {
importScripts(['there-is-nothing-here.js']);
} catch (ex) {
// Importing a non-existent script should not abort the SW load, bug 1198982.
}
onmessage = function(e) {
self.clients.matchAll().then(function(res) {
if (!res.length) {

View File

@ -25,7 +25,7 @@ Tests of DOM Worker Threads
worker.onerror = function(event) {
is(event.target, worker);
is(event.message, "Error: Script file not found: nonexistent_worker.js");
is(event.message, ": NetworkError: A network error occurred.");
event.preventDefault();
SimpleTest.finish();
};

View File

@ -25,7 +25,7 @@ function test(script) {
worker.onerror = function(event) {
is(event.target, worker);
is(event.message, "Error: Script file not found: " + script);
is(event.message, ": NetworkError: A network error occurred.");
event.preventDefault();
runTests();
};

View File

@ -142,7 +142,7 @@ function printCommand(rest) {
// This is the real deal.
var cv = saveExcursion(
() => focusedFrame == null
? debuggeeGlobalWrapper.evalInGlobalWithBindings(rest, debuggeeValues)
? debuggeeGlobalWrapper.executeInGlobalWithBindings(rest, debuggeeValues)
: focusedFrame.evalWithBindings(rest, debuggeeValues));
if (cv === null) {
if (!dbg.enabled)

View File

@ -382,7 +382,7 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
the referent is not callable, throw a `TypeError`. This function
follows the [invocation function conventions][inv fr].
<code>evalInGlobal(<i>code</i>, [<i>options</i>])</code>
<code>executeInGlobal(<i>code</i>, [<i>options</i>])</code>
: If the referent is a global object, evaluate <i>code</i> in that global
environment, and return a [completion value][cv] describing how it completed.
<i>Code</i> is a string. All extant handler methods, breakpoints,
@ -393,15 +393,15 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
<i>Code</i> is interpreted as strict mode code when it contains a Use
Strict Directive.
If <i>code</i> is not strict mode code, then variable declarations in
<i>code</i> affect the referent global object. (In the terms used by the
ECMAScript specification, the `VariableEnvironment` of the execution
context for the eval code is the referent.)
This evaluation is semantically equivalent to executing statements at the
global level, not an indirect eval. Regardless of <i>code</i> being strict
mode code, variable declarations in <i>code</i> affect the referent global
object.
The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval].
<code>evalInGlobalWithBindings(<i>code</i>, <i>bindings</i>, [<i>options</i>])</code>
: Like `evalInGlobal`, but evaluate <i>code</i> using the referent as the
<code>executeInGlobalWithBindings(<i>code</i>, <i>bindings</i>, [<i>options</i>])</code>
: Like `executeInGlobal`, but evaluate <i>code</i> using the referent as the
variable object, but with a lexical environment extended with bindings
from the object <i>bindings</i>. For each own enumerable property of
<i>bindings</i> named <i>name</i> whose value is <i>value</i>, include a
@ -416,14 +416,12 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
debuggee values, and do so without mutating any existing debuggee
environment.
Note that, like `evalInGlobal`, if the code passed to
`evalInGlobalWithBindings` is not strict mode code, then any
declarations it contains affect the referent global object, even as
<i>code</i> is evaluated in an environment extended according to
<i>bindings</i>. (In the terms used by the ECMAScript specification, the
`VariableEnvironment` of the execution context for non-strict eval code
is the referent, and the <i>bindings</i> appear in a new declarative
environment, which is the eval code's `LexicalEnvironment`.)
Note that, like `executeInGlobal`, any declarations it contains affect the
referent global object, even as <i>code</i> is evaluated in an environment
extended according to <i>bindings</i>. (In the terms used by the ECMAScript
specification, the `VariableEnvironment` of the execution context for
<i>code</i> is the referent, and the <i>bindings</i> appear in a new
declarative environment, which is the eval code's `LexicalEnvironment`.)
The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval].

View File

@ -74,7 +74,8 @@ class MOZ_STACK_CLASS BytecodeCompiler
bool createScript(bool savedCallerFun = false);
bool createEmitter(SharedContext* sharedContext, HandleScript evalCaller = nullptr,
bool insideNonGlobalEval = false);
bool isInsideNonGlobalEval();
bool isEvalCompilationUnit();
bool isNonGlobalEvalCompilationUnit();
bool createParseContext(Maybe<ParseContext<FullParseHandler>>& parseContext,
SharedContext& globalsc, uint32_t blockScopeDepth = 0);
bool saveCallerFun(HandleScript evalCaller, ParseContext<FullParseHandler>& parseContext);
@ -82,7 +83,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
Maybe<ParseContext<FullParseHandler>>& parseContext,
SharedContext& globalsc);
bool handleParseFailure(const Directives& newDirectives);
bool prepareAndEmitTree(ParseNode** pn);
bool prepareAndEmitTree(ParseNode** pn, ParseContext<FullParseHandler>& pc);
bool checkArgumentsWithinEval(JSContext* cx, HandleFunction fun);
bool maybeCheckEvalFreeVariables(HandleScript evalCaller, HandleObject scopeChain,
ParseContext<FullParseHandler>& pc);
@ -275,10 +276,17 @@ BytecodeCompiler::createEmitter(SharedContext* sharedContext, HandleScript evalC
return emitter->init();
}
bool BytecodeCompiler::isInsideNonGlobalEval()
bool
BytecodeCompiler::isEvalCompilationUnit()
{
return enclosingStaticScope && enclosingStaticScope->is<StaticEvalObject>() &&
enclosingStaticScope->as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
return enclosingStaticScope && enclosingStaticScope->is<StaticEvalObject>();
}
bool
BytecodeCompiler::isNonGlobalEvalCompilationUnit()
{
return isEvalCompilationUnit() &&
enclosingStaticScope->as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
}
bool
@ -365,8 +373,13 @@ BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
}
bool
BytecodeCompiler::prepareAndEmitTree(ParseNode** ppn)
BytecodeCompiler::prepareAndEmitTree(ParseNode** ppn, ParseContext<FullParseHandler>& pc)
{
// Accumulate the maximum block scope depth, so that emitTree can assert
// when emitting JSOP_GETLOCAL that the local is indeed within the fixed
// part of the stack frame.
script->bindings.updateNumBlockScoped(pc.blockScopeDepth);
if (!FoldConstants(cx, ppn, parser.ptr()) ||
!NameFunctions(cx, *ppn) ||
!emitter->updateLocalsToFrameSlots() ||
@ -532,7 +545,7 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller
return nullptr;
GlobalSharedContext globalsc(cx, enclosingStaticScope, directives, options.extraWarningsOption);
if (!createEmitter(&globalsc, evalCaller, isInsideNonGlobalEval()))
if (!createEmitter(&globalsc, evalCaller, isNonGlobalEvalCompilationUnit()))
return nullptr;
// Syntax parsing may cause us to restart processing of top level
@ -545,42 +558,55 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller
if (savedCallerFun && !saveCallerFun(evalCaller, pc.ref()))
return nullptr;
bool canHaveDirectives = true;
for (;;) {
TokenKind tt;
if (!parser->tokenStream.peekToken(&tt, TokenStream::Operand))
return nullptr;
if (tt == TOK_EOF)
break;
parser->tokenStream.tell(&startPosition);
ParseNode* pn = parser->statement(YieldIsName, canHaveDirectives);
if (!pn) {
if (!handleStatementParseFailure(scopeChain, evalCaller, pc, globalsc))
// Global scripts are parsed incrementally, statement by statement.
//
// Eval scripts cannot be, as the block depth needs to be computed for all
// lexical bindings in the entire eval script.
if (isEvalCompilationUnit()) {
ParseNode* pn;
do {
pn = parser->evalBody();
if (!pn && !handleStatementParseFailure(scopeChain, evalCaller, pc, globalsc))
return nullptr;
} while (!pn);
pn = parser->statement(YieldIsName);
if (!pn) {
MOZ_ASSERT(!parser->hadAbortedSyntaxParse());
return nullptr;
}
}
// Accumulate the maximum block scope depth, so that emitTree can assert
// when emitting JSOP_GETLOCAL that the local is indeed within the fixed
// part of the stack frame.
script->bindings.updateNumBlockScoped(pc->blockScopeDepth);
if (canHaveDirectives) {
if (!parser->maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives))
return nullptr;
}
if (!prepareAndEmitTree(&pn))
if (!prepareAndEmitTree(&pn, *pc))
return nullptr;
parser->handler.freeTree(pn);
} else {
bool canHaveDirectives = true;
for (;;) {
TokenKind tt;
if (!parser->tokenStream.peekToken(&tt, TokenStream::Operand))
return nullptr;
if (tt == TOK_EOF)
break;
parser->tokenStream.tell(&startPosition);
ParseNode* pn = parser->statement(YieldIsName, canHaveDirectives);
if (!pn) {
if (!handleStatementParseFailure(scopeChain, evalCaller, pc, globalsc))
return nullptr;
pn = parser->statement(YieldIsName);
if (!pn) {
MOZ_ASSERT(!parser->hadAbortedSyntaxParse());
return nullptr;
}
}
if (canHaveDirectives) {
if (!parser->maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives))
return nullptr;
}
if (!prepareAndEmitTree(&pn, *pc))
return nullptr;
parser->handler.freeTree(pn);
}
}
if (!maybeCheckEvalFreeVariables(evalCaller, scopeChain, *pc) ||

View File

@ -580,7 +580,7 @@ class NonLocalExitScope {
}
~NonLocalExitScope() {
for (uint32_t n = savedScopeIndex; n < bce->blockScopeList.length(); n++)
bce->blockScopeList.recordEnd(n, bce->offset());
bce->blockScopeList.recordEnd(n, bce->offset(), bce->inPrologue());
bce->stackDepth = savedDepth;
}
@ -588,7 +588,7 @@ class NonLocalExitScope {
uint32_t scopeObjectIndex = bce->blockScopeList.findEnclosingScope(blockScopeIndex);
uint32_t parent = openScopeIndex;
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), bce->inPrologue(), parent))
return false;
openScopeIndex = bce->blockScopeList.length() - 1;
return true;
@ -924,7 +924,7 @@ BytecodeEmitter::enterNestedScope(StmtInfoBCE* stmt, ObjectBox* objbox, StmtType
parent = stmt->blockScopeIndex;
stmt->blockScopeIndex = blockScopeList.length();
if (!blockScopeList.append(scopeObjectIndex, offset(), parent))
if (!blockScopeList.append(scopeObjectIndex, offset(), inPrologue(), parent))
return false;
pushStatement(stmt, stmtType, offset());
@ -980,7 +980,7 @@ BytecodeEmitter::leaveNestedScope(StmtInfoBCE* stmt)
return false;
}
blockScopeList.recordEnd(blockScopeIndex, offset());
blockScopeList.recordEnd(blockScopeIndex, offset(), inPrologue());
return true;
}
@ -1345,6 +1345,23 @@ BytecodeEmitter::emitVarIncDec(ParseNode* pn)
return true;
}
bool
BytecodeEmitter::atBodyLevel() const
{
// 'eval' scripts are always under an invisible lexical scope, but
// since it is not syntactic, it should still be considered at body
// level.
if (sc->staticScope() && sc->staticScope()->is<StaticEvalObject>()) {
bool bl = !innermostStmt()->enclosing;
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
->as<StaticBlockObject>()
.maybeEnclosingEval() == sc->staticScope());
return bl;
}
return !innermostStmt() || sc->isModuleBox();
}
uint32_t
BytecodeEmitter::computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut)
{
@ -3023,6 +3040,16 @@ bool
BytecodeEmitter::enterBlockScope(StmtInfoBCE* stmtInfo, ObjectBox* objbox, JSOp initialValueOp,
unsigned alreadyPushed)
{
// This is so terrible. The eval body-level lexical scope needs to be
// emitted in the prologue so DEFFUN can pick up the right scope chain.
bool isEvalBodyLexicalScope = sc->staticScope() &&
sc->staticScope()->is<StaticEvalObject>() &&
!innermostStmt();
if (isEvalBodyLexicalScope) {
MOZ_ASSERT(code().length() == 0);
switchToPrologue();
}
// Initial values for block-scoped locals. Whether it is undefined or the
// JS_UNINITIALIZED_LEXICAL magic value depends on the context. The
// current way we emit for-in and for-of heads means its let bindings will
@ -3037,6 +3064,9 @@ BytecodeEmitter::enterBlockScope(StmtInfoBCE* stmtInfo, ObjectBox* objbox, JSOp
if (!initializeBlockScopedLocalsFromStack(blockObj))
return false;
if (isEvalBodyLexicalScope)
switchToMain();
return true;
}
@ -5886,7 +5916,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
if (sc->isGlobalContext()) {
MOZ_ASSERT(pn->pn_scopecoord.isFree());
MOZ_ASSERT(pn->getOp() == JSOP_NOP);
MOZ_ASSERT(!innermostStmt() || sc->isModuleBox());
MOZ_ASSERT(atBodyLevel());
switchToPrologue();
if (!emitIndex32(JSOP_DEFFUN, index))
return false;
@ -6365,16 +6395,10 @@ bool
BytecodeEmitter::emitStatementList(ParseNode* pn, ptrdiff_t top)
{
MOZ_ASSERT(pn->isArity(PN_LIST));
StmtInfoBCE stmtInfo(cx);
pushStatement(&stmtInfo, StmtType::BLOCK, top);
for (ParseNode* pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
if (!emitTree(pn2))
return false;
}
popStatement();
return true;
}
@ -8427,14 +8451,16 @@ CGTryNoteList::finish(TryNoteArray* array)
}
bool
CGBlockScopeList::append(uint32_t scopeObject, uint32_t offset, uint32_t parent)
CGBlockScopeList::append(uint32_t scopeObject, uint32_t offset, bool inPrologue,
uint32_t parent)
{
BlockScopeNote note;
CGBlockScopeNote note;
mozilla::PodZero(&note);
note.index = scopeObject;
note.start = offset;
note.parent = parent;
note.startInPrologue = inPrologue;
return list.append(note);
}
@ -8445,10 +8471,11 @@ CGBlockScopeList::findEnclosingScope(uint32_t index)
MOZ_ASSERT(index < length());
MOZ_ASSERT(list[index].index != BlockScopeNote::NoBlockScopeIndex);
DebugOnly<bool> inPrologue = list[index].startInPrologue;
DebugOnly<uint32_t> pos = list[index].start;
while (index--) {
MOZ_ASSERT(list[index].start <= pos);
if (list[index].length == 0) {
MOZ_ASSERT_IF(inPrologue == list[index].startInPrologue, list[index].start <= pos);
if (list[index].end == 0) {
// We are looking for the nearest enclosing live scope. If the
// scope contains POS, it should still be open, so its length should
// be zero.
@ -8456,7 +8483,7 @@ CGBlockScopeList::findEnclosingScope(uint32_t index)
} else {
// Conversely, if the length is not zero, it should not contain
// POS.
MOZ_ASSERT(list[index].start + list[index].length <= pos);
MOZ_ASSERT_IF(inPrologue == list[index].endInPrologue, list[index].end <= pos);
}
}
@ -8464,22 +8491,28 @@ CGBlockScopeList::findEnclosingScope(uint32_t index)
}
void
CGBlockScopeList::recordEnd(uint32_t index, uint32_t offset)
CGBlockScopeList::recordEnd(uint32_t index, uint32_t offset, bool inPrologue)
{
MOZ_ASSERT(index < length());
MOZ_ASSERT(offset >= list[index].start);
MOZ_ASSERT(list[index].length == 0);
list[index].length = offset - list[index].start;
list[index].end = offset;
list[index].endInPrologue = inPrologue;
}
void
CGBlockScopeList::finish(BlockScopeArray* array)
CGBlockScopeList::finish(BlockScopeArray* array, uint32_t prologueLength)
{
MOZ_ASSERT(length() == array->length);
for (unsigned i = 0; i < length(); i++)
for (unsigned i = 0; i < length(); i++) {
if (!list[i].startInPrologue)
list[i].start += prologueLength;
if (!list[i].endInPrologue)
list[i].end += prologueLength;
list[i].length = list[i].end - list[i].start;
array->vector[i] = list[i];
}
}
void

View File

@ -63,15 +63,28 @@ struct CGTryNoteList {
void finish(TryNoteArray* array);
};
struct CGBlockScopeNote : public BlockScopeNote
{
// The end offset. Used to compute the length; may need adjusting first if
// in the prologue.
uint32_t end;
// Is the start offset in the prologue?
bool startInPrologue;
// Is the end offset in the prologue?
bool endInPrologue;
};
struct CGBlockScopeList {
Vector<BlockScopeNote> list;
Vector<CGBlockScopeNote> list;
explicit CGBlockScopeList(ExclusiveContext* cx) : list(cx) {}
bool append(uint32_t scopeObject, uint32_t offset, uint32_t parent);
bool append(uint32_t scopeObject, uint32_t offset, bool inPrologue, uint32_t parent);
uint32_t findEnclosingScope(uint32_t index);
void recordEnd(uint32_t index, uint32_t offset);
void recordEnd(uint32_t index, uint32_t offset, bool inPrologue);
size_t length() const { return list.length(); }
void finish(BlockScopeArray* array);
void finish(BlockScopeArray* array, uint32_t prologueLength);
};
struct CGYieldOffsetList {
@ -228,6 +241,7 @@ struct BytecodeEmitter
return parser->blockScopes[blockid];
}
bool atBodyLevel() const;
uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut);
bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn);
bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op);
@ -267,6 +281,7 @@ struct BytecodeEmitter
ptrdiff_t prologueOffset() const { return prologue.code.end() - prologue.code.begin(); }
void switchToMain() { current = &main; }
void switchToPrologue() { current = &prologue; }
bool inPrologue() const { return current == &prologue; }
SrcNotesVector& notes() const { return current->notes; }
ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; }

View File

@ -885,6 +885,39 @@ Parser<SyntaxParseHandler>::standaloneModule(HandleModuleObject module)
return SyntaxParseHandler::NodeFailure;
}
template <>
ParseNode*
Parser<FullParseHandler>::evalBody()
{
AutoPushStmtInfoPC stmtInfo(*this, StmtType::BLOCK);
ParseNode* block = pushLexicalScope(stmtInfo);
if (!block)
return nullptr;
// For parsing declarations and directives, eval scripts must be
// considered body level despite having a lexical scope.
MOZ_ASSERT(pc->atBodyLevel());
ParseNode* body = statements(YieldIsName);
if (!body)
return nullptr;
// The statements() call above breaks on TOK_RC, so make sure we've
// reached EOF here.
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return nullptr;
if (tt != TOK_EOF) {
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
"expression", TokenKindToDesc(tt));
return nullptr;
}
block->pn_expr = body;
block->pn_pos = body->pn_pos;
return block;
}
template <>
ParseNode*
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
@ -4296,7 +4329,7 @@ Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& e
static StaticBlockObject*
CurrentLexicalStaticBlock(ParseContext<FullParseHandler>* pc)
{
return pc->atBodyLevel() ? nullptr :
return !pc->innermostStmt() ? nullptr :
&pc->innermostStmt()->staticScope->as<StaticBlockObject>();
}

View File

@ -302,6 +302,17 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
// if (cond) { function f3() { if (cond) { function f4() { } } } }
//
bool atBodyLevel() {
// 'eval' scripts are always under an invisible lexical scope, but
// since it is not syntactic, it should still be considered at body
// level.
if (sc->staticScope() && sc->staticScope()->is<StaticEvalObject>()) {
bool bl = !innermostStmt()->enclosing;
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
->template as<StaticBlockObject>()
.maybeEnclosingEval() == sc->staticScope());
return bl;
}
return !innermostStmt();
}
@ -560,6 +571,12 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
bool maybeParseDirective(Node list, Node pn, bool* cont);
// Parse the body of an eval. It is distinguished from global scripts in
// that in ES6, per 18.2.1.1 steps 9 and 10, all eval scripts are executed
// under a fresh lexical scope.
Node evalBody();
// Parse a module.
Node standaloneModule(Handle<ModuleObject*> module);
// Parse a function, given only its body. Used for the Function and

View File

@ -6,7 +6,7 @@ var dbg = new Debugger;
var log;
dbg.onEnterFrame = function (frame) {
log += 'e';
log += frame.environment.object.label;
log += frame.environment.parent.object.label;
};
var g1 = newGlobal();

View File

@ -1,4 +1,4 @@
// Bug 744731 - findScripts() finds active debugger evalInGlobal scripts.
// Bug 744731 - findScripts() finds active debugger executeInGlobal scripts.
var g = newGlobal();
var dbg = new Debugger;
@ -8,5 +8,5 @@ dbg.onDebuggerStatement = function (frame) {
hits++;
assertEq(dbg.findScripts().indexOf(dbg.getNewestFrame().script) !== -1, true);
};
gw.evalInGlobal("debugger;");
gw.executeInGlobal("debugger;");
assertEq(hits, 1);

View File

@ -7,9 +7,9 @@ dbg.onNewGlobalObject = function (global) {
var gw = dbg.addDebuggee(global);
gw.defineProperty('x', { value: -1 });
// Check that the global's magic lazy properties are working.
assertEq(gw.evalInGlobalWithBindings('Math.atan2(y,x)', { y: 0 }).return, Math.PI);
assertEq(gw.executeInGlobalWithBindings('Math.atan2(y,x)', { y: 0 }).return, Math.PI);
// Check that the global's prototype is hooked up.
assertEq(gw.evalInGlobalWithBindings('x.toString()', { x: gw }).return, "[object global]");
assertEq(gw.executeInGlobalWithBindings('x.toString()', { x: gw }).return, "[object global]");
};
newGlobal();

View File

@ -19,7 +19,7 @@ function check(code, expectedType, expectedCallee) {
assertEq(hits, 1);
}
check('debugger;', 'object', null);
check('debugger;', 'declarative', null);
check('with({}) { debugger; };', 'with', null);
check('let (x=1) { debugger; };', 'declarative', null);
@ -30,16 +30,13 @@ g.eval('function g() { h(); }');
g.eval('function h() { debugger; }');
check('g()', 'declarative', gw.makeDebuggeeValue(g.h));
// Strict direct eval gets its own environment.
// All evals get a lexical scope.
check('"use strict"; eval("debugger");', 'declarative', null);
g.eval('function j() { "use strict"; eval("debugger;"); }');
check('j()', 'declarative', null);
// Lenient direct eval shares eval's caller's environment,
// although this is a great evil.
check('eval("debugger");', 'object', null);
g.eval('function k() { eval("debugger;"); }');
check('k()', 'declarative', gw.makeDebuggeeValue(g.k));
// All evals get a lexical scope.
check('eval("debugger");', 'declarative', null);
g.eval('function m() { debugger; yield true; }');
check('m().next();', 'declarative', gw.makeDebuggeeValue(g.m));

View File

@ -4,7 +4,7 @@ var g = newGlobal();
var dbg = Debugger(g);
var log = '';
dbg.onDebuggerStatement = function (frame) {
log += frame.environment.getVariable("x") + frame.environment.getVariable("y");
log += frame.environment.parent.getVariable("x") + frame.environment.parent.getVariable("y");
};
g.eval("var x = 'a'; this.y = 'b'; debugger;");
assertEq(log, 'ab');

View File

@ -4,7 +4,7 @@ var g = newGlobal();
var dbg = Debugger(g);
var log = '';
dbg.onDebuggerStatement = function (frame) {
log += frame.environment.getVariable("x") + frame.environment.getVariable("y");
log += frame.environment.parent.getVariable("x") + frame.environment.parent.getVariable("y");
};
g.eval("Object.getPrototypeOf(this).x = 'a';\n" +
"Object.prototype.y = 'b';\n" +

View File

@ -5,7 +5,7 @@ var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
var hits = 0;
dbg.onDebuggerStatement = function (frame) {
var a = frame.environment.getVariable('Math');
var a = frame.environment.parent.getVariable('Math');
assertEq(a instanceof Debugger.Object, true);
var b = gw.getOwnPropertyDescriptor('Math').value;
assertEq(a, b);

View File

@ -5,7 +5,7 @@ var gw = dbg.addDebuggee(g);
var hits = 0;
dbg.onDebuggerStatement = function (frame) {
hits++;
assertEq(frame.environment.parent.getVariable('y'), true);
assertEq(frame.environment.parent.parent.getVariable('y'), true);
};
g.eval("var g;" +

View File

@ -8,7 +8,7 @@ var dbg = Debugger(g);
var hits = 0;
dbg.onDebuggerStatement = function (frame) {
assertThrowsInstanceOf(function () {
frame.environment.getVariable("x");
frame.environment.parent.getVariable("x");
}, Error);
hits++;
};

View File

@ -51,7 +51,7 @@ function test(sharedName, expectedHits, code) {
// the optimization or with the debugger exposing it, but that's not what we
// want to test here.)
test("q", 2, "var q = function (a) { h(); }; q(1); q(2);");
test("q", 2, "let q = function (a) { h(); }; q(1); q(2);");
test("a", 2, "q = function (a) { (function (b) { h(); a = b; })(2); h(); }; q(1);");
test("a", 2, "q = function (a) { h(); return function (b) { h(); a = b; }; }; q(1)(2);");
test("n", 3, "q = function (n) { for (var i = 0; i < n; i++) { let (j = i) { h(); } } }; q(3);");

View File

@ -45,7 +45,8 @@ function debuggerHandler(frame) {
assertEq(ke.inspectable, true);
assertEq(ke.getVariable('xk'), 'value of xk');
assertEq(ee.inspectable, true);
assertEq(ee.type, 'object');
assertEq(ee.type, 'declarative');
assertEq(ee.parent.type, 'object');
dbg.removeDebuggee(g2);
@ -54,7 +55,8 @@ function debuggerHandler(frame) {
assertEq(ke.inspectable, false);
assertThrowsInstanceOf(() => ke.getVariable('xk'), Error);
assertEq(ee.inspectable, true);
assertEq(ee.type, 'object');
assertEq(ee.type, 'declarative');
assertEq(ee.parent.type, 'object');
dbg.removeDebuggee(g1);

View File

@ -35,6 +35,6 @@ assertEq(log, 'd');
dbg.addDebuggee(g);
g.eval('function f() { }');
let env = gw.getOwnPropertyDescriptor('f').value.environment;
assertEq(env.type, 'object');
assertEq(env.type, 'declarative');
dbg.removeDebuggee(g);
check(env);

View File

@ -28,7 +28,7 @@ dbg.onEnterFrame = function (f) {
assertEq(funenv.callee, f.older.callee);
assertEq(funenv.names().indexOf("x") !== -1, true);
globalenv = funenv.parent;
globalenv = funenv.parent.parent;
assertEq(globalenv.optimizedOut, false);
assertEq(globalenv.inspectable, true);
assertEq(globalenv.type, "object");

View File

@ -3,7 +3,7 @@
var g = newGlobal();
var dbg = Debugger(g);
dbg.onDebuggerStatement = function (frame) {
frame.environment.setVariable("x", 2);
frame.environment.parent.setVariable("x", 2);
};
g.eval("var x = 1; debugger;");
assertEq(g.x, 2);

View File

@ -4,7 +4,7 @@ var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
dbg.onDebuggerStatement = function (frame) {
frame.environment.setVariable("x", gw);
frame.environment.parent.setVariable("x", gw);
};
g.eval("var x = 1; debugger;");
assertEq(g.x, g);

View File

@ -9,7 +9,7 @@ function test(code, expected) {
assertEq(actual, expected);
}
test("h();", 'object');
test("h();", 'declarative');
test("(function (s) { eval(s); })('var v = h();')", 'declarative');
test("(function (s) { h(); })();", 'declarative');
test("{let x = 1, y = 2; h();}", 'declarative');
@ -17,7 +17,7 @@ test("with({x: 1, y: 2}) h();", 'with');
test("(function (s) { with ({x: 1, y: 2}) h(); })();", 'with');
test("let (x = 1) { h(); }", 'declarative');
test("for (let x = 0; x < 1; x++) h();", 'declarative');
test("for (let x in h()) ;", 'object');
test("for (let x in h()) ;", 'declarative');
test("for (let x in {a:1}) h();", 'declarative');
test("try { throw new Error; } catch (x) { h(x) }", 'declarative');
test("'use strict'; eval('var z = 1; h();');", 'declarative');
@ -33,5 +33,5 @@ test("for (var x in (0 for (m in h()))) ;", 'declarative');
dbg.onDebuggerStatement = function (frame) {
assertEq(frame.eval("h(), 2 + 2;").return, 4);
}
test("debugger;", 'object');
test("debugger;", 'declarative');
test("(function f() { debugger; })();", 'declarative');

View File

@ -4,7 +4,7 @@ var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
g.h = function () {
var env = dbg.getNewestFrame().environment;
var env = dbg.getNewestFrame().environment.parent;
assertEq(env instanceof Debugger.Environment, true);
assertEq(env.object, gw);
assertEq(env.parent, null);

View File

@ -14,7 +14,7 @@ dbg.onDebuggerStatement = function handleDebugger(frame) {
};
g.eval("function g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
g.eval("var it = g();");
var rv = gw.evalInGlobal("it.next();");
var rv = gw.executeInGlobal("it.next();");
assertEq(rv.throw, "fit");
dbg.enabled = false;

View File

@ -13,7 +13,7 @@ dbg.onDebuggerStatement = function handleDebugger(frame) {
};
g.eval("function g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
g.eval("var it = g();");
assertEq(gw.evalInGlobal("it.next();"), null);
assertEq(gw.executeInGlobal("it.next();"), null);
dbg.enabled = false;
assertEq(g.it.next(), 1);

View File

@ -45,7 +45,7 @@ var frames = [];
// We start off the test via Debugger.Frame.prototype.eval, so if we end
// with a termination, we still catch it, instead of aborting the whole
// test. (Debugger.Object.prototype.evalInGlobal would simplify this...)
// test. (Debugger.Object.prototype.executeInGlobal would simplify this...)
var dbg0 = new Debugger(g);
dbg0.onEnterFrame = function handleOriginalEnter(frame) {
dbg0.log += '(';

View File

@ -13,7 +13,7 @@ dbg.onDebuggerStatement = function handleDebugger(frame) {
};
g.eval("function* g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
g.eval("var it = g();");
var rv = gw.evalInGlobal("it.next();");
var rv = gw.executeInGlobal("it.next();");
assertEq(rv.throw, "fit");
dbg.enabled = false;

View File

@ -15,7 +15,7 @@ dbg.onDebuggerStatement = function handleDebugger(frame) {
};
g.eval("function* g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
g.eval("var it = g();");
assertEq(gw.evalInGlobal("it.next();"), null);
assertEq(gw.executeInGlobal("it.next();"), null);
dbg.enabled = false;
assertIteratorNext(g.it, 1);

View File

@ -6,7 +6,7 @@ let dbg = new Debugger(g);
let sizeOfAM = byteSize(allocationMarker());
// Allocate a single allocation marker, and check that we can find it.
g.eval('let hold = allocationMarker();');
g.eval('var hold = allocationMarker();');
let census = dbg.memory.takeCensus({ breakdown: { by: 'objectClass' } });
assertEq(census.AllocationMarker.count, 1);
assertEq(census.AllocationMarker.bytes, sizeOfAM);

View File

@ -3,13 +3,13 @@
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
var arrw = gw.evalInGlobal("var arr = []; arr;").return;
var pushw = gw.evalInGlobal("var push = arr.push.bind(arr); push;").return;
var arrw = gw.executeInGlobal("var arr = []; arr;").return;
var pushw = gw.executeInGlobal("var push = arr.push.bind(arr); push;").return;
assertEq(pushw.isBoundFunction, true);
assertEq(pushw.boundThis, arrw);
assertEq(pushw.boundArguments.length, 0);
var arr2 = gw.evalInGlobal("var arr2 = []; arr2").return;
var arr2 = gw.executeInGlobal("var arr2 = []; arr2").return;
assertEq(pushw.call(arr2, "tuesday").return, 1);
g.eval("assertEq(arr.length, 1);");
g.eval("assertEq(arr[0], 'tuesday');");

View File

@ -6,19 +6,19 @@
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
var fw = gw.evalInGlobal("function f() {}; f").return;
var fw = gw.executeInGlobal("function f() {}; f").return;
assertEq(fw.isBoundFunction, false);
assertEq(fw.boundThis, undefined);
assertEq(fw.boundArguments, undefined);
assertEq(fw.boundTargetFunction, undefined);
var nw = gw.evalInGlobal("var n = Math.max; n").return;
var nw = gw.executeInGlobal("var n = Math.max; n").return;
assertEq(nw.isBoundFunction, false);
assertEq(nw.boundThis, undefined);
assertEq(nw.boundArguments, undefined);
assertEq(nw.boundTargetFunction, undefined);
var ow = gw.evalInGlobal("var o = {}; o").return;
var ow = gw.executeInGlobal("var o = {}; o").return;
assertEq(ow.isBoundFunction, false);
assertEq(ow.boundThis, undefined);
assertEq(ow.boundArguments, undefined);

View File

@ -5,7 +5,7 @@ var g = newGlobal();
var dbg = new Debugger();
var gw = dbg.addDebuggee(g);
var expr = "function f() { return this; }; var bf = f.bind(1, 2).bind(3, 4); bf";
var bfw = gw.evalInGlobal(expr).return;
var bfw = gw.executeInGlobal(expr).return;
assertEq(bfw.isBoundFunction, true);
assertEq(bfw.boundThis, 3);

View File

@ -1,20 +0,0 @@
// Debugger.Object.prototype.evalInGlobal argument validation
load(libdir + 'asserts.js');
var g = newGlobal();
var dbg = new Debugger();
var gw = dbg.addDebuggee(g);
var gobj = gw.makeDebuggeeValue(g.eval("({})"));
assertThrowsInstanceOf(function () { gw.evalInGlobal(); }, TypeError);
assertThrowsInstanceOf(function () { gw.evalInGlobal(10); }, TypeError);
assertThrowsInstanceOf(function () { gobj.evalInGlobal('42'); }, TypeError);
assertEq(gw.evalInGlobal('42').return, 42);
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings(); }, TypeError);
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings('42'); }, TypeError);
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings(10, 1729); }, TypeError);
assertThrowsInstanceOf(function () { gw.evalInGlobalWithBindings('42', 1729); }, TypeError);
assertThrowsInstanceOf(function () { gobj.evalInGlobalWithBindings('42', {}); }, TypeError);
assertEq(gw.evalInGlobalWithBindings('42', {}).return, 42);

View File

@ -1,19 +0,0 @@
// Debugger.Object.prototype.evalInGlobal: closures capturing the global
var g = newGlobal();
var h = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
var hw = dbg.addDebuggee(h);
g.x = "W H O K I L L";
h.x = "No Color";
var c1 = gw.evalInGlobal('(function () { return x; })').return;
var c2 = hw.evalInGlobal('(function () { return x; })').return;
var c3 = gw.evalInGlobalWithBindings('(function () { return x + y; })', { y:" In Rainbows" }).return;
var c4 = hw.evalInGlobalWithBindings('(function () { return x + y; })', { y:" In Rainbows" }).return;
assertEq(c1.call(null).return, "W H O K I L L");
assertEq(c2.call(null).return, "No Color");
assertEq(c3.call(null).return, "W H O K I L L In Rainbows");
assertEq(c4.call(null).return, "No Color In Rainbows");

View File

@ -1,55 +0,0 @@
// Debugger.Object.prototype.evalInGlobal: nested evals
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
assertEq(gw.evalInGlobal("eval('\"Awake\"');").return, "Awake");
// Evaluating non-strict-mode code uses the given global as its variable
// environment.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.evalInGlobal("eval('var x = \"A Brief History of Love\"');\n"
+ "var y = 'Merriweather Post Pavilion';"
+ "x;").return,
"A Brief History of Love");
assertEq(g.x, "A Brief History of Love");
assertEq(g.y, "Merriweather Post Pavilion");
// As above, but notice that we still create bindings on the global, even
// when we've interposed a new environment via 'withBindings'.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.evalInGlobalWithBindings("eval('var x = d1;'); var y = d2; x;",
{ d1: "A Brief History of Love",
d2: "Merriweather Post Pavilion" }).return,
"A Brief History of Love");
assertEq(g.x, "A Brief History of Love");
assertEq(g.y, "Merriweather Post Pavilion");
// Strict mode code variants of the above:
// Evaluating strict-mode code uses a fresh call object as its variable environment.
// Also, calls to eval from strict-mode code run the eval code in a fresh
// call object.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.evalInGlobal("\'use strict\';\n"
+ "eval('var x = \"A Brief History of Love\"');\n"
+ "var y = \"Merriweather Post Pavilion\";"
+ "x;").return,
"Swing Lo Magellan");
assertEq(g.x, "Swing Lo Magellan");
assertEq(g.y, "The Milk-Eyed Mender");
// Introducing a bindings object shouldn't change this behavior.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.evalInGlobalWithBindings("'use strict'; eval('var x = d1;'); var y = d2; x;",
{ d1: "A Brief History of Love",
d2: "Merriweather Post Pavilion" }).return,
"Swing Lo Magellan");
assertEq(g.x, "Swing Lo Magellan");
assertEq(g.y, "The Milk-Eyed Mender");

View File

@ -1,22 +0,0 @@
// Debugger.Object.prototype.evalInGlobal throws when asked to evaluate in a CCW of a global.
load(libdir + 'asserts.js');
var dbg = new Debugger();
var g1 = newGlobal();
var dg1 = dbg.addDebuggee(g1);
var g2 = newGlobal();
var dg2 = dbg.addDebuggee(g2);
// Generate a Debugger.Object viewing g2 from g1's compartment.
var dg1wg2 = dg1.makeDebuggeeValue(g2);
assertEq(dg1wg2.global, dg1);
assertEq(dg1wg2.unwrap(), dg2);
assertThrowsInstanceOf(function () { dg1wg2.evalInGlobal('1'); }, TypeError);
assertThrowsInstanceOf(function () { dg1wg2.evalInGlobalWithBindings('x', { x: 1 }); }, TypeError);
// These, however, should not throw.
assertEq(dg1wg2.unwrap().evalInGlobal('1729').return, 1729);
assertEq(dg1wg2.unwrap().evalInGlobalWithBindings('x', { x: 1729 }).return, 1729);

View File

@ -1,8 +0,0 @@
// Debugger.Object.prototype.evalInGlobal sets 'this' to the global.
var dbg = new Debugger;
var g1 = newGlobal();
var g1w = dbg.addDebuggee(g1);
assertEq(g1w.evalInGlobal('this').return, g1w);
assertEq(g1w.evalInGlobalWithBindings('this', { x:42 }).return, g1w);

View File

@ -1,9 +0,0 @@
// The frame created for evalInGlobal is never marked as a 'FUNCTION' frame.
(function () {
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
gw.evalInGlobalWithBindings("eval('Math')",{}).return
})();

View File

@ -1,4 +1,4 @@
// Debugger.Object.prototype.evalInGlobal basics
// Debugger.Object.prototype.executeInGlobal basics
var g = newGlobal();
var h = newGlobal();
@ -9,5 +9,5 @@ var hw = dbg.addDebuggee(h);
g.y = "Bitte Orca";
h.y = "Visiter";
var y = "W H O K I L L";
assertEq(gw.evalInGlobal('y').return, "Bitte Orca");
assertEq(hw.evalInGlobal('y').return, "Visiter");
assertEq(gw.executeInGlobal('y').return, "Bitte Orca");
assertEq(hw.executeInGlobal('y').return, "Visiter");

View File

@ -0,0 +1,20 @@
// Debugger.Object.prototype.executeInGlobal argument validation
load(libdir + 'asserts.js');
var g = newGlobal();
var dbg = new Debugger();
var gw = dbg.addDebuggee(g);
var gobj = gw.makeDebuggeeValue(g.eval("({})"));
assertThrowsInstanceOf(function () { gw.executeInGlobal(); }, TypeError);
assertThrowsInstanceOf(function () { gw.executeInGlobal(10); }, TypeError);
assertThrowsInstanceOf(function () { gobj.executeInGlobal('42'); }, TypeError);
assertEq(gw.executeInGlobal('42').return, 42);
assertThrowsInstanceOf(function () { gw.executeInGlobalWithBindings(); }, TypeError);
assertThrowsInstanceOf(function () { gw.executeInGlobalWithBindings('42'); }, TypeError);
assertThrowsInstanceOf(function () { gw.executeInGlobalWithBindings(10, 1729); }, TypeError);
assertThrowsInstanceOf(function () { gw.executeInGlobalWithBindings('42', 1729); }, TypeError);
assertThrowsInstanceOf(function () { gobj.executeInGlobalWithBindings('42', {}); }, TypeError);
assertEq(gw.executeInGlobalWithBindings('42', {}).return, 42);

View File

@ -0,0 +1,19 @@
// Debugger.Object.prototype.executeInGlobal: closures capturing the global
var g = newGlobal();
var h = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
var hw = dbg.addDebuggee(h);
g.x = "W H O K I L L";
h.x = "No Color";
var c1 = gw.executeInGlobal('(function () { return x; })').return;
var c2 = hw.executeInGlobal('(function () { return x; })').return;
var c3 = gw.executeInGlobalWithBindings('(function () { return x + y; })', { y:" In Rainbows" }).return;
var c4 = hw.executeInGlobalWithBindings('(function () { return x + y; })', { y:" In Rainbows" }).return;
assertEq(c1.call(null).return, "W H O K I L L");
assertEq(c2.call(null).return, "No Color");
assertEq(c3.call(null).return, "W H O K I L L In Rainbows");
assertEq(c4.call(null).return, "No Color In Rainbows");

View File

@ -0,0 +1,55 @@
// Debugger.Object.prototype.executeInGlobal: nested evals
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
assertEq(gw.executeInGlobal("eval('\"Awake\"');").return, "Awake");
// Evaluating non-strict-mode code uses the given global as its variable
// environment.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.executeInGlobal("eval('var x = \"A Brief History of Love\"');\n"
+ "var y = 'Merriweather Post Pavilion';"
+ "x;").return,
"A Brief History of Love");
assertEq(g.x, "A Brief History of Love");
assertEq(g.y, "Merriweather Post Pavilion");
// As above, but notice that we still create bindings on the global, even
// when we've interposed a new environment via 'withBindings'.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.executeInGlobalWithBindings("eval('var x = d1;'); var y = d2; x;",
{ d1: "A Brief History of Love",
d2: "Merriweather Post Pavilion" }).return,
"A Brief History of Love");
assertEq(g.x, "A Brief History of Love");
assertEq(g.y, "Merriweather Post Pavilion");
// Strict mode code variants of the above:
// Strict mode still lets us create bindings on the global as this is
// equivalent to executing statements at the global level. But note that
// setting strict mode means that nested evals get their own call objects.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.executeInGlobal("\'use strict\';\n"
+ "eval('var x = \"A Brief History of Love\"');\n"
+ "var y = \"Merriweather Post Pavilion\";"
+ "x;").return,
"Swing Lo Magellan");
assertEq(g.x, "Swing Lo Magellan");
assertEq(g.y, "Merriweather Post Pavilion");
// Introducing a bindings object shouldn't change this behavior.
g.x = "Swing Lo Magellan";
g.y = "The Milk-Eyed Mender";
assertEq(gw.executeInGlobalWithBindings("'use strict'; eval('var x = d1;'); var y = d2; x;",
{ d1: "A Brief History of Love",
d2: "Merriweather Post Pavilion" }).return,
"Swing Lo Magellan");
assertEq(g.x, "Swing Lo Magellan");
assertEq(g.y, "Merriweather Post Pavilion");

View File

@ -0,0 +1,22 @@
// Debugger.Object.prototype.executeInGlobal throws when asked to evaluate in a CCW of a global.
load(libdir + 'asserts.js');
var dbg = new Debugger();
var g1 = newGlobal();
var dg1 = dbg.addDebuggee(g1);
var g2 = newGlobal();
var dg2 = dbg.addDebuggee(g2);
// Generate a Debugger.Object viewing g2 from g1's compartment.
var dg1wg2 = dg1.makeDebuggeeValue(g2);
assertEq(dg1wg2.global, dg1);
assertEq(dg1wg2.unwrap(), dg2);
assertThrowsInstanceOf(function () { dg1wg2.executeInGlobal('1'); }, TypeError);
assertThrowsInstanceOf(function () { dg1wg2.executeInGlobalWithBindings('x', { x: 1 }); }, TypeError);
// These, however, should not throw.
assertEq(dg1wg2.unwrap().executeInGlobal('1729').return, 1729);
assertEq(dg1wg2.unwrap().executeInGlobalWithBindings('x', { x: 1729 }).return, 1729);

View File

@ -0,0 +1,8 @@
// Debugger.Object.prototype.executeInGlobal sets 'this' to the global.
var dbg = new Debugger;
var g1 = newGlobal();
var g1w = dbg.addDebuggee(g1);
assertEq(g1w.executeInGlobal('this').return, g1w);
assertEq(g1w.executeInGlobalWithBindings('this', { x:42 }).return, g1w);

View File

@ -1,4 +1,4 @@
// evalInGlobal correctly handles optional custom url option
// executeInGlobal correctly handles optional custom url option
var g = newGlobal();
var dbg = new Debugger(g);
var debuggee = dbg.getDebuggees()[0];
@ -11,7 +11,7 @@ function testUrl (options, expected) {
assertEq(script.url, expected);
count--;
};
debuggee.evalInGlobal("", options);
debuggee.executeInGlobal("", options);
}

View File

@ -1,4 +1,4 @@
// evalInGlobal correctly handles optional lineNumber option
// executeInGlobal correctly handles optional lineNumber option
var g = newGlobal();
var dbg = new Debugger(g);
var debuggee = dbg.getDebuggees()[0];
@ -11,7 +11,7 @@ function testLineNumber (options, expected) {
assertEq(script.startLine, expected);
count--;
};
debuggee.evalInGlobal("", options);
debuggee.executeInGlobal("", options);
}

View File

@ -0,0 +1,9 @@
// The frame created for executeInGlobal is never marked as a 'FUNCTION' frame.
(function () {
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
gw.executeInGlobalWithBindings("eval('Math')",{}).return
})();

View File

@ -0,0 +1,13 @@
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
// executeInGlobal should be able to introduce and persist lexical bindings.
assertEq(gw.executeInGlobal(`let x = 42; x;`).return, 42);
assertEq(gw.executeInGlobal(`x;`).return, 42);
// By contrast, Debugger.Frame.eval is like direct eval, and shouldn't be able
// to introduce new lexical bindings.
dbg.onDebuggerStatement = function (frame) { frame.eval(`let y = 84;`); };
g.eval(`debugger;`);
assertEq(!!gw.executeInGlobal(`y;`).throw, true);

View File

@ -1,8 +1,8 @@
// obj.getOwnPropertyDescriptor works on global objects.
var g = newGlobal();
g.eval("var v; const k = 42;");
this.eval("var v; const k = 42;");
g.eval("var v;");
this.eval("var v;");
var dbg = Debugger();
var obj = dbg.addDebuggee(g);
@ -20,4 +20,3 @@ function test(name) {
test("Infinity");
test("v");
test("k");

View File

@ -7,7 +7,7 @@ var hits = 0;
function checkIsArrow(shouldBe, expr) {
print(expr);
assertEq(gDO.evalInGlobal(expr).return.isArrowFunction, shouldBe);
assertEq(gDO.executeInGlobal(expr).return.isArrowFunction, shouldBe);
}
checkIsArrow(true, '() => { }');

View File

@ -87,7 +87,7 @@ dbg.onDebuggerStatement = function (frame) {
introducer = frame.script;
};
log = '';
var fDO = gDO.evalInGlobal('debugger; Function("origami;")', { lineNumber: 1685 }).return;
var fDO = gDO.executeInGlobal('debugger; Function("origami;")', { lineNumber: 1685 }).return;
var source = fDO.script.source;
assertEq(log, 'F2');
assertEq(source.introductionScript, introducer);

View File

@ -100,21 +100,21 @@ log = '';
g.eval('debugger;');
assertEq(log, 'oi');
// Debugger.Object.evalInGlobal
// Debugger.Object.executeInGlobal
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.source.introductionType, "debugger eval");
};
log = '';
gDO.evalInGlobal('debugger;');
gDO.executeInGlobal('debugger;');
assertEq(log, 'd');
// Debugger.Object.evalInGlobalWithBindings
// Debugger.Object.executeInGlobalWithBindings
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.source.introductionType, "debugger eval");
};
log = '';
gDO.evalInGlobalWithBindings('debugger;', { x: 42 });
gDO.executeInGlobalWithBindings('debugger;', { x: 42 });
assertEq(log, 'd');

View File

@ -2,6 +2,6 @@ var g = newGlobal();
var dbg = new Debugger();
var gw = dbg.addDebuggee(g);
dbg.onDebuggerStatement = function (f) {
gw.evalInGlobal("eval('var x = \"A Brief History of Love\"');\n")
gw.executeInGlobal("eval('var x = \"A Brief History of Love\"');\n")
};
g.eval('debugger');

View File

@ -361,7 +361,7 @@ jit::CanEnterBaselineMethod(JSContext* cx, RunState& state)
} else {
MOZ_ASSERT(state.isExecute());
ExecuteType type = state.asExecute()->type();
if (type == EXECUTE_DEBUG || type == EXECUTE_DEBUG_GLOBAL) {
if (type == EXECUTE_DEBUG) {
JitSpew(JitSpew_BaselineAbort, "debugger frame");
return Method_CantCompile;
}

View File

@ -3375,11 +3375,9 @@ IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope)
// If the script is an indirect eval that is immediately scoped under
// the global, we can clone it.
if (scope->is<StaticEvalObject>() &&
!scope->as<StaticEvalObject>().isDirect() &&
!scope->as<StaticEvalObject>().isStrict())
{
return true;
if (scope->is<StaticBlockObject>()) {
if (StaticEvalObject* staticEval = scope->as<StaticBlockObject>().maybeEnclosingEval())
return !staticEval->isDirect();
}
// Any other enclosing static scope (e.g., function, block) cannot be

View File

@ -2842,7 +2842,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
if (bce->tryNoteList.length() != 0)
bce->tryNoteList.finish(script->trynotes());
if (bce->blockScopeList.length() != 0)
bce->blockScopeList.finish(script->blockScopes());
bce->blockScopeList.finish(script->blockScopes(), prologueLength);
script->strict_ = bce->sc->strict();
script->explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
script->bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
@ -3772,10 +3772,7 @@ JSScript::getStaticBlockScope(jsbytecode* pc)
if (!hasBlockScopes())
return nullptr;
if (pc < main())
return nullptr;
size_t offset = pc - main();
size_t offset = pc - code();
BlockScopeArray* scopes = blockScopes();
NestedScopeObject* blockChain = nullptr;

View File

@ -1,3 +1,8 @@
// |reftest| skip-if(!xulRuntime.shell)
//
// The above skip-if is because global lexicals aren't yet implemented. Remove
// that and the |evaluate| call below once they are.
//
// A class statement creates a mutable lexical outer binding.
var test = `
@ -13,8 +18,6 @@ assertEq(Foo, 5);
assertEq(foo, 4);
}
var ieval = eval;
{
class PermanentBinding { constructor() { } }
delete PermanentBinding;
@ -24,10 +27,10 @@ var ieval = eval;
{
try {
ieval(\`class x { constructor () { } }
throw new Error("FAIL");
class y { constructor () { } }
\`);
evaluate(\`class x { constructor () { } }
throw new Error("FAIL");
class y { constructor () { } }
\`);
} catch (e if e instanceof Error) { }
assertEq(typeof x, "function");
assertEq(y, undefined, "Congrats, you fixed top-level lexical scoping! " +

View File

@ -0,0 +1,45 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = "eval-has-lexical-environment.js"
//-----------------------------------------------------------------------------
var BUGNUMBER = 1193583;
var summary =
"Eval always has a lexical environment";
/**************
* BEGIN TEST *
**************/
eval(`
let foo = 42;
const kay = foo;
var bar = 84;
function f() {
return foo + kay;
}
`);
(1, eval)(`
let foo2 = 42;
const kay2 = foo2;
`);
// Lexical declarations should not have escaped eval.
assertEq(typeof foo, "undefined");
assertEq(typeof kay, "undefined");
assertEq(typeof foo2, "undefined");
assertEq(typeof kay2, "undefined");
// Eval'd functions can close over lexical bindings.
assertEq(f(), 84);
// Var can escape direct eval.
assertEq(bar, 84);
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -1,4 +1,4 @@
// |reftest| fails-if(Function("try{Function('let\x20x=5;');return(1,eval)('let\x20x=3;\\'x\\'\x20in\x20this');}catch(e){return(true);}")()) -- needs bug 589199 fix (top-level let not same as var); please convert AssertEq to assertEq when removing this fails-if, too
// |reftest| fails-if(Function("try{Function('let\x20x=5;');return(evaluate)('let\x20x=3;\\'x\\'\x20in\x20this');}catch(e){return(true);}")()) -- needs bug 589199 fix (top-level let not same as var); please convert AssertEq to assertEq when removing this fails-if, too
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/

View File

@ -1,134 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
/*
*
* Date: 10 Jan 2002
* SUMMARY: Reassignment to a const is NOT an error per ECMA
* See http://bugzilla.mozilla.org/show_bug.cgi?id=103602
*
* ------- Additional Comment #4 From Brendan Eich 2002-01-10 15:30 -------
*
* That's per ECMA (don't blame me, I fought for what Netscape always did:
* throw an error [could be a catchable exception since 1.3]).
* Readonly properties, when set by assignment, are not changed, but no error
* or exception is thrown. The value of the assignment expression is the value
* of the r.h.s.
*
* If you want a *strict* warning, pls change the summary of this bug
* to say so.
*/
//-----------------------------------------------------------------------------
var UBound = 0;
var BUGNUMBER = 103602;
var summary = 'Reassignment to a const is NOT an error per ECMA';
var status = '';
var statusitems = [];
var actual = '';
var actualvalues = [];
var expect= '';
var expectedvalues = [];
var cnFAIL_1 = 'Redeclaration of a const FAILED to cause an error';
var cnFAIL_2 = 'Reassigning to a const caused an ERROR! It should not!!!';
var sEval = '';
/*
* Not every implementation supports const (a non-ECMA extension)
* For example, Rhino does not; it would generate a complile-time error.
* So we have to hide this so it will be detected at run-time instead.
*/
try
{
sEval = 'const one = 1';
eval(sEval);
}
catch(e)
{
quit(); // if const is not supported, this testcase is over -
}
status = inSection(1);
try
{
/*
* Redeclaration of const should be a compile-time error.
* Hide so it will be detected at run-time.
*/
sEval = 'const one = 2;';
eval(sEval);
expect = ''; // we shouldn't reach this line
actual = cnFAIL_1;
addThis();
}
catch(e)
{
// good - we should be here.
actual = expect;
addThis();
}
status = inSection(2);
try
{
/*
* Reassignment to a const should be NOT be an error, per ECMA.
*/
one = 2;
actual = expect; // good: no error was generated
addThis();
// although no error, the assignment should have been ignored -
status = inSection(3);
actual = one;
expect = 1;
addThis();
// the value of the EXPRESSION, however, is the value of the r.h.s. -
status = inSection(4);
actual = (one = 2);
expect = 2;
addThis();
}
catch(e)
{
// BAD - we shouldn't be here
expect = '';
actual = cnFAIL_2;
addThis();
}
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
function addThis()
{
statusitems[UBound] = status;
actualvalues[UBound] = actual;
expectedvalues[UBound] = expect;
UBound++;
}
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
for (var i = 0; i < UBound; i++)
{
reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]);
}
exitFunc ('test');
}

View File

@ -1,37 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributor: Jason Orendorff
*/
//-----------------------------------------------------------------------------
var BUGNUMBER = 452498;
var summary = 'TM: upvar2 regression tests';
var actual = '';
var expect = '';
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
// ------- Comment #54 From Jason Orendorff
// Assertion failure: cg->flags & TCF_IN_FUNCTION
// at ../jsemit.cpp:1991
//
function f() { var x; eval("let x, x;"); }
f();
reportCompare(expect, actual, summary);
exitFunc ('test');
}

View File

@ -24,7 +24,7 @@ try {
assertEq(actual, expect);
s += s.slice(0, -2);
s += s.slice(0, -4);
try {
eval(s);

View File

@ -6344,9 +6344,21 @@ EvaluateInEnv(JSContext* cx, Handle<Env*> env, HandleValue thisv, AbstractFrameP
Rooted<ScopeObject*> enclosingStaticScope(cx);
if (!env->is<GlobalObject>())
enclosingStaticScope = StaticNonSyntacticScopeObjects::create(cx, nullptr);
Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, enclosingStaticScope));
if (!staticScope)
return false;
// Do not consider executeInGlobal{WithBindings} as an eval, but instead
// as executing a series of statements at the global level. This is to
// circumvent the fresh lexical scope that all eval have, so that the
// users of executeInGlobal, like the web console, may add new bindings to
// the global scope.
Rooted<ScopeObject*> staticScope(cx);
if (frame) {
staticScope = StaticEvalObject::create(cx, enclosingStaticScope);
if (!staticScope)
return false;
} else {
staticScope = enclosingStaticScope;
}
CompileOptions options(cx);
options.setIsRunOnce(true)
.setForEval(true)
@ -6363,11 +6375,14 @@ EvaluateInEnv(JSContext* cx, Handle<Env*> env, HandleValue thisv, AbstractFrameP
if (!script)
return false;
if (script->strict())
staticScope->setStrict();
// Again, executeInGlobal is not considered eval.
if (frame) {
if (script->strict())
staticScope->as<StaticEvalObject>().setStrict();
script->setActiveEval();
}
script->setActiveEval();
ExecuteType type = !frame ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG;
ExecuteType type = !frame ? EXECUTE_GLOBAL : EXECUTE_DEBUG;
return ExecuteKernel(cx, script, *env, thisv, NullValue(), type, frame, rval.address());
}
@ -7419,29 +7434,30 @@ RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent)
}
static bool
DebuggerObject_evalInGlobal(JSContext* cx, unsigned argc, Value* vp)
DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp)
{
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobal", args, dbg, referent);
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.evalInGlobal", 1))
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "executeInGlobal", args, dbg, referent);
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal", 1))
return false;
if (!RequireGlobalObject(cx, args.thisv(), referent))
return false;
return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobal",
return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobal",
args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue,
args.get(1), args.rval(), dbg, referent, nullptr);
}
static bool
DebuggerObject_evalInGlobalWithBindings(JSContext* cx, unsigned argc, Value* vp)
DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* vp)
{
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobalWithBindings", args, dbg, referent);
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.evalInGlobalWithBindings", 2))
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "executeInGlobalWithBindings", args, dbg,
referent);
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2))
return false;
if (!RequireGlobalObject(cx, args.thisv(), referent))
return false;
return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobalWithBindings",
return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobalWithBindings",
args[0], EvalHasExtraBindings, args[1], args.get(2),
args.rval(), dbg, referent, nullptr);
}
@ -7521,8 +7537,8 @@ static const JSFunctionSpec DebuggerObject_methods[] = {
JS_FN("apply", DebuggerObject_apply, 0, 0),
JS_FN("call", DebuggerObject_call, 0, 0),
JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0),
JS_FN("evalInGlobal", DebuggerObject_evalInGlobal, 1, 0),
JS_FN("evalInGlobalWithBindings", DebuggerObject_evalInGlobalWithBindings, 2, 0),
JS_FN("executeInGlobal", DebuggerObject_executeInGlobal, 1, 0),
JS_FN("executeInGlobalWithBindings", DebuggerObject_executeInGlobalWithBindings, 2, 0),
JS_FN("unwrap", DebuggerObject_unwrap, 0, 0),
JS_FN("unsafeDereference", DebuggerObject_unsafeDereference, 0, 0),
JS_FS_END

View File

@ -438,6 +438,7 @@ class StaticEvalObject : public ScopeObject
// Indirect evals terminate in the global at run time, and has no static
// enclosing scope.
bool isDirect() const {
MOZ_ASSERT_IF(!getReservedSlot(SCOPE_CHAIN_SLOT).isObject(), !isStrict());
return getReservedSlot(SCOPE_CHAIN_SLOT).isObject();
}
};
@ -643,6 +644,14 @@ class StaticBlockObject : public BlockObject
*/
inline StaticBlockObject* enclosingBlock() const;
StaticEvalObject* maybeEnclosingEval() const {
if (JSObject* enclosing = enclosingStaticScope()) {
if (enclosing->is<StaticEvalObject>())
return &enclosing->as<StaticEvalObject>();
}
return nullptr;
}
uint32_t localOffset() {
return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32();
}

View File

@ -253,20 +253,6 @@ InterpreterFrame::epilogue(JSContext* cx)
DebugScopes::onPopStrictEvalScope(this);
} else if (isDirectEvalFrame()) {
MOZ_ASSERT_IF(isDebuggerEvalFrame(), !IsSyntacticScope(scopeChain()));
} else {
/*
* Debugger.Object.prototype.evalInGlobal creates indirect eval
* frames scoped to the given global;
* Debugger.Object.prototype.evalInGlobalWithBindings creates
* indirect eval frames scoped to an object carrying the introduced
* bindings.
*/
if (isDebuggerEvalFrame()) {
MOZ_ASSERT(scopeChain()->is<GlobalObject>() ||
scopeChain()->enclosingScope()->is<GlobalObject>());
} else {
MOZ_ASSERT(scopeChain()->is<GlobalObject>());
}
}
return;
}

View File

@ -283,8 +283,7 @@ enum ExecuteType {
EXECUTE_MODULE = 0x4, /* == InterpreterFrame::GLOBAL */
EXECUTE_DIRECT_EVAL = 0x8, /* == InterpreterFrame::EVAL */
EXECUTE_INDIRECT_EVAL = 0x9, /* == InterpreterFrame::GLOBAL | EVAL */
EXECUTE_DEBUG = 0x18, /* == InterpreterFrame::EVAL | DEBUGGER */
EXECUTE_DEBUG_GLOBAL = 0x19 /* == InterpreterFrame::EVAL | DEBUGGER | GLOBAL */
EXECUTE_DEBUG = 0x18, /* == InterpreterFrame::EVAL | DEBUGGER_EVAL */
};
/*****************************************************************************/

View File

@ -85,7 +85,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=937317
// The incumbent script override doesn't take effect if the callback is scripted.
let d = Promise.defer();
iwin.wrappedJSObject.timeoutFun2 = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global setTimeout of scripted function"));
iwin.eval('let timeoutFun2Wrapper = function() { timeoutFun2(); }');
iwin.eval('var timeoutFun2Wrapper = function() { timeoutFun2(); }');
setTimeout(iwin.wrappedJSObject.timeoutFun2Wrapper, 0);
return d.promise;
}).then(function() {

View File

@ -31,8 +31,8 @@ function run_test() {
debuggeree.eval(
`
const dbg = new Debugger(this.debuggee);
let fired = 0;
var dbg = new Debugger(this.debuggee);
var fired = 0;
dbg.memory.onGarbageCollection = _ => {
fired++;
gc(this);

View File

@ -166,8 +166,7 @@ public:
nsDocViewerSelectionListener()
: mDocViewer(nullptr)
, mGotSelectionState(false)
, mSelectionWasCollapsed(false)
, mSelectionWasCollapsed(true)
{
}
@ -177,8 +176,7 @@ protected:
virtual ~nsDocViewerSelectionListener() {}
nsDocumentViewer* mDocViewer;
bool mGotSelectionState;
nsDocumentViewer* mDocViewer;
bool mSelectionWasCollapsed;
};
@ -3572,13 +3570,13 @@ NS_IMETHODIMP nsDocViewerSelectionListener::NotifySelectionChanged(nsIDOMDocumen
bool selectionCollapsed;
selection->GetIsCollapsed(&selectionCollapsed);
// we only call UpdateCommands when the selection changes from collapsed
// to non-collapsed or vice versa. We might need another update string
// for simple selection changes, but that would be expenseive.
if (!mGotSelectionState || mSelectionWasCollapsed != selectionCollapsed)
// We only call UpdateCommands when the selection changes from collapsed to
// non-collapsed or vice versa, however we skip the initializing collapse. We
// might need another update string for simple selection changes, but that
// would be expenseive.
if (mSelectionWasCollapsed != selectionCollapsed)
{
domWindow->UpdateCommands(NS_LITERAL_STRING("select"), selection, aReason);
mGotSelectionState = true;
mSelectionWasCollapsed = selectionCollapsed;
}

View File

@ -147,6 +147,6 @@ fuzzy-if(d2d,47,400) == linear-onestopposition-1.html linear-onestopposition-1-r
== repeating-radial-onestopposition-1c.html orange-square.html
== bug-916535-background-repeat-linear.html bug-916535-background-repeat-linear-ref.html
fuzzy(1,800000) == large-gradient-1.html large-gradient-1-ref.html
== large-gradient-2.html large-gradient-2-ref.html
fuzzy-if(Android,4,1) == large-gradient-2.html large-gradient-2-ref.html # Bug 1182082
fails-if(browserIsRemote&&!B2G) fuzzy-if(!browserIsRemote||B2G,1,800000) == large-gradient-3.html large-gradient-3-ref.html
== large-gradient-4.html large-gradient-4-ref.html

View File

@ -1,4 +1,4 @@
skip-if(B2G||Mulet) == mq_print_height.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(Android,8,454) skip-if(B2G||Mulet) == mq_print_height.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop, bug 1178697
skip-if(B2G||Mulet) == mq_print_deviceheight.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) == mq_print_width.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) == mq_print_minwidth.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop

View File

@ -7,4 +7,4 @@
== box-sizing-content-box-003.xht box-sizing-content-box-003-ref.xht
random-if(Android) skip-if((B2G&&browserIsRemote)||Mulet) == box-sizing-replaced-001.xht box-sizing-replaced-001-ref.xht #bug 982547 # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(Android,27,874) random-if((B2G&&browserIsRemote)||Mulet) == box-sizing-replaced-002.xht box-sizing-replaced-002-ref.xht # Bug 1128229 # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(Android,27,869) random-if((B2G&&browserIsRemote)||Mulet) == box-sizing-replaced-003.xht box-sizing-replaced-003-ref.xht # Bug 1128229 # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(Android,27,925) random-if((B2G&&browserIsRemote)||Mulet) == box-sizing-replaced-003.xht box-sizing-replaced-003-ref.xht # Bug 1128229 # Initial mulet triage: parity with B2G/B2G Desktop

View File

@ -1,5 +0,0 @@
[WorkerGlobalScope_importScripts_NetworkErr.htm]
type: testharness
[ importScripts() with non-existent script file ]
expected: FAIL

View File

@ -1,5 +0,0 @@
[uncontrolled-page.https.html]
type: testharness
[Fetch events should not go through uncontrolled page.]
expected: FAIL

View File

@ -26,7 +26,7 @@ async_test(function(t) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
return fetch_url('/service-worker/resources/simple.txt');
return fetch_url('resources/simple.txt');
})
.then(function(text) {
assert_equals(text, 'a simple text file\n');

View File

@ -975,7 +975,7 @@ function run_float_tests(library, t, name, size) {
function test_roundtrip(t, val)
{
let f1 = t(val);
eval("let f2 = " + f1.toSource());
eval("var f2 = " + f1.toSource());
do_check_eq(f1.value, f2.value);
}
vals = [Infinity, -Infinity, -0, 0, 1, -1, 1/3, -1/3, 1/4, -1/4,
@ -1582,7 +1582,7 @@ function run_StructType_tests() {
do_check_eq(s.toSource(), "s_t(4, {\"a\": 7, \"b\": 2}, 10)");
do_check_eq(s.toSource(), s.toString());
eval("let s2 = " + s.toSource());
eval("var s2 = " + s.toSource());
do_check_true(s2.constructor === s_t);
do_check_eq(s.b.b, s2.b.b);
@ -2060,7 +2060,7 @@ function run_ArrayType_tests() {
c.value = [1, 2, 3, 4, 5, 6];
do_check_eq(c.toSource(), "ctypes.int32_t.array(6)([1, 2, 3, 4, 5, 6])");
do_check_eq(c.toSource(), c.toString());
eval("let c2 = " + c.toSource());
eval("var c2 = " + c.toSource());
do_check_eq(c2.constructor.name, "int32_t[6]");
do_check_eq(c2.length, 6);
do_check_eq(c2[3], c[3]);

View File

@ -1060,7 +1060,7 @@ WebConsoleActor.prototype =
* provide the "bindObjectActor" mechanism: the Web Console tells the
* ObjectActor ID for which it desires to evaluate an expression. The
* Debugger.Object pointed at by the actor ID is bound such that it is
* available during expression evaluation (evalInGlobalWithBindings()).
* available during expression evaluation (executeInGlobalWithBindings()).
*
* Example:
* _self['foobar'] = 'test'
@ -1218,7 +1218,7 @@ WebConsoleActor.prototype =
result = frame.evalWithBindings(aString, bindings, evalOptions);
}
else {
result = dbgWindow.evalInGlobalWithBindings(aString, bindings, evalOptions);
result = dbgWindow.executeInGlobalWithBindings(aString, bindings, evalOptions);
}
let helperResult = helpers.helperResult;

View File

@ -37,7 +37,7 @@ skip-if = buildapp == 'mulet'
[test_director.html]
[test_director_connectToChild.html]
skip-if = buildapp == 'mulet'
[test_evalInGlobal-outerized_this.html]
[test_executeInGlobal-outerized_this.html]
[test_framerate_01.html]
skip-if = buildapp == 'mulet'
[test_framerate_02.html]

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