зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team
This commit is contained in:
Коммит
a48372de5d
|
@ -15,7 +15,7 @@ EXPORTS += [
|
|||
'ia2AccessibleValue.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'ia2Accessible.cpp',
|
||||
'ia2AccessibleAction.cpp',
|
||||
'ia2AccessibleComponent.cpp',
|
||||
|
@ -24,12 +24,17 @@ SOURCES += [
|
|||
'ia2AccessibleHypertext.cpp',
|
||||
'ia2AccessibleImage.cpp',
|
||||
'ia2AccessibleRelation.cpp',
|
||||
'ia2AccessibleTable.cpp',
|
||||
'ia2AccessibleTableCell.cpp',
|
||||
'ia2AccessibleText.cpp',
|
||||
'ia2AccessibleValue.cpp',
|
||||
]
|
||||
|
||||
# These files cannot be built in unified mode because they both include
|
||||
# AccessibleTable2_i.c.
|
||||
SOURCES += [
|
||||
'ia2AccessibleTable.cpp',
|
||||
'ia2AccessibleTableCell.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../../base',
|
||||
'../../generic',
|
||||
|
|
|
@ -14,7 +14,7 @@ EXPORTS.mozilla.a11y += [
|
|||
'HyperTextAccessibleWrap.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AccessibleWrap.cpp',
|
||||
'ApplicationAccessibleWrap.cpp',
|
||||
'ARIAGridAccessibleWrap.cpp',
|
||||
|
@ -29,12 +29,16 @@ SOURCES += [
|
|||
'nsWinUtils.cpp',
|
||||
'Platform.cpp',
|
||||
'RootAccessibleWrap.cpp',
|
||||
'ServiceProvider.cpp',
|
||||
'TextLeafAccessibleWrap.cpp',
|
||||
]
|
||||
|
||||
# This file cannot be built in unified mode because it includes ISimpleDOMNode_i.c.
|
||||
SOURCES += [
|
||||
'ServiceProvider.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_XUL']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'XULListboxAccessibleWrap.cpp',
|
||||
'XULMenuAccessibleWrap.cpp',
|
||||
'XULTreeGridAccessibleWrap.cpp',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# 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/.
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'sdnAccessible.cpp',
|
||||
'sdnDocAccessible.cpp',
|
||||
'sdnTextAccessible.cpp',
|
||||
|
|
|
@ -29,7 +29,7 @@ endif
|
|||
endif # WINNT
|
||||
|
||||
ifdef .PYMAKE
|
||||
include_deps = $(eval -includedeps $(1))
|
||||
include_deps = $(eval $(if $(2),,-)includedeps $(1))
|
||||
else
|
||||
include_deps = $(eval -include $(1))
|
||||
include_deps = $(eval $(if $(2),,-)include $(1))
|
||||
endif
|
||||
|
|
|
@ -116,11 +116,19 @@ void main(void) {
|
|||
checkGLError(ok, info);
|
||||
|
||||
var elemTestFunc = todo; // We fail on most implementations.
|
||||
var checkGLTestFunc = todo;
|
||||
if (DriverInfo.getDriver() == DriverInfo.DRIVER.ANGLE ||
|
||||
DriverInfo.getOS() == DriverInfo.OS.ANDROID)
|
||||
{
|
||||
// ANGLE and Android slaves seem to work fine.
|
||||
elemTestFunc = ok;
|
||||
checkGLTestFunc = ok;
|
||||
}
|
||||
if (DriverInfo.getDriver() == DriverInfo.DRIVER.ANDROID_X86_EMULATOR)
|
||||
{
|
||||
// ...but the Android 4.2 x86 emulator environment is different
|
||||
elemTestFunc = todo;
|
||||
checkGLTestFunc = ok;
|
||||
}
|
||||
|
||||
// Now for drawElements:
|
||||
|
@ -139,7 +147,7 @@ void main(void) {
|
|||
gl.drawElements(gl.POINTS, 1, indexType, 2*indexStride);
|
||||
elemTestFunc(!isScreenBlack(), '[' + info + '] drawElements[huge offset] should color pixels.');
|
||||
|
||||
checkGLError(elemTestFunc, info);
|
||||
checkGLError(checkGLTestFunc, info);
|
||||
}
|
||||
|
||||
// Begin drawing
|
||||
|
|
|
@ -85,7 +85,11 @@ public:
|
|||
nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE
|
||||
{
|
||||
// XXX: Merge result with audio reader.
|
||||
return GetVideoReader()->GetBuffered(aBuffered, aStartTime);
|
||||
MediaDecoderReader* reader = GetVideoReader() ? GetVideoReader() : GetAudioReader();
|
||||
if (reader) {
|
||||
return reader->GetBuffered(aBuffered, aStartTime);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaQueue<AudioData>& AudioQueue() MOZ_OVERRIDE
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
namespace MPAPI {
|
||||
|
||||
enum ColorFormat {
|
||||
YCbCr,
|
||||
I420,
|
||||
RGB565
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
namespace mozilla {
|
||||
|
||||
typedef mozilla::layers::Image Image;
|
||||
typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
|
||||
|
||||
MediaPluginReader::MediaPluginReader(AbstractMediaDecoder *aDecoder,
|
||||
const nsACString& aContentType) :
|
||||
|
@ -170,7 +171,7 @@ bool MediaPluginReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
|||
currentImage = bufferCallback.GetImage();
|
||||
int64_t pos = mDecoder->GetResource()->Tell();
|
||||
nsIntRect picture = mPicture;
|
||||
|
||||
|
||||
nsAutoPtr<VideoData> v;
|
||||
if (currentImage) {
|
||||
gfx::IntSize frameSize = currentImage->GetSize();
|
||||
|
@ -338,33 +339,79 @@ MediaPluginReader::ImageBufferCallback::ImageBufferCallback(mozilla::layers::Ima
|
|||
|
||||
void *
|
||||
MediaPluginReader::ImageBufferCallback::operator()(size_t aWidth, size_t aHeight,
|
||||
MPAPI::ColorFormat aColorFormat)
|
||||
MPAPI::ColorFormat aColorFormat)
|
||||
{
|
||||
if (!mImageContainer) {
|
||||
NS_WARNING("No image container to construct an image");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Image> rgbImage;
|
||||
nsRefPtr<Image> image;
|
||||
switch(aColorFormat) {
|
||||
case MPAPI::RGB565:
|
||||
rgbImage = mozilla::layers::CreateSharedRGBImage(mImageContainer,
|
||||
nsIntSize(aWidth, aHeight),
|
||||
gfxImageFormatRGB16_565);
|
||||
if (!rgbImage) {
|
||||
image = mozilla::layers::CreateSharedRGBImage(mImageContainer,
|
||||
nsIntSize(aWidth, aHeight),
|
||||
gfxImageFormatRGB16_565);
|
||||
if (!image) {
|
||||
NS_WARNING("Could not create rgb image");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mImage = rgbImage;
|
||||
return rgbImage->AsSharedImage()->GetBuffer();
|
||||
case MPAPI::YCbCr:
|
||||
mImage = image;
|
||||
return image->AsSharedImage()->GetBuffer();
|
||||
case MPAPI::I420:
|
||||
return CreateI420Image(aWidth, aHeight);
|
||||
default:
|
||||
NS_NOTREACHED("Color format not supported");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
MediaPluginReader::ImageBufferCallback::CreateI420Image(size_t aWidth,
|
||||
size_t aHeight)
|
||||
{
|
||||
ImageFormat format = PLANAR_YCBCR;
|
||||
|
||||
mImage = mImageContainer->CreateImage(&format, 1 /* numFormats */);
|
||||
PlanarYCbCrImage *yuvImage = static_cast<PlanarYCbCrImage *>(mImage.get());
|
||||
|
||||
if (!yuvImage) {
|
||||
NS_WARNING("Could not create I420 image");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t frameSize = aWidth * aHeight;
|
||||
|
||||
// Allocate enough for one full resolution Y plane
|
||||
// and two quarter resolution Cb/Cr planes.
|
||||
uint8_t *buffer = yuvImage->AllocateAndGetNewBuffer(frameSize * 3 / 2);
|
||||
|
||||
mozilla::layers::PlanarYCbCrData frameDesc;
|
||||
|
||||
frameDesc.mYChannel = buffer;
|
||||
frameDesc.mCbChannel = buffer + frameSize;
|
||||
frameDesc.mCrChannel = buffer + frameSize * 5 / 4;
|
||||
|
||||
frameDesc.mYSize = gfxIntSize(aWidth, aHeight);
|
||||
frameDesc.mCbCrSize = gfxIntSize(aWidth / 2, aHeight / 2);
|
||||
|
||||
frameDesc.mYStride = aWidth;
|
||||
frameDesc.mCbCrStride = aWidth / 2;
|
||||
|
||||
frameDesc.mYSkip = 0;
|
||||
frameDesc.mCbSkip = 0;
|
||||
frameDesc.mCrSkip = 0;
|
||||
|
||||
frameDesc.mPicX = 0;
|
||||
frameDesc.mPicY = 0;
|
||||
frameDesc.mPicSize = gfxIntSize(aWidth, aHeight);
|
||||
|
||||
yuvImage->SetDataNoCopy(frameDesc);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
already_AddRefed<Image>
|
||||
MediaPluginReader::ImageBufferCallback::GetImage()
|
||||
{
|
||||
|
|
|
@ -65,17 +65,23 @@ public:
|
|||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags);
|
||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
|
||||
|
||||
class ImageBufferCallback : public MPAPI::BufferCallback {
|
||||
typedef mozilla::layers::Image Image;
|
||||
|
||||
public:
|
||||
ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer);
|
||||
void *operator()(size_t aWidth, size_t aHeight,
|
||||
MPAPI::ColorFormat aColorFormat) MOZ_OVERRIDE;
|
||||
already_AddRefed<Image> GetImage();
|
||||
|
||||
private:
|
||||
uint8_t *CreateI420Image(size_t aWidth, size_t aHeight);
|
||||
|
||||
mozilla::layers::ImageContainer *mImageContainer;
|
||||
nsRefPtr<Image> mImage;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -58,11 +58,10 @@ codegen_dependencies := \
|
|||
$(GLOBAL_DEPS) \
|
||||
$(NULL)
|
||||
|
||||
$(call include_deps,codegen.pp)
|
||||
# The 1 is to make codegen.pp not optional.
|
||||
$(call include_deps,codegen.pp,1)
|
||||
|
||||
codegen.pp: codegen.done
|
||||
|
||||
codegen.done: $(codegen_dependencies)
|
||||
codegen.pp: $(codegen_dependencies)
|
||||
$(call py_action,webidl,$(srcdir))
|
||||
@$(TOUCH) $@
|
||||
|
||||
|
|
|
@ -546,7 +546,7 @@ def create_build_system_manager(topsrcdir, topobjdir, dist_dir):
|
|||
cache_dir=cache_dir,
|
||||
# The make rules include a codegen.pp file containing dependencies.
|
||||
make_deps_path=os.path.join(obj_dir, 'codegen.pp'),
|
||||
make_deps_target='codegen.done',
|
||||
make_deps_target='codegen.pp',
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
@ -60,8 +61,7 @@ class WorkerPromiseTask MOZ_FINAL : public WorkerRunnable
|
|||
{
|
||||
public:
|
||||
WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread,
|
||||
UnchangedBusyCount, SkipWhenClearing)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
, mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
@ -169,8 +169,7 @@ public:
|
|||
Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread,
|
||||
UnchangedBusyCount, SkipWhenClearing),
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
PromiseResolverMixin(aPromise, aValue, aState)
|
||||
{}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
using mozilla::dom::EventHandlerNonNull;
|
||||
using mozilla::dom::MessagePortBase;
|
||||
|
@ -28,13 +29,10 @@ class DelayedEventRunnable MOZ_FINAL : public WorkerRunnable
|
|||
|
||||
public:
|
||||
DelayedEventRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
Target aTarget,
|
||||
TargetAndBusyBehavior aBehavior,
|
||||
MessagePort* aMessagePort,
|
||||
nsTArray<nsCOMPtr<nsIDOMEvent>>& aEvents)
|
||||
: WorkerRunnable(aWorkerPrivate, aTarget,
|
||||
aTarget == WorkerThread ? ModifyBusyCount : UnchangedBusyCount,
|
||||
SkipWhenClearing),
|
||||
mMessagePort(aMessagePort)
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior), mMessagePort(aMessagePort)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aMessagePort);
|
||||
|
@ -109,16 +107,22 @@ MessagePort::Start()
|
|||
mStarted = true;
|
||||
|
||||
if (!mQueuedEvents.IsEmpty()) {
|
||||
WorkerRunnable::Target target = WorkerRunnable::WorkerThread;
|
||||
WorkerPrivate* workerPrivate = mWorkerPrivate;
|
||||
WorkerPrivate* workerPrivate;
|
||||
WorkerRunnable::TargetAndBusyBehavior behavior;
|
||||
|
||||
if (!workerPrivate) {
|
||||
target = WorkerRunnable::ParentThread;
|
||||
if (mWorkerPrivate) {
|
||||
workerPrivate = mWorkerPrivate;
|
||||
behavior = WorkerRunnable::WorkerThreadModifyBusyCount;
|
||||
}
|
||||
else {
|
||||
workerPrivate = mSharedWorker->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
behavior = WorkerRunnable::ParentThreadUnchangedBusyCount;
|
||||
}
|
||||
|
||||
nsRefPtr<DelayedEventRunnable> runnable =
|
||||
new DelayedEventRunnable(workerPrivate, target, this, mQueuedEvents);
|
||||
new DelayedEventRunnable(workerPrivate, behavior, this, mQueuedEvents);
|
||||
runnable->Dispatch(nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "nsLayoutStatics.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "nsXPCOM.h"
|
||||
|
@ -50,13 +51,18 @@
|
|||
#include "OSFileConstants.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "nsThreadManager.h"
|
||||
#endif
|
||||
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
@ -593,6 +599,8 @@ void
|
|||
ErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aReport)
|
||||
{
|
||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(worker);
|
||||
|
||||
return worker->ReportError(aCx, aMessage, aReport);
|
||||
}
|
||||
|
||||
|
@ -600,6 +608,7 @@ bool
|
|||
OperationCallback(JSContext* aCx)
|
||||
{
|
||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(worker);
|
||||
|
||||
// Now is a good time to turn on profiling if it's pending.
|
||||
profiler_js_operation_callback();
|
||||
|
@ -607,98 +616,42 @@ OperationCallback(JSContext* aCx)
|
|||
return worker->OperationCallback(aCx);
|
||||
}
|
||||
|
||||
class LogViolationDetailsRunnable : public nsRunnable
|
||||
class LogViolationDetailsRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
nsString mFileName;
|
||||
uint32_t mLineNum;
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
private:
|
||||
class LogViolationDetailsResponseRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
public:
|
||||
LogViolationDetailsResponseRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, false),
|
||||
mSyncQueueKey(aSyncQueueKey)
|
||||
{
|
||||
NS_ASSERTION(aWorkerPrivate, "Don't hand me a null WorkerPrivate!");
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
LogViolationDetailsRunnable(WorkerPrivate* aWorker,
|
||||
const nsString& aFileName,
|
||||
uint32_t aLineNum)
|
||||
: mWorkerPrivate(aWorker),
|
||||
mFileName(aFileName),
|
||||
mLineNum(aLineNum),
|
||||
mSyncQueueKey(0)
|
||||
: mWorkerPrivate(aWorker), mFileName(aFileName), mLineNum(aLineNum)
|
||||
{
|
||||
NS_ASSERTION(aWorker, "WorkerPrivate cannot be null");
|
||||
MOZ_ASSERT(aWorker);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx)
|
||||
{
|
||||
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
|
||||
mSyncQueueKey = syncLoop.SyncQueueKey();
|
||||
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
||||
JS_ReportError(aCx, "Failed to dispatch to main thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.RunAndForget(aCx);
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
|
||||
if (csp) {
|
||||
NS_NAMED_LITERAL_STRING(scriptSample,
|
||||
"Call to eval() or related function blocked by CSP.");
|
||||
if (mWorkerPrivate->GetReportCSPViolations()) {
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum, EmptyString());
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<LogViolationDetailsResponseRunnable> response =
|
||||
new LogViolationDetailsResponseRunnable(mWorkerPrivate, mSyncQueueKey);
|
||||
if (!response->Dispatch(nullptr)) {
|
||||
NS_WARNING("Failed to dispatch response!");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
bool
|
||||
|
@ -715,7 +668,7 @@ ContentSecurityPolicyAllows(JSContext* aCx)
|
|||
const char* file;
|
||||
if (JS_DescribeScriptedCaller(aCx, &script, &lineNum) &&
|
||||
(file = JS_GetScriptFilename(aCx, script))) {
|
||||
fileName.AssignASCII(file);
|
||||
fileName = NS_ConvertUTF8toUTF16(file);
|
||||
} else {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
|
@ -959,81 +912,158 @@ private:
|
|||
WorkerPrivate* mWorkerPrivate;
|
||||
};
|
||||
|
||||
class WorkerThreadRunnable : public nsRunnable
|
||||
class WorkerThreadPrimaryRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<RuntimeService::WorkerThread> mThread;
|
||||
|
||||
class FinishedRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
nsRefPtr<RuntimeService::WorkerThread> mThread;
|
||||
|
||||
public:
|
||||
FinishedRunnable(already_AddRefed<RuntimeService::WorkerThread> aThread)
|
||||
: mThread(aThread)
|
||||
{
|
||||
MOZ_ASSERT(mThread);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
~FinishedRunnable()
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
public:
|
||||
WorkerThreadRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
RuntimeService::WorkerThread* aThread)
|
||||
: mWorkerPrivate(aWorkerPrivate), mThread(aThread)
|
||||
{
|
||||
NS_ASSERTION(mWorkerPrivate, "This should never be null!");
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aThread);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
~WorkerThreadPrimaryRunnable()
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
class WorkerTaskRunnable MOZ_FINAL : public WorkerRunnable
|
||||
{
|
||||
nsRefPtr<WorkerTask> mTask;
|
||||
|
||||
public:
|
||||
WorkerTaskRunnable(WorkerPrivate* aWorkerPrivate, WorkerTask* aTask)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), mTask(aTask)
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
"NuwaMarkCurrentThread is undefined!");
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
NuwaFreezeCurrentThread();
|
||||
}
|
||||
#endif
|
||||
WorkerPrivate* workerPrivate = mWorkerPrivate;
|
||||
mWorkerPrivate = nullptr;
|
||||
MOZ_ASSERT(aTask);
|
||||
}
|
||||
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
// May be called on any thread!
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
nsCycleCollector_startup();
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE
|
||||
{
|
||||
// May be called on any thread!
|
||||
}
|
||||
|
||||
WorkerJSRuntime runtime(workerPrivate);
|
||||
JSRuntime* rt = runtime.Runtime();
|
||||
JSContext* cx = CreateJSContextForWorker(workerPrivate, rt);
|
||||
if (!cx) {
|
||||
// XXX need to fire an error at parent.
|
||||
NS_ERROR("Failed to create runtime and context!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
char aLocal;
|
||||
profiler_register_thread("WebWorker", &aLocal);
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (PseudoStack* stack = mozilla_get_pseudo_stack())
|
||||
stack->sampleRuntime(rt);
|
||||
#endif
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
workerPrivate->DoRunLoop(cx);
|
||||
}
|
||||
|
||||
// Destroy the main context. This will unroot the main worker global and
|
||||
// GC. This is not the last JSContext (WorkerJSRuntime maintains an
|
||||
// internal JSContext).
|
||||
JS_DestroyContext(cx);
|
||||
|
||||
// Now WorkerJSRuntime goes out of scope and its destructor will shut
|
||||
// down the cycle collector and destroy the final JSContext. This
|
||||
// breaks any remaining cycles and collects the C++ and JS objects
|
||||
// participating.
|
||||
}
|
||||
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (PseudoStack* stack = mozilla_get_pseudo_stack())
|
||||
stack->sampleRuntime(nullptr);
|
||||
#endif
|
||||
|
||||
workerPrivate->ScheduleDeletion(false);
|
||||
profiler_unregister_thread();
|
||||
return NS_OK;
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
return mTask->RunTask(aCx);
|
||||
}
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
class RuntimeService::WorkerThread MOZ_FINAL : public nsThread
|
||||
{
|
||||
class Observer MOZ_FINAL : public nsIThreadObserver
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
public:
|
||||
Observer(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
private:
|
||||
~Observer()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
};
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<Observer> mObserver;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Protected by nsThread::mLock.
|
||||
bool mAcceptingNonWorkerRunnables;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static already_AddRefed<WorkerThread>
|
||||
Create();
|
||||
|
||||
void
|
||||
SetWorker(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_IMETHOD
|
||||
Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
IsAcceptingNonWorkerRunnables()
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
return mAcceptingNonWorkerRunnables;
|
||||
}
|
||||
|
||||
void
|
||||
SetAcceptingNonWorkerRunnables(bool aAcceptingNonWorkerRunnables)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mAcceptingNonWorkerRunnables = aAcceptingNonWorkerRunnables;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
WorkerThread()
|
||||
: nsThread(nsThread::NOT_MAIN_THREAD, WORKER_STACK_SIZE),
|
||||
mWorkerPrivate(nullptr)
|
||||
#ifdef DEBUG
|
||||
, mAcceptingNonWorkerRunnables(true)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
~WorkerThread()
|
||||
{ }
|
||||
};
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
// Entry point for main thread non-window globals.
|
||||
|
@ -1113,71 +1143,64 @@ ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class WorkerTaskRunnable : public WorkerRunnable
|
||||
WorkerCrossThreadDispatcher::WorkerCrossThreadDispatcher(
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: mMutex("WorkerCrossThreadDispatcher::mMutex"),
|
||||
mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
public:
|
||||
WorkerTaskRunnable(WorkerPrivate* aPrivate, WorkerTask* aTask)
|
||||
: WorkerRunnable(aPrivate, WorkerThread, UnchangedBusyCount,
|
||||
SkipWhenClearing),
|
||||
mTask(aTask)
|
||||
{ }
|
||||
|
||||
virtual bool PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{ }
|
||||
|
||||
virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
private:
|
||||
nsRefPtr<WorkerTask> mTask;
|
||||
};
|
||||
|
||||
bool
|
||||
WorkerTaskRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
return mTask->RunTask(aCx);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerCrossThreadDispatcher::PostTask(WorkerTask* aTask)
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
if (!mPrivate) {
|
||||
MOZ_ASSERT(aTask);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (!mWorkerPrivate) {
|
||||
NS_WARNING("Posted a task to a WorkerCrossThreadDispatcher that is no "
|
||||
"longer accepting tasks!");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<WorkerTaskRunnable> runnable = new WorkerTaskRunnable(mPrivate, aTask);
|
||||
runnable->Dispatch(nullptr);
|
||||
return true;
|
||||
nsRefPtr<WorkerTaskRunnable> runnable =
|
||||
new WorkerTaskRunnable(mWorkerPrivate, aTask);
|
||||
return runnable->Dispatch(nullptr);
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
GetWorkerPrivateFromContext(JSContext* aCx)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(JS_GetRuntime(aCx)))->mWorkerPrivate;
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aCx);
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(aCx);
|
||||
MOZ_ASSERT(rt);
|
||||
|
||||
void* rtPrivate = JS_GetRuntimePrivate(rt);
|
||||
MOZ_ASSERT(rtPrivate);
|
||||
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate;
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
GetCurrentThreadWorkerPrivate()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get();
|
||||
if (!ccrt) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSRuntime* rt = ccrt->Runtime();
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(rt))->
|
||||
mWorkerPrivate;
|
||||
MOZ_ASSERT(rt);
|
||||
|
||||
void* rtPrivate = JS_GetRuntimePrivate(rt);
|
||||
MOZ_ASSERT(rtPrivate);
|
||||
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1427,7 +1450,7 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|||
}
|
||||
|
||||
if (!domainInfo->ActiveWorkerCount()) {
|
||||
NS_ASSERTION(domainInfo->mQueuedWorkers.IsEmpty(), "Huh?!");
|
||||
MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
|
||||
mDomainMap.Remove(domain);
|
||||
}
|
||||
}
|
||||
|
@ -1455,16 +1478,11 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|||
nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
|
||||
|
||||
nsTArray<WorkerPrivate*>* windowArray;
|
||||
if (!mWindowMap.Get(window, &windowArray)) {
|
||||
MOZ_ASSERT(false, "Don't have an entry for this window!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray));
|
||||
|
||||
if (!windowArray->RemoveElement(aWorkerPrivate)) {
|
||||
MOZ_ASSERT(false, "Worker wasn't in the correct window array!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(windowArray->RemoveElement(aWorkerPrivate));
|
||||
|
||||
if (windowArray->IsEmpty()) {
|
||||
MOZ_ASSERT(!queuedWorker, "queuedWorker should be in this array!");
|
||||
mWindowMap.Remove(window);
|
||||
}
|
||||
}
|
||||
|
@ -1482,7 +1500,7 @@ RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsRefPtr<WorkerThread> thread;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mIdleThreadArray.IsEmpty()) {
|
||||
|
@ -1493,35 +1511,36 @@ RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|||
}
|
||||
|
||||
if (!thread) {
|
||||
if (NS_FAILED(NS_NewNamedThread("DOM Worker",
|
||||
getter_AddRefs(thread), nullptr,
|
||||
WORKER_STACK_SIZE))) {
|
||||
thread = WorkerThread::Create();
|
||||
if (!thread) {
|
||||
UnregisterWorker(aCx, aWorkerPrivate);
|
||||
JS_ReportError(aCx, "Could not create new thread!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(thread->IsAcceptingNonWorkerRunnables());
|
||||
|
||||
int32_t priority = aWorkerPrivate->IsChromeWorker() ?
|
||||
nsISupportsPriority::PRIORITY_NORMAL :
|
||||
nsISupportsPriority::PRIORITY_LOW;
|
||||
|
||||
nsCOMPtr<nsISupportsPriority> threadPriority = do_QueryInterface(thread);
|
||||
if (!threadPriority || NS_FAILED(threadPriority->SetPriority(priority))) {
|
||||
if (NS_FAILED(thread->SetPriority(priority))) {
|
||||
NS_WARNING("Could not set the thread's priority!");
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
aWorkerPrivate->SetThread(thread);
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new WorkerThreadRunnable(aWorkerPrivate);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread);
|
||||
if (NS_FAILED(thread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
UnregisterWorker(aCx, aWorkerPrivate);
|
||||
JS_ReportError(aCx, "Could not dispatch to thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
thread->SetAcceptingNonWorkerRunnables(false);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1541,7 +1560,7 @@ RuntimeService::ShutdownIdleThreads(nsITimer* aTimer, void* /* aClosure */)
|
|||
|
||||
TimeStamp nextExpiration;
|
||||
|
||||
nsAutoTArray<nsCOMPtr<nsIThread>, 20> expiredThreads;
|
||||
nsAutoTArray<nsRefPtr<WorkerThread>, 20> expiredThreads;
|
||||
{
|
||||
MutexAutoLock lock(runtime->mMutex);
|
||||
|
||||
|
@ -1553,7 +1572,7 @@ RuntimeService::ShutdownIdleThreads(nsITimer* aTimer, void* /* aClosure */)
|
|||
break;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread>* thread = expiredThreads.AppendElement();
|
||||
nsRefPtr<WorkerThread>* thread = expiredThreads.AppendElement();
|
||||
thread->swap(info.mThread);
|
||||
}
|
||||
|
||||
|
@ -1756,7 +1775,6 @@ RuntimeService::Shutdown()
|
|||
mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
|
||||
// Cancel all top-level workers.
|
||||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
@ -1803,7 +1821,7 @@ RuntimeService::Cleanup()
|
|||
|
||||
// Shut down any idle threads.
|
||||
if (!mIdleThreadArray.IsEmpty()) {
|
||||
nsAutoTArray<nsCOMPtr<nsIThread>, 20> idleThreads;
|
||||
nsAutoTArray<nsRefPtr<WorkerThread>, 20> idleThreads;
|
||||
|
||||
uint32_t idleThreadCount = mIdleThreadArray.Length();
|
||||
idleThreads.SetLength(idleThreadCount);
|
||||
|
@ -2186,10 +2204,14 @@ RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate)
|
|||
}
|
||||
|
||||
void
|
||||
RuntimeService::NoteIdleThread(nsIThread* aThread)
|
||||
RuntimeService::NoteIdleThread(WorkerThread* aThread)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
NS_ASSERTION(aThread, "Null pointer!");
|
||||
MOZ_ASSERT(aThread);
|
||||
|
||||
#ifdef DEBUG
|
||||
aThread->SetAcceptingNonWorkerRunnables(true);
|
||||
#endif
|
||||
|
||||
static TimeDuration timeout =
|
||||
TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC);
|
||||
|
@ -2216,19 +2238,15 @@ RuntimeService::NoteIdleThread(nsIThread* aThread)
|
|||
|
||||
// Too many idle threads, just shut this one down.
|
||||
if (shutdown) {
|
||||
if (NS_FAILED(aThread->Shutdown())) {
|
||||
NS_WARNING("Failed to shutdown thread!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->Shutdown()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule timer.
|
||||
if (NS_FAILED(mIdleThreadTimer->
|
||||
InitWithFuncCallback(ShutdownIdleThreads, nullptr,
|
||||
IDLE_THREAD_TIMEOUT_SEC * 1000,
|
||||
nsITimer::TYPE_ONE_SHOT))) {
|
||||
NS_ERROR("Can't schedule timer!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mIdleThreadTimer->InitWithFuncCallback(
|
||||
ShutdownIdleThreads, nullptr,
|
||||
IDLE_THREAD_TIMEOUT_SEC * 1000,
|
||||
nsITimer::TYPE_ONE_SHOT)));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2354,3 +2372,272 @@ RuntimeService::JSVersionChanged(const char* /* aPrefName */, void* /* aClosure
|
|||
JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions;
|
||||
options.setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<RuntimeService::WorkerThread>
|
||||
RuntimeService::WorkerThread::Create()
|
||||
{
|
||||
MOZ_ASSERT(nsThreadManager::get());
|
||||
|
||||
nsRefPtr<WorkerThread> thread = new WorkerThread();
|
||||
if (NS_FAILED(thread->Init())) {
|
||||
NS_WARNING("Failed to create new thread!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_SetThreadName(thread, "DOM Worker");
|
||||
|
||||
return thread.forget();
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::WorkerThread::SetWorker(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == mThread);
|
||||
MOZ_ASSERT_IF(aWorkerPrivate, !mWorkerPrivate);
|
||||
MOZ_ASSERT_IF(!aWorkerPrivate, mWorkerPrivate);
|
||||
|
||||
// No need to lock here because mWorkerPrivate is only modified on mThread.
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
MOZ_ASSERT(mObserver);
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(RemoveObserver(mObserver)));
|
||||
|
||||
mObserver = nullptr;
|
||||
mWorkerPrivate->SetThread(nullptr);
|
||||
}
|
||||
|
||||
mWorkerPrivate = aWorkerPrivate;
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
mWorkerPrivate->SetThread(this);
|
||||
|
||||
nsRefPtr<Observer> observer = new Observer(mWorkerPrivate);
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(AddObserver(observer)));
|
||||
|
||||
mObserver.swap(observer);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(RuntimeService::WorkerThread, nsThread)
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags)
|
||||
{
|
||||
// May be called on any thread!
|
||||
|
||||
#ifdef DEBUG
|
||||
if (PR_GetCurrentThread() == mThread) {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
else if (aRunnable && !IsAcceptingNonWorkerRunnables()) {
|
||||
// Only enforce cancelable runnables after we've started the worker loop.
|
||||
nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable);
|
||||
MOZ_ASSERT(cancelable,
|
||||
"Should have been wrapped by the worker's event target!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Workers only support asynchronous dispatch for now.
|
||||
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsIRunnable* runnableToDispatch;
|
||||
nsRefPtr<WorkerRunnable> workerRunnable;
|
||||
|
||||
if (aRunnable && PR_GetCurrentThread() == mThread) {
|
||||
// No need to lock here because mWorkerPrivate is only modified on mThread.
|
||||
workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable);
|
||||
runnableToDispatch = workerRunnable;
|
||||
}
|
||||
else {
|
||||
runnableToDispatch = aRunnable;
|
||||
}
|
||||
|
||||
nsresult rv = nsThread::Dispatch(runnableToDispatch, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(RuntimeService::WorkerThread::Observer, nsIThreadObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Observer::OnDispatchedEvent(
|
||||
nsIThreadInternal* /*aThread */)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("This should never be called!");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Observer::OnProcessNextEvent(
|
||||
nsIThreadInternal* /* aThread */,
|
||||
bool aMayWait,
|
||||
uint32_t aRecursionDepth)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!aMayWait);
|
||||
|
||||
mWorkerPrivate->OnProcessNextEvent(aRecursionDepth);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Observer::AfterProcessNextEvent(
|
||||
nsIThreadInternal* /* aThread */,
|
||||
uint32_t aRecursionDepth,
|
||||
bool /* aEventWasProcessed */)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
mWorkerPrivate->AfterProcessNextEvent(aRecursionDepth);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(LogViolationDetailsRunnable, nsRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
LogViolationDetailsRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
|
||||
if (csp) {
|
||||
NS_NAMED_LITERAL_STRING(scriptSample,
|
||||
"Call to eval() or related function blocked by CSP.");
|
||||
if (mWorkerPrivate->GetReportCSPViolations()) {
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum,
|
||||
EmptyString());
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<MainThreadStopSyncLoopRunnable> response =
|
||||
new MainThreadStopSyncLoopRunnable(mWorkerPrivate, mSyncLoopTarget.forget(),
|
||||
true);
|
||||
MOZ_ALWAYS_TRUE(response->Dispatch(nullptr));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable, nsRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThreadPrimaryRunnable::Run()
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
"NuwaMarkCurrentThread is undefined!");
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
NuwaFreezeCurrentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
char stackBaseGuess;
|
||||
|
||||
nsAutoCString threadName;
|
||||
threadName.AssignLiteral("WebWorker '");
|
||||
threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL()));
|
||||
threadName.Append('\'');
|
||||
|
||||
profiler_register_thread(threadName.get(), &stackBaseGuess);
|
||||
|
||||
mThread->SetWorker(mWorkerPrivate);
|
||||
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
{
|
||||
nsCycleCollector_startup();
|
||||
|
||||
WorkerJSRuntime runtime(mWorkerPrivate);
|
||||
JSRuntime* rt = runtime.Runtime();
|
||||
|
||||
JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt);
|
||||
if (!cx) {
|
||||
// XXX need to fire an error at parent.
|
||||
NS_ERROR("Failed to create runtime and context!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
{
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
PseudoStack* stack = mozilla_get_pseudo_stack();
|
||||
if (stack) {
|
||||
stack->sampleRuntime(rt);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
mWorkerPrivate->DoRunLoop(cx);
|
||||
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (stack) {
|
||||
stack->sampleRuntime(nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Destroy the main context. This will unroot the main worker global and
|
||||
// GC. This is not the last JSContext (WorkerJSRuntime maintains an
|
||||
// internal JSContext).
|
||||
JS_DestroyContext(cx);
|
||||
|
||||
// Now WorkerJSRuntime goes out of scope and its destructor will shut
|
||||
// down the cycle collector and destroy the final JSContext. This
|
||||
// breaks any remaining cycles and collects the C++ and JS objects
|
||||
// participating.
|
||||
}
|
||||
|
||||
mThread->SetWorker(nullptr);
|
||||
|
||||
mWorkerPrivate->ScheduleDeletion();
|
||||
|
||||
// It is no longer safe to touch mWorkerPrivate.
|
||||
mWorkerPrivate = nullptr;
|
||||
|
||||
// Now recycle this thread.
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
MOZ_ASSERT(mainThread);
|
||||
|
||||
nsRefPtr<FinishedRunnable> finishedRunnable =
|
||||
new FinishedRunnable(mThread.forget());
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(finishedRunnable,
|
||||
NS_DISPATCH_NORMAL)));
|
||||
|
||||
profiler_unregister_thread();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable::FinishedRunnable,
|
||||
nsRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThreadPrimaryRunnable::FinishedRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsRefPtr<RuntimeService::WorkerThread> thread;
|
||||
mThread.swap(thread);
|
||||
|
||||
RuntimeService* rts = RuntimeService::GetService();
|
||||
if (rts) {
|
||||
rts->NoteIdleThread(thread);
|
||||
}
|
||||
else if (thread->ShutdownRequired()) {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Shutdown()));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -11,18 +11,13 @@
|
|||
|
||||
#include "nsIObserver.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIRunnable;
|
||||
class nsIThread;
|
||||
class nsITimer;
|
||||
class nsPIDOMWindow;
|
||||
|
@ -34,6 +29,10 @@ class WorkerPrivate;
|
|||
|
||||
class RuntimeService MOZ_FINAL : public nsIObserver
|
||||
{
|
||||
public:
|
||||
class WorkerThread;
|
||||
|
||||
private:
|
||||
struct SharedWorkerInfo
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
@ -68,7 +67,7 @@ class RuntimeService MOZ_FINAL : public nsIObserver
|
|||
|
||||
struct IdleThreadInfo
|
||||
{
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
nsRefPtr<WorkerThread> mThread;
|
||||
mozilla::TimeStamp mExpirationTime;
|
||||
};
|
||||
|
||||
|
@ -173,7 +172,7 @@ public:
|
|||
}
|
||||
|
||||
void
|
||||
NoteIdleThread(nsIThread* aThread);
|
||||
NoteIdleThread(WorkerThread* aThread);
|
||||
|
||||
static void
|
||||
GetDefaultJSSettings(JSSettings& aSettings)
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "Principal.h"
|
||||
#include "WorkerFeature.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#define MAX_CONCURRENT_SCRIPTS 1000
|
||||
|
||||
|
@ -131,8 +132,6 @@ ChannelFromScriptURL(nsIPrincipal* principal,
|
|||
return rv;
|
||||
}
|
||||
|
||||
class ScriptLoaderRunnable;
|
||||
|
||||
struct ScriptLoadInfo
|
||||
{
|
||||
ScriptLoadInfo()
|
||||
|
@ -155,7 +154,9 @@ struct ScriptLoadInfo
|
|||
bool mExecutionResult;
|
||||
};
|
||||
|
||||
class ScriptExecutorRunnable : public WorkerSyncRunnable
|
||||
class ScriptLoaderRunnable;
|
||||
|
||||
class ScriptExecutorRunnable MOZ_FINAL : public MainThreadWorkerSyncRunnable
|
||||
{
|
||||
ScriptLoaderRunnable& mScriptLoader;
|
||||
uint32_t mFirstIndex;
|
||||
|
@ -163,38 +164,29 @@ class ScriptExecutorRunnable : public WorkerSyncRunnable
|
|||
|
||||
public:
|
||||
ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
|
||||
uint32_t aSyncQueueKey, uint32_t aFirstIndex,
|
||||
nsIEventTarget* aSyncLoopTarget, uint32_t aFirstIndex,
|
||||
uint32_t aLastIndex);
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
~ScriptExecutorRunnable()
|
||||
{ }
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
|
||||
MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class ScriptLoaderRunnable : public WorkerFeature,
|
||||
public nsIRunnable,
|
||||
public nsIStreamLoaderObserver
|
||||
class ScriptLoaderRunnable MOZ_FINAL : public WorkerFeature,
|
||||
public nsIRunnable,
|
||||
public nsIStreamLoaderObserver
|
||||
{
|
||||
friend class ScriptExecutorRunnable;
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
uint32_t mSyncQueueKey;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
nsTArray<ScriptLoadInfo> mLoadInfos;
|
||||
bool mIsWorkerScript;
|
||||
bool mCanceled;
|
||||
|
@ -204,21 +196,26 @@ public:
|
|||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
nsTArray<ScriptLoadInfo>& aLoadInfos,
|
||||
bool aIsWorkerScript)
|
||||
: mWorkerPrivate(aWorkerPrivate), mSyncQueueKey(aSyncQueueKey),
|
||||
: mWorkerPrivate(aWorkerPrivate), mSyncLoopTarget(aSyncLoopTarget),
|
||||
mIsWorkerScript(aIsWorkerScript), mCanceled(false),
|
||||
mCanceledMainThread(false)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_ASSERTION(!aIsWorkerScript || aLoadInfos.Length() == 1, "Bad args!");
|
||||
MOZ_ASSERT(aSyncLoopTarget);
|
||||
MOZ_ASSERT_IF(aIsWorkerScript, aLoadInfos.Length() == 1);
|
||||
|
||||
mLoadInfos.SwapElements(aLoadInfos);
|
||||
}
|
||||
|
||||
private:
|
||||
~ScriptLoaderRunnable()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
|
@ -232,7 +229,7 @@ public:
|
|||
NS_IMETHOD
|
||||
OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
|
||||
nsresult aStatus, uint32_t aStringLen,
|
||||
const uint8_t* aString)
|
||||
const uint8_t* aString) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
|
@ -256,8 +253,8 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
Notify(JSContext* aCx, Status aStatus)
|
||||
virtual bool
|
||||
Notify(JSContext* aCx, Status aStatus) MOZ_OVERRIDE
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
|
@ -587,9 +584,10 @@ public:
|
|||
|
||||
if (firstIndex != UINT32_MAX && lastIndex != UINT32_MAX) {
|
||||
nsRefPtr<ScriptExecutorRunnable> runnable =
|
||||
new ScriptExecutorRunnable(*this, mSyncQueueKey, firstIndex, lastIndex);
|
||||
new ScriptExecutorRunnable(*this, mSyncLoopTarget, firstIndex,
|
||||
lastIndex);
|
||||
if (!runnable->Dispatch(nullptr)) {
|
||||
NS_ERROR("This should never fail!");
|
||||
MOZ_ASSERT(false, "This should never fail!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -597,45 +595,26 @@ public:
|
|||
|
||||
NS_IMPL_ISUPPORTS2(ScriptLoaderRunnable, nsIRunnable, nsIStreamLoaderObserver)
|
||||
|
||||
class StopSyncLoopRunnable MOZ_FINAL : public MainThreadSyncRunnable
|
||||
{
|
||||
public:
|
||||
StopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey)
|
||||
: MainThreadSyncRunnable(aWorkerPrivate, SkipWhenClearing, aSyncQueueKey,
|
||||
false)
|
||||
{ }
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ChannelGetterRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mParentWorker;
|
||||
uint32_t mSyncQueueKey;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
const nsAString& mScriptURL;
|
||||
nsIChannel** mChannel;
|
||||
nsresult mResult;
|
||||
|
||||
public:
|
||||
ChannelGetterRunnable(WorkerPrivate* aParentWorker,
|
||||
uint32_t aSyncQueueKey,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel)
|
||||
: mParentWorker(aParentWorker), mSyncQueueKey(aSyncQueueKey),
|
||||
: mParentWorker(aParentWorker), mSyncLoopTarget(aSyncLoopTarget),
|
||||
mScriptURL(aScriptURL), mChannel(aChannel), mResult(NS_ERROR_FAILURE)
|
||||
{
|
||||
aParentWorker->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aSyncLoopTarget);
|
||||
}
|
||||
|
||||
virtual ~ChannelGetterRunnable() { }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
|
@ -660,8 +639,9 @@ public:
|
|||
channel.forget(mChannel);
|
||||
}
|
||||
|
||||
nsRefPtr<StopSyncLoopRunnable> runnable =
|
||||
new StopSyncLoopRunnable(mParentWorker, mSyncQueueKey);
|
||||
nsRefPtr<MainThreadStopSyncLoopRunnable> runnable =
|
||||
new MainThreadStopSyncLoopRunnable(mParentWorker,
|
||||
mSyncLoopTarget.forget(), true);
|
||||
if (!runnable->Dispatch(nullptr)) {
|
||||
NS_ERROR("This should never fail!");
|
||||
}
|
||||
|
@ -675,19 +655,21 @@ public:
|
|||
return mResult;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~ChannelGetterRunnable()
|
||||
{ }
|
||||
};
|
||||
|
||||
ScriptExecutorRunnable::ScriptExecutorRunnable(
|
||||
ScriptLoaderRunnable& aScriptLoader,
|
||||
uint32_t aSyncQueueKey,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
uint32_t aFirstIndex,
|
||||
uint32_t aLastIndex)
|
||||
: WorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncQueueKey),
|
||||
: MainThreadWorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncLoopTarget),
|
||||
mScriptLoader(aScriptLoader), mFirstIndex(aFirstIndex), mLastIndex(aLastIndex)
|
||||
{
|
||||
NS_ASSERTION(aFirstIndex <= aLastIndex, "Bad first index!");
|
||||
NS_ASSERTION(aLastIndex < aScriptLoader.mLoadInfos.Length(),
|
||||
"Bad last index!");
|
||||
MOZ_ASSERT(aFirstIndex <= aLastIndex);
|
||||
MOZ_ASSERT(aLastIndex < aScriptLoader.mLoadInfos.Length());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -759,7 +741,7 @@ ScriptExecutorRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
|||
}
|
||||
|
||||
aWorkerPrivate->RemoveFeature(aCx, &mScriptLoader);
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, result);
|
||||
aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,7 +755,7 @@ LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
|||
AutoSyncLoopHolder syncLoop(aWorkerPrivate);
|
||||
|
||||
nsRefPtr<ScriptLoaderRunnable> loader =
|
||||
new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.SyncQueueKey(),
|
||||
new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.EventTarget(),
|
||||
aLoadInfos, aIsWorkerScript);
|
||||
|
||||
NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
|
||||
|
@ -789,7 +771,7 @@ LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
|||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.RunAndForget(aCx);
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
@ -832,15 +814,15 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
|||
AutoSyncLoopHolder syncLoop(aParent);
|
||||
|
||||
nsRefPtr<ChannelGetterRunnable> getter =
|
||||
new ChannelGetterRunnable(aParent, syncLoop.SyncQueueKey(),
|
||||
aScriptURL, aChannel);
|
||||
new ChannelGetterRunnable(aParent, syncLoop.EventTarget(), aScriptURL,
|
||||
aChannel);
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(getter, NS_DISPATCH_NORMAL))) {
|
||||
NS_ERROR("Failed to dispatch!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!syncLoop.RunAndForget(aCx)) {
|
||||
if (!syncLoop.Run()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,24 +4,24 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "URL.h"
|
||||
#include "File.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/dom/URL.h"
|
||||
#include "mozilla/dom/URLBinding.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "File.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
using mozilla::dom::GlobalObject;
|
||||
|
@ -67,43 +67,7 @@ class URLRunnable : public nsRunnable
|
|||
{
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
private:
|
||||
class ResponseRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
public:
|
||||
ResponseRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, false),
|
||||
mSyncQueueKey(aSyncQueueKey)
|
||||
{
|
||||
NS_ASSERTION(aWorkerPrivate, "Don't hand me a null WorkerPrivate!");
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
||||
protected:
|
||||
URLRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
|
@ -117,15 +81,17 @@ public:
|
|||
Dispatch(JSContext* aCx)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
|
||||
mSyncQueueKey = syncLoop.SyncQueueKey();
|
||||
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
||||
JS_ReportError(aCx, "Failed to dispatch to main thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.RunAndForget(aCx);
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -135,8 +101,10 @@ private:
|
|||
|
||||
MainThreadRun();
|
||||
|
||||
nsRefPtr<ResponseRunnable> response =
|
||||
new ResponseRunnable(mWorkerPrivate, mSyncQueueKey);
|
||||
nsRefPtr<MainThreadStopSyncLoopRunnable> response =
|
||||
new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
|
||||
mSyncLoopTarget.forget(),
|
||||
true);
|
||||
if (!response->Dispatch(nullptr)) {
|
||||
NS_WARNING("Failed to dispatch response!");
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -9,25 +9,19 @@
|
|||
#include "Workers.h"
|
||||
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsIThreadInternal.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "nsEventQueue.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTPriorityQueue.h"
|
||||
#include "StructuredCloneTags.h"
|
||||
|
||||
#include "Queue.h"
|
||||
|
@ -36,8 +30,10 @@
|
|||
class JSAutoStructuredCloneBuffer;
|
||||
class nsIChannel;
|
||||
class nsIDocument;
|
||||
class nsIEventTarget;
|
||||
class nsIPrincipal;
|
||||
class nsIScriptContext;
|
||||
class nsIThread;
|
||||
class nsITimer;
|
||||
class nsIURI;
|
||||
|
||||
|
@ -51,142 +47,19 @@ class Function;
|
|||
}
|
||||
}
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class MessagePort;
|
||||
class SharedWorker;
|
||||
class WorkerGlobalScope;
|
||||
class WorkerPrivate;
|
||||
|
||||
class WorkerRunnable : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
enum Target { ParentThread, WorkerThread };
|
||||
enum BusyBehavior { ModifyBusyCount, UnchangedBusyCount };
|
||||
enum ClearingBehavior { SkipWhenClearing, RunWhenClearing };
|
||||
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
Target mTarget;
|
||||
BusyBehavior mBusyBehavior;
|
||||
ClearingBehavior mClearingBehavior;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx);
|
||||
|
||||
static bool
|
||||
DispatchToMainThread(nsIRunnable*);
|
||||
|
||||
bool
|
||||
WantsToRunDuringClear()
|
||||
{
|
||||
return mClearingBehavior == RunWhenClearing;
|
||||
}
|
||||
|
||||
protected:
|
||||
WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
|
||||
BusyBehavior aBusyBehavior,
|
||||
ClearingBehavior aClearingBehavior)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: mWorkerPrivate(aWorkerPrivate), mTarget(aTarget),
|
||||
mBusyBehavior(aBusyBehavior), mClearingBehavior(aClearingBehavior)
|
||||
{ }
|
||||
struct PRThread;
|
||||
#endif
|
||||
|
||||
virtual ~WorkerRunnable()
|
||||
{ }
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult);
|
||||
|
||||
virtual bool
|
||||
DispatchInternal();
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
|
||||
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
|
||||
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
class WorkerSyncRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
uint32_t mSyncQueueKey;
|
||||
bool mBypassSyncQueue;
|
||||
|
||||
protected:
|
||||
friend class WorkerPrivate;
|
||||
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, uint32_t aSyncQueueKey,
|
||||
bool aBypassSyncQueue = false,
|
||||
ClearingBehavior aClearingBehavior = SkipWhenClearing)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
|
||||
aClearingBehavior),
|
||||
mSyncQueueKey(aSyncQueueKey), mBypassSyncQueue(aBypassSyncQueue)
|
||||
{ }
|
||||
|
||||
virtual ~WorkerSyncRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class MainThreadSyncRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
public:
|
||||
MainThreadSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
ClearingBehavior aClearingBehavior,
|
||||
uint32_t aSyncQueueKey,
|
||||
bool aBypassSyncEventQueue)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, aBypassSyncEventQueue,
|
||||
aClearingBehavior)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerControlRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
|
||||
BusyBehavior aBusyBehavior)
|
||||
: WorkerRunnable(aWorkerPrivate, aTarget, aBusyBehavior, SkipWhenClearing)
|
||||
{ }
|
||||
|
||||
virtual ~WorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
class AutoSyncLoopHolder;
|
||||
class MessagePort;
|
||||
class SharedWorker;
|
||||
class WorkerControlRunnable;
|
||||
class WorkerGlobalScope;
|
||||
class WorkerPrivate;
|
||||
class WorkerRunnable;
|
||||
|
||||
// SharedMutex is a small wrapper around an (internal) reference-counted Mutex
|
||||
// object. It exists to avoid changing a lot of code to use Mutex* instead of
|
||||
|
@ -195,7 +68,7 @@ class SharedMutex
|
|||
{
|
||||
typedef mozilla::Mutex Mutex;
|
||||
|
||||
class RefCountedMutex : public Mutex
|
||||
class RefCountedMutex MOZ_FINAL : public Mutex
|
||||
{
|
||||
public:
|
||||
RefCountedMutex(const char* aName)
|
||||
|
@ -222,20 +95,17 @@ public:
|
|||
|
||||
operator Mutex&()
|
||||
{
|
||||
MOZ_ASSERT(mMutex);
|
||||
return *mMutex;
|
||||
}
|
||||
|
||||
operator const Mutex&() const
|
||||
{
|
||||
MOZ_ASSERT(mMutex);
|
||||
return *mMutex;
|
||||
}
|
||||
|
||||
void
|
||||
AssertCurrentThreadOwns() const
|
||||
{
|
||||
MOZ_ASSERT(mMutex);
|
||||
mMutex->AssertCurrentThreadOwns();
|
||||
}
|
||||
};
|
||||
|
@ -245,6 +115,10 @@ class WorkerPrivateParent : public nsDOMEventTargetHelper
|
|||
{
|
||||
class SynchronizeAndResumeRunnable;
|
||||
|
||||
protected:
|
||||
class EventTarget;
|
||||
friend class EventTarget;
|
||||
|
||||
public:
|
||||
struct LocationInfo
|
||||
{
|
||||
|
@ -312,6 +186,10 @@ protected:
|
|||
mozilla::CondVar mCondVar;
|
||||
mozilla::CondVar mMemoryReportCondVar;
|
||||
|
||||
// Protected by mMutex.
|
||||
nsRefPtr<EventTarget> mEventTarget;
|
||||
nsTArray<nsRefPtr<WorkerRunnable>> mPreStartRunnables;
|
||||
|
||||
private:
|
||||
WorkerPrivate* mParent;
|
||||
nsString mScriptURL;
|
||||
|
@ -322,7 +200,7 @@ private:
|
|||
LoadInfo mLoadInfo;
|
||||
|
||||
// Only used for top level workers.
|
||||
nsTArray<nsRefPtr<WorkerRunnable> > mQueuedRunnables;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
|
||||
nsRevocableEventPtr<SynchronizeAndResumeRunnable> mSynchronizeRunnable;
|
||||
|
||||
// Only for ChromeWorkers without window and only touched on the main thread.
|
||||
|
@ -376,6 +254,9 @@ private:
|
|||
bool aToMessagePort, uint64_t aMessagePortSerial,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
DispatchPrivate(WorkerRunnable* aRunnable, nsIEventTarget* aSyncLoopTarget);
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
@ -384,6 +265,21 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerPrivateParent,
|
||||
nsDOMEventTargetHelper)
|
||||
|
||||
nsresult
|
||||
Dispatch(WorkerRunnable* aRunnable)
|
||||
{
|
||||
return DispatchPrivate(aRunnable, nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DispatchControlRunnable(WorkerControlRunnable* aWorkerControlRunnable);
|
||||
|
||||
already_AddRefed<WorkerRunnable>
|
||||
MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
GetEventTarget();
|
||||
|
||||
// May be called on any thread...
|
||||
bool
|
||||
Start();
|
||||
|
@ -515,7 +411,7 @@ public:
|
|||
WorkerScriptLoaded();
|
||||
|
||||
void
|
||||
QueueRunnable(WorkerRunnable* aRunnable)
|
||||
QueueRunnable(nsIRunnable* aRunnable)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mQueuedRunnables.AppendElement(aRunnable);
|
||||
|
@ -538,13 +434,10 @@ public:
|
|||
IsAcceptingEvents()
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
bool acceptingEvents;
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
acceptingEvents = mParentStatus < Terminating;
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mParentStatus < Terminating;
|
||||
}
|
||||
return acceptingEvents;
|
||||
}
|
||||
|
||||
Status
|
||||
ParentStatus() const
|
||||
|
@ -775,53 +668,62 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
|||
{
|
||||
friend class WorkerPrivateParent<WorkerPrivate>;
|
||||
typedef WorkerPrivateParent<WorkerPrivate> ParentType;
|
||||
friend class AutoSyncLoopHolder;
|
||||
|
||||
struct TimeoutInfo;
|
||||
|
||||
typedef Queue<WorkerRunnable*, 50> EventQueue;
|
||||
EventQueue mQueue;
|
||||
EventQueue mControlQueue;
|
||||
|
||||
struct SyncQueue
|
||||
{
|
||||
Queue<WorkerRunnable*, 10> mQueue;
|
||||
bool mComplete;
|
||||
bool mResult;
|
||||
|
||||
SyncQueue()
|
||||
: mComplete(false), mResult(false)
|
||||
{ }
|
||||
|
||||
~SyncQueue()
|
||||
{
|
||||
WorkerRunnable* event;
|
||||
while (mQueue.Pop(event)) {
|
||||
event->Release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryReporter;
|
||||
friend class MemoryReporter;
|
||||
|
||||
nsTArray<nsAutoPtr<SyncQueue> > mSyncQueues;
|
||||
enum GCTimerMode
|
||||
{
|
||||
PeriodicTimer = 0,
|
||||
IdleTimer,
|
||||
NoTimer
|
||||
};
|
||||
|
||||
Queue<WorkerControlRunnable*, 4> mControlQueue;
|
||||
|
||||
// Touched on multiple threads, protected with mMutex.
|
||||
JSContext* mJSContext;
|
||||
nsRefPtr<WorkerCrossThreadDispatcher> mCrossThreadDispatcher;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
|
||||
// Things touched on worker thread only.
|
||||
nsRefPtr<WorkerGlobalScope> mScope;
|
||||
nsTArray<ParentType*> mChildWorkers;
|
||||
nsTArray<WorkerFeature*> mFeatures;
|
||||
nsTArray<nsAutoPtr<TimeoutInfo> > mTimeouts;
|
||||
nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
|
||||
|
||||
struct SyncLoopInfo
|
||||
{
|
||||
SyncLoopInfo(EventTarget* aEventTarget);
|
||||
|
||||
nsRefPtr<EventTarget> mEventTarget;
|
||||
bool mCompleted;
|
||||
bool mResult;
|
||||
#ifdef DEBUG
|
||||
bool mHasRun;
|
||||
#endif
|
||||
};
|
||||
|
||||
// This is only modified on the worker thread, but in DEBUG builds
|
||||
// AssertValidSyncLoop function iterates it on other threads. Therefore
|
||||
// modifications are done with mMutex held *only* in DEBUG builds.
|
||||
nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
|
||||
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
nsCOMPtr<nsITimer> mGCTimer;
|
||||
nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget;
|
||||
nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget;
|
||||
|
||||
nsRefPtr<MemoryReporter> mMemoryReporter;
|
||||
|
||||
nsRefPtrHashtable<nsUint64HashKey, MessagePort> mWorkerPorts;
|
||||
|
||||
mozilla::TimeStamp mKillTime;
|
||||
TimeStamp mKillTime;
|
||||
uint32_t mErrorHandlerRecursionCount;
|
||||
uint32_t mNextTimeoutId;
|
||||
Status mStatus;
|
||||
|
@ -832,9 +734,11 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
|||
bool mCloseHandlerFinished;
|
||||
bool mMemoryReporterRunning;
|
||||
bool mBlockedForMemoryReporter;
|
||||
bool mCancelAllPendingRunnables;
|
||||
bool mPeriodicGCTimerRunning;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
PRThread* mPRThread;
|
||||
#endif
|
||||
|
||||
bool mPreferences[WORKERPREF_COUNT];
|
||||
|
@ -867,27 +771,8 @@ public:
|
|||
bool
|
||||
OperationCallback(JSContext* aCx);
|
||||
|
||||
bool
|
||||
Dispatch(WorkerRunnable* aEvent)
|
||||
{
|
||||
return Dispatch(aEvent, &mQueue);
|
||||
}
|
||||
|
||||
bool
|
||||
Dispatch(WorkerSyncRunnable* aEvent)
|
||||
{
|
||||
if (aEvent->mBypassSyncQueue) {
|
||||
return Dispatch(aEvent, &mQueue);
|
||||
}
|
||||
|
||||
return DispatchToSyncQueue(aEvent);
|
||||
}
|
||||
|
||||
bool
|
||||
Dispatch(WorkerControlRunnable* aEvent)
|
||||
{
|
||||
return Dispatch(aEvent, &mControlQueue);
|
||||
}
|
||||
nsresult
|
||||
IsOnCurrentThread(bool* aIsOnCurrentThread);
|
||||
|
||||
bool
|
||||
CloseInternal(JSContext* aCx)
|
||||
|
@ -930,18 +815,6 @@ public:
|
|||
mFeatures.IsEmpty());
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CreateNewSyncLoop();
|
||||
|
||||
bool
|
||||
RunSyncLoop(JSContext* aCx, uint32_t aSyncLoopKey);
|
||||
|
||||
void
|
||||
StopSyncLoop(uint32_t aSyncLoopKey, bool aSyncResult);
|
||||
|
||||
void
|
||||
DestroySyncLoop(uint32_t aSyncLoopKey);
|
||||
|
||||
void
|
||||
PostMessageToParent(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
|
@ -1008,7 +881,7 @@ public:
|
|||
UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue);
|
||||
|
||||
void
|
||||
ScheduleDeletion(bool aWasPending);
|
||||
ScheduleDeletion();
|
||||
|
||||
bool
|
||||
BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats);
|
||||
|
@ -1042,18 +915,14 @@ public:
|
|||
return mScope;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
AssertIsOnWorkerThread() const;
|
||||
SetThread(nsIThread* aThread);
|
||||
|
||||
void
|
||||
SetThread(nsIThread* aThread)
|
||||
{
|
||||
mThread = aThread;
|
||||
}
|
||||
#else
|
||||
void
|
||||
AssertIsOnWorkerThread() const
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
{ }
|
||||
#endif
|
||||
|
||||
|
@ -1113,20 +982,37 @@ public:
|
|||
return mPreferences[WORKERPREF_PROMISE];
|
||||
}
|
||||
|
||||
void
|
||||
StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult);
|
||||
|
||||
bool
|
||||
AllPendingRunnablesShouldBeCanceled() const
|
||||
{
|
||||
return mCancelAllPendingRunnables;
|
||||
}
|
||||
|
||||
void
|
||||
OnProcessNextEvent(uint32_t aRecursionDepth);
|
||||
|
||||
void
|
||||
AfterProcessNextEvent(uint32_t aRecursionDepth);
|
||||
|
||||
void
|
||||
AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
{ }
|
||||
#endif
|
||||
|
||||
private:
|
||||
WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
WorkerType aWorkerType, const nsAString& aSharedWorkerName,
|
||||
LoadInfo& aLoadInfo);
|
||||
|
||||
bool
|
||||
Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
|
||||
|
||||
bool
|
||||
DispatchToSyncQueue(WorkerSyncRunnable* aEvent);
|
||||
|
||||
void
|
||||
ClearQueue(EventQueue* aQueue);
|
||||
ClearMainEventQueue();
|
||||
|
||||
bool
|
||||
MayContinueRunning()
|
||||
|
@ -1135,7 +1021,7 @@ private:
|
|||
|
||||
Status status;
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
MutexAutoLock lock(mMutex);
|
||||
status = mStatus;
|
||||
}
|
||||
|
||||
|
@ -1157,22 +1043,15 @@ private:
|
|||
bool
|
||||
ScheduleKillCloseEventRunnable(JSContext* aCx);
|
||||
|
||||
void
|
||||
StopAcceptingEvents()
|
||||
bool
|
||||
ProcessAllControlRunnables()
|
||||
{
|
||||
AssertIsOnWorkerThread();
|
||||
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
|
||||
mStatus = Dead;
|
||||
mJSContext = nullptr;
|
||||
|
||||
ClearQueue(&mControlQueue);
|
||||
ClearQueue(&mQueue);
|
||||
MutexAutoLock lock(mMutex);
|
||||
return ProcessAllControlRunnablesLocked();
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessAllControlRunnables();
|
||||
ProcessAllControlRunnablesLocked();
|
||||
|
||||
void
|
||||
EnableMemoryReporter();
|
||||
|
@ -1197,6 +1076,21 @@ private:
|
|||
AssertIsOnWorkerThread();
|
||||
memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool));
|
||||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
CreateNewSyncLoop();
|
||||
|
||||
bool
|
||||
RunCurrentSyncLoop();
|
||||
|
||||
void
|
||||
InitializeGCTimers();
|
||||
|
||||
void
|
||||
SetGCTimerMode(GCTimerMode aMode);
|
||||
|
||||
void
|
||||
ShutdownGCTimers();
|
||||
};
|
||||
|
||||
// This class is only used to trick the DOM bindings. We never create
|
||||
|
@ -1246,38 +1140,40 @@ ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime);
|
|||
|
||||
class AutoSyncLoopHolder
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
|
||||
public:
|
||||
AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate), mSyncLoopKey(UINT32_MAX)
|
||||
: mWorkerPrivate(aWorkerPrivate), mTarget(aWorkerPrivate->CreateNewSyncLoop())
|
||||
{
|
||||
mSyncLoopKey = mWorkerPrivate->CreateNewSyncLoop();
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
~AutoSyncLoopHolder()
|
||||
{
|
||||
if (mWorkerPrivate) {
|
||||
mWorkerPrivate->StopSyncLoop(mSyncLoopKey, false);
|
||||
mWorkerPrivate->DestroySyncLoop(mSyncLoopKey);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mWorkerPrivate->StopSyncLoop(mTarget, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RunAndForget(JSContext* aCx)
|
||||
Run()
|
||||
{
|
||||
WorkerPrivate* workerPrivate = mWorkerPrivate;
|
||||
mWorkerPrivate = nullptr;
|
||||
return workerPrivate->RunSyncLoop(aCx, mSyncLoopKey);
|
||||
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
return workerPrivate->RunCurrentSyncLoop();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SyncQueueKey() const
|
||||
nsIEventTarget*
|
||||
EventTarget() const
|
||||
{
|
||||
return mSyncLoopKey;
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
private:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
uint32_t mSyncLoopKey;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
|
|
@ -0,0 +1,480 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIRunnable.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Value.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
const nsIID kWorkerRunnableIID = {
|
||||
0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 }
|
||||
};
|
||||
|
||||
void
|
||||
MaybeReportMainThreadException(JSContext* aCx, bool aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (aCx && !aResult) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#ifdef DEBUG
|
||||
WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
|
||||
mCallingCancelWithinRun(false)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
MOZ_ASSERT(aCx);
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
return aWorkerPrivate->ModifyBusyCount(aCx, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::Dispatch(JSContext* aCx)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
if (!aCx) {
|
||||
ok = PreDispatch(nullptr, mWorkerPrivate);
|
||||
if (ok) {
|
||||
ok = DispatchInternal();
|
||||
}
|
||||
PostDispatch(nullptr, mWorkerPrivate, ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (global) {
|
||||
ac.construct(aCx, global);
|
||||
}
|
||||
|
||||
ok = PreDispatch(aCx, mWorkerPrivate);
|
||||
|
||||
if (ok && !DispatchInternal()) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
PostDispatch(aCx, mWorkerPrivate, ok);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::DispatchInternal()
|
||||
{
|
||||
if (mBehavior == WorkerThreadModifyBusyCount ||
|
||||
mBehavior == WorkerThreadUnchangedBusyCount) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
|
||||
|
||||
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
|
||||
return NS_SUCCEEDED(parent->Dispatch(this));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
MOZ_ASSERT(mainThread);
|
||||
|
||||
return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
WorkerRunnable::PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
#ifdef DEBUG
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
MOZ_ASSERT(aCx);
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!aDispatchResult) {
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
aWorkerPrivate->ModifyBusyCount(aCx, false);
|
||||
}
|
||||
if (aCx) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aRunResult)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
#ifdef DEBUG
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
|
||||
aRunResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aRunResult) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
WorkerRunnable*
|
||||
WorkerRunnable::FromRunnable(nsIRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
WorkerRunnable* runnable;
|
||||
nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
|
||||
reinterpret_cast<void**>(&runnable));
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(runnable);
|
||||
return runnable;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(WorkerRunnable)
|
||||
NS_IMPL_RELEASE(WorkerRunnable)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
// kWorkerRunnableIID is special in that it does not AddRef its result.
|
||||
if (aIID.Equals(kWorkerRunnableIID)) {
|
||||
*aInstancePtr = this;
|
||||
return NS_OK;
|
||||
}
|
||||
else
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerRunnable::Run()
|
||||
{
|
||||
bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
|
||||
mBehavior == WorkerThreadUnchangedBusyCount;
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
|
||||
if (targetIsWorkerThread) {
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
else {
|
||||
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
|
||||
mWorkerPrivate->AssertIsOnParentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsCanceled() && !mCallingCancelWithinRun) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSContext* cx;
|
||||
nsRefPtr<WorkerPrivate> kungFuDeathGrip;
|
||||
nsCxPusher pusher;
|
||||
|
||||
if (targetIsWorkerThread) {
|
||||
if (mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
|
||||
!IsCanceled() &&
|
||||
!mCallingCancelWithinRun) {
|
||||
|
||||
// Prevent recursion.
|
||||
mCallingCancelWithinRun = true;
|
||||
|
||||
Cancel();
|
||||
|
||||
MOZ_ASSERT(mCallingCancelWithinRun);
|
||||
mCallingCancelWithinRun = false;
|
||||
|
||||
MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
cx = mWorkerPrivate->GetJSContext();
|
||||
MOZ_ASSERT(cx);
|
||||
}
|
||||
else {
|
||||
cx = mWorkerPrivate->ParentJSContext();
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
kungFuDeathGrip = mWorkerPrivate;
|
||||
|
||||
if (!mWorkerPrivate->GetParent()) {
|
||||
AssertIsOnMainThread();
|
||||
pusher.Push(cx);
|
||||
}
|
||||
}
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JS::Rooted<JSObject*> targetCompartmentObject(cx);
|
||||
if (targetIsWorkerThread) {
|
||||
targetCompartmentObject = JS::CurrentGlobalOrNull(cx);
|
||||
} else {
|
||||
targetCompartmentObject = mWorkerPrivate->GetWrapper();
|
||||
}
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (targetCompartmentObject) {
|
||||
ac.construct(cx, targetCompartmentObject);
|
||||
}
|
||||
|
||||
bool result = WorkerRun(cx, mWorkerPrivate);
|
||||
|
||||
// In the case of CompileScriptRunnnable, WorkerRun above can cause us to
|
||||
// lazily create a global, in which case we need to be in its compartment
|
||||
// when calling PostRun() below. Maybe<> this time...
|
||||
if (targetIsWorkerThread &&
|
||||
ac.empty() &&
|
||||
js::DefaultObjectForContextOrNull(cx)) {
|
||||
ac.construct(cx, js::DefaultObjectForContextOrNull(cx));
|
||||
}
|
||||
|
||||
PostRun(cx, mWorkerPrivate, result);
|
||||
|
||||
return result ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerRunnable::Cancel()
|
||||
{
|
||||
uint32_t canceledCount = ++mCanceled;
|
||||
|
||||
MOZ_ASSERT(canceledCount, "Cancel() overflow!");
|
||||
|
||||
// The docs say that Cancel() should not be called more than once and that we
|
||||
// should throw NS_ERROR_UNEXPECTED if it is.
|
||||
return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mSyncLoopTarget(aSyncLoopTarget)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (mSyncLoopTarget) {
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::WorkerSyncRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mSyncLoopTarget(aSyncLoopTarget)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (mSyncLoopTarget) {
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::~WorkerSyncRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerSyncRunnable::DispatchInternal()
|
||||
{
|
||||
if (mSyncLoopTarget) {
|
||||
return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
return WorkerRunnable::DispatchInternal();
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadWorkerSyncRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MaybeReportMainThreadException(aCx, aDispatchResult);
|
||||
}
|
||||
|
||||
StopSyncLoopRunnable::StopSyncLoopRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget,
|
||||
bool aResult)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget), mResult(aResult)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopSyncLoopRunnable::Cancel()
|
||||
{
|
||||
nsresult rv = Run();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
StopSyncLoopRunnable::WorkerRun(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> syncLoopTarget;
|
||||
mSyncLoopTarget.swap(syncLoopTarget);
|
||||
|
||||
if (!mResult) {
|
||||
MaybeSetException(aCx);
|
||||
}
|
||||
|
||||
aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StopSyncLoopRunnable::DispatchInternal()
|
||||
{
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadStopSyncLoopRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MaybeReportMainThreadException(aCx, aDispatchResult);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
|
||||
aBehavior == WorkerThreadUnchangedBusyCount,
|
||||
"WorkerControlRunnables should not modify the busy count");
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
WorkerControlRunnable::DispatchInternal()
|
||||
{
|
||||
if (mBehavior == WorkerThreadUnchangedBusyCount) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(this));
|
||||
}
|
||||
|
||||
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
|
||||
return NS_SUCCEEDED(parent->DispatchControlRunnable(this));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
MOZ_ASSERT(mainThread);
|
||||
|
||||
return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadWorkerControlRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (aCx && !aDispatchResult) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
|
|
@ -0,0 +1,319 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_workerrunnable_h__
|
||||
#define mozilla_dom_workers_workerrunnable_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "nsICancelableRunnable.h"
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
class JSContext;
|
||||
class nsIEventTarget;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class WorkerPrivate;
|
||||
|
||||
// Use this runnable to communicate from the worker to its parent or vice-versa.
|
||||
// The busy count must be taken into consideration and declared at construction
|
||||
// time.
|
||||
class WorkerRunnable : public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
enum TargetAndBusyBehavior {
|
||||
// Target the main thread for top-level workers, otherwise target the
|
||||
// WorkerThread of the worker's parent. No change to the busy count.
|
||||
ParentThreadUnchangedBusyCount,
|
||||
|
||||
// Target the thread where the worker event loop runs. The busy count will
|
||||
// be incremented before dispatching and decremented (asynchronously) after
|
||||
// running.
|
||||
WorkerThreadModifyBusyCount,
|
||||
|
||||
// Target the thread where the worker event loop runs. The busy count will
|
||||
// not be modified in any way. Besides worker-internal runnables this is
|
||||
// almost always the wrong choice.
|
||||
WorkerThreadUnchangedBusyCount
|
||||
};
|
||||
|
||||
protected:
|
||||
// The WorkerPrivate that this runnable is associated with.
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// See above.
|
||||
TargetAndBusyBehavior mBehavior;
|
||||
|
||||
// It's unclear whether or not Cancel() is supposed to work when called on any
|
||||
// thread. To be safe we're using an atomic but it's likely overkill.
|
||||
Atomic<uint32_t> mCanceled;
|
||||
|
||||
private:
|
||||
// Whether or not Cancel() is currently being called from inside the Run()
|
||||
// method. Avoids infinite recursion when a subclass calls Run() from inside
|
||||
// Cancel(). Only checked and modified on the target thread.
|
||||
bool mCallingCancelWithinRun;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
// If you override Cancel() then you'll need to either call the base class
|
||||
// Cancel() method or override IsCanceled() so that the Run() method bails out
|
||||
// appropriately.
|
||||
NS_DECL_NSICANCELABLERUNNABLE
|
||||
|
||||
// Passing a JSContext here is required for the WorkerThreadModifyBusyCount
|
||||
// behavior. It also guarantees that any failure (false return) will throw an
|
||||
// exception on the given context. If a context is not passed then failures
|
||||
// must be dealt with by the caller.
|
||||
bool
|
||||
Dispatch(JSContext* aCx);
|
||||
|
||||
// See above note about Cancel().
|
||||
virtual bool
|
||||
IsCanceled() const
|
||||
{
|
||||
return mCanceled != 0;
|
||||
}
|
||||
|
||||
static WorkerRunnable*
|
||||
FromRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
protected:
|
||||
WorkerRunnable(WorkerPrivate* aWorkerPrivate, TargetAndBusyBehavior aBehavior)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
|
||||
mCallingCancelWithinRun(false)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
// This class is reference counted.
|
||||
virtual ~WorkerRunnable()
|
||||
{ }
|
||||
|
||||
// By default asserts that Dispatch() is being called on the right thread
|
||||
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
|
||||
// Also increments the busy count of |mWorkerPrivate| if targeting the
|
||||
// WorkerThread.
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// By default asserts that Dispatch() is being called on the right thread
|
||||
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
|
||||
// Also reports any Dispatch() failures as an exception on |aCx|, and
|
||||
// busy count if targeting the WorkerThread and Dispatch() failed.
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult);
|
||||
|
||||
// Must be implemented by subclasses. Called on the target thread.
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
|
||||
|
||||
// By default asserts that Run() (and WorkerRun()) were called on the correct
|
||||
// thread. Any failures (false return from WorkerRun) are reported on |aCx|.
|
||||
// Also sends an asynchronous message to the ParentThread if the busy
|
||||
// count was previously modified in PreDispatch().
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
|
||||
|
||||
virtual bool
|
||||
DispatchInternal();
|
||||
|
||||
// Calling Run() directly is not supported. Just call Dispatch() and
|
||||
// WorkerRun() will be called on the correct thread automatically.
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
// This runnable is used to send a message directly to a worker's sync loop.
|
||||
class WorkerSyncRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
||||
// Passing null for aSyncLoopTarget is allowed and will result in the behavior
|
||||
// of a normal WorkerRunnable.
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget);
|
||||
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget);
|
||||
|
||||
virtual ~WorkerSyncRunnable();
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is identical to WorkerSyncRunnable except it is meant to be
|
||||
// used on the main thread only.
|
||||
class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
protected:
|
||||
// Passing null for aSyncLoopTarget is allowed and will result in the behavior
|
||||
// of a normal WorkerRunnable.
|
||||
MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
virtual ~MainThreadWorkerSyncRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is used to stop a sync loop . As sync loops keep the busy count
|
||||
// incremented as long as they run this runnable does not modify the busy count
|
||||
// in any way.
|
||||
class StopSyncLoopRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
bool mResult;
|
||||
|
||||
public:
|
||||
// Passing null for aSyncLoopTarget is not allowed.
|
||||
StopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget,
|
||||
bool aResult);
|
||||
|
||||
// By default StopSyncLoopRunnables cannot be canceled since they could leave
|
||||
// a sync loop spinning forever.
|
||||
NS_DECL_NSICANCELABLERUNNABLE
|
||||
|
||||
protected:
|
||||
virtual ~StopSyncLoopRunnable()
|
||||
{ }
|
||||
|
||||
// Called on the worker thread to set an exception on the context if mResult
|
||||
// is false. Override if you need an exception.
|
||||
virtual void
|
||||
MaybeSetException(JSContext* aCx)
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is identical to StopSyncLoopRunnable except it is meant to be
|
||||
// used on the main thread only.
|
||||
class MainThreadStopSyncLoopRunnable : public StopSyncLoopRunnable
|
||||
{
|
||||
public:
|
||||
// Passing null for aSyncLoopTarget is not allowed.
|
||||
MainThreadStopSyncLoopRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget,
|
||||
bool aResult)
|
||||
: StopSyncLoopRunnable(aWorkerPrivate, aSyncLoopTarget, aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~MainThreadStopSyncLoopRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is processed as soon as it is received by the worker,
|
||||
// potentially running before previously queued runnables and perhaps even with
|
||||
// other JS code executing on the stack. These runnables must not alter the
|
||||
// state of the JS runtime and should only twiddle state values. The busy count
|
||||
// is never modified.
|
||||
class WorkerControlRunnable : public WorkerRunnable
|
||||
{
|
||||
friend class WorkerPrivate;
|
||||
|
||||
protected:
|
||||
WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
virtual ~WorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
|
||||
// Should only be called by WorkerPrivate::DoRunLoop.
|
||||
using WorkerRunnable::Cancel;
|
||||
};
|
||||
|
||||
// A convenience class for WorkerControlRunnables that originate on the main
|
||||
// thread.
|
||||
class MainThreadWorkerControlRunnable : public WorkerControlRunnable
|
||||
{
|
||||
protected:
|
||||
MainThreadWorkerControlRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
{ }
|
||||
|
||||
virtual ~MainThreadWorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workerrunnable_h__
|
|
@ -184,39 +184,50 @@ SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
|
|||
void
|
||||
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
class WorkerTask {
|
||||
class WorkerTask
|
||||
{
|
||||
protected:
|
||||
WorkerTask()
|
||||
{ }
|
||||
|
||||
virtual ~WorkerTask()
|
||||
{ }
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerTask)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerTask)
|
||||
|
||||
virtual ~WorkerTask() { }
|
||||
|
||||
virtual bool RunTask(JSContext* aCx) = 0;
|
||||
virtual bool
|
||||
RunTask(JSContext* aCx) = 0;
|
||||
};
|
||||
|
||||
class WorkerCrossThreadDispatcher {
|
||||
class WorkerCrossThreadDispatcher
|
||||
{
|
||||
friend class WorkerPrivate;
|
||||
|
||||
// Must be acquired *before* the WorkerPrivate's mutex, when they're both
|
||||
// held.
|
||||
Mutex mMutex;
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
private:
|
||||
// Only created by WorkerPrivate.
|
||||
WorkerCrossThreadDispatcher(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// Only called by WorkerPrivate.
|
||||
void
|
||||
Forget()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerCrossThreadDispatcher)
|
||||
|
||||
WorkerCrossThreadDispatcher(WorkerPrivate* aPrivate) :
|
||||
mMutex("WorkerCrossThreadDispatcher"), mPrivate(aPrivate) {}
|
||||
void Forget()
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
mPrivate = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generically useful function for running a bit of C++ code on the worker
|
||||
* thread.
|
||||
*/
|
||||
bool PostTask(WorkerTask* aTask);
|
||||
|
||||
protected:
|
||||
friend class WorkerPrivate;
|
||||
|
||||
// Must be acquired *before* the WorkerPrivate's mutex, when they're both held.
|
||||
mozilla::Mutex mMutex;
|
||||
WorkerPrivate* mPrivate;
|
||||
// Generically useful function for running a bit of C++ code on the worker
|
||||
// thread.
|
||||
bool
|
||||
PostTask(WorkerTask* aTask);
|
||||
};
|
||||
|
||||
WorkerCrossThreadDispatcher*
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -22,8 +22,8 @@ class Proxy;
|
|||
class XMLHttpRequestUpload;
|
||||
class WorkerPrivate;
|
||||
|
||||
class XMLHttpRequest : public nsXHREventTarget,
|
||||
public WorkerFeature
|
||||
class XMLHttpRequest MOZ_FINAL: public nsXHREventTarget,
|
||||
public WorkerFeature
|
||||
{
|
||||
public:
|
||||
struct StateData
|
||||
|
@ -61,10 +61,6 @@ private:
|
|||
bool mMozAnon;
|
||||
bool mMozSystem;
|
||||
|
||||
protected:
|
||||
XMLHttpRequest(WorkerPrivate* aWorkerPrivate);
|
||||
virtual ~XMLHttpRequest();
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
@ -272,6 +268,9 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
XMLHttpRequest(WorkerPrivate* aWorkerPrivate);
|
||||
~XMLHttpRequest();
|
||||
|
||||
enum ReleaseType { Default, XHRIsGoingAway, WorkerIsGoingAway };
|
||||
|
||||
void
|
||||
|
|
|
@ -9,6 +9,7 @@ TEST_DIRS += ['test']
|
|||
# Public stuff.
|
||||
EXPORTS.mozilla.dom += [
|
||||
'WorkerPrivate.h',
|
||||
'WorkerRunnable.h',
|
||||
'WorkerScope.h',
|
||||
]
|
||||
|
||||
|
@ -43,6 +44,7 @@ SOURCES += [
|
|||
'SharedWorker.cpp',
|
||||
'URL.cpp',
|
||||
'WorkerPrivate.cpp',
|
||||
'WorkerRunnable.cpp',
|
||||
'WorkerScope.cpp',
|
||||
'XMLHttpRequest.cpp',
|
||||
'XMLHttpRequestUpload.cpp',
|
||||
|
@ -58,6 +60,7 @@ LOCAL_INCLUDES += [
|
|||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/xpcom/build',
|
||||
'/xpcom/threads',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* 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/. */
|
||||
|
||||
/* A handy class that will allocate data for size*T objects on the stack and
|
||||
* otherwise allocate them on the heap. It is similar in purpose to nsAutoTArray */
|
||||
|
||||
template <class T, size_t size>
|
||||
class StackArray
|
||||
{
|
||||
public:
|
||||
StackArray(size_t count) {
|
||||
if (count > size) {
|
||||
mData = new T[count];
|
||||
} else {
|
||||
mData = mStackData;
|
||||
}
|
||||
}
|
||||
~StackArray() {
|
||||
if (mData != mStackData) {
|
||||
delete[] mData;
|
||||
}
|
||||
}
|
||||
T& operator[](size_t n) { return mData[n]; }
|
||||
const T& operator[](size_t n) const { return mData[n]; }
|
||||
T* data() { return mData; };
|
||||
private:
|
||||
T mStackData[size];
|
||||
T* mData;
|
||||
};
|
|
@ -153,17 +153,18 @@ YCbCrImageDataSerializer::ComputeMinBufferSize(uint32_t aSize)
|
|||
}
|
||||
|
||||
void
|
||||
YCbCrImageDataSerializer::InitializeBufferInfo(const gfx::IntSize& aYSize,
|
||||
YCbCrImageDataSerializer::InitializeBufferInfo(uint32_t aYOffset,
|
||||
uint32_t aCbOffset,
|
||||
uint32_t aCrOffset,
|
||||
const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode)
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData);
|
||||
info->mYOffset = MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo));
|
||||
info->mCbOffset = info->mYOffset
|
||||
+ MOZ_ALIGN_WORD(aYSize.width * aYSize.height);
|
||||
info->mCrOffset = info->mCbOffset
|
||||
+ MOZ_ALIGN_WORD(aCbCrSize.width * aCbCrSize.height);
|
||||
|
||||
uint32_t info_size = MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo));
|
||||
info->mYOffset = info_size + aYOffset;
|
||||
info->mCbOffset = info_size + aCbOffset;
|
||||
info->mCrOffset = info_size + aCrOffset;
|
||||
info->mYWidth = aYSize.width;
|
||||
info->mYHeight = aYSize.height;
|
||||
info->mCbCrWidth = aCbCrSize.width;
|
||||
|
@ -171,6 +172,17 @@ YCbCrImageDataSerializer::InitializeBufferInfo(const gfx::IntSize& aYSize,
|
|||
info->mStereoMode = aStereoMode;
|
||||
}
|
||||
|
||||
void
|
||||
YCbCrImageDataSerializer::InitializeBufferInfo(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode)
|
||||
{
|
||||
uint32_t yOffset = 0;
|
||||
uint32_t cbOffset = yOffset + MOZ_ALIGN_WORD(aYSize.width * aYSize.height);
|
||||
uint32_t crOffset = cbOffset + MOZ_ALIGN_WORD(aCbCrSize.width * aCbCrSize.height);
|
||||
return InitializeBufferInfo(yOffset, cbOffset, crOffset, aYSize, aCbCrSize, aStereoMode);
|
||||
}
|
||||
|
||||
void
|
||||
YCbCrImageDataSerializer::InitializeBufferInfo(const gfxIntSize& aYSize,
|
||||
const gfxIntSize& aCbCrSize,
|
||||
|
|
|
@ -114,6 +114,12 @@ public:
|
|||
* The provided pointer should point to the beginning of the (chunk of)
|
||||
* buffer on which we want to store the image.
|
||||
*/
|
||||
void InitializeBufferInfo(uint32_t aYOffset,
|
||||
uint32_t aCbOffset,
|
||||
uint32_t aCrOffset,
|
||||
const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode);
|
||||
void InitializeBufferInfo(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode);
|
||||
|
|
|
@ -1213,21 +1213,6 @@ const CSSRect AsyncPanZoomController::CalculatePendingDisplayPort(
|
|||
scrollOffset.y = scrollableRect.y;
|
||||
}
|
||||
|
||||
// FIXME/bug 936500: Make sure the displayport contains the composition
|
||||
// bounds. This is to work around a layout bug that means if a display item's
|
||||
// corresponding displayport doesn't contain its frame's bounds, it may get
|
||||
// optimised out and the layer won't get created.
|
||||
if (displayPort.x + displayPort.width < compositionBounds.width) {
|
||||
displayPort.x = -(displayPort.width - compositionBounds.width);
|
||||
} else if (displayPort.x > 0) {
|
||||
displayPort.x = 0;
|
||||
}
|
||||
if (displayPort.y + displayPort.height < compositionBounds.height) {
|
||||
displayPort.y = -(displayPort.height - compositionBounds.height);
|
||||
} else if (displayPort.y > 0) {
|
||||
displayPort.y = 0;
|
||||
}
|
||||
|
||||
CSSRect shiftedDisplayPort = displayPort + scrollOffset;
|
||||
return scrollableRect.ClampRect(shiftedDisplayPort) - scrollOffset;
|
||||
}
|
||||
|
|
|
@ -143,8 +143,21 @@ SharedPlanarYCbCrImage::SetDataNoCopy(const Data &aData)
|
|||
{
|
||||
mData = aData;
|
||||
mSize = aData.mPicSize;
|
||||
/* SetDataNoCopy is used to update YUV plane offsets without (re)allocating
|
||||
* memory previously allocated with AllocateAndGetNewBuffer().
|
||||
* serializer.GetData() returns the address of the memory previously allocated
|
||||
* with AllocateAndGetNewBuffer(), that we subtract from the Y, Cb, Cr
|
||||
* channels to compute 0-based offsets to pass to InitializeBufferInfo.
|
||||
*/
|
||||
YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer());
|
||||
serializer.InitializeBufferInfo(aData.mYSize,
|
||||
uint8_t *base = serializer.GetData();
|
||||
uint32_t yOffset = aData.mYChannel - base;
|
||||
uint32_t cbOffset = aData.mCbChannel - base;
|
||||
uint32_t crOffset = aData.mCrChannel - base;
|
||||
serializer.InitializeBufferInfo(yOffset,
|
||||
cbOffset,
|
||||
crOffset,
|
||||
aData.mYSize,
|
||||
aData.mCbCrSize,
|
||||
aData.mStereoMode);
|
||||
}
|
||||
|
|
|
@ -78,16 +78,13 @@ bool
|
|||
EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut)
|
||||
{
|
||||
// Determine byte order.
|
||||
if (MatchString("MM", 2))
|
||||
if (MatchString("MM\0*", 4))
|
||||
mByteOrder = ByteOrder::BigEndian;
|
||||
else if (MatchString("II", 2))
|
||||
else if (MatchString("II*\0", 4))
|
||||
mByteOrder = ByteOrder::LittleEndian;
|
||||
else
|
||||
return false;
|
||||
|
||||
if (!MatchString("\0*", 2))
|
||||
return false;
|
||||
|
||||
// Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
|
||||
// is the maximum size of the entry APP1 segment.)
|
||||
uint32_t ifd0Offset;
|
||||
|
|
|
@ -397,7 +397,7 @@ IntlInitialize(JSContext *cx, HandleObject obj, Handle<PropertyName*> initialize
|
|||
HandleValue locales, HandleValue options)
|
||||
{
|
||||
RootedValue initializerValue(cx);
|
||||
if (!cx->global()->getIntrinsicValue(cx, initializer, &initializerValue))
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), initializer, &initializerValue))
|
||||
return false;
|
||||
JS_ASSERT(initializerValue.isObject());
|
||||
JS_ASSERT(initializerValue.toObject().is<JSFunction>());
|
||||
|
@ -463,7 +463,7 @@ static bool
|
|||
GetInternals(JSContext *cx, HandleObject obj, MutableHandleObject internals)
|
||||
{
|
||||
RootedValue getInternalsValue(cx);
|
||||
if (!cx->global()->getIntrinsicValue(cx, cx->names().getInternals, &getInternalsValue))
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals, &getInternalsValue))
|
||||
return false;
|
||||
JS_ASSERT(getInternalsValue.isObject());
|
||||
JS_ASSERT(getInternalsValue.toObject().is<JSFunction>());
|
||||
|
@ -690,7 +690,7 @@ InitCollatorClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global
|
|||
* passing to methods like Array.prototype.sort).
|
||||
*/
|
||||
RootedValue getter(cx);
|
||||
if (!cx->global()->getIntrinsicValue(cx, cx->names().CollatorCompareGet, &getter))
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CollatorCompareGet, &getter))
|
||||
return nullptr;
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
if (!JSObject::defineProperty(cx, proto, cx->names().compare, undefinedValue,
|
||||
|
@ -1178,7 +1178,7 @@ InitNumberFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> gl
|
|||
* for passing to methods like Array.prototype.map).
|
||||
*/
|
||||
RootedValue getter(cx);
|
||||
if (!cx->global()->getIntrinsicValue(cx, cx->names().NumberFormatFormatGet, &getter))
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().NumberFormatFormatGet, &getter))
|
||||
return nullptr;
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
if (!JSObject::defineProperty(cx, proto, cx->names().format, undefinedValue,
|
||||
|
@ -1635,7 +1635,7 @@ InitDateTimeFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*>
|
|||
* (suitable for passing to methods like Array.prototype.map).
|
||||
*/
|
||||
RootedValue getter(cx);
|
||||
if (!cx->global()->getIntrinsicValue(cx, cx->names().DateTimeFormatFormatGet, &getter))
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().DateTimeFormatFormatGet, &getter))
|
||||
return nullptr;
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
if (!JSObject::defineProperty(cx, proto, cx->names().format, undefinedValue,
|
||||
|
|
|
@ -709,18 +709,18 @@ AllLocalsAliased(StaticBlockObject &obj)
|
|||
#endif
|
||||
|
||||
static bool
|
||||
ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, StaticBlockObject &blockObj)
|
||||
ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
|
||||
{
|
||||
uint32_t depthPlusFixed = blockObj.stackDepth();
|
||||
uint32_t depthPlusFixed = blockObj->stackDepth();
|
||||
if (!AdjustBlockSlot(cx, bce, &depthPlusFixed))
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < blockObj.slotCount(); i++) {
|
||||
Definition *dn = blockObj.maybeDefinitionParseNode(i);
|
||||
for (unsigned i = 0; i < blockObj->slotCount(); i++) {
|
||||
Definition *dn = blockObj->maybeDefinitionParseNode(i);
|
||||
|
||||
/* Beware the empty destructuring dummy. */
|
||||
if (!dn) {
|
||||
blockObj.setAliased(i, bce->sc->allLocalsAliased());
|
||||
blockObj->setAliased(i, bce->sc->allLocalsAliased());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -738,10 +738,10 @@ ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, StaticBlockObjec
|
|||
}
|
||||
#endif
|
||||
|
||||
blockObj.setAliased(i, bce->isAliasedName(dn));
|
||||
blockObj->setAliased(i, bce->isAliasedName(dn));
|
||||
}
|
||||
|
||||
JS_ASSERT_IF(bce->sc->allLocalsAliased(), AllLocalsAliased(blockObj));
|
||||
JS_ASSERT_IF(bce->sc->allLocalsAliased(), AllLocalsAliased(*blockObj));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -809,18 +809,18 @@ EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, O
|
|||
parent = stmt->blockScopeIndex;
|
||||
}
|
||||
|
||||
StaticBlockObject &blockObj = objbox->object->as<StaticBlockObject>();
|
||||
Rooted<StaticBlockObject *> blockObj(cx, &objbox->object->as<StaticBlockObject>());
|
||||
|
||||
uint32_t scopeObjectIndex = bce->objectList.add(objbox);
|
||||
|
||||
int depth = bce->stackDepth - (blockObj.slotCount() + extraSlots);
|
||||
int depth = bce->stackDepth - (blockObj->slotCount() + extraSlots);
|
||||
JS_ASSERT(depth >= 0);
|
||||
blockObj.setStackDepth(depth);
|
||||
blockObj->setStackDepth(depth);
|
||||
|
||||
if (!ComputeAliasedSlots(cx, bce, blockObj))
|
||||
return false;
|
||||
|
||||
if (blockObj.needsClone()) {
|
||||
if (blockObj->needsClone()) {
|
||||
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
|
||||
return false;
|
||||
}
|
||||
|
@ -830,8 +830,8 @@ EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, O
|
|||
return false;
|
||||
|
||||
PushStatementBCE(bce, stmt, STMT_BLOCK, bce->offset());
|
||||
blockObj.initEnclosingStaticScope(EnclosingStaticScope(bce));
|
||||
FinishPushBlockScope(bce, stmt, blockObj);
|
||||
blockObj->initEnclosingStaticScope(EnclosingStaticScope(bce));
|
||||
FinishPushBlockScope(bce, stmt, *blockObj);
|
||||
|
||||
JS_ASSERT(stmt->isBlockScope);
|
||||
|
||||
|
@ -2384,7 +2384,7 @@ EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
* If there are hoisted let declarations, their stack slots go under the
|
||||
* discriminant's value so push their slots now and enter the block later.
|
||||
*/
|
||||
StaticBlockObject *blockObj = nullptr;
|
||||
Rooted<StaticBlockObject *> blockObj(cx, nullptr);
|
||||
if (pn2->isKind(PNK_LEXICALSCOPE)) {
|
||||
blockObj = &pn2->pn_objbox->object->as<StaticBlockObject>();
|
||||
for (uint32_t i = 0; i < blockObj->slotCount(); ++i) {
|
||||
|
@ -2804,7 +2804,13 @@ frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNo
|
|||
/* Initialize fun->script() so that the debugger has a valid fun->script(). */
|
||||
RootedFunction fun(cx, bce->script->function());
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
fun->setScript(bce->script);
|
||||
|
||||
if (fun->isInterpretedLazy()) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
fun->setUnlazifiedScript(bce->script);
|
||||
} else {
|
||||
fun->setScript(bce->script);
|
||||
}
|
||||
|
||||
bce->tellDebuggerAboutCompiledScript(cx);
|
||||
|
||||
|
|
|
@ -2853,17 +2853,18 @@ Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data,
|
|||
if (pc->sc->isFunctionBox()) {
|
||||
FunctionBox *funbox = pc->sc->asFunctionBox();
|
||||
funbox->setMightAliasLocals();
|
||||
|
||||
/*
|
||||
* This definition isn't being added to the parse context's
|
||||
* declarations, so make sure to indicate the need to deoptimize
|
||||
* the script's arguments object. Mark the function as if it
|
||||
* contained a debugger statement, which will deoptimize arguments
|
||||
* as much as possible.
|
||||
*/
|
||||
if (name == cx->names().arguments)
|
||||
funbox->setHasDebuggerStatement();
|
||||
}
|
||||
|
||||
/*
|
||||
* This definition isn't being added to the parse context's
|
||||
* declarations, so make sure to indicate the need to deoptimize
|
||||
* the script's arguments object. Mark the function as if it
|
||||
* contained a debugger statement, which will deoptimize arguments
|
||||
* as much as possible.
|
||||
*/
|
||||
if (name == cx->names().arguments)
|
||||
pc->sc->setHasDebuggerStatement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1129,23 +1129,17 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
|
|||
PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
|
||||
}
|
||||
|
||||
if (TaggedProto(type->proto).isObject())
|
||||
PushMarkStack(gcmarker, type->proto);
|
||||
if (type->proto().isObject())
|
||||
PushMarkStack(gcmarker, type->proto().toObject());
|
||||
|
||||
if (type->singleton && !type->lazy())
|
||||
PushMarkStack(gcmarker, type->singleton);
|
||||
|
||||
if (type->addendum) {
|
||||
switch (type->addendum->kind) {
|
||||
case types::TypeObjectAddendum::NewScript:
|
||||
PushMarkStack(gcmarker, type->newScript()->fun);
|
||||
PushMarkStack(gcmarker, type->newScript()->templateObject);
|
||||
break;
|
||||
|
||||
case types::TypeObjectAddendum::TypedObject:
|
||||
PushMarkStack(gcmarker, type->typedObject()->typeRepr->ownerObject());
|
||||
break;
|
||||
}
|
||||
if (type->hasNewScript()) {
|
||||
PushMarkStack(gcmarker, type->newScript()->fun);
|
||||
PushMarkStack(gcmarker, type->newScript()->templateObject);
|
||||
} else if (type->hasTypedObject()) {
|
||||
PushMarkStack(gcmarker, type->typedObject()->typeRepr->ownerObject());
|
||||
}
|
||||
|
||||
if (type->interpretedFunction)
|
||||
|
@ -1162,23 +1156,17 @@ gc::MarkChildren(JSTracer *trc, types::TypeObject *type)
|
|||
MarkId(trc, &prop->id, "type_prop");
|
||||
}
|
||||
|
||||
if (TaggedProto(type->proto).isObject())
|
||||
MarkObject(trc, &type->proto, "type_proto");
|
||||
if (type->proto().isObject())
|
||||
MarkObject(trc, &type->protoRaw(), "type_proto");
|
||||
|
||||
if (type->singleton && !type->lazy())
|
||||
MarkObject(trc, &type->singleton, "type_singleton");
|
||||
|
||||
if (type->addendum) {
|
||||
switch (type->addendum->kind) {
|
||||
case types::TypeObjectAddendum::NewScript:
|
||||
MarkObject(trc, &type->newScript()->fun, "type_new_function");
|
||||
MarkObject(trc, &type->newScript()->templateObject, "type_new_template");
|
||||
break;
|
||||
|
||||
case types::TypeObjectAddendum::TypedObject:
|
||||
type->typedObject()->typeRepr->mark(trc);
|
||||
break;
|
||||
}
|
||||
if (type->hasNewScript()) {
|
||||
MarkObject(trc, &type->newScript()->fun, "type_new_function");
|
||||
MarkObject(trc, &type->newScript()->templateObject, "type_new_template");
|
||||
} else if (type->hasTypedObject()) {
|
||||
type->typedObject()->typeRepr->mark(trc);
|
||||
}
|
||||
|
||||
if (type->interpretedFunction)
|
||||
|
@ -1441,7 +1429,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
|||
PushMarkStack(this, shape);
|
||||
|
||||
/* Call the trace hook if necessary. */
|
||||
const Class *clasp = type->clasp;
|
||||
const Class *clasp = type->clasp();
|
||||
if (clasp->trace) {
|
||||
JS_ASSERT_IF(runtime->gcMode() == JSGC_MODE_INCREMENTAL &&
|
||||
runtime->gcIncrementalEnabled,
|
||||
|
|
|
@ -47,7 +47,7 @@ MarkExactStackRoot(JSTracer *trc, Rooted<void*> *rooter, ThingRootKind kind)
|
|||
if (IsNullTaggedPointer(*addr))
|
||||
return;
|
||||
|
||||
if (kind == THING_ROOT_OBJECT && *addr == Proxy::LazyProto)
|
||||
if (kind == THING_ROOT_OBJECT && *addr == TaggedProto::LazyProto)
|
||||
return;
|
||||
|
||||
switch (kind) {
|
||||
|
|
|
@ -107,38 +107,6 @@ assertTypeFailInEval('function f(global, {imports}) { "use asm"; function g() {}
|
|||
assertTypeFailInEval('function f(g = 2) { "use asm"; function g() {} return g }');
|
||||
assertTypeFailInEval('function *f() { "use asm"; function g() {} return g }');
|
||||
|
||||
function assertLinkFailInEval(str)
|
||||
{
|
||||
if (!isAsmJSCompilationAvailable())
|
||||
return;
|
||||
|
||||
var caught = false;
|
||||
var oldOpts = options("werror");
|
||||
assertEq(oldOpts.indexOf("werror"), -1);
|
||||
try {
|
||||
eval(str);
|
||||
} catch (e) {
|
||||
assertEq((''+e).indexOf(ASM_OK_STRING) == -1, false);
|
||||
caught = true;
|
||||
}
|
||||
assertEq(caught, true);
|
||||
options("werror");
|
||||
|
||||
var code = eval(str);
|
||||
|
||||
var caught = false;
|
||||
var oldOpts = options("werror");
|
||||
assertEq(oldOpts.indexOf("werror"), -1);
|
||||
try {
|
||||
code.apply(null, Array.slice(arguments, 1));
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
assertEq(caught, true);
|
||||
options("werror");
|
||||
}
|
||||
assertLinkFailInEval('(function(global) { "use asm"; var im=global.Math.imul; function g() {} return g })');
|
||||
|
||||
assertThrowsInstanceOf(function() { new Function(USE_ASM + 'var)') }, SyntaxError);
|
||||
assertThrowsInstanceOf(function() { new Function(USE_ASM + 'return)') }, SyntaxError);
|
||||
assertThrowsInstanceOf(function() { new Function(USE_ASM + 'var z=-2w') }, SyntaxError);
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
function test1() {
|
||||
eval("with (arguments) var arguments = 0;");
|
||||
}
|
||||
test1();
|
||||
|
||||
function test2() {
|
||||
eval("eval('with (arguments) var arguments = 0;')");
|
||||
}
|
||||
test2();
|
|
@ -186,10 +186,8 @@ BaselineCompiler::compile()
|
|||
baselineScript->setMethod(code);
|
||||
baselineScript->setTemplateScope(templateScope);
|
||||
|
||||
script->setBaselineScript(baselineScript);
|
||||
|
||||
IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
|
||||
(void *) script->baselineScript(), (void *) code->raw(),
|
||||
(void *) baselineScript, (void *) code->raw(),
|
||||
script->filename(), script->lineno());
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
|
@ -254,6 +252,8 @@ BaselineCompiler::compile()
|
|||
if (script->compartment()->debugMode())
|
||||
baselineScript->setDebugMode();
|
||||
|
||||
script->setBaselineScript(cx, baselineScript);
|
||||
|
||||
return Method_Compiled;
|
||||
}
|
||||
|
||||
|
|
|
@ -130,12 +130,16 @@ ICStubIterator::operator++()
|
|||
}
|
||||
|
||||
void
|
||||
ICStubIterator::unlink(Zone *zone)
|
||||
ICStubIterator::unlink(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(currentStub_->next() != nullptr);
|
||||
JS_ASSERT(currentStub_ != fallbackStub_);
|
||||
JS_ASSERT(!unlinked_);
|
||||
fallbackStub_->unlinkStub(zone, previousStub_, currentStub_);
|
||||
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
fallbackStub_->unlinkStub(cx->zone(), previousStub_, currentStub_);
|
||||
}
|
||||
|
||||
// Mark the current iterator position as unlinked, so operator++ works properly.
|
||||
unlinked_ = true;
|
||||
|
@ -172,14 +176,14 @@ ICStub::trace(JSTracer *trc)
|
|||
ICTypeMonitor_Fallback *lastMonStub = toMonitoredFallbackStub()->fallbackMonitorStub();
|
||||
for (ICStubConstIterator iter = lastMonStub->firstMonitorStub(); !iter.atEnd(); iter++) {
|
||||
JS_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
|
||||
iter->markCode(trc, "baseline-monitor-stub-ioncode");
|
||||
iter->trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
if (isUpdated()) {
|
||||
for (ICStubConstIterator iter = toUpdatedStub()->firstUpdateStub(); !iter.atEnd(); iter++) {
|
||||
JS_ASSERT_IF(iter->next() == nullptr, iter->isTypeUpdate_Fallback());
|
||||
iter->markCode(trc, "baseline-update-stub-ioncode");
|
||||
iter->trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,7 +489,7 @@ ICFallbackStub::unlinkStubsWithKind(JSContext *cx, ICStub::Kind kind)
|
|||
{
|
||||
for (ICStubIterator iter = beginChain(); !iter.atEnd(); iter++) {
|
||||
if (iter->kind() == kind)
|
||||
iter.unlink(cx->zone());
|
||||
iter.unlink(cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1065,7 @@ DoProfilerFallback(JSContext *cx, BaselineFrame *frame, ICProfiler_Fallback *stu
|
|||
ICStub *optStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!optStub)
|
||||
return false;
|
||||
stub->addNewStub(optStub);
|
||||
stub->addNewStub(cx, optStub);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1146,8 +1150,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext *cx, HandleScript scrip
|
|||
ICTypeMonitor_PrimitiveSet::Compiler compiler(cx, existingStub, type);
|
||||
ICStub *stub = existingStub ? compiler.updateStub()
|
||||
: compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!stub)
|
||||
if (!stub) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_BaselineIC, " %s TypeMonitor stub %p for primitive type %d",
|
||||
existingStub ? "Modified existing" : "Created new", stub, type);
|
||||
|
@ -1171,8 +1177,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext *cx, HandleScript scrip
|
|||
|
||||
ICTypeMonitor_SingleObject::Compiler compiler(cx, obj);
|
||||
ICStub *stub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!stub)
|
||||
if (!stub) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_BaselineIC, " Added TypeMonitor stub %p for singleton %p",
|
||||
stub, obj.get());
|
||||
|
@ -1193,8 +1201,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext *cx, HandleScript scrip
|
|||
|
||||
ICTypeMonitor_TypeObject::Compiler compiler(cx, type);
|
||||
ICStub *stub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!stub)
|
||||
if (!stub) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_BaselineIC, " Added TypeMonitor stub %p for TypeObject %p",
|
||||
stub, type.get());
|
||||
|
@ -1785,7 +1795,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!int32Stub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(int32Stub);
|
||||
stub->addNewStub(cx, int32Stub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1803,7 +1813,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!doubleStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(doubleStub);
|
||||
stub->addNewStub(cx, doubleStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1818,7 +1828,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!doubleStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(doubleStub);
|
||||
stub->addNewStub(cx, doubleStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1829,7 +1839,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!booleanStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(booleanStub);
|
||||
stub->addNewStub(cx, booleanStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1842,7 +1852,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!optStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(optStub);
|
||||
stub->addNewStub(cx, optStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1854,7 +1864,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!stringStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(stringStub);
|
||||
stub->addNewStub(cx, stringStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1866,7 +1876,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!objectStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(objectStub);
|
||||
stub->addNewStub(cx, objectStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1884,7 +1894,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub,
|
|||
if (!objectStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(objectStub);
|
||||
stub->addNewStub(cx, objectStub);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -2090,7 +2100,7 @@ ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
Label emulatesUndefined;
|
||||
Register obj = masm.extractObject(objectOperand, ExtractTemp0);
|
||||
masm.loadPtr(Address(obj, JSObject::offsetOfType()), obj);
|
||||
masm.loadPtr(Address(obj, offsetof(types::TypeObject, clasp)), obj);
|
||||
masm.loadPtr(Address(obj, types::TypeObject::offsetOfClasp()), obj);
|
||||
masm.branchTest32(Assembler::NonZero,
|
||||
Address(obj, Class::offsetOfFlags()),
|
||||
Imm32(JSCLASS_EMULATES_UNDEFINED),
|
||||
|
@ -2196,7 +2206,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H
|
|||
if (!int32Stub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(int32Stub);
|
||||
stub->addNewStub(cx, int32Stub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2207,7 +2217,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H
|
|||
if (!doubleStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(doubleStub);
|
||||
stub->addNewStub(cx, doubleStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2218,7 +2228,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H
|
|||
if (!stringStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(stringStub);
|
||||
stub->addNewStub(cx, stringStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2228,7 +2238,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H
|
|||
if (!nilStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(nilStub);
|
||||
stub->addNewStub(cx, nilStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2239,7 +2249,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H
|
|||
if (!objStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(objStub);
|
||||
stub->addNewStub(cx, objStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2531,11 +2541,11 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
}
|
||||
|
||||
if (ret.isDouble())
|
||||
stub->setSawDoubleResult();
|
||||
stub->setSawDoubleResult(cx);
|
||||
|
||||
// Check to see if a new stub should be generated.
|
||||
if (stub->numOptimizedStubs() >= ICBinaryArith_Fallback::MAX_OPTIMIZED_STUBS) {
|
||||
stub->noteUnoptimizableOperands();
|
||||
stub->noteUnoptimizableOperands(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2548,7 +2558,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
ICStub *strcatStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!strcatStub)
|
||||
return false;
|
||||
stub->addNewStub(strcatStub);
|
||||
stub->addNewStub(cx, strcatStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2561,7 +2571,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
ICStub *strcatStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!strcatStub)
|
||||
return false;
|
||||
stub->addNewStub(strcatStub);
|
||||
stub->addNewStub(cx, strcatStub);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -2577,13 +2587,13 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
ICStub *arithStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!arithStub)
|
||||
return false;
|
||||
stub->addNewStub(arithStub);
|
||||
stub->addNewStub(cx, arithStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle only int32 or double.
|
||||
if (!lhs.isNumber() || !rhs.isNumber()) {
|
||||
stub->noteUnoptimizableOperands();
|
||||
stub->noteUnoptimizableOperands(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2607,7 +2617,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
ICStub *doubleStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!doubleStub)
|
||||
return false;
|
||||
stub->addNewStub(doubleStub);
|
||||
stub->addNewStub(cx, doubleStub);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
@ -2625,7 +2635,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
ICStub *int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script));
|
||||
if (!int32Stub)
|
||||
return false;
|
||||
stub->addNewStub(int32Stub);
|
||||
stub->addNewStub(cx, int32Stub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2644,7 +2654,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
ICStub *optStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!optStub)
|
||||
return false;
|
||||
stub->addNewStub(optStub);
|
||||
stub->addNewStub(cx, optStub);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
@ -2652,7 +2662,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||
}
|
||||
}
|
||||
|
||||
stub->noteUnoptimizableOperands();
|
||||
stub->noteUnoptimizableOperands(cx);
|
||||
return true;
|
||||
}
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -3047,7 +3057,7 @@ DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback
|
|||
ICStub *int32Stub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!int32Stub)
|
||||
return false;
|
||||
stub->addNewStub(int32Stub);
|
||||
stub->addNewStub(cx, int32Stub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3061,7 +3071,7 @@ DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback
|
|||
ICStub *doubleStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!doubleStub)
|
||||
return false;
|
||||
stub->addNewStub(doubleStub);
|
||||
stub->addNewStub(cx, doubleStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3629,7 +3639,7 @@ RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, Handle
|
|||
// If the holder matches, but the holder's lastProperty doesn't match, then
|
||||
// this stub is invalid anyway. Unlink it.
|
||||
if (holder->lastProperty() != protoStub->holderShape()) {
|
||||
iter.unlink(cx->zone());
|
||||
iter.unlink(cx);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
|
@ -3645,7 +3655,7 @@ RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, Handle
|
|||
// If the holder matches, but the holder's lastProperty doesn't match, then
|
||||
// this stub is invalid anyway. Unlink it.
|
||||
if (holder->lastProperty() != protoStub->holderShape()) {
|
||||
iter.unlink(cx->zone());
|
||||
iter.unlink(cx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -3654,7 +3664,7 @@ RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, Handle
|
|||
// If the new stub needs atomization, and the old stub doesn't atomize, then
|
||||
// remove the old stub.
|
||||
if (needsAtomize && !getElemNativeStub->needsAtomize()) {
|
||||
iter.unlink(cx->zone());
|
||||
iter.unlink(cx);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -3745,7 +3755,7 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3797,7 +3807,7 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3831,7 +3841,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl
|
|||
if (!stringStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(stringStub);
|
||||
stub->addNewStub(cx, stringStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3849,7 +3859,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl
|
|||
if (!argsStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(argsStub);
|
||||
stub->addNewStub(cx, argsStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3871,7 +3881,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl
|
|||
if (!argsStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(argsStub);
|
||||
stub->addNewStub(cx, argsStub);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3886,7 +3896,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl
|
|||
if (!denseStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(denseStub);
|
||||
stub->addNewStub(cx, denseStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3920,7 +3930,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl
|
|||
if (!typedArrayStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(typedArrayStub);
|
||||
stub->addNewStub(cx, typedArrayStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3928,13 +3938,13 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl
|
|||
// be cached by either Baseline or Ion. Indicate this in the cache so that
|
||||
// Ion does not generate a cache for this op.
|
||||
if (!obj->isNative() && !obj->is<TypedArrayObject>())
|
||||
stub->noteNonNativeAccess();
|
||||
stub->noteNonNativeAccess(cx);
|
||||
|
||||
// GetElem operations which could access negative indexes generally can't
|
||||
// be optimized without the potential for bailouts, as we can't statically
|
||||
// determine that an object has no properties on such indexes.
|
||||
if (rhs.isNumber() && rhs.toNumber() < 0)
|
||||
stub->noteNegativeIndex();
|
||||
stub->noteNegativeIndex(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -4844,7 +4854,7 @@ RemoveExistingTypedArraySetElemStub(JSContext *cx, ICSetElem_Fallback *stub, Han
|
|||
// TypedArraySetElem stubs are only removed using this procedure if
|
||||
// being replaced with one that expects out of bounds index.
|
||||
JS_ASSERT(!iter->toSetElem_TypedArray()->expectOutOfBounds());
|
||||
iter.unlink(cx->zone());
|
||||
iter.unlink(cx);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -5001,7 +5011,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub,
|
|||
if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs))
|
||||
return false;
|
||||
|
||||
stub->addNewStub(denseStub);
|
||||
stub->addNewStub(cx, denseStub);
|
||||
} else if (!addingCase &&
|
||||
!DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, obj))
|
||||
{
|
||||
|
@ -5015,7 +5025,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub,
|
|||
if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs))
|
||||
return false;
|
||||
|
||||
stub->addNewStub(denseStub);
|
||||
stub->addNewStub(cx, denseStub);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5048,7 +5058,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub,
|
|||
if (!typedArrayStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(typedArrayStub);
|
||||
stub->addNewStub(cx, typedArrayStub);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -5098,13 +5108,13 @@ ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
}
|
||||
|
||||
void
|
||||
BaselineScript::noteArrayWriteHole(uint32_t pcOffset)
|
||||
BaselineScript::noteArrayWriteHole(JSContext *cx, uint32_t pcOffset)
|
||||
{
|
||||
ICEntry &entry = icEntryFromPCOffset(pcOffset);
|
||||
ICFallbackStub *stub = entry.fallbackStub();
|
||||
|
||||
if (stub->isSetElem_Fallback())
|
||||
stub->toSetElem_Fallback()->noteArrayWriteHole();
|
||||
stub->toSetElem_Fallback()->noteArrayWriteHole(cx);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -5626,7 +5636,7 @@ TryAttachGlobalNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5716,7 +5726,7 @@ TryAttachScopeNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *s
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5923,7 +5933,7 @@ DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallb
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5971,7 +5981,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
|
|||
return false;
|
||||
|
||||
*attached = true;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5983,7 +5993,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
|
|||
return false;
|
||||
|
||||
*attached = true;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6000,7 +6010,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
|
|||
return false;
|
||||
|
||||
*attached = true;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
if (obj->is<TypedArrayObject>()) {
|
||||
|
@ -6012,7 +6022,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
|
|||
return false;
|
||||
|
||||
*attached = true;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6028,7 +6038,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
|
|||
return false;
|
||||
|
||||
*attached = true;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6111,7 +6121,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -6142,7 +6152,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -6189,7 +6199,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
|||
}
|
||||
if (!newStub)
|
||||
return false;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -6209,7 +6219,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
|||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -6263,7 +6273,7 @@ TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -6349,7 +6359,7 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub,
|
|||
}
|
||||
|
||||
JS_ASSERT(!attached);
|
||||
stub->noteUnoptimizableAccess();
|
||||
stub->noteUnoptimizableAccess(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -7094,13 +7104,13 @@ ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
}
|
||||
|
||||
void
|
||||
BaselineScript::noteAccessedGetter(uint32_t pcOffset)
|
||||
BaselineScript::noteAccessedGetter(JSContext *cx, uint32_t pcOffset)
|
||||
{
|
||||
ICEntry &entry = icEntryFromPCOffset(pcOffset);
|
||||
ICFallbackStub *stub = entry.fallbackStub();
|
||||
|
||||
if (stub->isGetProp_Fallback())
|
||||
stub->toGetProp_Fallback()->noteAccessedGetter();
|
||||
stub->toGetProp_Fallback()->noteAccessedGetter(cx);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -7141,7 +7151,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
|
|||
if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -7159,7 +7169,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
|
|||
if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -7181,7 +7191,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -7200,7 +7210,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -7271,7 +7281,7 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub,
|
|||
return true;
|
||||
|
||||
JS_ASSERT(!attached);
|
||||
stub->noteUnoptimizableAccess();
|
||||
stub->noteUnoptimizableAccess(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -7775,7 +7785,7 @@ TryAttachFunApplyStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script,
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7792,7 +7802,7 @@ TryAttachFunApplyStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script,
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -7933,7 +7943,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
|
|||
stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted);
|
||||
|
||||
// Add new generalized stub.
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7962,7 +7972,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7999,7 +8009,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
|
|||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -9123,7 +9133,7 @@ DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallb
|
|||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!newStub)
|
||||
return false;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -9197,7 +9207,7 @@ DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallb
|
|||
return false;
|
||||
|
||||
if (!res.isString() && !stub->hasNonStringResult())
|
||||
stub->setHasNonStringResult();
|
||||
stub->setHasNonStringResult(cx);
|
||||
|
||||
if (iteratorObject->is<PropertyIteratorObject>() &&
|
||||
!stub->hasStub(ICStub::IteratorNext_Native))
|
||||
|
@ -9206,7 +9216,7 @@ DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallb
|
|||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!newStub)
|
||||
return false;
|
||||
stub->addNewStub(newStub);
|
||||
stub->addNewStub(cx, newStub);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -9370,7 +9380,7 @@ DoTypeOfFallback(JSContext *cx, BaselineFrame *frame, ICTypeOf_Fallback *stub, H
|
|||
ICStub *typeOfStub = compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!typeOfStub)
|
||||
return false;
|
||||
stub->addNewStub(typeOfStub);
|
||||
stub->addNewStub(cx, typeOfStub);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -9457,7 +9467,7 @@ DoRetSubFallback(JSContext *cx, BaselineFrame *frame, ICRetSub_Fallback *stub,
|
|||
if (!optStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(optStub);
|
||||
stub->addNewStub(cx, optStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -516,7 +516,7 @@ class ICStubIterator
|
|||
return currentStub_ == (ICStub *) fallbackStub_;
|
||||
}
|
||||
|
||||
void unlink(Zone *zone);
|
||||
void unlink(JSContext *cx);
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -687,6 +687,8 @@ class ICStub
|
|||
}
|
||||
|
||||
inline void setNext(ICStub *stub) {
|
||||
// Note: next_ only needs to be changed under the compilation lock for
|
||||
// non-type-monitor/update ICs.
|
||||
next_ = stub;
|
||||
}
|
||||
|
||||
|
@ -833,7 +835,8 @@ class ICFallbackStub : public ICStub
|
|||
}
|
||||
|
||||
// Add a new stub to the IC chain terminated by this fallback stub.
|
||||
void addNewStub(ICStub *stub) {
|
||||
void addNewStub(JSContext *cx, ICStub *stub) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
JS_ASSERT(*lastStubPtrAddr_ == this);
|
||||
JS_ASSERT(stub->next() == nullptr);
|
||||
stub->setNext(this);
|
||||
|
@ -2439,13 +2442,15 @@ class ICBinaryArith_Fallback : public ICFallbackStub
|
|||
bool sawDoubleResult() const {
|
||||
return extra_ & SAW_DOUBLE_RESULT_BIT;
|
||||
}
|
||||
void setSawDoubleResult() {
|
||||
void setSawDoubleResult(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ |= SAW_DOUBLE_RESULT_BIT;
|
||||
}
|
||||
bool hadUnoptimizableOperands() const {
|
||||
return extra_ & UNOPTIMIZABLE_OPERANDS_BIT;
|
||||
}
|
||||
void noteUnoptimizableOperands() {
|
||||
void noteUnoptimizableOperands(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ |= UNOPTIMIZABLE_OPERANDS_BIT;
|
||||
}
|
||||
|
||||
|
@ -2846,14 +2851,16 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub
|
|||
return space->allocate<ICGetElem_Fallback>(code);
|
||||
}
|
||||
|
||||
void noteNonNativeAccess() {
|
||||
void noteNonNativeAccess(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ |= EXTRA_NON_NATIVE;
|
||||
}
|
||||
bool hasNonNativeAccess() const {
|
||||
return extra_ & EXTRA_NON_NATIVE;
|
||||
}
|
||||
|
||||
void noteNegativeIndex() {
|
||||
void noteNegativeIndex(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ |= EXTRA_NEGATIVE_INDEX;
|
||||
}
|
||||
bool hasNegativeIndex() const {
|
||||
|
@ -3441,7 +3448,8 @@ class ICSetElem_Fallback : public ICFallbackStub
|
|||
return space->allocate<ICSetElem_Fallback>(code);
|
||||
}
|
||||
|
||||
void noteArrayWriteHole() {
|
||||
void noteArrayWriteHole(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ = 1;
|
||||
}
|
||||
bool hasArrayWriteHole() const {
|
||||
|
@ -4016,14 +4024,16 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub
|
|||
static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
|
||||
static const size_t ACCESSED_GETTER_BIT = 1;
|
||||
|
||||
void noteUnoptimizableAccess() {
|
||||
void noteUnoptimizableAccess(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
|
||||
}
|
||||
bool hadUnoptimizableAccess() const {
|
||||
return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
|
||||
}
|
||||
|
||||
void noteAccessedGetter() {
|
||||
void noteAccessedGetter(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ |= (1u << ACCESSED_GETTER_BIT);
|
||||
}
|
||||
bool hasAccessedGetter() const {
|
||||
|
@ -4830,7 +4840,8 @@ class ICSetProp_Fallback : public ICFallbackStub
|
|||
}
|
||||
|
||||
static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
|
||||
void noteUnoptimizableAccess() {
|
||||
void noteUnoptimizableAccess(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
|
||||
}
|
||||
bool hadUnoptimizableAccess() const {
|
||||
|
@ -5721,7 +5732,8 @@ class ICIteratorNext_Fallback : public ICFallbackStub
|
|||
return space->allocate<ICIteratorNext_Fallback>(code);
|
||||
}
|
||||
|
||||
void setHasNonStringResult() {
|
||||
void setHasNonStringResult(JSContext *cx) {
|
||||
AutoLockForCompilation lock(cx);
|
||||
JS_ASSERT(extra_ == 0);
|
||||
extra_ = 1;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ using mozilla::DebugOnly;
|
|||
bool
|
||||
SetElemICInspector::sawOOBDenseWrite() const
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!icEntry_)
|
||||
return false;
|
||||
|
||||
|
@ -38,6 +40,8 @@ SetElemICInspector::sawOOBDenseWrite() const
|
|||
bool
|
||||
SetElemICInspector::sawOOBTypedArrayWrite() const
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!icEntry_)
|
||||
return false;
|
||||
|
||||
|
@ -54,6 +58,8 @@ SetElemICInspector::sawOOBTypedArrayWrite() const
|
|||
bool
|
||||
SetElemICInspector::sawDenseWrite() const
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!icEntry_)
|
||||
return false;
|
||||
|
||||
|
@ -68,6 +74,8 @@ SetElemICInspector::sawDenseWrite() const
|
|||
bool
|
||||
SetElemICInspector::sawTypedArrayWrite() const
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!icEntry_)
|
||||
return false;
|
||||
|
||||
|
@ -82,6 +90,8 @@ SetElemICInspector::sawTypedArrayWrite() const
|
|||
bool
|
||||
BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
// Return a list of shapes seen by the baseline IC for the current op.
|
||||
// An empty list indicates no shapes are known, or there was an uncacheable
|
||||
// access.
|
||||
|
@ -139,6 +149,8 @@ BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes)
|
|||
ICStub *
|
||||
BaselineInspector::monomorphicStub(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
|
@ -156,6 +168,8 @@ BaselineInspector::monomorphicStub(jsbytecode *pc)
|
|||
bool
|
||||
BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
|
@ -176,6 +190,8 @@ BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **pseco
|
|||
MIRType
|
||||
BaselineInspector::expectedResultType(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
// Look at the IC entries for this op to guess what type it will produce,
|
||||
// returning MIRType_None otherwise.
|
||||
|
||||
|
@ -222,6 +238,8 @@ CanUseInt32Compare(ICStub::Kind kind)
|
|||
MCompare::CompareType
|
||||
BaselineInspector::expectedCompareType(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
ICStub *first = monomorphicStub(pc), *second = nullptr;
|
||||
if (!first && !dimorphicStub(pc, &first, &second))
|
||||
return MCompare::Compare_Unknown;
|
||||
|
@ -304,6 +322,8 @@ TryToSpecializeBinaryArithOp(ICStub **stubs,
|
|||
MIRType
|
||||
BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
MIRType result;
|
||||
ICStub *stubs[2];
|
||||
|
||||
|
@ -332,6 +352,8 @@ BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc)
|
|||
bool
|
||||
BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
|
@ -346,6 +368,8 @@ BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc)
|
|||
bool
|
||||
BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
|
@ -360,6 +384,8 @@ BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc)
|
|||
bool
|
||||
BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
|
@ -374,6 +400,8 @@ BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc)
|
|||
bool
|
||||
BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT);
|
||||
|
||||
if (!hasBaselineScript())
|
||||
|
@ -388,6 +416,8 @@ BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
|
|||
bool
|
||||
BaselineInspector::hasSeenDoubleResult(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
|
@ -407,6 +437,8 @@ BaselineInspector::hasSeenDoubleResult(jsbytecode *pc)
|
|||
JSObject *
|
||||
BaselineInspector::getTemplateObject(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
|
@ -434,6 +466,8 @@ BaselineInspector::getTemplateObject(jsbytecode *pc)
|
|||
JSObject *
|
||||
BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
|
@ -467,6 +501,8 @@ BaselineInspector::templateCallObject()
|
|||
JSObject *
|
||||
BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
const ICEntry &entry = icEntryFromPC(pc);
|
||||
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isGetProp_CallScripted() || stub->isGetProp_CallNative()) {
|
||||
|
@ -482,6 +518,8 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
|
|||
JSObject *
|
||||
BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
const ICEntry &entry = icEntryFromPC(pc);
|
||||
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
|
||||
|
|
|
@ -237,7 +237,7 @@ jit::BaselineCompile(JSContext *cx, HandleScript script)
|
|||
JS_ASSERT_IF(status != Method_Compiled, !script->hasBaselineScript());
|
||||
|
||||
if (status == Method_CantCompile)
|
||||
script->setBaselineScript(BASELINE_DISABLED_SCRIPT);
|
||||
script->setBaselineScript(cx, BASELINE_DISABLED_SCRIPT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -650,7 +650,7 @@ BaselineScript::copyPCMappingIndexEntries(const PCMappingIndexEntry *entries)
|
|||
uint8_t *
|
||||
BaselineScript::nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo)
|
||||
{
|
||||
JS_ASSERT(script->baselineScript() == this);
|
||||
JS_ASSERT_IF(script->hasBaselineScript(), script->baselineScript() == this);
|
||||
|
||||
uint32_t pcOffset = script->pcToOffset(pc);
|
||||
|
||||
|
@ -892,7 +892,7 @@ jit::FinishDiscardBaselineScript(FreeOp *fop, JSScript *script)
|
|||
}
|
||||
|
||||
BaselineScript *baseline = script->baselineScript();
|
||||
script->setBaselineScript(nullptr);
|
||||
script->setBaselineScript(nullptr, nullptr);
|
||||
BaselineScript::Destroy(fop, baseline);
|
||||
}
|
||||
|
||||
|
|
|
@ -289,8 +289,8 @@ struct BaselineScript
|
|||
|
||||
void toggleSPS(bool enable);
|
||||
|
||||
void noteAccessedGetter(uint32_t pcOffset);
|
||||
void noteArrayWriteHole(uint32_t pcOffset);
|
||||
void noteAccessedGetter(JSContext *cx, uint32_t pcOffset);
|
||||
void noteArrayWriteHole(JSContext *cx, uint32_t pcOffset);
|
||||
|
||||
static size_t offsetOfFlags() {
|
||||
return offsetof(BaselineScript, flags_);
|
||||
|
|
|
@ -36,8 +36,9 @@ bool
|
|||
BitSet::empty() const
|
||||
{
|
||||
JS_ASSERT(bits_);
|
||||
for (unsigned int i = 0; i < numWords(); i++) {
|
||||
if (bits_[i])
|
||||
const uint32_t *bits = bits_;
|
||||
for (unsigned int i = 0, e = numWords(); i < e; i++) {
|
||||
if (bits[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -50,8 +51,10 @@ BitSet::insertAll(const BitSet *other)
|
|||
JS_ASSERT(other->numBits_ == numBits_);
|
||||
JS_ASSERT(other->bits_);
|
||||
|
||||
for (unsigned int i = 0; i < numWords(); i++)
|
||||
bits_[i] |= other->bits_[i];
|
||||
uint32_t *bits = bits_;
|
||||
const uint32_t *otherBits = other->bits_;
|
||||
for (unsigned int i = 0, e = numWords(); i < e; i++)
|
||||
bits[i] |= otherBits[i];
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -61,8 +64,10 @@ BitSet::removeAll(const BitSet *other)
|
|||
JS_ASSERT(other->numBits_ == numBits_);
|
||||
JS_ASSERT(other->bits_);
|
||||
|
||||
for (unsigned int i = 0; i < numWords(); i++)
|
||||
bits_[i] &= ~other->bits_[i];
|
||||
uint32_t *bits = bits_;
|
||||
const uint32_t *otherBits = other->bits_;
|
||||
for (unsigned int i = 0, e = numWords(); i < e; i++)
|
||||
bits[i] &= ~otherBits[i];
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -72,8 +77,10 @@ BitSet::intersect(const BitSet *other)
|
|||
JS_ASSERT(other->numBits_ == numBits_);
|
||||
JS_ASSERT(other->bits_);
|
||||
|
||||
for (unsigned int i = 0; i < numWords(); i++)
|
||||
bits_[i] &= other->bits_[i];
|
||||
uint32_t *bits = bits_;
|
||||
const uint32_t *otherBits = other->bits_;
|
||||
for (unsigned int i = 0, e = numWords(); i < e; i++)
|
||||
bits[i] &= otherBits[i];
|
||||
}
|
||||
|
||||
// returns true if the intersection caused the contents of the set to change.
|
||||
|
@ -86,11 +93,13 @@ BitSet::fixedPointIntersect(const BitSet *other)
|
|||
|
||||
bool changed = false;
|
||||
|
||||
for (unsigned int i = 0; i < numWords(); i++) {
|
||||
uint32_t old = bits_[i];
|
||||
bits_[i] &= other->bits_[i];
|
||||
uint32_t *bits = bits_;
|
||||
const uint32_t *otherBits = other->bits_;
|
||||
for (unsigned int i = 0, e = numWords(); i < e; i++) {
|
||||
uint32_t old = bits[i];
|
||||
bits[i] &= otherBits[i];
|
||||
|
||||
if (!changed && old != bits_[i])
|
||||
if (!changed && old != bits[i])
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
|
@ -100,14 +109,16 @@ void
|
|||
BitSet::complement()
|
||||
{
|
||||
JS_ASSERT(bits_);
|
||||
for (unsigned int i = 0; i < numWords(); i++)
|
||||
bits_[i] = ~bits_[i];
|
||||
uint32_t *bits = bits_;
|
||||
for (unsigned int i = 0, e = numWords(); i < e; i++)
|
||||
bits[i] = ~bits[i];
|
||||
}
|
||||
|
||||
void
|
||||
BitSet::clear()
|
||||
{
|
||||
JS_ASSERT(bits_);
|
||||
for (unsigned int i = 0; i < numWords(); i++)
|
||||
bits_[i] = 0;
|
||||
uint32_t *bits = bits_;
|
||||
for (unsigned int i = 0, e = numWords(); i < e; i++)
|
||||
bits[i] = 0;
|
||||
}
|
||||
|
|
|
@ -29,11 +29,11 @@ class BitSet : private TempObject
|
|||
|
||||
private:
|
||||
BitSet(unsigned int numBits) :
|
||||
numBits_(numBits),
|
||||
bits_(nullptr) {}
|
||||
bits_(nullptr),
|
||||
numBits_(numBits) {}
|
||||
|
||||
unsigned int numBits_;
|
||||
uint32_t *bits_;
|
||||
const unsigned int numBits_;
|
||||
|
||||
static inline uint32_t bitForValue(unsigned int value) {
|
||||
return 1l << uint32_t(value % BitsPerWord);
|
||||
|
@ -120,6 +120,29 @@ class BitSet::Iterator
|
|||
unsigned word_;
|
||||
uint32_t value_;
|
||||
|
||||
void skipEmpty() {
|
||||
// Skip words containing only zeros.
|
||||
unsigned numWords = set_.numWords();
|
||||
const uint32_t *bits = set_.bits_;
|
||||
while (value_ == 0) {
|
||||
word_++;
|
||||
if (word_ == numWords)
|
||||
return;
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(value_) * 8 == BitSet::BitsPerWord);
|
||||
index_ = word_ * sizeof(value_) * 8;
|
||||
value_ = bits[word_];
|
||||
}
|
||||
|
||||
// Be careful: the result of CountTrailingZeroes32 is undefined if the
|
||||
// input is 0.
|
||||
int numZeros = mozilla::CountTrailingZeroes32(value_);
|
||||
index_ += numZeros;
|
||||
value_ >>= numZeros;
|
||||
|
||||
JS_ASSERT_IF(index_ < set_.numBits_, set_.contains(index_));
|
||||
}
|
||||
|
||||
public:
|
||||
Iterator(BitSet &set) :
|
||||
set_(set),
|
||||
|
@ -127,8 +150,7 @@ class BitSet::Iterator
|
|||
word_(0),
|
||||
value_(set.bits_[0])
|
||||
{
|
||||
if (!set_.contains(index_))
|
||||
(*this)++;
|
||||
skipEmpty();
|
||||
}
|
||||
|
||||
inline bool more() const {
|
||||
|
@ -145,23 +167,7 @@ class BitSet::Iterator
|
|||
index_++;
|
||||
value_ >>= 1;
|
||||
|
||||
// Skip words containing only zeros.
|
||||
while (value_ == 0) {
|
||||
word_++;
|
||||
if (!more())
|
||||
return *this;
|
||||
|
||||
index_ = word_ * sizeof(value_) * 8;
|
||||
value_ = set_.bits_[word_];
|
||||
}
|
||||
|
||||
// Be careful: the result of CountTrailingZeroes32 is undefined if the
|
||||
// input is 0.
|
||||
int numZeros = mozilla::CountTrailingZeroes32(value_);
|
||||
index_ += numZeros;
|
||||
value_ >>= numZeros;
|
||||
|
||||
JS_ASSERT_IF(index_ < set_.numBits_, set_.contains(index_));
|
||||
skipEmpty();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -3389,14 +3389,14 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir)
|
|||
ool = oolCallVM(NewCallObjectInfo, lir,
|
||||
(ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
|
||||
ImmGCPtr(templateObj->lastProperty()),
|
||||
ImmGCPtr(templateObj->hasLazyType() ? nullptr : templateObj->type()),
|
||||
ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
|
||||
ToRegister(lir->slots())),
|
||||
StoreRegisterTo(obj));
|
||||
} else {
|
||||
ool = oolCallVM(NewCallObjectInfo, lir,
|
||||
(ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
|
||||
ImmGCPtr(templateObj->lastProperty()),
|
||||
ImmGCPtr(templateObj->hasLazyType() ? nullptr : templateObj->type()),
|
||||
ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
|
||||
ImmPtr(nullptr)),
|
||||
StoreRegisterTo(obj));
|
||||
}
|
||||
|
@ -7409,8 +7409,7 @@ CodeGenerator::emitInstanceOf(LInstruction *ins, JSObject *prototypeObject)
|
|||
// out of the loop on Proxy::LazyProto.
|
||||
|
||||
// Load the lhs's prototype.
|
||||
masm.loadPtr(Address(objReg, JSObject::offsetOfType()), output);
|
||||
masm.loadPtr(Address(output, offsetof(types::TypeObject, proto)), output);
|
||||
masm.loadObjProto(objReg, output);
|
||||
|
||||
Label testLazy;
|
||||
{
|
||||
|
@ -7424,14 +7423,13 @@ CodeGenerator::emitInstanceOf(LInstruction *ins, JSObject *prototypeObject)
|
|||
masm.jump(&done);
|
||||
masm.bind(¬PrototypeObject);
|
||||
|
||||
JS_ASSERT(uintptr_t(Proxy::LazyProto) == 1);
|
||||
JS_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
|
||||
|
||||
// Test for nullptr or Proxy::LazyProto
|
||||
masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
|
||||
|
||||
// Load the current object's prototype.
|
||||
masm.loadPtr(Address(output, JSObject::offsetOfType()), output);
|
||||
masm.loadPtr(Address(output, offsetof(types::TypeObject, proto)), output);
|
||||
masm.loadObjProto(output, output);
|
||||
|
||||
masm.jump(&loopPrototypeChain);
|
||||
}
|
||||
|
|
|
@ -45,9 +45,9 @@ class CompileInfo
|
|||
{
|
||||
public:
|
||||
CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
|
||||
ExecutionMode executionMode)
|
||||
ExecutionMode executionMode, bool scriptNeedsArgsObj)
|
||||
: script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
|
||||
executionMode_(executionMode)
|
||||
executionMode_(executionMode), scriptNeedsArgsObj_(scriptNeedsArgsObj)
|
||||
{
|
||||
JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
|
||||
|
||||
|
@ -68,7 +68,7 @@ class CompileInfo
|
|||
|
||||
CompileInfo(unsigned nlocals, ExecutionMode executionMode)
|
||||
: script_(nullptr), fun_(nullptr), osrPc_(nullptr), constructing_(false),
|
||||
executionMode_(executionMode)
|
||||
executionMode_(executionMode), scriptNeedsArgsObj_(false)
|
||||
{
|
||||
nimplicit_ = 0;
|
||||
nargs_ = 0;
|
||||
|
@ -250,10 +250,10 @@ class CompileInfo
|
|||
return script()->argumentsAliasesFormals();
|
||||
}
|
||||
bool needsArgsObj() const {
|
||||
return script()->needsArgsObj();
|
||||
return scriptNeedsArgsObj_;
|
||||
}
|
||||
bool argsObjAliasesFormals() const {
|
||||
return script()->argsObjAliasesFormals();
|
||||
return scriptNeedsArgsObj_ && !script()->strict();
|
||||
}
|
||||
|
||||
ExecutionMode executionMode() const {
|
||||
|
@ -275,6 +275,11 @@ class CompileInfo
|
|||
jsbytecode *osrPc_;
|
||||
bool constructing_;
|
||||
ExecutionMode executionMode_;
|
||||
|
||||
// Whether a script needs an arguments object is unstable over compilation
|
||||
// since the arguments optimization could be marked as failed on the main
|
||||
// thread, so cache a value here and use it throughout for consistency.
|
||||
bool scriptNeedsArgsObj_;
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -125,11 +125,13 @@ CompileRuntime::positiveInfinityValue()
|
|||
return runtime()->positiveInfinityValue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
CompileRuntime::isInsideNursery(gc::Cell *cell)
|
||||
{
|
||||
return UninlinedIsInsideNursery(runtime(), cell);
|
||||
}
|
||||
#endif
|
||||
|
||||
const DOMCallbacks *
|
||||
CompileRuntime::DOMcallbacks()
|
||||
|
@ -228,3 +230,10 @@ CompileCompartment::hasObjectMetadataCallback()
|
|||
{
|
||||
return compartment()->hasObjectMetadataCallback();
|
||||
}
|
||||
|
||||
AutoLockForCompilation::AutoLockForCompilation(CompileCompartment *compartment
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(compartment->compartment()->runtimeFromAnyThread());
|
||||
}
|
||||
|
|
|
@ -66,7 +66,9 @@ class CompileRuntime
|
|||
const Value &NaNValue();
|
||||
const Value &positiveInfinityValue();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool isInsideNursery(gc::Cell *cell);
|
||||
#endif
|
||||
|
||||
// DOM callbacks must be threadsafe (and will hopefully be removed soon).
|
||||
const DOMCallbacks *DOMcallbacks();
|
||||
|
@ -96,6 +98,8 @@ class CompileCompartment
|
|||
{
|
||||
JSCompartment *compartment();
|
||||
|
||||
friend class js::AutoLockForCompilation;
|
||||
|
||||
public:
|
||||
static CompileCompartment *get(JSCompartment *comp);
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ namespace jit {
|
|||
template <typename T>
|
||||
class FixedList
|
||||
{
|
||||
size_t length_;
|
||||
T *list_;
|
||||
size_t length_;
|
||||
|
||||
private:
|
||||
FixedList(const FixedList&); // no copy definition.
|
||||
|
|
|
@ -49,6 +49,8 @@
|
|||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
using mozilla::Maybe;
|
||||
|
||||
// Global variables.
|
||||
IonOptions jit::js_IonOptions;
|
||||
|
||||
|
@ -516,6 +518,8 @@ JitCompartment::ensureIonStubsExist(JSContext *cx)
|
|||
void
|
||||
jit::FinishOffThreadBuilder(IonBuilder *builder)
|
||||
{
|
||||
builder->script()->runtimeFromMainThread()->removeCompilationThread();
|
||||
|
||||
ExecutionMode executionMode = builder->info().executionMode();
|
||||
|
||||
// Clear the recompiling flag if it would have failed.
|
||||
|
@ -1563,6 +1567,9 @@ AttachFinishedCompilations(JSContext *cx)
|
|||
// operation callback and can't propagate failures.
|
||||
cx->clearPendingException();
|
||||
}
|
||||
} else {
|
||||
if (builder->abortReason() == AbortReason_Disable)
|
||||
SetIonScript(builder->script(), builder->info().executionMode(), ION_DISABLED_SCRIPT);
|
||||
}
|
||||
|
||||
FinishOffThreadBuilder(builder);
|
||||
|
@ -1663,11 +1670,13 @@ IonCompile(JSContext *cx, JSScript *script,
|
|||
return AbortReason_Alloc;
|
||||
|
||||
CompileInfo *info = alloc->new_<CompileInfo>(script, script->function(), osrPc, constructing,
|
||||
executionMode);
|
||||
executionMode, script->needsArgsObj());
|
||||
if (!info)
|
||||
return AbortReason_Alloc;
|
||||
|
||||
BaselineInspector inspector(script);
|
||||
BaselineInspector *inspector = alloc->new_<BaselineInspector>(script);
|
||||
if (!inspector)
|
||||
return AbortReason_Alloc;
|
||||
|
||||
BaselineFrameInspector *baselineFrameInspector = nullptr;
|
||||
if (baselineFrame) {
|
||||
|
@ -1686,7 +1695,7 @@ IonCompile(JSContext *cx, JSScript *script,
|
|||
IonBuilder *builder = alloc->new_<IonBuilder>((JSContext *) nullptr,
|
||||
CompileCompartment::get(cx->compartment()),
|
||||
temp, graph, constraints,
|
||||
&inspector, info, baselineFrameInspector);
|
||||
inspector, info, baselineFrameInspector);
|
||||
if (!builder)
|
||||
return AbortReason_Alloc;
|
||||
|
||||
|
@ -1696,28 +1705,6 @@ IonCompile(JSContext *cx, JSScript *script,
|
|||
RootedScript builderScript(cx, builder->script());
|
||||
IonSpewNewFunction(graph, builderScript);
|
||||
|
||||
mozilla::Maybe<AutoProtectHeapForCompilation> protect;
|
||||
if (js_IonOptions.checkThreadSafety &&
|
||||
cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL &&
|
||||
!cx->runtime()->profilingScripts &&
|
||||
!cx->runtime()->spsProfiler.enabled())
|
||||
{
|
||||
protect.construct(cx->runtime());
|
||||
}
|
||||
|
||||
bool succeeded = builder->build();
|
||||
builder->clearForBackEnd();
|
||||
|
||||
if (!succeeded) {
|
||||
if (cx->isExceptionPending()) {
|
||||
IonSpew(IonSpew_Abort, "Builder raised exception.");
|
||||
return AbortReason_Error;
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_Abort, "Builder failed to build.");
|
||||
return builder->abortReason();
|
||||
}
|
||||
|
||||
// If possible, compile the script off thread.
|
||||
if (OffThreadCompilationAvailable(cx)) {
|
||||
if (recompile) {
|
||||
|
@ -1739,6 +1726,24 @@ IonCompile(JSContext *cx, JSScript *script,
|
|||
return AbortReason_NoAbort;
|
||||
}
|
||||
|
||||
Maybe<AutoEnterIonCompilation> ionCompiling;
|
||||
ionCompiling.construct();
|
||||
|
||||
Maybe<AutoProtectHeapForIonCompilation> protect;
|
||||
if (js_IonOptions.checkThreadSafety &&
|
||||
cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL &&
|
||||
!cx->runtime()->profilingScripts &&
|
||||
!cx->runtime()->spsProfiler.enabled())
|
||||
{
|
||||
protect.construct(cx->runtime());
|
||||
}
|
||||
|
||||
bool succeeded = builder->build();
|
||||
builder->clearForBackEnd();
|
||||
|
||||
if (!succeeded)
|
||||
return builder->abortReason();
|
||||
|
||||
ScopedJSDeletePtr<CodeGenerator> codegen(CompileBackEnd(builder));
|
||||
if (!codegen) {
|
||||
IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
|
||||
|
@ -1747,6 +1752,7 @@ IonCompile(JSContext *cx, JSScript *script,
|
|||
|
||||
if (!protect.empty())
|
||||
protect.destroy();
|
||||
ionCompiling.destroy();
|
||||
|
||||
bool success = codegen->link(cx, builder->constraints());
|
||||
|
||||
|
|
|
@ -222,11 +222,10 @@ IsPhiObservable(MPhi *phi, Observability observe)
|
|||
if (fun && slot == info.thisSlot())
|
||||
return true;
|
||||
|
||||
// If the function is heavyweight, and the Phi is of the |scopeChain|
|
||||
// value, and the function may need an arguments object, then make sure
|
||||
// to preserve the scope chain, because it may be needed to construct the
|
||||
// arguments object during bailout.
|
||||
if (fun && fun->isHeavyweight() && info.hasArguments() && slot == info.scopeChainSlot())
|
||||
// If the function may need an arguments object, then make sure to preserve
|
||||
// the scope chain, because it may be needed to construct the arguments
|
||||
// object during bailout.
|
||||
if (fun && info.hasArguments() && slot == info.scopeChainSlot())
|
||||
return true;
|
||||
|
||||
// If the Phi is one of the formal argument, and we are using an argument
|
||||
|
@ -2177,7 +2176,8 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
|||
MIRGraph graph(&temp);
|
||||
CompileInfo info(script, fun,
|
||||
/* osrPc = */ nullptr, /* constructing = */ false,
|
||||
DefinitePropertiesAnalysis);
|
||||
DefinitePropertiesAnalysis,
|
||||
script->needsArgsObj());
|
||||
|
||||
AutoTempAllocatorRooter root(cx, &temp);
|
||||
|
||||
|
|
|
@ -135,16 +135,24 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, Tem
|
|||
lazyArguments_(nullptr),
|
||||
inlineCallInfo_(nullptr)
|
||||
{
|
||||
script_.init(info->script());
|
||||
script_ = info->script();
|
||||
pc = info->startPC();
|
||||
|
||||
#ifdef DEBUG
|
||||
lock();
|
||||
JS_ASSERT(script()->hasBaselineScript());
|
||||
unlock();
|
||||
#endif
|
||||
JS_ASSERT(!!analysisContext == (info->executionMode() == DefinitePropertiesAnalysis));
|
||||
}
|
||||
|
||||
void
|
||||
IonBuilder::clearForBackEnd()
|
||||
{
|
||||
// This case should only be hit if there was a failure while building.
|
||||
if (!lock_.empty())
|
||||
lock_.destroy();
|
||||
|
||||
JS_ASSERT(!analysisContext);
|
||||
baselineFrame_ = nullptr;
|
||||
|
||||
|
@ -581,12 +589,16 @@ IonBuilder::pushLoop(CFGState::State initial, jsbytecode *stopAt, MBasicBlock *e
|
|||
bool
|
||||
IonBuilder::init()
|
||||
{
|
||||
lock();
|
||||
|
||||
if (!types::TypeScript::FreezeTypeSets(constraints(), script(),
|
||||
&thisTypes, &argTypes, &typeArray))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
if (!analysis().init(alloc(), gsn))
|
||||
return false;
|
||||
|
||||
|
@ -694,6 +706,8 @@ IonBuilder::build()
|
|||
if (!traverseBytecode())
|
||||
return false;
|
||||
|
||||
unlock();
|
||||
|
||||
if (!maybeAddOsrTypeBarriers())
|
||||
return false;
|
||||
|
||||
|
@ -851,6 +865,7 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
|
|||
if (!traverseBytecode())
|
||||
return false;
|
||||
|
||||
unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -905,6 +920,9 @@ IonBuilder::initParameters()
|
|||
// interpreter and didn't accumulate type information, try to use that OSR
|
||||
// frame to determine possible initial types for 'this' and parameters.
|
||||
|
||||
// For unknownProperties() tests under addType.
|
||||
lock();
|
||||
|
||||
if (thisTypes->empty() && baselineFrame_) {
|
||||
if (!thisTypes->addType(baselineFrame_->thisType, alloc_->lifoAlloc()))
|
||||
return false;
|
||||
|
@ -928,6 +946,8 @@ IonBuilder::initParameters()
|
|||
current->initSlot(info().argSlotUnchecked(i), param);
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -950,6 +970,8 @@ IonBuilder::initScopeChain(MDefinition *callee)
|
|||
if (!script()->compileAndGo())
|
||||
return abort("non-CNG global scripts are not supported");
|
||||
|
||||
lock();
|
||||
|
||||
if (JSFunction *fun = info().fun()) {
|
||||
if (!callee) {
|
||||
MCallee *calleeIns = MCallee::New(alloc());
|
||||
|
@ -975,6 +997,8 @@ IonBuilder::initScopeChain(MDefinition *callee)
|
|||
scope = constant(ObjectValue(script()->global()));
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
current->setScopeChain(scope);
|
||||
return true;
|
||||
}
|
||||
|
@ -1168,6 +1192,14 @@ IonBuilder::maybeAddOsrTypeBarriers()
|
|||
bool
|
||||
IonBuilder::traverseBytecode()
|
||||
{
|
||||
// Always hold the compilation lock when traversing bytecode, though release
|
||||
// it before reacquiring it every few opcodes so that the main thread does not
|
||||
// block for long when updating compilation data.
|
||||
lock();
|
||||
|
||||
size_t lockOpcodeCount = 0;
|
||||
static const size_t LOCK_OPCODE_GRANULARITY = 5;
|
||||
|
||||
for (;;) {
|
||||
JS_ASSERT(pc < info().limitPC());
|
||||
|
||||
|
@ -1242,6 +1274,12 @@ IonBuilder::traverseBytecode()
|
|||
if (!inspectOpcode(op))
|
||||
return false;
|
||||
|
||||
if (++lockOpcodeCount == LOCK_OPCODE_GRANULARITY) {
|
||||
unlock();
|
||||
lock();
|
||||
lockOpcodeCount = 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (size_t i = 0; i < popped.length(); i++) {
|
||||
// Call instructions can discard PassArg instructions. Ignore them.
|
||||
|
@ -3845,13 +3883,16 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
|
|||
LifoAlloc *lifoAlloc = alloc_->lifoAlloc();
|
||||
CompileInfo *info = lifoAlloc->new_<CompileInfo>(calleeScript, target,
|
||||
(jsbytecode *)nullptr, callInfo.constructing(),
|
||||
this->info().executionMode());
|
||||
this->info().executionMode(),
|
||||
/* needsArgsObj = */ false);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
MIRGraphReturns returns(alloc());
|
||||
AutoAccumulateReturns aar(graph(), returns);
|
||||
|
||||
unlock();
|
||||
|
||||
// Build the graph.
|
||||
IonBuilder inlineBuilder(analysisContext, compartment,
|
||||
&alloc(), &graph(), constraints(), &inspector, info, nullptr,
|
||||
|
@ -3875,6 +3916,8 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
|
|||
return false;
|
||||
}
|
||||
|
||||
lock();
|
||||
|
||||
// Create return block.
|
||||
jsbytecode *postCall = GetNextPc(pc);
|
||||
MBasicBlock *returnBlock = newBlock(nullptr, postCall);
|
||||
|
@ -4698,7 +4741,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
|
|||
JSObject *templateObject = inspector->getTemplateObject(pc);
|
||||
if (!templateObject || !templateObject->is<JSObject>())
|
||||
return nullptr;
|
||||
if (templateObject->getProto() != proto)
|
||||
if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
|
||||
return nullptr;
|
||||
|
||||
if (!target->nonLazyScript()->types)
|
||||
|
@ -5096,6 +5139,8 @@ IonBuilder::testShouldDOMCall(types::TypeSet *inTypes,
|
|||
if (!curType)
|
||||
continue;
|
||||
|
||||
if (!curType->hasTenuredProto())
|
||||
return false;
|
||||
JSObject *proto = curType->proto().toObjectOrNull();
|
||||
if (!instanceChecker(proto, jinfo->protoID, jinfo->depth))
|
||||
return false;
|
||||
|
@ -6031,6 +6076,8 @@ IonBuilder::testSingletonProperty(JSObject *obj, PropertyName *name)
|
|||
if (ClassHasResolveHook(compartment, obj->getClass(), name))
|
||||
return nullptr;
|
||||
|
||||
if (!obj->hasTenuredProto())
|
||||
return nullptr;
|
||||
obj = obj->getProto();
|
||||
}
|
||||
|
||||
|
@ -6104,6 +6151,8 @@ IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, Pr
|
|||
if (property.isOwnProperty(constraints()))
|
||||
return false;
|
||||
|
||||
if (!object->hasTenuredProto())
|
||||
return false;
|
||||
if (JSObject *proto = object->proto().toObjectOrNull()) {
|
||||
// Test this type.
|
||||
if (testSingletonProperty(proto, name) != singleton)
|
||||
|
@ -7897,6 +7946,8 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!type->hasTenuredProto())
|
||||
return false;
|
||||
JSObject *proto = type->proto().toObjectOrNull();
|
||||
if (proto == foundProto)
|
||||
break;
|
||||
|
@ -7996,7 +8047,7 @@ IonBuilder::annotateGetPropertyCache(MDefinition *obj, MGetPropertyCache *getPro
|
|||
if (!baseTypeObj)
|
||||
continue;
|
||||
types::TypeObjectKey *typeObj = types::TypeObjectKey::get(baseTypeObj);
|
||||
if (typeObj->unknownProperties() || !typeObj->proto().isObject())
|
||||
if (typeObj->unknownProperties() || !typeObj->hasTenuredProto() || !typeObj->proto().isObject())
|
||||
continue;
|
||||
|
||||
const Class *clasp = typeObj->clasp();
|
||||
|
|
|
@ -724,7 +724,7 @@ class IonBuilder : public MIRGenerator
|
|||
}
|
||||
|
||||
// A builder is inextricably tied to a particular script.
|
||||
HeapPtrScript script_;
|
||||
JSScript *script_;
|
||||
|
||||
// If off thread compilation is successful, the final code generator is
|
||||
// attached here. Code has been generated, but not linked (there is not yet
|
||||
|
@ -735,7 +735,7 @@ class IonBuilder : public MIRGenerator
|
|||
public:
|
||||
void clearForBackEnd();
|
||||
|
||||
JSScript *script() const { return script_.get(); }
|
||||
JSScript *script() const { return script_; }
|
||||
|
||||
CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
|
||||
void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
|
||||
|
@ -765,6 +765,17 @@ class IonBuilder : public MIRGenerator
|
|||
// Constraints for recording dependencies on type information.
|
||||
types::CompilerConstraintList *constraints_;
|
||||
|
||||
mozilla::Maybe<AutoLockForCompilation> lock_;
|
||||
|
||||
void lock() {
|
||||
if (!analysisContext)
|
||||
lock_.construct(compartment);
|
||||
}
|
||||
void unlock() {
|
||||
if (!analysisContext)
|
||||
lock_.destroy();
|
||||
}
|
||||
|
||||
// Basic analysis information about the script.
|
||||
BytecodeAnalysis analysis_;
|
||||
BytecodeAnalysis &analysis() {
|
||||
|
|
|
@ -468,7 +468,7 @@ GeneratePrototypeGuards(JSContext *cx, IonScript *ion, MacroAssembler &masm, JSO
|
|||
// Note: objectReg and scratchReg may be the same register, so we cannot
|
||||
// use objectReg in the rest of this function.
|
||||
masm.loadPtr(Address(objectReg, JSObject::offsetOfType()), scratchReg);
|
||||
Address proto(scratchReg, offsetof(types::TypeObject, proto));
|
||||
Address proto(scratchReg, types::TypeObject::offsetOfProto());
|
||||
masm.branchNurseryPtr(Assembler::NotEqual, proto,
|
||||
ImmMaybeNurseryPtr(obj->getProto()), failures);
|
||||
}
|
||||
|
@ -796,11 +796,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
|||
Register lastReg = object;
|
||||
JS_ASSERT(scratchReg != object);
|
||||
while (proto) {
|
||||
Address addrType(lastReg, JSObject::offsetOfType());
|
||||
masm.loadPtr(addrType, scratchReg);
|
||||
Address addrProto(scratchReg, offsetof(types::TypeObject, proto));
|
||||
masm.loadPtr(addrProto, scratchReg);
|
||||
Address addrShape(scratchReg, JSObject::offsetOfShape());
|
||||
masm.loadObjProto(lastReg, scratchReg);
|
||||
|
||||
// Guard the shape of the current prototype.
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
|
@ -2584,8 +2580,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
Shape *protoShape = proto->lastProperty();
|
||||
|
||||
// load next prototype
|
||||
masm.loadPtr(Address(protoReg, JSObject::offsetOfType()), protoReg);
|
||||
masm.loadPtr(Address(protoReg, offsetof(types::TypeObject, proto)), protoReg);
|
||||
masm.loadObjProto(protoReg, protoReg);
|
||||
|
||||
// Ensure that its shape matches.
|
||||
masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &failuresPopObject);
|
||||
|
|
|
@ -292,12 +292,12 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
}
|
||||
void loadObjClass(Register objReg, Register dest) {
|
||||
loadPtr(Address(objReg, JSObject::offsetOfType()), dest);
|
||||
loadPtr(Address(dest, offsetof(types::TypeObject, clasp)), dest);
|
||||
loadPtr(Address(dest, types::TypeObject::offsetOfClasp()), dest);
|
||||
}
|
||||
void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp,
|
||||
Label *label) {
|
||||
loadPtr(Address(obj, JSObject::offsetOfType()), scratch);
|
||||
branchPtr(cond, Address(scratch, offsetof(types::TypeObject, clasp)), ImmPtr(clasp), label);
|
||||
branchPtr(cond, Address(scratch, types::TypeObject::offsetOfClasp()), ImmPtr(clasp), label);
|
||||
}
|
||||
void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) {
|
||||
branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label);
|
||||
|
@ -353,7 +353,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
void loadObjProto(Register obj, Register dest) {
|
||||
loadPtr(Address(obj, JSObject::offsetOfType()), dest);
|
||||
loadPtr(Address(dest, offsetof(types::TypeObject, proto)), dest);
|
||||
loadPtr(Address(dest, types::TypeObject::offsetOfProto()), dest);
|
||||
}
|
||||
|
||||
void loadStringLength(Register str, Register dest) {
|
||||
|
|
|
@ -2929,7 +2929,13 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
|||
// If this access has never executed, try to add types to the observed set
|
||||
// according to any property which exists on the object or its prototype.
|
||||
if (updateObserved && observed->empty() && name) {
|
||||
JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull();
|
||||
JSObject *obj;
|
||||
if (object->singleton())
|
||||
obj = object->singleton();
|
||||
else if (object->hasTenuredProto())
|
||||
obj = object->proto().toObjectOrNull();
|
||||
else
|
||||
obj = nullptr;
|
||||
|
||||
while (obj) {
|
||||
if (!obj->getClass()->isNative())
|
||||
|
@ -2953,6 +2959,8 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx,
|
|||
}
|
||||
}
|
||||
|
||||
if (!obj->hasTenuredProto())
|
||||
break;
|
||||
obj = obj->getProto();
|
||||
}
|
||||
}
|
||||
|
@ -3004,7 +3012,11 @@ jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *cons
|
|||
types::TypeObjectKey *object = types->getObject(i);
|
||||
if (!object)
|
||||
continue;
|
||||
while (object->proto().isObject()) {
|
||||
while (true) {
|
||||
if (!object->hasTenuredProto())
|
||||
return true;
|
||||
if (!object->proto().isObject())
|
||||
break;
|
||||
object = types::TypeObjectKey::get(object->proto().toObject());
|
||||
if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed))
|
||||
return true;
|
||||
|
|
|
@ -106,7 +106,7 @@ MapSlotsToBitset(BitSet *set, CompactBufferWriter &stream, uint32_t nslots, uint
|
|||
}
|
||||
|
||||
size_t count = set->rawLength();
|
||||
uint32_t *words = set->raw();
|
||||
const uint32_t *words = set->raw();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
stream.writeUnsigned(words[i]);
|
||||
}
|
||||
|
|
|
@ -552,7 +552,7 @@ OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, bool *out)
|
|||
bool
|
||||
GetIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue rval)
|
||||
{
|
||||
if (!cx->global()->getIntrinsicValue(cx, name, rval))
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, rval))
|
||||
return false;
|
||||
|
||||
// This function is called when we try to compile a cold getintrinsic
|
||||
|
@ -946,7 +946,7 @@ AssertValidObjectPtr(JSContext *cx, JSObject *obj)
|
|||
JS_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
|
||||
|
||||
JS_ASSERT_IF(!obj->hasLazyType(),
|
||||
obj->type()->clasp == obj->lastProperty()->getObjectClass());
|
||||
obj->type()->clasp() == obj->lastProperty()->getObjectClass());
|
||||
|
||||
if (obj->isTenured()) {
|
||||
JS_ASSERT(obj->isAligned());
|
||||
|
|
|
@ -1725,7 +1725,7 @@ CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard)
|
|||
Register tmp = ToRegister(guard->tempInt());
|
||||
|
||||
masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp);
|
||||
masm.cmpPtr(Operand(tmp, offsetof(types::TypeObject, clasp)), ImmPtr(guard->mir()->getClass()));
|
||||
masm.cmpPtr(Operand(tmp, types::TypeObject::offsetOfClasp()), ImmPtr(guard->mir()->getClass()));
|
||||
if (!bailoutIf(Assembler::NotEqual, guard->snapshot()))
|
||||
return false;
|
||||
return true;
|
||||
|
|
|
@ -391,7 +391,7 @@ MSG_DEF(JSMSG_DATE_NOT_FINITE, 337, 0, JSEXN_RANGEERR, "date value is not
|
|||
MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 338, 0, JSEXN_SYNTAXERR, "\"use asm\" is only meaningful in the Directive Prologue of a function body")
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 339, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
|
||||
MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 340, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 341, 1, JSEXN_ERR, "successfully compiled asm.js code ({0})")
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 341, 1, JSEXN_NONE, "Successfully compiled asm.js code ({0})")
|
||||
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 342, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
|
||||
MSG_DEF(JSMSG_YIELD_IN_ARROW, 343, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield")
|
||||
MSG_DEF(JSMSG_WRONG_VALUE, 344, 2, JSEXN_ERR, "expected {0} but found {1}")
|
||||
|
|
|
@ -700,9 +700,7 @@ StartRequest(JSContext *cx)
|
|||
} else {
|
||||
/* Indicate that a request is running. */
|
||||
rt->requestDepth = 1;
|
||||
|
||||
if (rt->activityCallback)
|
||||
rt->activityCallback(rt->activityCallbackArg, true);
|
||||
rt->triggerActivityCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,9 +716,7 @@ StopRequest(JSContext *cx)
|
|||
} else {
|
||||
rt->conservativeGC.updateForRequestEnd();
|
||||
rt->requestDepth = 0;
|
||||
|
||||
if (rt->activityCallback)
|
||||
rt->activityCallback(rt->activityCallbackArg, false);
|
||||
rt->triggerActivityCallback(false);
|
||||
}
|
||||
}
|
||||
#endif /* JS_THREADSAFE */
|
||||
|
|
|
@ -116,6 +116,7 @@ js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction
|
|||
JS_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite());
|
||||
JS_ASSERT(!fun->nonLazyScript()->enclosingStaticScope());
|
||||
JS_ASSERT(types::UseNewTypeForClone(fun));
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
/*
|
||||
* If we start allocating function objects in the nursery, then the callsite
|
||||
|
@ -126,7 +127,7 @@ js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction
|
|||
if (!table.initialized())
|
||||
return nullptr;
|
||||
|
||||
CallsiteCloneTable::Ptr p = table.lookup(CallsiteCloneKey(fun, script, script->pcToOffset(pc)));
|
||||
CallsiteCloneTable::Ptr p = table.readonlyThreadsafeLookup(CallsiteCloneKey(fun, script, script->pcToOffset(pc)));
|
||||
if (p)
|
||||
return p->value();
|
||||
|
||||
|
@ -153,6 +154,8 @@ js::CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript scri
|
|||
typedef CallsiteCloneKey Key;
|
||||
typedef CallsiteCloneTable Table;
|
||||
|
||||
AutoLockForCompilation lock(cx);
|
||||
|
||||
Table &table = cx->compartment()->callsiteClones;
|
||||
if (!table.initialized() && !table.init())
|
||||
return nullptr;
|
||||
|
|
|
@ -32,7 +32,10 @@ js_ReportOverRecursed(js::ThreadSafeContext *cx);
|
|||
|
||||
namespace js {
|
||||
|
||||
namespace jit { class IonContext; }
|
||||
namespace jit {
|
||||
class IonContext;
|
||||
class CompileCompartment;
|
||||
}
|
||||
|
||||
struct CallsiteCloneKey {
|
||||
/* The original function that we are cloning. */
|
||||
|
@ -1038,7 +1041,9 @@ class AutoLockForExclusiveAccess
|
|||
if (runtime->numExclusiveThreads) {
|
||||
runtime->assertCanLock(JSRuntime::ExclusiveAccessLock);
|
||||
PR_Lock(runtime->exclusiveAccessLock);
|
||||
#ifdef DEBUG
|
||||
runtime->exclusiveAccessOwner = PR_GetCurrentThread();
|
||||
#endif
|
||||
} else {
|
||||
JS_ASSERT(!runtime->mainThreadHasExclusiveAccess);
|
||||
runtime->mainThreadHasExclusiveAccess = true;
|
||||
|
@ -1057,9 +1062,7 @@ class AutoLockForExclusiveAccess
|
|||
~AutoLockForExclusiveAccess() {
|
||||
if (runtime->numExclusiveThreads) {
|
||||
JS_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread());
|
||||
#ifdef DEBUG
|
||||
runtime->exclusiveAccessOwner = nullptr;
|
||||
#endif
|
||||
PR_Unlock(runtime->exclusiveAccessLock);
|
||||
} else {
|
||||
JS_ASSERT(runtime->mainThreadHasExclusiveAccess);
|
||||
|
@ -1083,6 +1086,69 @@ class AutoLockForExclusiveAccess
|
|||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
class AutoLockForCompilation
|
||||
{
|
||||
#ifdef JS_WORKER_THREADS
|
||||
JSRuntime *runtime;
|
||||
|
||||
void init(JSRuntime *rt) {
|
||||
runtime = rt;
|
||||
if (runtime->numCompilationThreads) {
|
||||
runtime->assertCanLock(JSRuntime::CompilationLock);
|
||||
PR_Lock(runtime->compilationLock);
|
||||
#ifdef DEBUG
|
||||
runtime->compilationLockOwner = PR_GetCurrentThread();
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(!runtime->mainThreadHasCompilationLock);
|
||||
runtime->mainThreadHasCompilationLock = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AutoLockForCompilation(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
if (cx->isJSContext())
|
||||
init(cx->asJSContext()->runtime());
|
||||
else
|
||||
runtime = nullptr;
|
||||
}
|
||||
AutoLockForCompilation(jit::CompileCompartment *compartment MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
~AutoLockForCompilation() {
|
||||
if (runtime) {
|
||||
if (runtime->numCompilationThreads) {
|
||||
JS_ASSERT(runtime->compilationLockOwner == PR_GetCurrentThread());
|
||||
#ifdef DEBUG
|
||||
runtime->compilationLockOwner = nullptr;
|
||||
#endif
|
||||
PR_Unlock(runtime->compilationLock);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(runtime->mainThreadHasCompilationLock);
|
||||
runtime->mainThreadHasCompilationLock = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // JS_WORKER_THREADS
|
||||
public:
|
||||
AutoLockForCompilation(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
AutoLockForCompilation(jit::CompileCompartment *compartment MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
~AutoLockForCompilation() {
|
||||
// An empty destructor is needed to avoid warnings from clang about
|
||||
// unused local variables of this type.
|
||||
}
|
||||
#endif // JS_WORKER_THREADS
|
||||
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -338,7 +338,7 @@ JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existin
|
|||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, Proxy::LazyProto);
|
||||
RootedObject proto(cx, TaggedProto::LazyProto);
|
||||
RootedObject existing(cx, existingArg);
|
||||
if (existing) {
|
||||
/* Is it possible to reuse |existing|? */
|
||||
|
|
|
@ -1124,25 +1124,21 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti
|
|||
// THING_ROOT_LAZY_SCRIPT).
|
||||
AutoSuppressGC suppressGC(cx);
|
||||
|
||||
fun->flags_ &= ~INTERPRETED_LAZY;
|
||||
fun->flags_ |= INTERPRETED;
|
||||
|
||||
RootedScript script(cx, lazy->maybeScript());
|
||||
|
||||
if (script) {
|
||||
fun->initScript(script);
|
||||
AutoLockForCompilation lock(cx);
|
||||
fun->setUnlazifiedScript(script);
|
||||
return true;
|
||||
}
|
||||
|
||||
fun->initScript(nullptr);
|
||||
|
||||
if (fun != lazy->function()) {
|
||||
script = lazy->function()->getOrCreateScript(cx);
|
||||
if (!script) {
|
||||
fun->initLazyScript(lazy);
|
||||
if (!script)
|
||||
return false;
|
||||
}
|
||||
fun->initScript(script);
|
||||
|
||||
AutoLockForCompilation lock(cx);
|
||||
fun->setUnlazifiedScript(script);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1162,17 +1158,19 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti
|
|||
if (script) {
|
||||
RootedObject enclosingScope(cx, lazy->enclosingScope());
|
||||
RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script));
|
||||
if (!clonedScript) {
|
||||
fun->initLazyScript(lazy);
|
||||
if (!clonedScript)
|
||||
return false;
|
||||
}
|
||||
|
||||
clonedScript->setSourceObject(lazy->sourceObject());
|
||||
|
||||
fun->initAtom(script->function()->displayAtom());
|
||||
fun->initScript(clonedScript);
|
||||
clonedScript->setFunction(fun);
|
||||
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
fun->setUnlazifiedScript(clonedScript);
|
||||
}
|
||||
|
||||
CallNewScriptHook(cx, clonedScript, fun);
|
||||
|
||||
lazy->initScript(clonedScript);
|
||||
|
@ -1184,18 +1182,14 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti
|
|||
// Parse and compile the script from source.
|
||||
SourceDataCache::AutoSuppressPurge asp(cx);
|
||||
const jschar *chars = lazy->source()->chars(cx, asp);
|
||||
if (!chars) {
|
||||
fun->initLazyScript(lazy);
|
||||
if (!chars)
|
||||
return false;
|
||||
}
|
||||
|
||||
const jschar *lazyStart = chars + lazy->begin();
|
||||
size_t lazyLength = lazy->end() - lazy->begin();
|
||||
|
||||
if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) {
|
||||
fun->initLazyScript(lazy);
|
||||
if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength))
|
||||
return false;
|
||||
}
|
||||
|
||||
script = fun->nonLazyScript();
|
||||
|
||||
|
|
|
@ -123,8 +123,6 @@ class JSFunction : public JSObject
|
|||
|
||||
/* Possible attributes of an interpreted function: */
|
||||
bool isFunctionPrototype() const { return flags() & IS_FUN_PROTO; }
|
||||
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
|
||||
bool hasScript() const { return flags() & INTERPRETED; }
|
||||
bool isExprClosure() const { return flags() & EXPR_CLOSURE; }
|
||||
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
|
||||
bool isLambda() const { return flags() & LAMBDA; }
|
||||
|
@ -136,6 +134,17 @@ class JSFunction : public JSObject
|
|||
return flags() & SH_WRAPPABLE;
|
||||
}
|
||||
|
||||
// Functions can change between being lazily interpreted and having scripts
|
||||
// when under the compilation lock.
|
||||
bool isInterpretedLazy() const {
|
||||
JS_ASSERT(js::CurrentThreadCanReadCompilationData());
|
||||
return flags() & INTERPRETED_LAZY;
|
||||
}
|
||||
bool hasScript() const {
|
||||
JS_ASSERT(js::CurrentThreadCanReadCompilationData());
|
||||
return flags() & INTERPRETED;
|
||||
}
|
||||
|
||||
bool hasJITCode() const {
|
||||
if (!hasScript())
|
||||
return false;
|
||||
|
@ -321,6 +330,7 @@ class JSFunction : public JSObject
|
|||
|
||||
JSScript *nonLazyScript() const {
|
||||
JS_ASSERT(hasScript());
|
||||
JS_ASSERT(js::CurrentThreadCanReadCompilationData());
|
||||
return u.i.s.script_;
|
||||
}
|
||||
|
||||
|
@ -331,11 +341,13 @@ class JSFunction : public JSObject
|
|||
|
||||
js::LazyScript *lazyScript() const {
|
||||
JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
|
||||
JS_ASSERT(js::CurrentThreadCanReadCompilationData());
|
||||
return u.i.s.lazy_;
|
||||
}
|
||||
|
||||
js::LazyScript *lazyScriptOrNull() const {
|
||||
JS_ASSERT(isInterpretedLazy());
|
||||
JS_ASSERT(js::CurrentThreadCanReadCompilationData());
|
||||
return u.i.s.lazy_;
|
||||
}
|
||||
|
||||
|
@ -357,15 +369,25 @@ class JSFunction : public JSObject
|
|||
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
|
||||
|
||||
void setScript(JSScript *script_) {
|
||||
JS_ASSERT(isInterpreted());
|
||||
JS_ASSERT(hasScript());
|
||||
mutableScript() = script_;
|
||||
}
|
||||
|
||||
void initScript(JSScript *script_) {
|
||||
JS_ASSERT(isInterpreted());
|
||||
JS_ASSERT(hasScript());
|
||||
mutableScript().init(script_);
|
||||
}
|
||||
|
||||
void setUnlazifiedScript(JSScript *script) {
|
||||
// Note: createScriptForLazilyInterpretedFunction triggers a barrier on
|
||||
// lazy script before it is overwritten here.
|
||||
JS_ASSERT(js::CurrentThreadCanWriteCompilationData());
|
||||
JS_ASSERT(isInterpretedLazy());
|
||||
flags_ &= ~INTERPRETED_LAZY;
|
||||
flags_ |= INTERPRETED;
|
||||
initScript(script);
|
||||
}
|
||||
|
||||
void initLazyScript(js::LazyScript *lazy) {
|
||||
JS_ASSERT(isInterpreted());
|
||||
flags_ &= ~INTERPRETED;
|
||||
|
|
|
@ -5520,6 +5520,12 @@ AutoSuppressGC::AutoSuppressGC(JSCompartment *comp)
|
|||
suppressGC_++;
|
||||
}
|
||||
|
||||
AutoSuppressGC::AutoSuppressGC(JSRuntime *rt)
|
||||
: suppressGC_(rt->mainThread.suppressGC)
|
||||
{
|
||||
suppressGC_++;
|
||||
}
|
||||
|
||||
bool
|
||||
js::UninlinedIsInsideNursery(JSRuntime *rt, const void *thing)
|
||||
{
|
||||
|
|
|
@ -1398,6 +1398,7 @@ class AutoSuppressGC
|
|||
public:
|
||||
AutoSuppressGC(ExclusiveContext *cx);
|
||||
AutoSuppressGC(JSCompartment *comp);
|
||||
AutoSuppressGC(JSRuntime *rt);
|
||||
|
||||
~AutoSuppressGC()
|
||||
{
|
||||
|
|
|
@ -685,6 +685,8 @@ TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script
|
|||
TemporaryTypeSet **pArgTypes,
|
||||
TemporaryTypeSet **pBytecodeTypes)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
LifoAlloc *alloc = constraints->alloc();
|
||||
StackTypeSet *existing = script->types->typeArray();
|
||||
|
||||
|
@ -791,13 +793,26 @@ CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileIn
|
|||
const Class *
|
||||
TypeObjectKey::clasp()
|
||||
{
|
||||
return isTypeObject() ? asTypeObject()->clasp : asSingleObject()->getClass();
|
||||
return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass();
|
||||
}
|
||||
|
||||
TaggedProto
|
||||
TypeObjectKey::proto()
|
||||
{
|
||||
return isTypeObject() ? TaggedProto(asTypeObject()->proto) : asSingleObject()->getTaggedProto();
|
||||
JS_ASSERT(hasTenuredProto());
|
||||
return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
|
||||
}
|
||||
|
||||
bool
|
||||
ObjectImpl::hasTenuredProto() const
|
||||
{
|
||||
return type_->hasTenuredProto();
|
||||
}
|
||||
|
||||
bool
|
||||
TypeObjectKey::hasTenuredProto()
|
||||
{
|
||||
return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto();
|
||||
}
|
||||
|
||||
JSObject *
|
||||
|
@ -836,6 +851,7 @@ HeapTypeSetKey
|
|||
TypeObjectKey::property(jsid id)
|
||||
{
|
||||
JS_ASSERT(!unknownProperties());
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
HeapTypeSetKey property;
|
||||
property.object_ = this;
|
||||
|
@ -1429,8 +1445,10 @@ ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnkno
|
|||
HeapTypeSet *types = object->maybeGetProperty(JSID_EMPTY);
|
||||
|
||||
/* Mark as unknown after getting the types, to avoid assertion. */
|
||||
if (markingUnknown)
|
||||
object->flags |= OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES;
|
||||
if (markingUnknown) {
|
||||
AutoLockForCompilation lock(cxArg);
|
||||
object->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
|
||||
}
|
||||
|
||||
if (types) {
|
||||
if (JSContext *cx = cxArg->maybeJSContext()) {
|
||||
|
@ -1678,6 +1696,9 @@ TemporaryTypeSet::getCommonPrototype()
|
|||
if (!object)
|
||||
continue;
|
||||
|
||||
if (!object->hasTenuredProto())
|
||||
return nullptr;
|
||||
|
||||
TaggedProto nproto = object->proto();
|
||||
if (proto) {
|
||||
if (nproto != proto)
|
||||
|
@ -1737,18 +1758,24 @@ TypeZone::init(JSContext *cx)
|
|||
}
|
||||
|
||||
TypeObject *
|
||||
TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto, bool unknown)
|
||||
TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
|
||||
TypeObjectFlags initialFlags)
|
||||
{
|
||||
JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
|
||||
|
||||
if (!cx->typeInferenceEnabled())
|
||||
initialFlags |= OBJECT_FLAG_UNKNOWN_MASK;
|
||||
|
||||
if (cx->isJSContext()) {
|
||||
if (proto.isObject() && IsInsideNursery(cx->asJSContext()->runtime(), proto.toObject()))
|
||||
initialFlags |= OBJECT_FLAG_NURSERY_PROTO;
|
||||
}
|
||||
|
||||
TypeObject *object = gc::NewGCThing<TypeObject, CanGC>(cx, gc::FINALIZE_TYPE_OBJECT,
|
||||
sizeof(TypeObject), gc::TenuredHeap);
|
||||
if (!object)
|
||||
return nullptr;
|
||||
new(object) TypeObject(clasp, proto, unknown);
|
||||
|
||||
if (!cx->typeInferenceEnabled())
|
||||
object->flags |= OBJECT_FLAG_UNKNOWN_MASK;
|
||||
new(object) TypeObject(clasp, proto, initialFlags);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
@ -1871,12 +1898,11 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
|
|||
return nullptr;
|
||||
|
||||
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
||||
res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged);
|
||||
res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE);
|
||||
if (!res) {
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
return nullptr;
|
||||
}
|
||||
res->flags |= OBJECT_FLAG_FROM_ALLOCATION_SITE;
|
||||
key.script = keyScript;
|
||||
}
|
||||
|
||||
|
@ -2010,6 +2036,8 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
|
|||
HeapTypeSetKey index = type->property(JSID_VOID);
|
||||
if (index.configured(constraints) || index.isOwnProperty(constraints))
|
||||
return true;
|
||||
if (!obj->hasTenuredProto())
|
||||
return true;
|
||||
obj = obj->getProto();
|
||||
} while (obj);
|
||||
|
||||
|
@ -2175,10 +2203,9 @@ void
|
|||
TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
|
||||
{
|
||||
JS_ASSERT(this == &cx->compartment()->types);
|
||||
JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
|
||||
JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
|
||||
JS_ASSERT(!target->singleton);
|
||||
JS_ASSERT(target->unknownProperties());
|
||||
target->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
|
@ -2219,6 +2246,9 @@ TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
AutoLockForCompilation lock(cx);
|
||||
target->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2511,7 +2541,7 @@ TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj)
|
|||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
|
||||
|
||||
if (p) {
|
||||
JS_ASSERT(obj->getProto() == p->value().object->proto);
|
||||
JS_ASSERT(obj->getProto() == p->value().object->proto().toObject());
|
||||
JS_ASSERT(obj->lastProperty() == p->value().shape);
|
||||
|
||||
UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length());
|
||||
|
@ -2614,7 +2644,7 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n
|
|||
cx->clearPendingException();
|
||||
return nullptr;
|
||||
}
|
||||
JS_ASSERT(obj->getProto() == p->value().object->proto);
|
||||
JS_ASSERT(obj->getProto() == p->value().object->proto().toObject());
|
||||
|
||||
RootedShape shape(cx, p->value().shape);
|
||||
if (!JSObject::setLastProperty(cx, obj, shape)) {
|
||||
|
@ -2635,6 +2665,36 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n
|
|||
// TypeObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
TypeObject::assertCanAccessProto()
|
||||
{
|
||||
// The proto pointer for type objects representing singletons may move.
|
||||
JS_ASSERT_IF(singleton, CurrentThreadCanReadCompilationData());
|
||||
|
||||
// Any proto pointer which is in the nursery may be moved, and may not be
|
||||
// accessed during off thread compilation.
|
||||
#if defined(JSGC_GENERATIONAL) && defined(JS_WORKER_THREADS)
|
||||
PerThreadData *pt = TlsPerThreadData.get();
|
||||
TaggedProto proto(proto_);
|
||||
JS_ASSERT_IF(proto.isObject() && !proto.toObject()->isTenured(),
|
||||
!pt || !pt->ionCompiling);
|
||||
#endif
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void
|
||||
TypeObject::setProto(JSContext *cx, TaggedProto proto)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanWriteCompilationData());
|
||||
JS_ASSERT(singleton);
|
||||
|
||||
if (proto.isObject() && IsInsideNursery(cx->runtime(), proto.toObject()))
|
||||
addFlags(OBJECT_FLAG_NURSERY_PROTO);
|
||||
|
||||
setProtoUnchecked(proto);
|
||||
}
|
||||
|
||||
static inline void
|
||||
UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shape *shape,
|
||||
bool indexed)
|
||||
|
@ -2644,7 +2704,8 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap
|
|||
|
||||
if (shape->hasGetterValue() || shape->hasSetterValue()) {
|
||||
types->setConfiguredProperty(cx);
|
||||
types->addType(cx, Type::UnknownType());
|
||||
if (!types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc()))
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
} else if (shape->hasDefaultGetter() && shape->hasSlot()) {
|
||||
if (!indexed && types->canSetDefinite(shape->slot()))
|
||||
types->setDefinite(shape->slot());
|
||||
|
@ -2658,7 +2719,8 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap
|
|||
*/
|
||||
if (indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) {
|
||||
Type type = GetValueType(value);
|
||||
types->addType(cx, type);
|
||||
if (!types->TypeSet::addType(type, &cx->typeLifoAlloc()))
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2696,7 +2758,8 @@ TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop)
|
|||
const Value &value = singleton->getDenseElement(i);
|
||||
if (!value.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
Type type = GetValueType(value);
|
||||
base->types.addType(cx, type);
|
||||
if (!base->types.TypeSet::addType(type, &cx->typeLifoAlloc()))
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
}
|
||||
}
|
||||
} else if (!JSID_IS_EMPTY(id)) {
|
||||
|
@ -2871,7 +2934,7 @@ TypeObject::markStateChange(ExclusiveContext *cxArg)
|
|||
void
|
||||
TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags)
|
||||
{
|
||||
if ((this->flags & flags) == flags)
|
||||
if (hasAllFlags(flags))
|
||||
return;
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
@ -2882,7 +2945,10 @@ TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags)
|
|||
singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
|
||||
}
|
||||
|
||||
this->flags |= flags;
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
addFlags(flags);
|
||||
}
|
||||
|
||||
InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
|
||||
|
||||
|
@ -2897,7 +2963,7 @@ TypeObject::markUnknown(ExclusiveContext *cx)
|
|||
JS_ASSERT(cx->compartment()->activeAnalysis);
|
||||
JS_ASSERT(!unknownProperties());
|
||||
|
||||
if (!(flags & OBJECT_FLAG_ADDENDUM_CLEARED))
|
||||
if (!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED))
|
||||
clearAddendum(cx);
|
||||
|
||||
InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
|
||||
|
@ -2926,8 +2992,11 @@ TypeObject::markUnknown(ExclusiveContext *cx)
|
|||
void
|
||||
TypeObject::clearAddendum(ExclusiveContext *cx)
|
||||
{
|
||||
JS_ASSERT(!(flags & OBJECT_FLAG_ADDENDUM_CLEARED));
|
||||
flags |= OBJECT_FLAG_ADDENDUM_CLEARED;
|
||||
JS_ASSERT(!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED));
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
addFlags(OBJECT_FLAG_ADDENDUM_CLEARED);
|
||||
}
|
||||
|
||||
/*
|
||||
* It is possible for the object to not have a new script or other
|
||||
|
@ -3072,11 +3141,11 @@ TypeObject::clearTypedObjectAddendum(ExclusiveContext *cx)
|
|||
void
|
||||
TypeObject::print()
|
||||
{
|
||||
TaggedProto tagged(proto);
|
||||
TaggedProto tagged(proto());
|
||||
fprintf(stderr, "%s : %s",
|
||||
TypeObjectString(this),
|
||||
tagged.isObject() ? TypeString(Type::ObjectType(proto))
|
||||
: (tagged.isLazy() ? "(lazy)" : "(null)"));
|
||||
TypeObjectString(this),
|
||||
tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject()))
|
||||
: (tagged.isLazy() ? "(lazy)" : "(null)"));
|
||||
|
||||
if (unknownProperties()) {
|
||||
fprintf(stderr, " unknown");
|
||||
|
@ -3142,7 +3211,7 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
|
|||
* non-writable, both of which are indicated by the source type set
|
||||
* being marked as configured.
|
||||
*/
|
||||
if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->configuredProperty())
|
||||
if (!(object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED) && source->configuredProperty())
|
||||
object->clearAddendum(cx);
|
||||
}
|
||||
|
||||
|
@ -3166,7 +3235,7 @@ types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *
|
|||
* a permanent property in any transitive prototype, the definite
|
||||
* properties get cleared from the type.
|
||||
*/
|
||||
RootedObject parent(cx, type->proto);
|
||||
RootedObject parent(cx, type->proto().toObjectOrNull());
|
||||
while (parent) {
|
||||
TypeObject *parentObject = parent->getType(cx);
|
||||
if (!parentObject || parentObject->unknownProperties())
|
||||
|
@ -3196,7 +3265,7 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint
|
|||
const char *kind() { return "clearDefiniteSingle"; }
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, Type type) {
|
||||
if (object->flags & OBJECT_FLAG_ADDENDUM_CLEARED)
|
||||
if (object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED)
|
||||
return;
|
||||
|
||||
if (source->baseFlags() || source->getObjectCount() > 1)
|
||||
|
@ -3279,9 +3348,9 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
|
|||
}
|
||||
|
||||
if (baseobj->slotSpan() == 0 ||
|
||||
!!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED))
|
||||
!!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED))
|
||||
{
|
||||
if (type->addendum)
|
||||
if (type->hasNewScript())
|
||||
type->clearAddendum(cx);
|
||||
return;
|
||||
}
|
||||
|
@ -3296,8 +3365,8 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
|
|||
type->clearAddendum(cx);
|
||||
return;
|
||||
}
|
||||
JS_ASSERT(!type->addendum);
|
||||
JS_ASSERT(!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED));
|
||||
JS_ASSERT(!type->hasNewScript());
|
||||
JS_ASSERT(!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED));
|
||||
|
||||
gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan());
|
||||
|
||||
|
@ -3336,7 +3405,11 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
|
|||
newScript = (TypeNewScript *) cx->calloc_(numBytes);
|
||||
#endif
|
||||
new (newScript) TypeNewScript();
|
||||
type->addendum = newScript;
|
||||
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
type->setAddendum(newScript);
|
||||
}
|
||||
|
||||
if (!newScript) {
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
|
@ -3633,8 +3706,11 @@ JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto>
|
|||
return true;
|
||||
}
|
||||
|
||||
type->clasp = clasp;
|
||||
type->proto = proto.raw();
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
type->setClasp(clasp);
|
||||
type->setProto(cx, proto);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3651,8 +3727,22 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj)
|
|||
if (!fun->getOrCreateScript(cx))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Find flags which need to be specified immediately on the object.
|
||||
// Don't track whether singletons are packed.
|
||||
TypeObjectFlags initialFlags = OBJECT_FLAG_NON_PACKED;
|
||||
|
||||
if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
|
||||
initialFlags |= OBJECT_FLAG_ITERATED;
|
||||
|
||||
if (obj->isIndexed())
|
||||
initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
|
||||
|
||||
if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
|
||||
initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
|
||||
|
||||
Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
|
||||
TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto);
|
||||
TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags);
|
||||
if (!type) {
|
||||
if (cx->typeInferenceEnabled())
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
|
@ -3674,24 +3764,10 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj)
|
|||
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
|
||||
type->interpretedFunction = &obj->as<JSFunction>();
|
||||
|
||||
if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
|
||||
type->flags |= OBJECT_FLAG_ITERATED;
|
||||
|
||||
/*
|
||||
* Adjust flags for objects which will have the wrong flags set by just
|
||||
* looking at the class prototype key.
|
||||
*/
|
||||
|
||||
/* Don't track whether singletons are packed. */
|
||||
type->flags |= OBJECT_FLAG_NON_PACKED;
|
||||
|
||||
if (obj->isIndexed())
|
||||
type->flags |= OBJECT_FLAG_SPARSE_INDEXES;
|
||||
|
||||
if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
|
||||
type->flags |= OBJECT_FLAG_LENGTH_OVERFLOW;
|
||||
|
||||
obj->type_ = type;
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
obj->type_ = type;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
@ -3707,8 +3783,8 @@ TypeObjectWithNewScriptEntry::hash(const Lookup &lookup)
|
|||
/* static */ inline bool
|
||||
TypeObjectWithNewScriptEntry::match(const TypeObjectWithNewScriptEntry &key, const Lookup &lookup)
|
||||
{
|
||||
return key.object->proto == lookup.matchProto.raw() &&
|
||||
key.object->clasp == lookup.clasp &&
|
||||
return key.object->proto() == lookup.matchProto &&
|
||||
key.object->clasp() == lookup.clasp &&
|
||||
key.newFunction == lookup.newFunction;
|
||||
}
|
||||
|
||||
|
@ -3805,8 +3881,8 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction *
|
|||
newTypeObjects.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, fun));
|
||||
if (p) {
|
||||
TypeObject *type = p->object;
|
||||
JS_ASSERT(type->clasp == clasp);
|
||||
JS_ASSERT(type->proto.get() == proto.raw());
|
||||
JS_ASSERT(type->clasp() == clasp);
|
||||
JS_ASSERT(type->proto() == proto);
|
||||
JS_ASSERT_IF(type->hasNewScript(), type->newScript()->fun == fun);
|
||||
return type;
|
||||
}
|
||||
|
@ -3816,13 +3892,19 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction *
|
|||
if (proto.isObject() && !proto.toObject()->setDelegate(this))
|
||||
return nullptr;
|
||||
|
||||
bool markUnknown =
|
||||
proto.isObject()
|
||||
? proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)
|
||||
: true;
|
||||
TypeObjectFlags initialFlags = 0;
|
||||
if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) {
|
||||
// The new type is not present in any type sets, so mark the object as
|
||||
// unknown in all type sets it appears in. This allows the prototype of
|
||||
// such objects to mutate freely without triggering an expensive walk of
|
||||
// the compartment's type sets. (While scripts normally don't mutate
|
||||
// __proto__, the browser will for proxies and such, and we need to
|
||||
// accommodate this behavior).
|
||||
initialFlags = OBJECT_FLAG_UNKNOWN_MASK | OBJECT_FLAG_SETS_MARKED_UNKNOWN;
|
||||
}
|
||||
|
||||
Rooted<TaggedProto> protoRoot(this, proto);
|
||||
TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, markUnknown);
|
||||
TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags);
|
||||
if (!type)
|
||||
return nullptr;
|
||||
|
||||
|
@ -3872,17 +3954,6 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction *
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The new type is not present in any type sets, so mark the object as
|
||||
* unknown in all type sets it appears in. This allows the prototype of
|
||||
* such objects to mutate freely without triggering an expensive walk of
|
||||
* the compartment's type sets. (While scripts normally don't mutate
|
||||
* __proto__, the browser will for proxies and such, and we need to
|
||||
* accommodate this behavior).
|
||||
*/
|
||||
if (type->unknownProperties())
|
||||
type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -3907,7 +3978,7 @@ ExclusiveContext::getLazyType(const Class *clasp, TaggedProto proto)
|
|||
}
|
||||
|
||||
Rooted<TaggedProto> protoRoot(this, proto);
|
||||
TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, false);
|
||||
TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot);
|
||||
if (!type)
|
||||
return nullptr;
|
||||
|
||||
|
@ -4159,8 +4230,8 @@ JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
|
|||
} else if (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)) {
|
||||
e.removeFront();
|
||||
} else if (entry.object != e.front().object) {
|
||||
TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp,
|
||||
entry.object->proto.get(),
|
||||
TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp(),
|
||||
entry.object->proto(),
|
||||
entry.newFunction);
|
||||
e.rekeyFront(lookup, entry);
|
||||
}
|
||||
|
@ -4428,7 +4499,7 @@ TypeObject::addTypedObjectAddendum(JSContext *cx,
|
|||
|
||||
JS_ASSERT(repr);
|
||||
|
||||
if (flags & OBJECT_FLAG_ADDENDUM_CLEARED)
|
||||
if (flags() & OBJECT_FLAG_ADDENDUM_CLEARED)
|
||||
return true;
|
||||
|
||||
JS_ASSERT(!unknownProperties());
|
||||
|
|
141
js/src/jsinfer.h
141
js/src/jsinfer.h
|
@ -25,20 +25,38 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
#ifdef DEBUG
|
||||
bool CurrentThreadCanWriteCompilationData();
|
||||
bool CurrentThreadCanReadCompilationData();
|
||||
#endif
|
||||
|
||||
class TypeRepresentation;
|
||||
|
||||
class TaggedProto
|
||||
{
|
||||
public:
|
||||
static JSObject * const LazyProto;
|
||||
|
||||
TaggedProto() : proto(nullptr) {}
|
||||
TaggedProto(JSObject *proto) : proto(proto) {}
|
||||
|
||||
uintptr_t toWord() const { return uintptr_t(proto); }
|
||||
|
||||
inline bool isLazy() const;
|
||||
inline bool isObject() const;
|
||||
inline JSObject *toObject() const;
|
||||
inline JSObject *toObjectOrNull() const;
|
||||
bool isLazy() const {
|
||||
return proto == LazyProto;
|
||||
}
|
||||
bool isObject() const {
|
||||
/* Skip nullptr and LazyProto. */
|
||||
return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto);
|
||||
}
|
||||
JSObject *toObject() const {
|
||||
JS_ASSERT(isObject());
|
||||
return proto;
|
||||
}
|
||||
JSObject *toObjectOrNull() const {
|
||||
JS_ASSERT(!proto || isObject());
|
||||
return proto;
|
||||
}
|
||||
JSObject *raw() const { return proto; }
|
||||
|
||||
bool operator ==(const TaggedProto &other) { return proto == other.proto; }
|
||||
|
@ -77,10 +95,10 @@ class TaggedProtoOperations
|
|||
|
||||
public:
|
||||
uintptr_t toWord() const { return value()->toWord(); }
|
||||
inline bool isLazy() const;
|
||||
inline bool isObject() const;
|
||||
inline JSObject *toObject() const;
|
||||
inline JSObject *toObjectOrNull() const;
|
||||
inline bool isLazy() const { return value()->isLazy(); }
|
||||
inline bool isObject() const { return value()->isObject(); }
|
||||
inline JSObject *toObject() const { return value()->toObject(); }
|
||||
inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); }
|
||||
JSObject *raw() const { return value()->raw(); }
|
||||
};
|
||||
|
||||
|
@ -383,6 +401,12 @@ enum MOZ_ENUM_TYPE(uint32_t) {
|
|||
/* If set, addendum information should not be installed on this object. */
|
||||
OBJECT_FLAG_ADDENDUM_CLEARED = 0x2,
|
||||
|
||||
/*
|
||||
* If set, the object's prototype might be in the nursery and can't be
|
||||
* used during Ion compilation (which may be occurring off thread).
|
||||
*/
|
||||
OBJECT_FLAG_NURSERY_PROTO = 0x4,
|
||||
|
||||
/*
|
||||
* Whether we have ensured all type sets in the compartment contain
|
||||
* ANYOBJECT instead of this object.
|
||||
|
@ -503,7 +527,7 @@ class TypeSet
|
|||
static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc);
|
||||
|
||||
/* Add a type to this set using the specified allocator. */
|
||||
inline bool addType(Type type, LifoAlloc *alloc, bool *padded = nullptr);
|
||||
inline bool addType(Type type, LifoAlloc *alloc);
|
||||
|
||||
/* Get a list of all types in this set. */
|
||||
typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
|
||||
|
@ -857,11 +881,45 @@ struct TypeTypedObject : public TypeObjectAddendum
|
|||
/* Type information about an object accessed by a script. */
|
||||
struct TypeObject : gc::BarrieredCell<TypeObject>
|
||||
{
|
||||
/* Class shared by objects using this type. */
|
||||
const Class *clasp;
|
||||
private:
|
||||
/* Class shared by object using this type. */
|
||||
const Class *clasp_;
|
||||
|
||||
/* Prototype shared by objects using this type. */
|
||||
HeapPtrObject proto;
|
||||
HeapPtrObject proto_;
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertCanAccessProto();
|
||||
#else
|
||||
void assertCanAccessProto() {}
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
const Class *clasp() {
|
||||
return clasp_;
|
||||
}
|
||||
|
||||
void setClasp(const Class *clasp) {
|
||||
JS_ASSERT(CurrentThreadCanWriteCompilationData());
|
||||
JS_ASSERT(singleton);
|
||||
clasp_ = clasp;
|
||||
}
|
||||
|
||||
TaggedProto proto() {
|
||||
assertCanAccessProto();
|
||||
return TaggedProto(proto_);
|
||||
}
|
||||
|
||||
HeapPtrObject &protoRaw() {
|
||||
// For use during marking, don't call otherwise.
|
||||
return proto_;
|
||||
}
|
||||
|
||||
void setProto(JSContext *cx, TaggedProto proto);
|
||||
void setProtoUnchecked(TaggedProto proto) {
|
||||
proto_ = proto.raw();
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether there is a singleton JS object with this type. That JS object
|
||||
|
@ -877,8 +935,9 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
static const size_t LAZY_SINGLETON = 1;
|
||||
bool lazy() const { return singleton == (JSObject *) LAZY_SINGLETON; }
|
||||
|
||||
private:
|
||||
/* Flags for this object. */
|
||||
TypeObjectFlags flags;
|
||||
TypeObjectFlags flags_;
|
||||
|
||||
/*
|
||||
* This field allows various special classes of objects to attach
|
||||
|
@ -891,23 +950,48 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
* before the object escapes.
|
||||
*/
|
||||
HeapPtr<TypeObjectAddendum> addendum;
|
||||
public:
|
||||
|
||||
TypeObjectFlags flags() const {
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void addFlags(TypeObjectFlags flags) {
|
||||
JS_ASSERT(CurrentThreadCanWriteCompilationData());
|
||||
flags_ |= flags;
|
||||
}
|
||||
|
||||
void clearFlags(TypeObjectFlags flags) {
|
||||
JS_ASSERT(CurrentThreadCanWriteCompilationData());
|
||||
flags_ &= ~flags;
|
||||
}
|
||||
|
||||
bool hasNewScript() const {
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return addendum && addendum->isNewScript();
|
||||
}
|
||||
|
||||
TypeNewScript *newScript() {
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return addendum->asNewScript();
|
||||
}
|
||||
|
||||
bool hasTypedObject() {
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return addendum && addendum->isTypedObject();
|
||||
}
|
||||
|
||||
TypeTypedObject *typedObject() {
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return addendum->asTypedObject();
|
||||
}
|
||||
|
||||
void setAddendum(TypeObjectAddendum *addendum) {
|
||||
JS_ASSERT(CurrentThreadCanWriteCompilationData());
|
||||
this->addendum = addendum;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tag the type object for a binary data type descriptor, instance,
|
||||
* or handle with the type representation of the data it points at.
|
||||
|
@ -919,6 +1003,7 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
TypeTypedObject::Kind kind ,
|
||||
TypeRepresentation *repr);
|
||||
|
||||
private:
|
||||
/*
|
||||
* Properties of this object. This may contain JSID_VOID, representing the
|
||||
* types of all integer indexes of the object, and/or JSID_EMPTY, holding
|
||||
|
@ -953,6 +1038,7 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
* might update the property with a new type.
|
||||
*/
|
||||
Property **propertySet;
|
||||
public:
|
||||
|
||||
/* If this is an interpreted function, the function object. */
|
||||
HeapPtrFunction interpretedFunction;
|
||||
|
@ -961,27 +1047,31 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
uint32_t padding;
|
||||
#endif
|
||||
|
||||
inline TypeObject(const Class *clasp, TaggedProto proto, bool unknown);
|
||||
inline TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags);
|
||||
|
||||
bool hasAnyFlags(TypeObjectFlags flags) {
|
||||
JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
|
||||
return !!(this->flags & flags);
|
||||
return !!(this->flags() & flags);
|
||||
}
|
||||
bool hasAllFlags(TypeObjectFlags flags) {
|
||||
JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
|
||||
return (this->flags & flags) == flags;
|
||||
return (this->flags() & flags) == flags;
|
||||
}
|
||||
|
||||
bool unknownProperties() {
|
||||
JS_ASSERT_IF(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES,
|
||||
JS_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
|
||||
hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
|
||||
return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES);
|
||||
return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
|
||||
}
|
||||
|
||||
bool shouldPreTenure() {
|
||||
return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
|
||||
}
|
||||
|
||||
bool hasTenuredProto() const {
|
||||
return !(flags() & OBJECT_FLAG_NURSERY_PROTO);
|
||||
}
|
||||
|
||||
gc::InitialHeap initialHeap(CompilerConstraintList *constraints);
|
||||
|
||||
bool canPreTenure() {
|
||||
|
@ -991,7 +1081,7 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
// this bit reliably.
|
||||
if (unknownProperties())
|
||||
return false;
|
||||
return (flags & OBJECT_FLAG_FROM_ALLOCATION_SITE) || hasNewScript();
|
||||
return (flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE) || hasNewScript();
|
||||
}
|
||||
|
||||
void setShouldPreTenure(ExclusiveContext *cx) {
|
||||
|
@ -1046,12 +1136,20 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
|
||||
static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; }
|
||||
|
||||
static inline uint32_t offsetOfClasp() {
|
||||
return offsetof(TypeObject, clasp_);
|
||||
}
|
||||
|
||||
static inline uint32_t offsetOfProto() {
|
||||
return offsetof(TypeObject, proto_);
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint32_t basePropertyCount() const;
|
||||
inline void setBasePropertyCount(uint32_t count);
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(offsetof(TypeObject, proto) == offsetof(js::shadow::TypeObject, proto));
|
||||
JS_STATIC_ASSERT(offsetof(TypeObject, proto_) == offsetof(js::shadow::TypeObject, proto));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1247,6 +1345,7 @@ struct TypeObjectKey
|
|||
|
||||
const Class *clasp();
|
||||
TaggedProto proto();
|
||||
bool hasTenuredProto();
|
||||
JSObject *singleton();
|
||||
TypeNewScript *newScript();
|
||||
|
||||
|
@ -1416,7 +1515,7 @@ struct TypeCompartment
|
|||
* js_ObjectClass).
|
||||
*/
|
||||
TypeObject *newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
|
||||
bool unknown = false);
|
||||
TypeObjectFlags initialFlags = 0);
|
||||
|
||||
/* Get or make an object for an allocation site, and add to the allocation site table. */
|
||||
TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);
|
||||
|
|
|
@ -25,61 +25,6 @@
|
|||
#include "jsanalyzeinlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
|
||||
inline bool
|
||||
js::TaggedProto::isObject() const
|
||||
{
|
||||
/* Skip nullptr and Proxy::LazyProto. */
|
||||
return uintptr_t(proto) > uintptr_t(Proxy::LazyProto);
|
||||
}
|
||||
|
||||
inline bool
|
||||
js::TaggedProto::isLazy() const
|
||||
{
|
||||
return proto == Proxy::LazyProto;
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
js::TaggedProto::toObject() const
|
||||
{
|
||||
JS_ASSERT(isObject());
|
||||
return proto;
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
js::TaggedProto::toObjectOrNull() const
|
||||
{
|
||||
JS_ASSERT(!proto || isObject());
|
||||
return proto;
|
||||
}
|
||||
|
||||
template<class Outer>
|
||||
inline bool
|
||||
js::TaggedProtoOperations<Outer>::isLazy() const
|
||||
{
|
||||
return value()->isLazy();
|
||||
}
|
||||
|
||||
template<class Outer>
|
||||
inline bool
|
||||
js::TaggedProtoOperations<Outer>::isObject() const
|
||||
{
|
||||
return value()->isObject();
|
||||
}
|
||||
|
||||
template<class Outer>
|
||||
inline JSObject *
|
||||
js::TaggedProtoOperations<Outer>::toObject() const
|
||||
{
|
||||
return value()->toObject();
|
||||
}
|
||||
|
||||
template<class Outer>
|
||||
inline JSObject *
|
||||
js::TaggedProtoOperations<Outer>::toObjectOrNull() const
|
||||
{
|
||||
return value()->toObjectOrNull();
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace types {
|
||||
|
||||
|
@ -534,7 +479,7 @@ MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
|
|||
if (cx->typeInferenceEnabled()) {
|
||||
if (!obj->unknownProperties())
|
||||
obj->markUnknown(cx);
|
||||
if (markSetsUnknown && !(obj->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
|
||||
if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
|
||||
cx->compartment()->types.markSetsUnknown(cx, obj);
|
||||
}
|
||||
}
|
||||
|
@ -605,7 +550,8 @@ TypeScript::NumTypeSets(JSScript *script)
|
|||
/* static */ inline StackTypeSet *
|
||||
TypeScript::ThisTypes(JSScript *script)
|
||||
{
|
||||
return script->types->typeArray() + script->nTypeSets() + js::analyze::ThisSlot();
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return script->types->typeArray() + script->nTypeSets() + analyze::ThisSlot();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -618,7 +564,8 @@ TypeScript::ThisTypes(JSScript *script)
|
|||
TypeScript::ArgTypes(JSScript *script, unsigned i)
|
||||
{
|
||||
JS_ASSERT(i < script->function()->nargs());
|
||||
return script->types->typeArray() + script->nTypeSets() + js::analyze::ArgSlot(i);
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i);
|
||||
}
|
||||
|
||||
template <typename TYPESET>
|
||||
|
@ -1089,10 +1036,8 @@ TypeSet::clearObjects()
|
|||
}
|
||||
|
||||
bool
|
||||
TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded)
|
||||
TypeSet::addType(Type type, LifoAlloc *alloc)
|
||||
{
|
||||
JS_ASSERT_IF(padded, !*padded);
|
||||
|
||||
if (unknown())
|
||||
return true;
|
||||
|
||||
|
@ -1100,8 +1045,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded)
|
|||
flags |= TYPE_FLAG_BASE_MASK;
|
||||
clearObjects();
|
||||
JS_ASSERT(unknown());
|
||||
if (padded)
|
||||
*padded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1115,8 +1058,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded)
|
|||
flag |= TYPE_FLAG_INT32;
|
||||
|
||||
flags |= flag;
|
||||
if (padded)
|
||||
*padded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1156,8 +1097,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded)
|
|||
clearObjects();
|
||||
}
|
||||
|
||||
if (padded)
|
||||
*padded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1166,13 +1105,16 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
|
|||
{
|
||||
JS_ASSERT(cxArg->compartment()->activeAnalysis);
|
||||
|
||||
bool added = false;
|
||||
if (!TypeSet::addType(type, &cxArg->typeLifoAlloc(), &added)) {
|
||||
cxArg->compartment()->types.setPendingNukeTypes(cxArg);
|
||||
if (hasType(type))
|
||||
return;
|
||||
|
||||
{
|
||||
AutoLockForCompilation lock(cxArg);
|
||||
if (!TypeSet::addType(type, &cxArg->typeLifoAlloc())) {
|
||||
cxArg->compartment()->types.setPendingNukeTypes(cxArg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!added)
|
||||
return;
|
||||
|
||||
InferSpew(ISpewOps, "addType: %sT%p%s %s",
|
||||
InferSpewColor(this), this, InferSpewColorReset(),
|
||||
|
@ -1275,7 +1217,7 @@ TypeSet::getObjectClass(unsigned i) const
|
|||
if (JSObject *object = getSingleObject(i))
|
||||
return object->getClass();
|
||||
if (TypeObject *object = getTypeObject(i))
|
||||
return object->clasp;
|
||||
return object->clasp();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1283,18 +1225,16 @@ TypeSet::getObjectClass(unsigned i) const
|
|||
// TypeObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, bool unknown)
|
||||
inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags)
|
||||
{
|
||||
mozilla::PodZero(this);
|
||||
|
||||
/* Inner objects may not appear on prototype chains. */
|
||||
JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
|
||||
|
||||
this->clasp = clasp;
|
||||
this->proto = proto.raw();
|
||||
|
||||
if (unknown)
|
||||
flags |= OBJECT_FLAG_UNKNOWN_MASK;
|
||||
this->clasp_ = clasp;
|
||||
this->proto_ = proto.raw();
|
||||
this->flags_ = initialFlags;
|
||||
|
||||
InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
|
||||
}
|
||||
|
@ -1302,15 +1242,17 @@ inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, bool unknow
|
|||
inline uint32_t
|
||||
TypeObject::basePropertyCount() const
|
||||
{
|
||||
return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeObject::setBasePropertyCount(uint32_t count)
|
||||
{
|
||||
// Note: Callers must ensure they are performing threadsafe operations.
|
||||
JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
|
||||
flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
|
||||
| (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
|
||||
flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
|
||||
| (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
|
||||
}
|
||||
|
||||
inline HeapTypeSet *
|
||||
|
@ -1322,36 +1264,46 @@ TypeObject::getProperty(ExclusiveContext *cx, jsid id)
|
|||
JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
|
||||
JS_ASSERT(!unknownProperties());
|
||||
|
||||
uint32_t propertyCount = basePropertyCount();
|
||||
Property **pprop = HashSetInsert<jsid,Property,Property>
|
||||
(cx->typeLifoAlloc(), propertySet, propertyCount, id);
|
||||
if (!pprop) {
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
return nullptr;
|
||||
}
|
||||
if (HeapTypeSet *types = maybeGetProperty(id))
|
||||
return types;
|
||||
|
||||
uint32_t propertyCount;
|
||||
Property **pprop;
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
|
||||
propertyCount = basePropertyCount();
|
||||
pprop = HashSetInsert<jsid,Property,Property>
|
||||
(cx->typeLifoAlloc(), propertySet, propertyCount, id);
|
||||
if (!pprop) {
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS_ASSERT(!*pprop);
|
||||
|
||||
if (!*pprop) {
|
||||
setBasePropertyCount(propertyCount);
|
||||
if (!addProperty(cx, id, pprop)) {
|
||||
setBasePropertyCount(0);
|
||||
propertySet = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
|
||||
markUnknown(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an arbitrary property in the object, as all have unknown
|
||||
* type and are treated as configured.
|
||||
*/
|
||||
unsigned count = getPropertyCount();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (Property *prop = getProperty(i))
|
||||
return &prop->types;
|
||||
}
|
||||
if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
|
||||
markUnknown(cx);
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Missing property");
|
||||
/*
|
||||
* Return an arbitrary property in the object, as all have unknown
|
||||
* type and are treated as configured.
|
||||
*/
|
||||
unsigned count = getPropertyCount();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (Property *prop = getProperty(i))
|
||||
return &prop->types;
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Missing property");
|
||||
}
|
||||
|
||||
return &(*pprop)->types;
|
||||
|
@ -1363,6 +1315,7 @@ TypeObject::maybeGetProperty(jsid id)
|
|||
JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
|
||||
JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
|
||||
JS_ASSERT(!unknownProperties());
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
|
||||
Property *prop = HashSetLookup<jsid,Property,Property>
|
||||
(propertySet, basePropertyCount(), id);
|
||||
|
|
|
@ -1937,6 +1937,7 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
|
|||
if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
|
||||
return false;
|
||||
|
||||
AutoLockForCompilation lock(cx);
|
||||
global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
|
||||
global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
|
||||
global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
|
||||
|
|
|
@ -1273,7 +1273,7 @@ NewObject(ExclusiveContext *cx, const Class *clasp, types::TypeObject *type_, JS
|
|||
if (!NewObjectMetadata(cx, &metadata))
|
||||
return nullptr;
|
||||
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(type->proto),
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(),
|
||||
parent, metadata, kind));
|
||||
if (!shape)
|
||||
return nullptr;
|
||||
|
@ -1457,7 +1457,7 @@ js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc
|
|||
NewObjectCache &cache = cx->runtime()->newObjectCache;
|
||||
|
||||
NewObjectCache::EntryIndex entry = -1;
|
||||
if (parent == type->proto->getParent() &&
|
||||
if (parent == type->proto().toObject()->getParent() &&
|
||||
newKind == GenericObject &&
|
||||
!cx->compartment()->hasObjectMetadataCallback())
|
||||
{
|
||||
|
@ -1985,9 +1985,12 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
|
|||
* Swap the object's types, to restore their initial type information.
|
||||
* The prototypes and classes of the objects were swapped in ReserveForTradeGuts.
|
||||
*/
|
||||
TypeObject *tmp = a->type_;
|
||||
a->type_ = b->type_;
|
||||
b->type_ = tmp;
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
TypeObject *tmp = a->type_;
|
||||
a->type_ = b->type_;
|
||||
b->type_ = tmp;
|
||||
}
|
||||
|
||||
/* Don't try to swap a JSFunction for a plain function JSObject. */
|
||||
JS_ASSERT_IF(a->is<JSFunction>(), a->tenuredSizeOfThis() == b->tenuredSizeOfThis());
|
||||
|
@ -2019,9 +2022,12 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
|
|||
char tmp[mozilla::tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::value];
|
||||
JS_ASSERT(size <= sizeof(tmp));
|
||||
|
||||
js_memcpy(tmp, a, size);
|
||||
js_memcpy(a, b, size);
|
||||
js_memcpy(b, tmp, size);
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
js_memcpy(tmp, a, size);
|
||||
js_memcpy(a, b, size);
|
||||
js_memcpy(b, tmp, size);
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
/*
|
||||
|
@ -2059,9 +2065,12 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
|
|||
void *bpriv = b->hasPrivate() ? b->getPrivate() : nullptr;
|
||||
|
||||
char tmp[sizeof(JSObject)];
|
||||
js_memcpy(&tmp, a, sizeof tmp);
|
||||
js_memcpy(a, b, sizeof tmp);
|
||||
js_memcpy(b, &tmp, sizeof tmp);
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
js_memcpy(&tmp, a, sizeof tmp);
|
||||
js_memcpy(a, b, sizeof tmp);
|
||||
js_memcpy(b, &tmp, sizeof tmp);
|
||||
}
|
||||
|
||||
if (a->isNative())
|
||||
a->shape_->setNumFixedSlots(reserved.newafixed);
|
||||
|
@ -2967,7 +2976,11 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj,
|
|||
MarkTypeObjectUnknownProperties(cx, obj->type(), true);
|
||||
MarkTypeObjectUnknownProperties(cx, type, true);
|
||||
|
||||
obj->setType(type);
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
obj->setType(type);
|
||||
}
|
||||
|
||||
*succeeded = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -4030,7 +4043,7 @@ NativeGetInline(JSContext *cx,
|
|||
case JSOP_GETPROP:
|
||||
case JSOP_CALLPROP:
|
||||
case JSOP_LENGTH:
|
||||
script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc));
|
||||
script->baselineScript()->noteAccessedGetter(cx, script->pcToOffset(pc));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -474,7 +474,7 @@ class JSObject : public js::ObjectImpl
|
|||
bool uninlinedIsProxy() const;
|
||||
JSObject *getProto() const {
|
||||
JS_ASSERT(!uninlinedIsProxy());
|
||||
return js::ObjectImpl::getProto();
|
||||
return getTaggedProto().toObjectOrNull();
|
||||
}
|
||||
static inline bool getProto(JSContext *cx, js::HandleObject obj,
|
||||
js::MutableHandleObject protop);
|
||||
|
|
|
@ -393,6 +393,9 @@ JSObject::clearType(JSContext *cx, js::HandleObject obj)
|
|||
inline void
|
||||
JSObject::setType(js::types::TypeObject *newType)
|
||||
{
|
||||
// Note: This is usually called for newly created objects that haven't
|
||||
// escaped to script yet, so don't require that the compilation lock be
|
||||
// held here.
|
||||
JS_ASSERT(newType);
|
||||
JS_ASSERT(!hasSingletonType());
|
||||
type_ = newType;
|
||||
|
@ -405,7 +408,7 @@ JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject
|
|||
JS_ASSERT(obj->is<js::ProxyObject>());
|
||||
return js::Proxy::getPrototypeOf(cx, obj, protop);
|
||||
} else {
|
||||
protop.set(obj->js::ObjectImpl::getProto());
|
||||
protop.set(obj->getTaggedProto().toObjectOrNull());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -461,11 +464,11 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi
|
|||
* make sure their presence is consistent with the shape.
|
||||
*/
|
||||
JS_ASSERT(shape && type);
|
||||
JS_ASSERT(type->clasp == shape->getObjectClass());
|
||||
JS_ASSERT(type->clasp != &js::ArrayObject::class_);
|
||||
JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp) == shape->numFixedSlots());
|
||||
JS_ASSERT_IF(type->clasp->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
|
||||
JS_ASSERT_IF(type->clasp->finalize, heap == js::gc::TenuredHeap);
|
||||
JS_ASSERT(type->clasp() == shape->getObjectClass());
|
||||
JS_ASSERT(type->clasp() != &js::ArrayObject::class_);
|
||||
JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots());
|
||||
JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
|
||||
JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
|
||||
JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan()));
|
||||
|
||||
js::HeapSlot *slots = extantSlots;
|
||||
|
@ -495,7 +498,7 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi
|
|||
obj->slots = slots;
|
||||
obj->elements = js::emptyObjectElements;
|
||||
|
||||
const js::Class *clasp = type->clasp;
|
||||
const js::Class *clasp = type->clasp();
|
||||
if (clasp->hasPrivate())
|
||||
obj->privateRef(shape->numFixedSlots()) = nullptr;
|
||||
|
||||
|
@ -512,9 +515,9 @@ JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::
|
|||
uint32_t length)
|
||||
{
|
||||
JS_ASSERT(shape && type);
|
||||
JS_ASSERT(type->clasp == shape->getObjectClass());
|
||||
JS_ASSERT(type->clasp == &js::ArrayObject::class_);
|
||||
JS_ASSERT_IF(type->clasp->finalize, heap == js::gc::TenuredHeap);
|
||||
JS_ASSERT(type->clasp() == shape->getObjectClass());
|
||||
JS_ASSERT(type->clasp() == &js::ArrayObject::class_);
|
||||
JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
|
||||
|
||||
/*
|
||||
* Arrays use their fixed slots to store elements, and must have enough
|
||||
|
@ -979,8 +982,11 @@ DefineConstructorAndPrototype(JSContext *cx, Handle<GlobalObject*> global,
|
|||
JS_ASSERT(!global->nativeLookup(cx, id));
|
||||
|
||||
/* Set these first in case AddTypePropertyId looks for this class. */
|
||||
global->setConstructor(key, ObjectValue(*ctor));
|
||||
global->setPrototype(key, ObjectValue(*proto));
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
global->setConstructor(key, ObjectValue(*ctor));
|
||||
global->setPrototype(key, ObjectValue(*proto));
|
||||
}
|
||||
global->setConstructorPropertySlot(key, ObjectValue(*ctor));
|
||||
|
||||
if (!global->addDataProperty(cx, id, GlobalObject::constructorPropertySlot(key), 0)) {
|
||||
|
|
|
@ -2748,7 +2748,7 @@ Proxy::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandl
|
|||
return proxy->as<ProxyObject>().handler()->defaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
JSObject * const Proxy::LazyProto = reinterpret_cast<JSObject *>(0x1);
|
||||
JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject *>(0x1);
|
||||
|
||||
bool
|
||||
Proxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject proto)
|
||||
|
|
|
@ -324,8 +324,6 @@ class Proxy
|
|||
/* IC entry path for handling __noSuchMethod__ on access. */
|
||||
static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
|
||||
static JSObject * const LazyProto;
|
||||
};
|
||||
|
||||
// Use these in places where you don't want to #include vm/ProxyObject.h.
|
||||
|
|
|
@ -3015,7 +3015,10 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
|
|||
|
||||
JS_ASSERT(!script->isGenerator());
|
||||
|
||||
script->needsArgsObj_ = true;
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
script->needsArgsObj_ = true;
|
||||
}
|
||||
|
||||
#ifdef JS_ION
|
||||
/*
|
||||
|
|
|
@ -977,7 +977,11 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
|||
* that needsArgsObj is only called after the script has been analyzed.
|
||||
*/
|
||||
bool analyzedArgsUsage() const { return !needsArgsAnalysis_; }
|
||||
bool needsArgsObj() const { JS_ASSERT(analyzedArgsUsage()); return needsArgsObj_; }
|
||||
bool needsArgsObj() const {
|
||||
JS_ASSERT(analyzedArgsUsage());
|
||||
JS_ASSERT(js::CurrentThreadCanReadCompilationData());
|
||||
return needsArgsObj_;
|
||||
}
|
||||
void setNeedsArgsObj(bool needsArgsObj);
|
||||
static bool argumentsOptimizationFailed(JSContext *cx, js::HandleScript script);
|
||||
|
||||
|
@ -1027,6 +1031,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
|||
}
|
||||
|
||||
bool hasBaselineScript() const {
|
||||
JS_ASSERT(js::CurrentThreadCanReadCompilationData());
|
||||
return baseline && baseline != BASELINE_DISABLED_SCRIPT;
|
||||
}
|
||||
bool canBaselineCompile() const {
|
||||
|
@ -1036,7 +1041,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
|||
JS_ASSERT(hasBaselineScript());
|
||||
return baseline;
|
||||
}
|
||||
inline void setBaselineScript(js::jit::BaselineScript *baselineScript);
|
||||
inline void setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript);
|
||||
|
||||
void updateBaselineOrIonRaw();
|
||||
|
||||
|
|
|
@ -125,11 +125,14 @@ JSScript::setIsCallsiteClone(JSObject *fun) {
|
|||
}
|
||||
|
||||
inline void
|
||||
JSScript::setBaselineScript(js::jit::BaselineScript *baselineScript) {
|
||||
JSScript::setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript) {
|
||||
#ifdef JS_ION
|
||||
if (hasBaselineScript())
|
||||
js::jit::BaselineScript::writeBarrierPre(tenuredZone(), baseline);
|
||||
#endif
|
||||
mozilla::Maybe<js::AutoLockForCompilation> lock;
|
||||
if (maybecx)
|
||||
lock.construct(maybecx);
|
||||
baseline = baselineScript;
|
||||
updateBaselineOrIonRaw();
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
|
|||
if (!state.ionWorklist.append(builder))
|
||||
return false;
|
||||
|
||||
cx->runtime()->addCompilationThread();
|
||||
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
@ -615,7 +617,7 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke
|
|||
iter.next())
|
||||
{
|
||||
types::TypeObject *object = iter.get<types::TypeObject>();
|
||||
TaggedProto proto(object->proto);
|
||||
TaggedProto proto(object->proto());
|
||||
if (!proto.isObject())
|
||||
continue;
|
||||
|
||||
|
@ -626,7 +628,9 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke
|
|||
JSObject *newProto = GetClassPrototypePure(&parseTask->scopeChain->global(), key);
|
||||
JS_ASSERT(newProto);
|
||||
|
||||
object->proto = newProto;
|
||||
// Note: this is safe to do without requiring the compilation lock, as
|
||||
// the new type is not yet available to compilation threads.
|
||||
object->setProtoUnchecked(newProto);
|
||||
}
|
||||
|
||||
// Move the parsed script and all its contents into the desired compartment.
|
||||
|
@ -760,7 +764,11 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
|||
jit::IonContext ictx(jit::CompileRuntime::get(runtime),
|
||||
jit::CompileCompartment::get(ionBuilder->script()->compartment()),
|
||||
&ionBuilder->alloc());
|
||||
ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder));
|
||||
AutoEnterIonCompilation ionCompiling;
|
||||
bool succeeded = ionBuilder->build();
|
||||
ionBuilder->clearForBackEnd();
|
||||
if (succeeded)
|
||||
ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder));
|
||||
}
|
||||
state.lock();
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler)
|
|||
RootedValue priv(cx, ObjectValue(*obj));
|
||||
ProxyOptions options;
|
||||
options.setCallable(obj->isCallable());
|
||||
return NewProxyObject(cx, handler, priv, Proxy::LazyProto, parent, options);
|
||||
return NewProxyObject(cx, handler, priv, TaggedProto::LazyProto, parent, options);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
|
@ -141,7 +141,7 @@ js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject
|
|||
{
|
||||
// Allow wrapping outer window proxies.
|
||||
JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
|
||||
JS_ASSERT(wrappedProto == Proxy::LazyProto);
|
||||
JS_ASSERT(wrappedProto == TaggedProto::LazyProto);
|
||||
return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton);
|
||||
}
|
||||
|
||||
|
|
|
@ -669,7 +669,7 @@ GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, Ha
|
|||
RootedId shId(cx, AtomToId(selfHostedName));
|
||||
RootedObject holder(cx, cx->global()->intrinsicsHolder());
|
||||
|
||||
if (HasDataProperty(cx, holder, shId, funVal.address()))
|
||||
if (cx->global()->maybeGetIntrinsicValue(shId, funVal.address()))
|
||||
return true;
|
||||
|
||||
if (!cx->runtime()->maybeWrappedSelfHostedFunction(cx, shId, funVal))
|
||||
|
@ -685,5 +685,31 @@ GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, Ha
|
|||
fun->setExtendedSlot(0, StringValue(selfHostedName));
|
||||
funVal.setObject(*fun);
|
||||
|
||||
return JSObject::defineGeneric(cx, holder, shId, funVal, nullptr, nullptr, 0);
|
||||
return cx->global()->addIntrinsicValue(cx, shId, funVal);
|
||||
}
|
||||
|
||||
bool
|
||||
GlobalObject::addIntrinsicValue(JSContext *cx, HandleId id, HandleValue value)
|
||||
{
|
||||
RootedObject holder(cx, intrinsicsHolder());
|
||||
|
||||
// Work directly with the shape machinery underlying the object, so that we
|
||||
// don't take the compilation lock until we are ready to update the object
|
||||
// without triggering a GC.
|
||||
|
||||
uint32_t slot = holder->slotSpan();
|
||||
RootedShape last(cx, holder->lastProperty());
|
||||
Rooted<UnownedBaseShape*> base(cx, last->base()->unowned());
|
||||
|
||||
StackShape child(base, id, slot, holder->numFixedSlots(), 0, 0, 0);
|
||||
RootedShape shape(cx, cx->compartment()->propertyTree.getChild(cx, last, holder->numFixedSlots(), child));
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
AutoLockForCompilation lock(cx);
|
||||
if (!JSObject::setLastProperty(cx, holder, shape))
|
||||
return false;
|
||||
|
||||
holder->setSlot(shape->slot(), value);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -513,26 +513,32 @@ class GlobalObject : public JSObject
|
|||
return &getSlotRefForCompilation(INTRINSICS).toObject();
|
||||
}
|
||||
|
||||
bool maybeGetIntrinsicValue(PropertyName *name, Value *vp) {
|
||||
bool maybeGetIntrinsicValue(jsid id, Value *vp) {
|
||||
JS_ASSERT(CurrentThreadCanReadCompilationData());
|
||||
JSObject *holder = intrinsicsHolder();
|
||||
if (Shape *shape = holder->nativeLookupPure(name)) {
|
||||
if (Shape *shape = holder->nativeLookupPure(id)) {
|
||||
*vp = holder->getSlot(shape->slot());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool maybeGetIntrinsicValue(PropertyName *name, Value *vp) {
|
||||
return maybeGetIntrinsicValue(NameToId(name), vp);
|
||||
}
|
||||
|
||||
bool getIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue value) {
|
||||
if (maybeGetIntrinsicValue(name, value.address()))
|
||||
static bool getIntrinsicValue(JSContext *cx, Handle<GlobalObject*> global,
|
||||
HandlePropertyName name, MutableHandleValue value)
|
||||
{
|
||||
if (global->maybeGetIntrinsicValue(name, value.address()))
|
||||
return true;
|
||||
Rooted<GlobalObject*> self(cx, this);
|
||||
if (!cx->runtime()->cloneSelfHostedValue(cx, name, value))
|
||||
return false;
|
||||
RootedObject holder(cx, self->intrinsicsHolder());
|
||||
RootedId id(cx, NameToId(name));
|
||||
return JS_DefinePropertyById(cx, holder, id, value, nullptr, nullptr, 0);
|
||||
return global->addIntrinsicValue(cx, id, value);
|
||||
}
|
||||
|
||||
bool addIntrinsicValue(JSContext *cx, HandleId id, HandleValue value);
|
||||
|
||||
bool setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value) {
|
||||
#ifdef DEBUG
|
||||
RootedObject self(cx, this);
|
||||
|
|
|
@ -210,7 +210,7 @@ inline bool
|
|||
GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp)
|
||||
{
|
||||
RootedPropertyName name(cx, cx->currentScript()->getName(pc));
|
||||
return cx->global()->getIntrinsicValue(cx, name, vp);
|
||||
return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -1294,7 +1294,7 @@ SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, con
|
|||
if ((uint32_t)i >= length) {
|
||||
// Annotate script if provided with information (e.g. baseline)
|
||||
if (script && script->hasBaselineScript() && *pc == JSOP_SETELEM)
|
||||
script->baselineScript()->noteArrayWriteHole(script->pcToOffset(pc));
|
||||
script->baselineScript()->noteArrayWriteHole(cx, script->pcToOffset(pc));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -360,7 +360,7 @@ js::ObjectImpl::markChildren(JSTracer *trc)
|
|||
|
||||
MarkShape(trc, &shape_, "shape");
|
||||
|
||||
const Class *clasp = type_->clasp;
|
||||
const Class *clasp = type_->clasp();
|
||||
JSObject *obj = asObjectPtr();
|
||||
if (clasp->trace)
|
||||
clasp->trace(trc, obj);
|
||||
|
@ -538,7 +538,7 @@ js::ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
|
|||
if (obj->getPrivate())
|
||||
return static_cast<JSObject *>(obj->getPrivate());
|
||||
JSObject *delegate = NewObjectWithGivenProto(cx, &JSObject::class_,
|
||||
obj->getProto(), nullptr);
|
||||
obj->getTaggedProto(), nullptr);
|
||||
obj->setPrivateGCThing(delegate);
|
||||
return delegate;
|
||||
}
|
||||
|
@ -684,7 +684,7 @@ js::GetProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> rece
|
|||
|
||||
/* No property? Recur or bottom out. */
|
||||
if (desc.isUndefined()) {
|
||||
current = current->getProto();
|
||||
current = current->getTaggedProto().toObjectOrNull();
|
||||
if (current)
|
||||
continue;
|
||||
|
||||
|
@ -746,7 +746,7 @@ js::GetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> recei
|
|||
|
||||
/* No property? Recur or bottom out. */
|
||||
if (desc.isUndefined()) {
|
||||
current = current->getProto();
|
||||
current = current->getTaggedProto().toObjectOrNull();
|
||||
if (current)
|
||||
continue;
|
||||
|
||||
|
@ -811,7 +811,7 @@ js::HasElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, unsigned
|
|||
return true;
|
||||
}
|
||||
|
||||
current = current->getProto();
|
||||
current = current->getTaggedProto().toObjectOrNull();
|
||||
if (current)
|
||||
continue;
|
||||
|
||||
|
@ -1009,7 +1009,7 @@ js::SetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> recei
|
|||
MOZ_ASSUME_UNREACHABLE("NYI: setting PropertyOp-based property");
|
||||
}
|
||||
|
||||
current = current->getProto();
|
||||
current = current->getTaggedProto().toObjectOrNull();
|
||||
if (current)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -980,12 +980,14 @@ class ObjectImpl : public gc::BarrieredCell<ObjectImpl>
|
|||
/* These functions are public, and they should remain public. */
|
||||
|
||||
public:
|
||||
JSObject * getProto() const {
|
||||
return type_->proto;
|
||||
js::TaggedProto getTaggedProto() const {
|
||||
return type_->proto();
|
||||
}
|
||||
|
||||
bool hasTenuredProto() const;
|
||||
|
||||
const Class *getClass() const {
|
||||
return type_->clasp;
|
||||
return type_->clasp();
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
@ -1172,10 +1174,6 @@ class ObjectImpl : public gc::BarrieredCell<ObjectImpl>
|
|||
*/
|
||||
|
||||
public:
|
||||
js::TaggedProto getTaggedProto() const {
|
||||
return TaggedProto(getProto());
|
||||
}
|
||||
|
||||
Shape * lastProperty() const {
|
||||
MOZ_ASSERT(shape_);
|
||||
return shape_;
|
||||
|
|
|
@ -75,6 +75,9 @@ PerThreadData::PerThreadData(JSRuntime *runtime)
|
|||
asmJSActivationStack_(nullptr),
|
||||
dtoaState(nullptr),
|
||||
suppressGC(0),
|
||||
#ifdef DEBUG
|
||||
ionCompiling(false),
|
||||
#endif
|
||||
activeCompilations(0)
|
||||
{}
|
||||
|
||||
|
@ -135,6 +138,12 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||
exclusiveAccessOwner(nullptr),
|
||||
mainThreadHasExclusiveAccess(false),
|
||||
numExclusiveThreads(0),
|
||||
compilationLock(nullptr),
|
||||
#ifdef DEBUG
|
||||
compilationLockOwner(nullptr),
|
||||
mainThreadHasCompilationLock(false),
|
||||
#endif
|
||||
numCompilationThreads(0),
|
||||
#endif
|
||||
systemZone(nullptr),
|
||||
numCompartments(0),
|
||||
|
@ -362,6 +371,10 @@ JSRuntime::init(uint32_t maxbytes)
|
|||
exclusiveAccessLock = PR_NewLock();
|
||||
if (!exclusiveAccessLock)
|
||||
return false;
|
||||
|
||||
compilationLock = PR_NewLock();
|
||||
if (!compilationLock)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (!mainThread.init())
|
||||
|
@ -483,6 +496,10 @@ JSRuntime::~JSRuntime()
|
|||
// Avoid bogus asserts during teardown.
|
||||
JS_ASSERT(!numExclusiveThreads);
|
||||
mainThreadHasExclusiveAccess = true;
|
||||
|
||||
JS_ASSERT(!compilationLockOwner);
|
||||
if (compilationLock)
|
||||
PR_DestroyLock(compilationLock);
|
||||
#endif
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
@ -736,6 +753,24 @@ JSRuntime::getDefaultLocale()
|
|||
return defaultLocale;
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::triggerActivityCallback(bool active)
|
||||
{
|
||||
if (!activityCallback)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The activity callback must not trigger a GC: it would create a cirular
|
||||
* dependency between entering a request and Rooted's requirement of being
|
||||
* in a request. In practice this callback already cannot trigger GC. The
|
||||
* suppression serves to inform the exact rooting hazard analysis of this
|
||||
* property and ensures that it remains true in the future.
|
||||
*/
|
||||
AutoSuppressGC suppress(this);
|
||||
|
||||
activityCallback(activityCallbackArg, active);
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::setGCMaxMallocBytes(size_t value)
|
||||
{
|
||||
|
@ -816,7 +851,7 @@ JSRuntime::activeGCInAtomsZone()
|
|||
|
||||
#if defined(DEBUG) && !defined(XP_WIN)
|
||||
|
||||
AutoProtectHeapForCompilation::AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
AutoProtectHeapForIonCompilation::AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
: runtime(rt)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
|
@ -834,7 +869,7 @@ AutoProtectHeapForCompilation::AutoProtectHeapForCompilation(JSRuntime *rt MOZ_G
|
|||
}
|
||||
}
|
||||
|
||||
AutoProtectHeapForCompilation::~AutoProtectHeapForCompilation()
|
||||
AutoProtectHeapForIonCompilation::~AutoProtectHeapForIonCompilation()
|
||||
{
|
||||
JS_ASSERT(runtime->heapProtected_);
|
||||
JS_ASSERT(runtime->unprotectedArenas.empty());
|
||||
|
@ -936,7 +971,7 @@ js::CurrentThreadCanAccessZone(Zone *zone)
|
|||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // JS_THREADSAFE
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
|
@ -954,13 +989,73 @@ JSRuntime::assertCanLock(RuntimeLock which)
|
|||
JS_ASSERT_IF(workerThreadState, !workerThreadState->isLocked());
|
||||
case OperationCallbackLock:
|
||||
JS_ASSERT(!currentThreadOwnsOperationCallbackLock());
|
||||
case CompilationLock:
|
||||
JS_ASSERT(compilationLockOwner != PR_GetCurrentThread());
|
||||
case GCLock:
|
||||
JS_ASSERT(gcLockOwner != PR_GetCurrentThread());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
#endif // JS_THREADSAFE
|
||||
#endif // JS_WORKER_THREADS
|
||||
}
|
||||
|
||||
AutoEnterIonCompilation::AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
|
||||
#ifdef JS_WORKER_THREADS
|
||||
PerThreadData *pt = js::TlsPerThreadData.get();
|
||||
JS_ASSERT(!pt->ionCompiling);
|
||||
pt->ionCompiling = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
AutoEnterIonCompilation::~AutoEnterIonCompilation()
|
||||
{
|
||||
#ifdef JS_WORKER_THREADS
|
||||
PerThreadData *pt = js::TlsPerThreadData.get();
|
||||
JS_ASSERT(pt->ionCompiling);
|
||||
pt->ionCompiling = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
js::CurrentThreadCanWriteCompilationData()
|
||||
{
|
||||
#ifdef JS_WORKER_THREADS
|
||||
PerThreadData *pt = TlsPerThreadData.get();
|
||||
|
||||
// Data can only be read from during compilation.
|
||||
if (pt->ionCompiling)
|
||||
return false;
|
||||
|
||||
// Ignore what threads with exclusive contexts are doing; these never have
|
||||
// run scripts or have associated compilation threads.
|
||||
JSRuntime *rt = pt->runtimeIfOnOwnerThread();
|
||||
if (!rt)
|
||||
return true;
|
||||
|
||||
return rt->currentThreadHasCompilationLock();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
js::CurrentThreadCanReadCompilationData()
|
||||
{
|
||||
#ifdef JS_WORKER_THREADS
|
||||
PerThreadData *pt = TlsPerThreadData.get();
|
||||
|
||||
// Data can always be read from freely outside of compilation.
|
||||
if (!pt || !pt->ionCompiling)
|
||||
return true;
|
||||
|
||||
return pt->runtime_->currentThreadHasCompilationLock();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
|
|
@ -535,6 +535,9 @@ class PerThreadData : public PerThreadDataFriendFields,
|
|||
friend class js::ActivationIterator;
|
||||
friend class js::jit::JitActivation;
|
||||
friend class js::AsmJSActivation;
|
||||
#ifdef DEBUG
|
||||
friend bool js::CurrentThreadCanReadCompilationData();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Points to the most recent activation running on the thread.
|
||||
|
@ -577,7 +580,12 @@ class PerThreadData : public PerThreadDataFriendFields,
|
|||
*/
|
||||
int32_t suppressGC;
|
||||
|
||||
// Whether there is an active compilation on this thread.
|
||||
#ifdef DEBUG
|
||||
// Whether this thread is actively Ion compiling.
|
||||
bool ionCompiling;
|
||||
#endif
|
||||
|
||||
// Number of active bytecode compilation on this thread.
|
||||
unsigned activeCompilations;
|
||||
|
||||
PerThreadData(JSRuntime *runtime);
|
||||
|
@ -677,7 +685,8 @@ class MarkingValidator;
|
|||
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
|
||||
|
||||
class AutoLockForExclusiveAccess;
|
||||
class AutoProtectHeapForCompilation;
|
||||
class AutoLockForCompilation;
|
||||
class AutoProtectHeapForIonCompilation;
|
||||
|
||||
void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
|
||||
|
||||
|
@ -727,6 +736,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
ExclusiveAccessLock,
|
||||
WorkerThreadStateLock,
|
||||
OperationCallbackLock,
|
||||
CompilationLock,
|
||||
GCLock
|
||||
};
|
||||
#ifdef DEBUG
|
||||
|
@ -805,20 +815,44 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
|
||||
friend class js::AutoLockForExclusiveAccess;
|
||||
|
||||
/*
|
||||
* Lock taken when using data that can be modified by the main thread but
|
||||
* read by Ion compilation threads. Any time either the main thread writes
|
||||
* such data or the compilation thread reads it, this lock must be taken.
|
||||
* Note that no externally visible data is modified by the compilation
|
||||
* thread, so the main thread never needs to take this lock when reading.
|
||||
*/
|
||||
PRLock *compilationLock;
|
||||
#ifdef DEBUG
|
||||
PRThread *compilationLockOwner;
|
||||
bool mainThreadHasCompilationLock;
|
||||
#endif
|
||||
|
||||
/* Number of in flight Ion compilations. */
|
||||
size_t numCompilationThreads;
|
||||
|
||||
friend class js::AutoLockForCompilation;
|
||||
#ifdef DEBUG
|
||||
friend bool js::CurrentThreadCanWriteCompilationData();
|
||||
friend bool js::CurrentThreadCanReadCompilationData();
|
||||
#endif
|
||||
|
||||
public:
|
||||
void setUsedByExclusiveThread(JS::Zone *zone);
|
||||
void clearUsedByExclusiveThread(JS::Zone *zone);
|
||||
|
||||
#endif // JS_THREADSAFE && JS_ION
|
||||
|
||||
#ifdef DEBUG
|
||||
bool currentThreadHasExclusiveAccess() {
|
||||
#if defined(JS_WORKER_THREADS) && defined(DEBUG)
|
||||
#ifdef JS_WORKER_THREADS
|
||||
return (!numExclusiveThreads && mainThreadHasExclusiveAccess) ||
|
||||
exclusiveAccessOwner == PR_GetCurrentThread();
|
||||
exclusiveAccessOwner == PR_GetCurrentThread();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
bool exclusiveThreadsPresent() const {
|
||||
#ifdef JS_WORKER_THREADS
|
||||
|
@ -828,6 +862,33 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
#endif
|
||||
}
|
||||
|
||||
void addCompilationThread() {
|
||||
numCompilationThreads++;
|
||||
}
|
||||
void removeCompilationThread() {
|
||||
JS_ASSERT(numCompilationThreads);
|
||||
numCompilationThreads--;
|
||||
}
|
||||
|
||||
bool compilationThreadsPresent() const {
|
||||
#ifdef JS_WORKER_THREADS
|
||||
return numCompilationThreads > 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool currentThreadHasCompilationLock() {
|
||||
#ifdef JS_WORKER_THREADS
|
||||
return (!numCompilationThreads && mainThreadHasCompilationLock) ||
|
||||
compilationLockOwner == PR_GetCurrentThread();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
/* Embedders can use this zone however they wish. */
|
||||
JS::Zone *systemZone;
|
||||
|
||||
|
@ -972,6 +1033,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
|
||||
js::ActivityCallback activityCallback;
|
||||
void *activityCallbackArg;
|
||||
void triggerActivityCallback(bool active);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/* The request depth for this thread. */
|
||||
|
@ -1431,7 +1493,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
const char *numGrouping;
|
||||
#endif
|
||||
|
||||
friend class js::AutoProtectHeapForCompilation;
|
||||
friend class js::AutoProtectHeapForIonCompilation;
|
||||
friend class js::AutoThreadSafeAccess;
|
||||
mozilla::DebugOnly<bool> heapProtected_;
|
||||
#ifdef DEBUG
|
||||
|
@ -2049,16 +2111,36 @@ class RuntimeAllocPolicy
|
|||
|
||||
extern const JSSecurityCallbacks NullSecurityCallbacks;
|
||||
|
||||
class AutoProtectHeapForCompilation
|
||||
// Debugging RAII class which marks the current thread as performing an Ion
|
||||
// compilation, for use by CurrentThreadCan{Read,Write}CompilationData
|
||||
class AutoEnterIonCompilation
|
||||
{
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
|
||||
~AutoEnterIonCompilation();
|
||||
#else
|
||||
AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
#endif
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
// Debugging RAII class which protects the entire GC heap for the duration of an
|
||||
// Ion compilation. When used only the main thread will be active and all
|
||||
// accesses to GC things must be wrapped by an AutoThreadSafeAccess instance.
|
||||
class AutoProtectHeapForIonCompilation
|
||||
{
|
||||
public:
|
||||
#if defined(DEBUG) && !defined(XP_WIN)
|
||||
JSRuntime *runtime;
|
||||
|
||||
AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
~AutoProtectHeapForCompilation();
|
||||
AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
~AutoProtectHeapForIonCompilation();
|
||||
#else
|
||||
AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
|
|
@ -56,13 +56,14 @@ SPSProfiler::enable(bool enabled)
|
|||
if (enabled_ == enabled)
|
||||
return;
|
||||
|
||||
enabled_ = enabled;
|
||||
/*
|
||||
* Ensure all future generated code will be instrumented, or that all
|
||||
* currently instrumented code is discarded
|
||||
*/
|
||||
ReleaseAllJITCode(rt->defaultFreeOp());
|
||||
|
||||
enabled_ = enabled;
|
||||
|
||||
#ifdef JS_ION
|
||||
/* Toggle SPS-related jumps on baseline jitcode.
|
||||
* The call to |ReleaseAllJITCode| above will release most baseline jitcode, but not
|
||||
|
|
|
@ -958,10 +958,11 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, Handle<PropertyName*> na
|
|||
JSScript *cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript);
|
||||
if (!cscript)
|
||||
return false;
|
||||
targetFun->setScript(cscript);
|
||||
cscript->setFunction(targetFun);
|
||||
|
||||
JS_ASSERT(sourceFun->nargs() == targetFun->nargs());
|
||||
targetFun->setFlags(sourceFun->flags() | JSFunction::EXTENDED);
|
||||
targetFun->setScript(cscript);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1010,7 +1011,7 @@ JSFunction *
|
|||
js::SelfHostedFunction(JSContext *cx, HandlePropertyName propName)
|
||||
{
|
||||
RootedValue func(cx);
|
||||
if (!cx->global()->getIntrinsicValue(cx, propName, &func))
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), propName, &func))
|
||||
return nullptr;
|
||||
|
||||
JS_ASSERT(func.isObject());
|
||||
|
|
|
@ -338,7 +338,7 @@ ArrayBufferObject::neuterViews(JSContext *cx, Handle<ArrayBufferObject*> buffer)
|
|||
size_t numViews = 0;
|
||||
for (view = GetViewList(buffer); view; view = view->nextView()) {
|
||||
numViews++;
|
||||
view->neuter();
|
||||
view->neuter(cx);
|
||||
|
||||
// Notify compiled jit code that the base pointer has moved.
|
||||
MarkObjectStateChange(cx, view);
|
||||
|
@ -1182,8 +1182,10 @@ TypedArrayObject::isArrayIndex(jsid id, uint32_t *ip)
|
|||
}
|
||||
|
||||
void
|
||||
TypedArrayObject::neuter()
|
||||
TypedArrayObject::neuter(JSContext *cx)
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
|
||||
setSlot(LENGTH_SLOT, Int32Value(0));
|
||||
setSlot(BYTELENGTH_SLOT, Int32Value(0));
|
||||
setSlot(BYTEOFFSET_SLOT, Int32Value(0));
|
||||
|
@ -2625,12 +2627,12 @@ ArrayBufferViewObject::prependToViews(ArrayBufferViewObject *viewsHead)
|
|||
}
|
||||
|
||||
void
|
||||
ArrayBufferViewObject::neuter()
|
||||
ArrayBufferViewObject::neuter(JSContext *cx)
|
||||
{
|
||||
if (is<DataViewObject>())
|
||||
as<DataViewObject>().neuter();
|
||||
else
|
||||
as<TypedArrayObject>().neuter();
|
||||
as<TypedArrayObject>().neuter(cx);
|
||||
}
|
||||
|
||||
// this default implementation is only valid for integer types
|
||||
|
|
|
@ -276,7 +276,7 @@ class ArrayBufferViewObject : public JSObject
|
|||
|
||||
void prependToViews(ArrayBufferViewObject *viewsHead);
|
||||
|
||||
void neuter();
|
||||
void neuter(JSContext *cx);
|
||||
|
||||
static void trace(JSTracer *trc, JSObject *obj);
|
||||
};
|
||||
|
@ -353,7 +353,7 @@ class TypedArrayObject : public ArrayBufferViewObject
|
|||
inline bool isArrayIndex(jsid id, uint32_t *ip = nullptr);
|
||||
void copyTypedArrayElement(uint32_t index, MutableHandleValue vp);
|
||||
|
||||
void neuter();
|
||||
void neuter(JSContext *cx);
|
||||
|
||||
static uint32_t slotWidth(int atype) {
|
||||
switch (atype) {
|
||||
|
|
|
@ -53,6 +53,8 @@ EXTRA_DSO_LDOPTS += \
|
|||
-lutils \
|
||||
-L$(DEPTH)/media/omx-plugin/lib/ics/libstagefright \
|
||||
-lstagefright \
|
||||
-L$(DEPTH)/media/omx-plugin/lib/ics/libvideoeditorplayer \
|
||||
-lvideoeditorplayer \
|
||||
$(NULL)
|
||||
|
||||
INCLUDES += \
|
||||
|
|
|
@ -37,6 +37,14 @@
|
|||
#define MOZ_ANDROID_V2_X_X
|
||||
#endif
|
||||
|
||||
#if !defined(MOZ_ANDROID_V2_X_X) && !defined(MOZ_ANDROID_HC)
|
||||
#define MOZ_ANDROID_V4_OR_ABOVE
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_ANDROID_V4_OR_ABOVE)
|
||||
#include <I420ColorConverter.h>
|
||||
#endif
|
||||
|
||||
using namespace MPAPI;
|
||||
|
||||
#if !defined(MOZ_STAGEFRIGHT_OFF_T)
|
||||
|
@ -68,6 +76,8 @@ class OmxDecoder {
|
|||
int32_t mVideoSliceHeight;
|
||||
int32_t mVideoCropLeft;
|
||||
int32_t mVideoCropTop;
|
||||
int32_t mVideoCropRight;
|
||||
int32_t mVideoCropBottom;
|
||||
int32_t mVideoRotation;
|
||||
int32_t mAudioChannels;
|
||||
int32_t mAudioSampleRate;
|
||||
|
@ -92,6 +102,7 @@ class OmxDecoder {
|
|||
void ToVideoFrame_YVU420PackedSemiPlanar32m4ka(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
|
||||
bool ToVideoFrame_RGB565(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
|
||||
bool ToVideoFrame_ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
|
||||
bool ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
|
||||
bool ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
|
||||
bool ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize,
|
||||
int32_t aAudioChannels, int32_t aAudioSampleRate);
|
||||
|
@ -139,6 +150,8 @@ OmxDecoder::OmxDecoder(PluginHost *aPluginHost, Decoder *aDecoder) :
|
|||
mVideoSliceHeight(0),
|
||||
mVideoCropLeft(0),
|
||||
mVideoCropTop(0),
|
||||
mVideoCropRight(0),
|
||||
mVideoCropBottom(0),
|
||||
mVideoRotation(0),
|
||||
mAudioChannels(-1),
|
||||
mAudioSampleRate(-1),
|
||||
|
@ -226,11 +239,48 @@ static uint32_t GetVideoCreationFlags(PluginHost* aPluginHost)
|
|||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
IsColorFormatSupported(OMX_COLOR_FORMATTYPE aColorFormat)
|
||||
{
|
||||
switch (aColorFormat) {
|
||||
case OMX_COLOR_FormatCbYCrY:
|
||||
case OMX_COLOR_FormatYUV420Planar:
|
||||
case OMX_COLOR_FormatYUV420SemiPlanar:
|
||||
case OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka:
|
||||
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
|
||||
case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
|
||||
LOG("Colour format %#x supported natively.", aColorFormat);
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#if !defined(MOZ_ANDROID_HC)
|
||||
if (ColorConverter(aColorFormat, OMX_COLOR_Format16bitRGB565).isValid()) {
|
||||
LOG("Colour format %#x supported by Android ColorConverter.", aColorFormat);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_ANDROID_V4_OR_ABOVE)
|
||||
I420ColorConverter yuvConverter;
|
||||
|
||||
if (yuvConverter.isLoaded() &&
|
||||
yuvConverter.getDecoderOutputFormat() == aColorFormat) {
|
||||
LOG("Colour format %#x supported by Android I420ColorConverter.", aColorFormat);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static sp<MediaSource> CreateVideoSource(PluginHost* aPluginHost,
|
||||
const sp<IOMX>& aOmx,
|
||||
const sp<MediaSource>& aVideoTrack)
|
||||
{
|
||||
uint32_t flags = GetVideoCreationFlags(aPluginHost);
|
||||
|
||||
if (flags == DEFAULT_STAGEFRIGHT_FLAGS) {
|
||||
// Let Stagefright choose hardware or software decoder.
|
||||
sp<MediaSource> videoSource = OMXCodec::Create(aOmx, aVideoTrack->getFormat(),
|
||||
|
@ -242,30 +292,14 @@ static sp<MediaSource> CreateVideoSource(PluginHost* aPluginHost,
|
|||
// check whether we know how to decode this video.
|
||||
int32_t videoColorFormat;
|
||||
if (videoSource->getFormat()->findInt32(kKeyColorFormat, &videoColorFormat)) {
|
||||
switch (videoColorFormat) {
|
||||
// We know how to convert these color formats.
|
||||
case OMX_COLOR_FormatCbYCrY:
|
||||
case OMX_COLOR_FormatYUV420Planar:
|
||||
case OMX_COLOR_FormatYUV420SemiPlanar:
|
||||
case OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka:
|
||||
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
|
||||
case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
|
||||
// Use the decoder Stagefright chose for us!
|
||||
return videoSource;
|
||||
|
||||
// Use software decoder for color formats we don't know how to convert.
|
||||
default:
|
||||
#ifndef MOZ_ANDROID_HC
|
||||
if (ColorConverter((OMX_COLOR_FORMATTYPE)videoColorFormat,
|
||||
OMX_COLOR_Format16bitRGB565).isValid()) {
|
||||
return videoSource;
|
||||
}
|
||||
#endif
|
||||
// We need to implement a ToVideoFrame_*() color conversion
|
||||
// function for this video color format.
|
||||
LOG("Unknown video color format: %#x", videoColorFormat);
|
||||
break;
|
||||
if (IsColorFormatSupported((OMX_COLOR_FORMATTYPE)videoColorFormat)) {
|
||||
return videoSource;
|
||||
}
|
||||
|
||||
// We need to implement a ToVideoFrame_*() color conversion
|
||||
// function for this video color format.
|
||||
LOG("Unknown video color format: %#x", videoColorFormat);
|
||||
} else {
|
||||
LOG("Video color format not found");
|
||||
}
|
||||
|
@ -472,29 +506,28 @@ bool OmxDecoder::SetVideoFormat() {
|
|||
return false;
|
||||
}
|
||||
|
||||
int32_t cropRight, cropBottom;
|
||||
// Gingerbread does not support the kKeyCropRect key
|
||||
#if !defined(MOZ_ANDROID_V2_X_X)
|
||||
if (!format->findRect(kKeyCropRect, &mVideoCropLeft, &mVideoCropTop,
|
||||
&cropRight, &cropBottom)) {
|
||||
&mVideoCropRight, &mVideoCropBottom)) {
|
||||
#endif
|
||||
mVideoCropLeft = 0;
|
||||
mVideoCropTop = 0;
|
||||
cropRight = mVideoStride - 1;
|
||||
cropBottom = mVideoSliceHeight - 1;
|
||||
mVideoCropRight = mVideoStride - 1;
|
||||
mVideoCropBottom = mVideoSliceHeight - 1;
|
||||
LOG("crop rect not available, assuming no cropping");
|
||||
#if !defined(MOZ_ANDROID_V2_X_X)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mVideoCropLeft < 0 || mVideoCropLeft >= cropRight || cropRight >= mVideoStride ||
|
||||
mVideoCropTop < 0 || mVideoCropTop >= cropBottom || cropBottom >= mVideoSliceHeight) {
|
||||
LOG("invalid crop rect %d,%d-%d,%d", mVideoCropLeft, mVideoCropTop, cropRight, cropBottom);
|
||||
if (mVideoCropLeft < 0 || mVideoCropLeft >= mVideoCropRight || mVideoCropRight >= mVideoStride ||
|
||||
mVideoCropTop < 0 || mVideoCropTop >= mVideoCropBottom || mVideoCropBottom >= mVideoSliceHeight) {
|
||||
LOG("invalid crop rect %d,%d-%d,%d", mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom);
|
||||
return false;
|
||||
}
|
||||
|
||||
mVideoWidth = cropRight - mVideoCropLeft + 1;
|
||||
mVideoHeight = cropBottom - mVideoCropTop + 1;
|
||||
mVideoWidth = mVideoCropRight - mVideoCropLeft + 1;
|
||||
mVideoHeight = mVideoCropBottom - mVideoCropTop + 1;
|
||||
MOZ_ASSERT(mVideoWidth > 0 && mVideoWidth <= mVideoStride);
|
||||
MOZ_ASSERT(mVideoHeight > 0 && mVideoHeight <= mVideoSliceHeight);
|
||||
|
||||
|
@ -515,7 +548,7 @@ bool OmxDecoder::SetVideoFormat() {
|
|||
LOG("width: %d height: %d component: %s format: %#x stride: %d sliceHeight: %d rotation: %d crop: %d,%d-%d,%d",
|
||||
mVideoWidth, mVideoHeight, componentName, mVideoColorFormat,
|
||||
mVideoStride, mVideoSliceHeight, mVideoRotation,
|
||||
mVideoCropLeft, mVideoCropTop, cropRight, cropBottom);
|
||||
mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -682,6 +715,40 @@ bool OmxDecoder::ToVideoFrame_ColorConverter(VideoFrame *aFrame, int64_t aTimeUs
|
|||
#endif
|
||||
}
|
||||
|
||||
bool OmxDecoder::ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback)
|
||||
{
|
||||
#if defined(MOZ_ANDROID_V4_OR_ABOVE)
|
||||
I420ColorConverter yuvConverter;
|
||||
|
||||
if (!yuvConverter.isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (yuvConverter.getDecoderOutputFormat() != mVideoColorFormat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void *buffer = (*aBufferCallback)(mVideoWidth, mVideoHeight, MPAPI::I420);
|
||||
|
||||
ARect crop = { mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom };
|
||||
int result = yuvConverter.convertDecoderOutputToI420(aData,
|
||||
mVideoWidth,
|
||||
mVideoHeight,
|
||||
crop,
|
||||
buffer);
|
||||
|
||||
// result is 0 on success, -1 otherwise.
|
||||
if (result == OK) {
|
||||
aFrame->mTimeUs = aTimeUs;
|
||||
aFrame->mSize = mVideoWidth * mVideoHeight * 3 / 2;
|
||||
}
|
||||
|
||||
return result == OK;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OmxDecoder::ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) {
|
||||
switch (mVideoColorFormat) {
|
||||
// Froyo support is best handled with the android color conversion code. I
|
||||
|
@ -710,7 +777,8 @@ bool OmxDecoder::ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData,
|
|||
break;
|
||||
#endif
|
||||
default:
|
||||
if (!ToVideoFrame_ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback)) {
|
||||
if (!ToVideoFrame_ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback) &&
|
||||
!ToVideoFrame_I420ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback)) {
|
||||
LOG("Unknown video color format: %#x", mVideoColorFormat);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef I420_COLOR_CONVERTER_H
|
||||
#define I420_COLOR_CONVERTER_H
|
||||
|
||||
#include <II420ColorConverter.h>
|
||||
|
||||
// This is a wrapper around the I420 color converter functions in
|
||||
// II420ColorConverter, which is loaded from a shared library.
|
||||
class I420ColorConverter: public II420ColorConverter {
|
||||
public:
|
||||
I420ColorConverter();
|
||||
~I420ColorConverter();
|
||||
|
||||
// Returns true if the converter functions are successfully loaded.
|
||||
bool isLoaded();
|
||||
private:
|
||||
void* mHandle;
|
||||
};
|
||||
|
||||
#endif /* I420_COLOR_CONVERTER_H */
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef II420_COLOR_CONVERTER_H
|
||||
|
||||
#define II420_COLOR_CONVERTER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <android/rect.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct II420ColorConverter {
|
||||
|
||||
/*
|
||||
* getDecoderOutputFormat
|
||||
* Returns the color format (OMX_COLOR_FORMATTYPE) of the decoder output.
|
||||
* If it is I420 (OMX_COLOR_FormatYUV420Planar), no conversion is needed,
|
||||
* and convertDecoderOutputToI420() can be a no-op.
|
||||
*/
|
||||
int (*getDecoderOutputFormat)();
|
||||
|
||||
/*
|
||||
* convertDecoderOutputToI420
|
||||
* @Desc Converts from the decoder output format to I420 format.
|
||||
* @note Caller (e.g. VideoEditor) owns the buffers
|
||||
* @param decoderBits (IN) Pointer to the buffer contains decoder output
|
||||
* @param decoderWidth (IN) Buffer width, as reported by the decoder
|
||||
* metadata (kKeyWidth)
|
||||
* @param decoderHeight (IN) Buffer height, as reported by the decoder
|
||||
* metadata (kKeyHeight)
|
||||
* @param decoderRect (IN) The rectangle of the actual frame, as
|
||||
* reported by decoder metadata (kKeyCropRect)
|
||||
* @param dstBits (OUT) Pointer to the output I420 buffer
|
||||
* @return -1 Any error
|
||||
* @return 0 No Error
|
||||
*/
|
||||
int (*convertDecoderOutputToI420)(
|
||||
void* decoderBits, int decoderWidth, int decoderHeight,
|
||||
ARect decoderRect, void* dstBits);
|
||||
|
||||
/*
|
||||
* getEncoderIntputFormat
|
||||
* Returns the color format (OMX_COLOR_FORMATTYPE) of the encoder input.
|
||||
* If it is I420 (OMX_COLOR_FormatYUV420Planar), no conversion is needed,
|
||||
* and convertI420ToEncoderInput() and getEncoderInputBufferInfo() can
|
||||
* be no-ops.
|
||||
*/
|
||||
int (*getEncoderInputFormat)();
|
||||
|
||||
/* convertI420ToEncoderInput
|
||||
* @Desc This function converts from I420 to the encoder input format
|
||||
* @note Caller (e.g. VideoEditor) owns the buffers
|
||||
* @param srcBits (IN) Pointer to the input I420 buffer
|
||||
* @param srcWidth (IN) Width of the I420 frame
|
||||
* @param srcHeight (IN) Height of the I420 frame
|
||||
* @param encoderWidth (IN) Encoder buffer width, as calculated by
|
||||
* getEncoderBufferInfo()
|
||||
* @param encoderHeight (IN) Encoder buffer height, as calculated by
|
||||
* getEncoderBufferInfo()
|
||||
* @param encoderRect (IN) Rect coordinates of the actual frame inside
|
||||
* the encoder buffer, as calculated by
|
||||
* getEncoderBufferInfo().
|
||||
* @param encoderBits (OUT) Pointer to the output buffer. The size of
|
||||
* this buffer is calculated by
|
||||
* getEncoderBufferInfo()
|
||||
* @return -1 Any error
|
||||
* @return 0 No Error
|
||||
*/
|
||||
int (*convertI420ToEncoderInput)(
|
||||
void* srcBits, int srcWidth, int srcHeight,
|
||||
int encoderWidth, int encoderHeight, ARect encoderRect,
|
||||
void* encoderBits);
|
||||
|
||||
/* getEncoderInputBufferInfo
|
||||
* @Desc This function returns metadata for the encoder input buffer
|
||||
* based on the actual I420 frame width and height.
|
||||
* @note This API should be be used to obtain the necessary information
|
||||
* before calling convertI420ToEncoderInput().
|
||||
* VideoEditor knows only the width and height of the I420 buffer,
|
||||
* but it also needs know the width, height, and size of the
|
||||
* encoder input buffer. The encoder input buffer width and height
|
||||
* are used to set the metadata for the encoder.
|
||||
* @param srcWidth (IN) Width of the I420 frame
|
||||
* @param srcHeight (IN) Height of the I420 frame
|
||||
* @param encoderWidth (OUT) Encoder buffer width needed
|
||||
* @param encoderHeight (OUT) Encoder buffer height needed
|
||||
* @param encoderRect (OUT) Rect coordinates of the actual frame inside
|
||||
* the encoder buffer
|
||||
* @param encoderBufferSize (OUT) The size of the buffer that need to be
|
||||
* allocated by the caller before invoking
|
||||
* convertI420ToEncoderInput().
|
||||
* @return -1 Any error
|
||||
* @return 0 No Error
|
||||
*/
|
||||
int (*getEncoderInputBufferInfo)(
|
||||
int srcWidth, int srcHeight,
|
||||
int* encoderWidth, int* encoderHeight,
|
||||
ARect* encoderRect, int* encoderBufferSize);
|
||||
|
||||
} II420ColorConverter;
|
||||
|
||||
/* The only function that the shared library needs to expose: It fills the
|
||||
function pointers in II420ColorConverter */
|
||||
void getI420ColorConverter(II420ColorConverter *converter);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // II420_COLOR_CONVERTER_H
|
||||
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче