Merge m-c to f-t

This commit is contained in:
Phil Ringnalda 2016-02-05 19:45:14 -08:00
commit 80c8b4cc56
98 changed files with 1480 additions and 683 deletions

View File

@ -48,6 +48,27 @@ if test -n "$ENABLE_CLANG_PLUGIN"; then
CLANG_LDFLAGS="-lclangASTMatchers"
fi
dnl Check for the new ASTMatcher API names. Since this happened in the
dnl middle of the 3.8 cycle, our CLANG_VERSION_FULL is impossible to use
dnl correctly, so we have to detect this at configure time.
AC_CACHE_CHECK(for new ASTMatcher names,
ac_cv_have_new_ASTMatcher_names,
[
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
_SAVE_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="${LLVM_CXXFLAGS}"
AC_TRY_COMPILE([#include "clang/ASTMatchers/ASTMatchers.h"],
[clang::ast_matchers::cxxConstructExpr();],
ac_cv_have_new_ASTMatcher_names="yes",
ac_cv_have_new_ASTMatcher_names="no")
CXXFLAGS="$_SAVE_CXXFLAGS"
AC_LANG_RESTORE
])
if test "$ac_cv_have_new_ASTMatcher_names" = "yes"; then
LLVM_CXXFLAGS="$LLVM_CXXFLAGS -DHAVE_NEW_ASTMATCHER_NAMES"
fi
AC_DEFINE(MOZ_CLANG_PLUGIN)
fi

View File

@ -5,8 +5,8 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/Version.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
@ -28,6 +28,17 @@ typedef std::unique_ptr<ASTConsumer> ASTConsumerPtr;
typedef ASTConsumer *ASTConsumerPtr;
#endif
#ifndef HAVE_NEW_ASTMATCHER_NAMES
// In clang 3.8, a number of AST matchers were renamed to better match the
// respective AST node. We use the new names, and #define them to the old
// ones for compatibility with older versions.
#define cxxConstructExpr constructExpr
#define cxxConstructorDecl constructorDecl
#define cxxMethodDecl methodDecl
#define cxxNewExpr newExpr
#define cxxRecordDecl recordDecl
#endif
namespace {
using namespace clang::ast_matchers;
@ -893,7 +904,7 @@ bool isPlacementNew(const CXXNewExpr *Expr) {
DiagnosticsMatcher::DiagnosticsMatcher() {
astMatcher.addMatcher(varDecl().bind("node"), &scopeChecker);
astMatcher.addMatcher(newExpr().bind("node"), &scopeChecker);
astMatcher.addMatcher(cxxNewExpr().bind("node"), &scopeChecker);
astMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &scopeChecker);
astMatcher.addMatcher(
callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
@ -919,7 +930,7 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
.bind("call"),
&arithmeticArgChecker);
astMatcher.addMatcher(
constructExpr(
cxxConstructExpr(
allOf(hasDeclaration(noArithmeticExprInArgs()),
anyOf(hasDescendant(
binaryOperator(
@ -938,7 +949,7 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
.bind("call"),
&arithmeticArgChecker);
astMatcher.addMatcher(recordDecl(hasTrivialCtorDtor()).bind("node"),
astMatcher.addMatcher(cxxRecordDecl(hasTrivialCtorDtor()).bind("node"),
&trivialCtorDtorChecker);
astMatcher.addMatcher(
@ -975,18 +986,19 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
// lambda, where the declaration they reference is not inside the lambda.
// This excludes arguments and local variables, leaving only captured
// variables.
astMatcher.addMatcher(lambdaExpr().bind("lambda"), &refCountedInsideLambdaChecker);
astMatcher.addMatcher(lambdaExpr().bind("lambda"),
&refCountedInsideLambdaChecker);
// Older clang versions such as the ones used on the infra recognize these
// conversions as 'operator _Bool', but newer clang versions recognize these
// as 'operator bool'.
astMatcher.addMatcher(
methodDecl(anyOf(hasName("operator bool"), hasName("operator _Bool")))
cxxMethodDecl(anyOf(hasName("operator bool"), hasName("operator _Bool")))
.bind("node"),
&explicitOperatorBoolChecker);
astMatcher.addMatcher(
recordDecl(allOf(decl().bind("decl"), hasRefCntMember())),
cxxRecordDecl(allOf(decl().bind("decl"), hasRefCntMember())),
&noDuplicateRefCntMemberChecker);
astMatcher.addMatcher(
@ -1004,24 +1016,26 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
.bind("specialization"),
&nonMemMovableChecker);
astMatcher.addMatcher(
constructorDecl(isInterestingImplicitCtor(),
ofClass(allOf(isConcreteClass(), decl().bind("class"))),
unless(isMarkedImplicit()))
.bind("ctor"),
&explicitImplicitChecker);
astMatcher.addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(),
ofClass(allOf(isConcreteClass(),
decl().bind("class"))),
unless(isMarkedImplicit()))
.bind("ctor"),
&explicitImplicitChecker);
astMatcher.addMatcher(varDecl(hasType(autoNonAutoableType())).bind("node"),
&noAutoTypeChecker);
astMatcher.addMatcher(constructorDecl(isExplicitMoveConstructor()).bind("node"),
&noExplicitMoveConstructorChecker);
astMatcher.addMatcher(
cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"),
&noExplicitMoveConstructorChecker);
astMatcher.addMatcher(constructExpr(hasDeclaration(
constructorDecl(
isCompilerProvidedCopyConstructor(),
ofClass(hasRefCntMember())))).bind("node"),
&refCountedCopyConstructorChecker);
astMatcher.addMatcher(
cxxConstructExpr(
hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(),
ofClass(hasRefCntMember()))))
.bind("node"),
&refCountedCopyConstructorChecker);
}
// These enum variants determine whether an allocation has occured in the code.
@ -1036,7 +1050,8 @@ enum AllocationVariety {
// XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it
// probably will be used at some point in the future, in order to produce better
// error messages.
typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *> AutomaticTemporaryMap;
typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *>
AutomaticTemporaryMap;
AutomaticTemporaryMap AutomaticTemporaries;
void DiagnosticsMatcher::ScopeChecker::run(
@ -1048,9 +1063,11 @@ void DiagnosticsMatcher::ScopeChecker::run(
SourceLocation Loc;
QualType T;
if (const ParmVarDecl *D = Result.Nodes.getNodeAs<ParmVarDecl>("parm_vardecl")) {
if (const ParmVarDecl *D =
Result.Nodes.getNodeAs<ParmVarDecl>("parm_vardecl")) {
if (const Expr *Default = D->getDefaultArg()) {
if (const MaterializeTemporaryExpr *E = dyn_cast<MaterializeTemporaryExpr>(Default)) {
if (const MaterializeTemporaryExpr *E =
dyn_cast<MaterializeTemporaryExpr>(Default)) {
// We have just found a ParmVarDecl which has, as its default argument,
// a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as
// automatic, by adding it to the AutomaticTemporaryMap.
@ -1089,18 +1106,17 @@ void DiagnosticsMatcher::ScopeChecker::run(
// XXX We maybe should mark these lifetimes as being due to a temporary
// which has had its lifetime extended, to improve the error messages.
switch (E->getStorageDuration()) {
case SD_FullExpression:
{
// Check if this temporary is allocated as a default argument!
// if it is, we want to pretend that it is automatic.
AutomaticTemporaryMap::iterator AutomaticTemporary = AutomaticTemporaries.find(E);
if (AutomaticTemporary != AutomaticTemporaries.end()) {
Variety = AV_Automatic;
} else {
Variety = AV_Temporary;
}
case SD_FullExpression: {
// Check if this temporary is allocated as a default argument!
// if it is, we want to pretend that it is automatic.
AutomaticTemporaryMap::iterator AutomaticTemporary =
AutomaticTemporaries.find(E);
if (AutomaticTemporary != AutomaticTemporaries.end()) {
Variety = AV_Automatic;
} else {
Variety = AV_Temporary;
}
break;
} break;
case SD_Automatic:
Variety = AV_Automatic;
break;
@ -1165,8 +1181,8 @@ void DiagnosticsMatcher::ScopeChecker::run(
case AV_Temporary:
GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, TemporaryNoteID);
HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, TemporaryNoteID);
NonTemporaryClass.reportErrorIfPresent(Diag, T, Loc,
NonTemporaryID, TemporaryNoteID);
NonTemporaryClass.reportErrorIfPresent(Diag, T, Loc, NonTemporaryID,
TemporaryNoteID);
break;
case AV_Heap:
@ -1276,8 +1292,8 @@ void DiagnosticsMatcher::RefCountedInsideLambdaChecker::run(
QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();
if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
Diag.Report(Capture.getLocation(), errorID)
<< Capture.getCapturedVar() << Pointee;
Diag.Report(Capture.getLocation(), errorID) << Capture.getCapturedVar()
<< Pointee;
Diag.Report(Capture.getLocation(), noteID);
}
}
@ -1445,7 +1461,7 @@ void DiagnosticsMatcher::NoExplicitMoveConstructorChecker::run(
// Everything we needed to know was checked in the matcher - we just report
// the error here
const CXXConstructorDecl *D =
Result.Nodes.getNodeAs<CXXConstructorDecl>("node");
Result.Nodes.getNodeAs<CXXConstructorDecl>("node");
Diag.Report(D->getLocation(), ErrorID);
}
@ -1457,16 +1473,16 @@ void DiagnosticsMatcher::RefCountedCopyConstructorChecker::run(
DiagnosticIDs::Error, "Invalid use of compiler-provided copy constructor "
"on refcounted type");
unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "The default copy constructor also copies the "
"default mRefCnt property, leading to reference "
"count imbalance issues. Please provide your own "
"copy constructor which only copies the fields which "
"need to be copied");
DiagnosticIDs::Note,
"The default copy constructor also copies the "
"default mRefCnt property, leading to reference "
"count imbalance issues. Please provide your own "
"copy constructor which only copies the fields which "
"need to be copied");
// Everything we needed to know was checked in the matcher - we just report
// the error here
const CXXConstructExpr *E =
Result.Nodes.getNodeAs<CXXConstructExpr>("node");
const CXXConstructExpr *E = Result.Nodes.getNodeAs<CXXConstructExpr>("node");
Diag.Report(E->getLocation(), ErrorID);
Diag.Report(E->getLocation(), NoteID);

View File

@ -357,36 +357,6 @@ CXXFLAGS += $(WARNINGS_AS_ERRORS)
CFLAGS += $(WARNINGS_AS_ERRORS)
endif # ALLOW_COMPILER_WARNINGS
ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
#// Currently, unless USE_STATIC_LIBS is defined, the multithreaded
#// DLL version of the RTL is used...
#//
#//------------------------------------------------------------------------
ifdef MOZ_ASAN
# ASAN-instrumented code tries to link against the dynamic CRT, which can't be
# used in the same link as the static CRT.
USE_STATIC_LIBS=
endif # MOZ_ASAN
ifdef USE_STATIC_LIBS
RTL_FLAGS=-MT # Statically linked multithreaded RTL
ifdef MOZ_DEBUG
ifndef MOZ_NO_DEBUG_RTL
RTL_FLAGS=-MTd # Statically linked multithreaded MSVC4.0 debug RTL
endif
endif # MOZ_DEBUG
else # !USE_STATIC_LIBS
RTL_FLAGS=-MD # Dynamically linked, multithreaded RTL
ifdef MOZ_DEBUG
ifndef MOZ_NO_DEBUG_RTL
RTL_FLAGS=-MDd # Dynamically linked, multithreaded MSVC4.0 debug RTL
endif
endif # MOZ_DEBUG
endif # USE_STATIC_LIBS
endif # WINNT && !GNU_CC
COMPILE_CFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CFLAGS) $(_DEPEND_CFLAGS) $(CFLAGS) $(MOZBUILD_CFLAGS)
COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WRAPPING),,$(STL_FLAGS)) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CXXFLAGS) $(_DEPEND_CFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS)
COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)

View File

@ -158,9 +158,6 @@ public:
// 2nd half of ScheduleConnectionCloseEvents, sometimes run in its own event.
void DispatchConnectionCloseEvents();
// Dispatch a runnable to the right thread.
nsresult DispatchRunnable(nsIRunnable* aRunnable);
nsresult UpdateURI();
void AddRefObject();

View File

@ -10645,7 +10645,7 @@ nsDocument::SetImagesNeedAnimating(bool aAnimating)
}
already_AddRefed<Touch>
nsIDocument::CreateTouch(nsIDOMWindow* aView,
nsIDocument::CreateTouch(nsGlobalWindow* aView,
EventTarget* aTarget,
int32_t aIdentifier,
int32_t aPageX, int32_t aPageY,
@ -10655,6 +10655,7 @@ nsIDocument::CreateTouch(nsIDOMWindow* aView,
float aRotationAngle,
float aForce)
{
MOZ_ASSERT_IF(aView, aView->IsInnerWindow());
RefPtr<Touch> touch = new Touch(aTarget,
aIdentifier,
aPageX, aPageY,

View File

@ -8543,9 +8543,9 @@ public:
}
#endif
nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mWindow);
nsCOMPtr<nsISupports> window = do_QueryReferent(mWindow);
if (!skipNukeCrossCompartment && window) {
nsGlobalWindow* win = static_cast<nsGlobalWindow*>(window.get());
nsGlobalWindow* win = nsGlobalWindow::FromSupports(window);
nsGlobalWindow* currentInner = win->IsInnerWindow() ? win : win->GetCurrentInnerWindowInternal();
NS_ENSURE_TRUE(currentInner, NS_OK);

View File

@ -306,7 +306,7 @@ private:
class nsGlobalWindow : public mozilla::dom::EventTarget,
public nsPIDOMWindow<nsISupports>,
public nsIDOMWindowInternal,
private nsIDOMWindowInternal,
public nsIScriptGlobalObject,
public nsIScriptObjectPrincipal,
public nsSupportsWeakReference,

View File

@ -2577,7 +2577,7 @@ public:
JS::Handle<JSObject*> aResult, mozilla::ErrorResult& rv);
// Touch event handlers already on nsINode
already_AddRefed<mozilla::dom::Touch>
CreateTouch(nsIDOMWindow* aView, mozilla::dom::EventTarget* aTarget,
CreateTouch(nsGlobalWindow* aView, mozilla::dom::EventTarget* aTarget,
int32_t aIdentifier, int32_t aPageX, int32_t aPageY,
int32_t aScreenX, int32_t aScreenY, int32_t aClientX,
int32_t aClientY, int32_t aRadiusX, int32_t aRadiusY,

View File

@ -2001,7 +2001,7 @@ void
Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
PromiseCallback* aRejectCallback)
{
if (mGlobal->IsDying()) {
if (!mGlobal || mGlobal->IsDying()) {
return;
}

View File

@ -372,7 +372,7 @@ SVGAnimationElement::ActivateByHyperlink()
// else, silently fail. We mustn't be part of an SVG document fragment that
// is attached to the document tree so there's nothing we can do here
} else {
ErrorResult rv;
IgnoredErrorResult rv;
BeginElement(rv);
}
}

View File

@ -0,0 +1,14 @@
<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
<script>
window.addEventListener("load", function() {
location.hash = "#a";
document.documentElement.removeAttribute("class");
}, false);
</script>
<animate xmlns="http://www.w3.org/2000/svg" id="a"/>
</html>

View File

@ -74,4 +74,5 @@ load 880544-5.svg
load 898915-1.svg
load 1035248-1.svg
load 1035248-2.svg
load 1244898-1.xhtml
load zero-size-image.svg

View File

@ -0,0 +1,21 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
function boom()
{
document.designMode = 'on';
document.execCommand("indent", false, null);
document.execCommand("insertText", false, "a");
document.execCommand("forwardDelete", false, null);
document.execCommand("justifyfull", false, null);
}
window.addEventListener("load", boom, false);
</script>
</head>
<body> <span class="v"></span></body><body><input type="file" /></body>
</html>

View File

@ -66,3 +66,4 @@ needs-focus load 1128787.html
load 1134545.html
load 1158452.html
load 1158651.html
load 1244894.xhtml

View File

@ -3837,7 +3837,7 @@ nsEditor::SplitNodeDeep(nsIContent& aNode,
didSplit = true;
ErrorResult rv;
nsCOMPtr<nsIContent> newLeftNode = SplitNode(nodeToSplit, offset, rv);
NS_ENSURE_TRUE(!rv.Failed(), -1);
NS_ENSURE_TRUE(!NS_FAILED(rv.StealNSResult()), -1);
rightNode = nodeToSplit;
leftNode = newLeftNode;

View File

@ -32,6 +32,7 @@
#include "gfxUtils.h"
#include "gfx2DGlue.h"
#include "GLScreenBuffer.h"
#include "gfxPrefs.h"
#include "gfxCrashReporterUtils.h"
@ -227,11 +228,7 @@ GLXLibrary::EnsureInitialized()
GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_texturefrompixmap,
(GLLibraryLoader::PlatformLookupFunction)&xGetProcAddress))
{
#ifdef MOZ_WIDGET_GTK
mUseTextureFromPixmap = gfxPlatformGtk::GetPlatform()->UseXRender();
#else
mUseTextureFromPixmap = true;
#endif
mUseTextureFromPixmap = gfxPrefs::UseGLXTextureFromPixmap();
} else {
mUseTextureFromPixmap = false;
NS_WARNING("Texture from pixmap disabled");

View File

@ -81,7 +81,7 @@ GLScreenBuffer::CreateFactory(GLContext* gl,
#elif defined(MOZ_WIDGET_GONK)
factory = MakeUnique<SurfaceFactory_Gralloc>(gl, caps, forwarder, flags);
#elif defined(GL_PROVIDER_GLX)
if (sGLXLibrary.UseSurfaceSharing())
if (sGLXLibrary.UseTextureFromPixmap())
factory = SurfaceFactory_GLXDrawable::Create(gl, caps, forwarder, flags);
#elif defined(MOZ_WIDGET_UIKIT)
factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, caps, forwarder, mFlags);
@ -116,7 +116,7 @@ GLScreenBuffer::CreateFactory(GLContext* gl,
}
#ifdef GL_PROVIDER_GLX
if (!factory && sGLXLibrary.UseSurfaceSharing()) {
if (!factory && sGLXLibrary.UseTextureFromPixmap()) {
factory = SurfaceFactory_GLXDrawable::Create(gl, caps, forwarder, flags);
}
#endif

View File

@ -134,12 +134,6 @@ public:
bool SupportsTextureFromPixmap(gfxASurface* aSurface);
bool IsATI() { return mIsATI; }
bool GLXVersionCheck(int aMajor, int aMinor);
bool UseSurfaceSharing() {
// Disable surface sharing due to issues with compatible FBConfigs on
// NVIDIA drivers as described in bug 1193015.
static bool useSharing = PR_GetEnv("MOZ_GLX_USE_SURFACE_SHARING");
return mUseTextureFromPixmap && useSharing;
}
private:

View File

@ -123,6 +123,7 @@ BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const
group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
return group;
}
aContext->Restore();
}
Matrix maskTransform;

View File

@ -1386,12 +1386,12 @@ CompositorD3D11::VerifyBufferSize()
if (newRefCnt > 0) {
gfxCriticalError() << "Unexpecting lingering references to backbuffer! RefCnt: " << newRefCnt;
}
hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
DXGI_FORMAT_B8G8R8A8_UNORM,
0);
}
hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
DXGI_FORMAT_B8G8R8A8_UNORM,
0);
mVerifyBuffersFailed = FAILED(hr);
if (mVerifyBuffersFailed) {
gfxCriticalNote << "D3D11 swap resize buffers failed " << hexa(hr) << " on " << mSize;

View File

@ -508,6 +508,13 @@ CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
mCurrentCompositeTask = nullptr;
}
if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) {
// We can sometimes get vsync timestamps that are in the past
// compared to the last compose with force composites.
// In those cases, wait until the next vsync;
return;
}
DispatchTouchEvents(aVsyncTimestamp);
DispatchVREvents(aVsyncTimestamp);
@ -545,6 +552,7 @@ CompositorVsyncScheduler::OnForceComposeToTarget()
void
CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
OnForceComposeToTarget();
mLastCompose = TimeStamp::Now();
ComposeToTarget(aTarget, aRect);
@ -632,6 +640,7 @@ CompositorVsyncScheduler::ScheduleTask(CancelableTask* aTask, int aTime)
void
CompositorVsyncScheduler::ResumeComposition()
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
mLastCompose = TimeStamp::Now();
ComposeToTarget(nullptr);
}

View File

@ -263,6 +263,10 @@ private:
DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer", UseNativePushLayer, bool, false);
// Disable surface sharing due to issues with compatible FBConfigs on
// NVIDIA drivers as described in bug 1193015.
DECL_GFX_PREF(Live, "gfx.use-glx-texture-from-pixmap", UseGLXTextureFromPixmap, bool, false);
// These times should be in milliseconds
DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold", TouchResampleVsyncDelayThreshold, int32_t, 20);
DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict", TouchResampleMaxPredict, int32_t, 8);

View File

@ -149,12 +149,12 @@ public:
mDirection(direction),
mMoved(false)
{
MOZ_ASSERT(mMessageName);
MOZ_RELEASE_ASSERT(mMessageName);
}
InterruptFrame(InterruptFrame&& aOther)
{
MOZ_ASSERT(aOther.mMessageName);
MOZ_RELEASE_ASSERT(aOther.mMessageName);
mMessageName = aOther.mMessageName;
aOther.mMessageName = nullptr;
aOther.mMoved = true;
@ -166,7 +166,7 @@ public:
~InterruptFrame()
{
MOZ_ASSERT_IF(!mMessageName, mMoved);
MOZ_RELEASE_ASSERT(mMessageName || mMoved);
}
InterruptFrame& operator=(InterruptFrame&& aOther)
@ -213,7 +213,7 @@ private:
int32_t mMessageRoutingId;
Semantics mMesageSemantics;
Direction mDirection;
DebugOnly<bool> mMoved;
bool mMoved;
// Disable harmful methods.
InterruptFrame(const InterruptFrame& aOther) = delete;
@ -248,7 +248,7 @@ public:
~CxxStackFrame() {
mThat.AssertWorkerThread();
MOZ_ASSERT(!mThat.mCxxStackFrames.empty());
MOZ_RELEASE_ASSERT(!mThat.mCxxStackFrames.empty());
const InterruptFrame& frame = mThat.mCxxStackFrames.back();
bool exitingSync = frame.IsOutgoingSync();
@ -328,7 +328,7 @@ MessageChannel::MessageChannel(MessageListener *aListener)
#ifdef OS_WIN
mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
NS_ASSERTION(mEvent, "CreateEvent failed! Nothing is going to work!");
MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
#endif
}
@ -337,8 +337,8 @@ MessageChannel::~MessageChannel()
MOZ_COUNT_DTOR(ipc::MessageChannel);
IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
#ifdef OS_WIN
DebugOnly<BOOL> ok = CloseHandle(mEvent);
MOZ_ASSERT(ok);
BOOL ok = CloseHandle(mEvent);
MOZ_RELEASE_ASSERT(ok);
#endif
Clear();
}
@ -466,7 +466,7 @@ MessageChannel::Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side
while (ChannelOpening == mChannelState)
mMonitor->Wait();
NS_ASSERTION(ChannelConnected == mChannelState, "not connected when awoken");
MOZ_RELEASE_ASSERT(ChannelConnected == mChannelState, "not connected when awoken");
return (ChannelConnected == mChannelState);
}
@ -483,8 +483,8 @@ MessageChannel::OnOpenAsSlave(MessageChannel *aTargetChan, Side aSide)
mMonitor = aTargetChan->mMonitor;
MonitorAutoLock lock(*mMonitor);
NS_ASSERTION(ChannelOpening == aTargetChan->mChannelState,
"Target channel not in the process of opening");
MOZ_RELEASE_ASSERT(ChannelOpening == aTargetChan->mChannelState,
"Target channel not in the process of opening");
mChannelState = ChannelConnected;
aTargetChan->mChannelState = ChannelConnected;
aTargetChan->mMonitor->Notify();
@ -596,7 +596,7 @@ MessageChannel::ShouldDeferMessage(const Message& aMsg)
// Unless they're urgent, we always defer async messages.
if (!aMsg.is_sync()) {
MOZ_ASSERT(aMsg.priority() == IPC::Message::PRIORITY_NORMAL);
MOZ_RELEASE_ASSERT(aMsg.priority() == IPC::Message::PRIORITY_NORMAL);
return true;
}
@ -658,10 +658,10 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
return;
}
MOZ_ASSERT(aMsg.transaction_id() == mCurrentTransaction);
MOZ_ASSERT(AwaitingSyncReply());
MOZ_ASSERT(!mRecvd);
MOZ_ASSERT(!mTimedOutMessageSeqno);
MOZ_RELEASE_ASSERT(aMsg.transaction_id() == mCurrentTransaction);
MOZ_RELEASE_ASSERT(AwaitingSyncReply());
MOZ_RELEASE_ASSERT(!mRecvd);
MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
// Rather than storing errors in mRecvd, we mark them in
// mRecvdErrors. We need a counter because multiple replies can arrive
@ -683,8 +683,8 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
}
// Prioritized messages cannot be compressed.
MOZ_ASSERT_IF(aMsg.compress_type() != IPC::Message::COMPRESSION_NONE,
aMsg.priority() == IPC::Message::PRIORITY_NORMAL);
MOZ_RELEASE_ASSERT(aMsg.compress_type() == IPC::Message::COMPRESSION_NONE ||
aMsg.priority() == IPC::Message::PRIORITY_NORMAL);
bool compress = false;
if (aMsg.compress_type() == IPC::Message::COMPRESSION_ENABLED) {
@ -695,8 +695,8 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
// This message type has compression enabled, and the back of the
// queue was the same message type and routed to the same destination.
// Replace it with the newer message.
MOZ_ASSERT(mPending.back().compress_type() ==
IPC::Message::COMPRESSION_ENABLED);
MOZ_RELEASE_ASSERT(mPending.back().compress_type() ==
IPC::Message::COMPRESSION_ENABLED);
mPending.pop_back();
}
} else if (aMsg.compress_type() == IPC::Message::COMPRESSION_ALL) {
@ -709,7 +709,7 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
// Erase it. Note that, since we always compress these redundancies, There Can
// Be Only One.
compress = true;
MOZ_ASSERT((*it).compress_type() == IPC::Message::COMPRESSION_ALL);
MOZ_RELEASE_ASSERT((*it).compress_type() == IPC::Message::COMPRESSION_ALL);
mPending.erase((++it).base());
}
}
@ -787,8 +787,8 @@ MessageChannel::ProcessPendingRequests(int seqno, int transaction)
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) {
Message &msg = *it;
MOZ_ASSERT(mCurrentTransaction == transaction,
"Calling ShouldDeferMessage when cancelled");
MOZ_RELEASE_ASSERT(mCurrentTransaction == transaction,
"Calling ShouldDeferMessage when cancelled");
bool defer = ShouldDeferMessage(msg);
// Only log the interesting messages.
@ -883,7 +883,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
// Generally only the parent dispatches urgent messages. And the only
// sync messages it can send are high-priority. Mainly we want to ensure
// here that we don't return false for non-CPOW messages.
MOZ_ASSERT(msg->priority() == IPC::Message::PRIORITY_HIGH);
MOZ_RELEASE_ASSERT(msg->priority() == IPC::Message::PRIORITY_HIGH);
IPC_LOG("Sending while dispatching urgent message");
mLastSendError = SyncSendError::SendingCPOWWhileDispatchingUrgent;
return false;
@ -893,7 +893,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
(msg->priority() < DispatchingSyncMessagePriority() ||
msg->priority() < AwaitingSyncReplyPriority()))
{
MOZ_ASSERT(DispatchingSyncMessage() || DispatchingAsyncMessage());
MOZ_RELEASE_ASSERT(DispatchingSyncMessage() || DispatchingAsyncMessage());
IPC_LOG("Cancel from Send");
CancelMessage *cancel = new CancelMessage(mCurrentTransaction);
CancelTransaction(mCurrentTransaction);
@ -924,7 +924,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
int32_t seqno = msg->seqno();
int prio = msg->priority();
DebugOnly<msgid_t> replyType = msg->type() + 1;
msgid_t replyType = msg->type() + 1;
AutoSetValue<bool> replies(mAwaitingSyncReply, true);
AutoSetValue<int> prioSet(mAwaitingSyncReplyPriority, prio);
@ -967,9 +967,9 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
break;
}
MOZ_ASSERT(!mTimedOutMessageSeqno);
MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
MOZ_ASSERT(mCurrentTransaction == transaction);
MOZ_RELEASE_ASSERT(mCurrentTransaction == transaction);
bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages);
if (!Connected()) {
@ -1015,12 +1015,12 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
}
}
MOZ_ASSERT(mRecvd);
MOZ_ASSERT(mRecvd->is_reply(), "expected reply");
MOZ_ASSERT(!mRecvd->is_reply_error());
MOZ_ASSERT(mRecvd->type() == replyType, "wrong reply type");
MOZ_ASSERT(mRecvd->seqno() == seqno);
MOZ_ASSERT(mRecvd->is_sync());
MOZ_RELEASE_ASSERT(mRecvd);
MOZ_RELEASE_ASSERT(mRecvd->is_reply(), "expected reply");
MOZ_RELEASE_ASSERT(!mRecvd->is_reply_error());
MOZ_RELEASE_ASSERT(mRecvd->seqno() == seqno);
MOZ_RELEASE_ASSERT(mRecvd->type() == replyType, "wrong reply type");
MOZ_RELEASE_ASSERT(mRecvd->is_sync());
*aReply = Move(*mRecvd);
mRecvd = nullptr;
@ -1348,7 +1348,7 @@ MessageChannel::OnMaybeDequeueOne()
}
// We should not be in a transaction yet if we're not blocked.
MOZ_ASSERT(mCurrentTransaction == 0);
MOZ_RELEASE_ASSERT(mCurrentTransaction == 0);
DispatchMessage(recvd);
return true;
@ -1369,7 +1369,7 @@ MessageChannel::DispatchMessage(const Message &aMsg)
AutoEnterTransaction transaction(this, aMsg);
int id = aMsg.transaction_id();
MOZ_ASSERT_IF(aMsg.is_sync(), id == mCurrentTransaction);
MOZ_RELEASE_ASSERT(!aMsg.is_sync() || id == mCurrentTransaction);
{
MonitorAutoUnlock unlock(*mMonitor);
@ -1401,7 +1401,7 @@ MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
int prio = aMsg.priority();
MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread());
MOZ_RELEASE_ASSERT(prio == IPC::Message::PRIORITY_NORMAL || NS_IsMainThread());
MessageChannel* dummy;
MessageChannel*& blockingVar = mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy;
@ -1429,7 +1429,7 @@ void
MessageChannel::DispatchAsyncMessage(const Message& aMsg)
{
AssertWorkerThread();
MOZ_ASSERT(!aMsg.is_interrupt() && !aMsg.is_sync());
MOZ_RELEASE_ASSERT(!aMsg.is_interrupt() && !aMsg.is_sync());
if (aMsg.routing_id() == MSG_ROUTING_NONE) {
NS_RUNTIMEABORT("unhandled special message!");
@ -1548,7 +1548,7 @@ MessageChannel::MaybeUndeferIncall()
IPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
--mRemoteStackDepthGuess;
MOZ_ASSERT(call.priority() == IPC::Message::PRIORITY_NORMAL);
MOZ_RELEASE_ASSERT(call.priority() == IPC::Message::PRIORITY_NORMAL);
mPending.push_back(call);
}
@ -1697,7 +1697,7 @@ MessageChannel::SetReplyTimeoutMs(int32_t aTimeoutMs)
void
MessageChannel::OnChannelConnected(int32_t peer_id)
{
MOZ_ASSERT(!mPeerPidSet);
MOZ_RELEASE_ASSERT(!mPeerPidSet);
mPeerPidSet = true;
mPeerPid = peer_id;
mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mOnChannelConnectedTask));
@ -1707,7 +1707,7 @@ void
MessageChannel::DispatchOnChannelConnected()
{
AssertWorkerThread();
MOZ_ASSERT(mPeerPidSet);
MOZ_RELEASE_ASSERT(mPeerPidSet);
if (mListener)
mListener->OnChannelConnected(mPeerPid);
}
@ -2061,7 +2061,7 @@ MessageChannel::DumpInterruptStack(const char* const pfx) const
int32_t
MessageChannel::GetTopmostMessageRoutingId() const
{
MOZ_ASSERT(MessageLoop::current() == mWorkerLoop);
MOZ_RELEASE_ASSERT(MessageLoop::current() == mWorkerLoop);
if (mCxxStackFrames.empty()) {
return MSG_ROUTING_NONE;
}
@ -2147,20 +2147,20 @@ MessageChannel::CancelTransaction(int transaction)
// 2. Parent times out H.
// 3. Child dispatches H and sends nested message H' (same transaction).
// 4. Parent dispatches H' and cancels.
MOZ_ASSERT_IF(mCurrentTransaction, mCurrentTransaction == transaction);
MOZ_RELEASE_ASSERT(!mCurrentTransaction || mCurrentTransaction == transaction);
mCurrentTransaction = 0;
// During a timeout Send should always fail.
MOZ_ASSERT(!mAwaitingSyncReply);
MOZ_RELEASE_ASSERT(!mAwaitingSyncReply);
} else {
MOZ_ASSERT(mCurrentTransaction == transaction);
MOZ_RELEASE_ASSERT(mCurrentTransaction == transaction);
mCurrentTransaction = 0;
mAwaitingSyncReply = false;
mAwaitingSyncReplyPriority = 0;
}
DebugOnly<bool> foundSync = false;
bool foundSync = false;
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) {
Message &msg = *it;
@ -2172,8 +2172,8 @@ MessageChannel::CancelTransaction(int transaction)
// avoid processing messages out of order in the short time before it
// crashes.
if (msg.is_sync() && msg.priority() != IPC::Message::PRIORITY_NORMAL) {
MOZ_ASSERT(!foundSync);
MOZ_ASSERT(msg.transaction_id() != transaction);
MOZ_RELEASE_ASSERT(!foundSync);
MOZ_RELEASE_ASSERT(msg.transaction_id() != transaction);
IPC_LOG("Removing msg from queue seqno=%d xid=%d", msg.seqno(), msg.transaction_id());
foundSync = true;
it = mPending.erase(it);
@ -2207,7 +2207,7 @@ MessageChannel::CancelCurrentTransaction()
}
IPC_LOG("Cancel requested: current xid=%d", mCurrentTransaction);
MOZ_ASSERT(DispatchingSyncMessage());
MOZ_RELEASE_ASSERT(DispatchingSyncMessage());
CancelMessage *cancel = new CancelMessage(mCurrentTransaction);
CancelTransaction(mCurrentTransaction);
mLink->SendMessage(cancel);

View File

@ -624,8 +624,9 @@ class MessageChannel : HasResultCodes
if (!aMessage.is_sync())
return;
MOZ_ASSERT_IF(mChan->mSide == ParentSide && mOldTransaction != aMessage.transaction_id(),
!mOldTransaction || aMessage.priority() > mChan->AwaitingSyncReplyPriority());
MOZ_DIAGNOSTIC_ASSERT(
!(mChan->mSide == ParentSide && mOldTransaction != aMessage.transaction_id()) ||
!mOldTransaction || aMessage.priority() > mChan->AwaitingSyncReplyPriority());
mChan->mCurrentTransaction = aMessage.transaction_id();
}
~AutoEnterTransaction() {
@ -776,7 +777,7 @@ class MessageChannel : HasResultCodes
// safely. This is necessary to be able to cancel notification if we are
// closed at the same time.
RefPtr<RefCountedTask> mOnChannelConnectedTask;
DebugOnly<bool> mPeerPidSet;
bool mPeerPidSet;
int32_t mPeerPid;
};

View File

@ -588,6 +588,11 @@ GCZeal(JSContext* cx, unsigned argc, Value* vp)
if (!ToUint32(cx, args.get(0), &zeal))
return false;
if (zeal > uint32_t(gc::ZealMode::Limit)) {
JS_ReportError(cx, "gczeal argument out of range");
return false;
}
uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
if (args.length() >= 2) {
if (!ToUint32(cx, args.get(1), &frequency))
@ -624,10 +629,10 @@ ScheduleGC(JSContext* cx, unsigned argc, Value* vp)
PrepareZoneForGC(args[0].toString()->zone());
}
uint8_t zeal;
uint32_t zealBits;
uint32_t freq;
uint32_t next;
JS_GetGCZeal(cx, &zeal, &freq, &next);
JS_GetGCZealBits(cx, &zealBits, &freq, &next);
args.rval().setInt32(next);
return true;
}

View File

@ -580,7 +580,8 @@ class GCRuntime
void finishRoots();
void finish();
inline int zeal();
inline bool hasZealMode(ZealMode mode);
inline void clearZealMode(ZealMode mode);
inline bool upcomingZealousGC();
inline bool needZealousGC();
@ -636,8 +637,8 @@ class GCRuntime
void onOutOfMallocMemory(const AutoLockGC& lock);
#ifdef JS_GC_ZEAL
const void* addressOfZealMode() { return &zealMode; }
void getZeal(uint8_t* zeal, uint32_t* frequency, uint32_t* nextScheduled);
const void* addressOfZealModeBits() { return &zealModeBits; }
void getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
void setZeal(uint8_t zeal, uint32_t frequency);
bool parseAndSetZeal(const char* str);
void setNextScheduled(uint32_t count);
@ -1259,7 +1260,7 @@ class GCRuntime
* zeal_ value 14 performs periodic shrinking collections.
*/
#ifdef JS_GC_ZEAL
int zealMode;
uint32_t zealModeBits;
int zealFrequency;
int nextScheduled;
bool deterministicOnly;
@ -1347,9 +1348,20 @@ class MOZ_RAII AutoEnterIteration {
};
#ifdef JS_GC_ZEAL
inline int
GCRuntime::zeal() {
return zealMode;
inline bool
GCRuntime::hasZealMode(ZealMode mode)
{
static_assert(size_t(ZealMode::Limit) < sizeof(zealModeBits) * 8,
"Zeal modes must fit in zealModeBits");
return zealModeBits & (1 << uint32_t(mode));
}
inline void
GCRuntime::clearZealMode(ZealMode mode)
{
zealModeBits &= ~(1 << uint32_t(mode));
MOZ_ASSERT(!hasZealMode(mode));
}
inline bool
@ -1360,11 +1372,12 @@ GCRuntime::upcomingZealousGC() {
inline bool
GCRuntime::needZealousGC() {
if (nextScheduled > 0 && --nextScheduled == 0) {
if (zealMode == ZealAllocValue ||
zealMode == ZealGenerationalGCValue ||
(zealMode >= ZealIncrementalRootsThenFinish &&
zealMode <= ZealIncrementalMultipleSlices) ||
zealMode == ZealCompactValue)
if (hasZealMode(ZealMode::Alloc) ||
hasZealMode(ZealMode::GenerationalGC) ||
hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
hasZealMode(ZealMode::IncrementalMultipleSlices) ||
hasZealMode(ZealMode::Compact))
{
nextScheduled = zealFrequency;
}
@ -1373,7 +1386,8 @@ GCRuntime::needZealousGC() {
return false;
}
#else
inline int GCRuntime::zeal() { return 0; }
inline bool GCRuntime::hasZealMode(ZealMode mode) { return false; }
inline void GCRuntime::clearZealMode(ZealMode mode) { }
inline bool GCRuntime::upcomingZealousGC() { return false; }
inline bool GCRuntime::needZealousGC() { return false; }
#endif

View File

@ -139,7 +139,7 @@ js::Nursery::enable()
setCurrentChunk(0);
currentStart_ = position();
#ifdef JS_GC_ZEAL
if (runtime()->gcZeal() == ZealGenerationalGCValue)
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
enterZealMode();
#endif
}
@ -161,7 +161,7 @@ js::Nursery::isEmpty() const
MOZ_ASSERT(runtime_);
if (!isEnabled())
return true;
MOZ_ASSERT_IF(runtime_->gcZeal() != ZealGenerationalGCValue, currentStart_ == start());
MOZ_ASSERT_IF(!runtime_->hasZealMode(ZealMode::GenerationalGC), currentStart_ == start());
return position() == currentStart_;
}
@ -516,7 +516,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
// Make sure hashtables have been updated after the collection.
TIME_START(checkHashTables);
#ifdef JS_GC_ZEAL
if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC)
if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
CheckHashTablesAfterMovingGC(rt);
#endif
TIME_END(checkHashTables);
@ -678,7 +678,7 @@ js::Nursery::sweep()
for (int i = 0; i < numNurseryChunks_; ++i)
initChunk(i);
if (runtime()->gcZeal() == ZealGenerationalGCValue) {
if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
MOZ_ASSERT(numActiveChunks_ == numNurseryChunks_);
/* Only reset the alloc point when we are close to the end. */
@ -704,7 +704,7 @@ void
js::Nursery::growAllocableSpace()
{
#ifdef JS_GC_ZEAL
MOZ_ASSERT_IF(runtime()->gcZeal() == ZealGenerationalGCValue,
MOZ_ASSERT_IF(runtime()->hasZealMode(ZealMode::GenerationalGC),
numActiveChunks_ == numNurseryChunks_);
#endif
numActiveChunks_ = Min(numActiveChunks_ * 2, numNurseryChunks_);
@ -714,7 +714,7 @@ void
js::Nursery::shrinkAllocableSpace()
{
#ifdef JS_GC_ZEAL
if (runtime()->gcZeal() == ZealGenerationalGCValue)
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
return;
#endif
numActiveChunks_ = Max(numActiveChunks_ - 1, 1);

View File

@ -381,7 +381,7 @@ gc::VerifyBarriers(JSRuntime* rt, VerifierType type)
void
gc::GCRuntime::maybeVerifyPreBarriers(bool always)
{
if (zealMode != ZealVerifierPreValue)
if (!hasZealMode(ZealMode::VerifierPre))
return;
if (rt->mainThread.suppressGC)

View File

@ -233,7 +233,7 @@ struct Zone : public JS::shadow::Zone,
bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); }
bool compileBarriers(bool needsIncrementalBarrier) const {
return needsIncrementalBarrier ||
runtimeFromMainThread()->gcZeal() == js::gc::ZealVerifierPreValue;
runtimeFromMainThread()->hasZealMode(js::gc::ZealMode::VerifierPre);
}
enum ShouldUpdateJit { DontUpdateJit, UpdateJit };

View File

@ -48,3 +48,11 @@ for (var code of compilable_units) {
for (var code of non_compilable_units) {
assertEq(Debugger.isCompilableUnit(code), false);
}
// Supplying no arguments should throw a type error
assertThrowsInstanceOf(() => {
Debugger.isCompilableUnit();
}, TypeError);
// Supplying extra arguments should be fine
assertEq(Debugger.isCompilableUnit("", 1, 2, 3, 4, {}, []), true);

View File

@ -0,0 +1,5 @@
if (!('oomTest' in this))
quit();
var t = {};
oomTest(() => serialize(t));

View File

@ -0,0 +1,27 @@
gczeal(12);
var length = 10000;
var array = new Array(length);
array.fill(null);
// Promote the array to the tenured heap, if it isn't already there.
minorgc();
for (var i = 0; i < length; i++) {
// Exercise that barrier with some fresh nursery object references!
array[i] = {};
}
minorgc();
for (var i = length; i > 0; i--) {
array[i - 1] = {};
}
minorgc();
for (var i = 0; i < length; i++) {
array[Math.floor(Math.random() * length)] = {};
}
gc();

View File

@ -0,0 +1,5 @@
try {
gczeal(123);
} catch(e) {
assertEq(e.toString().includes("out of range"), true);
}

View File

@ -0,0 +1,15 @@
evalInFrame = function(global) {
dbgGlobal = newGlobal();
dbg = new dbgGlobal.Debugger();
return function(upCount, code) {
dbg.addDebuggee(global);
frame = dbg.getNewestFrame().older;
frame.eval(code);
}
}(this);
m = parseModule(`
function g() this.hours = 0;
evalInFrame.call(0, 0, "g()")
`);
m.declarationInstantiation();
m.evaluation();

View File

@ -3133,16 +3133,16 @@ CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier*
masm.jump(ool->rejoin());
}
template <class LPostBarrierType>
void
CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir)
CodeGenerator::visitPostWriteBarrierCommonO(LPostBarrierType* lir, OutOfLineCode* ool)
{
OutOfLineCallPostWriteBarrier* ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
addOutOfLineCode(ool, lir->mir());
Register temp = ToTempRegisterOrInvalid(lir->temp());
if (lir->object()->isConstant()) {
// Constant nursery objects cannot appear here, see LIRGenerator::visitPostWriteBarrier.
// Constant nursery objects cannot appear here, see LIRGenerator::visitPostWriteElementBarrier.
MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()));
} else {
masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->object()), temp,
@ -3154,28 +3154,122 @@ CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir)
masm.bind(ool->rejoin());
}
template <class LPostBarrierType>
void
CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir)
CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool)
{
OutOfLineCallPostWriteBarrier* ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
addOutOfLineCode(ool, lir->mir());
Register temp = ToTempRegisterOrInvalid(lir->temp());
if (lir->object()->isConstant()) {
// Constant nursery objects cannot appear here, see LIRGenerator::visitPostWriteBarrier.
// Constant nursery objects cannot appear here, see LIRGenerator::visitPostWriteElementBarrier.
MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()));
} else {
masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->object()), temp,
ool->rejoin());
}
ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
ValueOperand value = ToValue(lir, LPostBarrierType::Input);
masm.branchValueIsNurseryObject(Assembler::Equal, value, temp, ool->entry());
masm.bind(ool->rejoin());
}
void
CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir)
{
auto ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
visitPostWriteBarrierCommonO(lir, ool);
}
void
CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir)
{
auto ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
visitPostWriteBarrierCommonV(lir, ool);
}
// Out-of-line path to update the store buffer.
class OutOfLineCallPostWriteElementBarrier : public OutOfLineCodeBase<CodeGenerator>
{
LInstruction* lir_;
const LAllocation* object_;
const LAllocation* index_;
public:
OutOfLineCallPostWriteElementBarrier(LInstruction* lir, const LAllocation* object,
const LAllocation* index)
: lir_(lir),
object_(object),
index_(index)
{ }
void accept(CodeGenerator* codegen) {
codegen->visitOutOfLineCallPostWriteElementBarrier(this);
}
LInstruction* lir() const {
return lir_;
}
const LAllocation* object() const {
return object_;
}
const LAllocation* index() const {
return index_;
}
};
void
CodeGenerator::visitOutOfLineCallPostWriteElementBarrier(OutOfLineCallPostWriteElementBarrier* ool)
{
saveLiveVolatile(ool->lir());
const LAllocation* obj = ool->object();
const LAllocation* index = ool->index();
Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj);
Register indexreg = ToRegister(index);
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
regs.takeUnchecked(indexreg);
if (obj->isConstant()) {
objreg = regs.takeAny();
masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg);
} else {
regs.takeUnchecked(objreg);
}
Register runtimereg = regs.takeAny();
masm.setupUnalignedABICall(runtimereg);
masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg);
masm.passABIArg(runtimereg);
masm.passABIArg(objreg);
masm.passABIArg(indexreg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteElementBarrier));
restoreLiveVolatile(ool->lir());
masm.jump(ool->rejoin());
}
void
CodeGenerator::visitPostWriteElementBarrierO(LPostWriteElementBarrierO* lir)
{
auto ool = new(alloc()) OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
visitPostWriteBarrierCommonO(lir, ool);
}
void
CodeGenerator::visitPostWriteElementBarrierV(LPostWriteElementBarrierV* lir)
{
auto ool = new(alloc()) OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
visitPostWriteBarrierCommonV(lir, ool);
}
void
CodeGenerator::visitCallNative(LCallNative* call)
{

View File

@ -43,6 +43,7 @@ class OutOfLineStoreElementHole;
class OutOfLineTypeOfV;
class OutOfLineUpdateCache;
class OutOfLineCallPostWriteBarrier;
class OutOfLineCallPostWriteElementBarrier;
class OutOfLineIsCallable;
class OutOfLineRegExpMatcher;
class OutOfLineRegExpTester;
@ -137,9 +138,16 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitTypeBarrierV(LTypeBarrierV* lir);
void visitTypeBarrierO(LTypeBarrierO* lir);
void visitMonitorTypes(LMonitorTypes* lir);
template <class LPostBarrierType>
void visitPostWriteBarrierCommonO(LPostBarrierType* lir, OutOfLineCode* ool);
template <class LPostBarrierType>
void visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool);
void visitPostWriteBarrierO(LPostWriteBarrierO* lir);
void visitPostWriteElementBarrierO(LPostWriteElementBarrierO* lir);
void visitPostWriteBarrierV(LPostWriteBarrierV* lir);
void visitPostWriteElementBarrierV(LPostWriteElementBarrierV* lir);
void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool);
void visitOutOfLineCallPostWriteElementBarrier(OutOfLineCallPostWriteElementBarrier* ool);
void visitCallNative(LCallNative* call);
void emitCallInvokeFunction(LInstruction* call, Register callereg,
bool isConstructing, uint32_t argc,

View File

@ -78,9 +78,9 @@ CompileRuntime::addressOfLastCachedNativeIterator()
#ifdef JS_GC_ZEAL
const void*
CompileRuntime::addressOfGCZeal()
CompileRuntime::addressOfGCZealModeBits()
{
return runtime()->gc.addressOfZealMode();
return runtime()->gc.addressOfZealModeBits();
}
#endif

View File

@ -53,7 +53,7 @@ class CompileRuntime
const void* addressOfLastCachedNativeIterator();
#ifdef JS_GC_ZEAL
const void* addressOfGCZeal();
const void* addressOfGCZealModeBits();
#endif
const void* addressOfInterruptUint32();

View File

@ -10003,8 +10003,9 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
}
bool barrier = true;
bool indexIsInt32 = index->type() == MIRType_Int32;
if (index->type() == MIRType_Int32 &&
if (indexIsInt32 &&
!PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
&object, nullptr, &value, /* canModify = */ true))
{
@ -10022,8 +10023,12 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
bool checkNative = !clasp || !clasp->isNative();
object = addMaybeCopyElementsForWrite(object, checkNative);
if (NeedsPostBarrier(value))
current->add(MPostWriteBarrier::New(alloc(), object, value));
if (NeedsPostBarrier(value)) {
if (indexIsInt32)
current->add(MPostWriteElementBarrier::New(alloc(), object, value, index));
else
current->add(MPostWriteBarrier::New(alloc(), object, value));
}
// Emit SetPropertyCache.
bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
@ -10054,14 +10059,14 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
// cannot hit another indexed property on the object or its prototypes.
bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(this, obj);
if (NeedsPostBarrier(value))
current->add(MPostWriteBarrier::New(alloc(), obj, value));
// Ensure id is an integer.
MInstruction* idInt32 = MToInt32::New(alloc(), id);
current->add(idInt32);
id = idInt32;
if (NeedsPostBarrier(value))
current->add(MPostWriteElementBarrier::New(alloc(), obj, value, id));
// Copy the elements vector if necessary.
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);

View File

@ -2523,6 +2523,55 @@ LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins)
}
}
void
LIRGenerator::visitPostWriteElementBarrier(MPostWriteElementBarrier* ins)
{
MOZ_ASSERT(ins->object()->type() == MIRType_Object);
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
// LPostWriteElementBarrier assumes that if it has a constant object then that
// object is tenured, and does not need to be tested for being in the
// nursery. Ensure that assumption holds by lowering constant nursery
// objects to a register.
bool useConstantObject =
ins->object()->isConstant() &&
!IsInsideNursery(&ins->object()->toConstant()->value().toObject());
switch (ins->value()->type()) {
case MIRType_Object:
case MIRType_ObjectOrNull: {
LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
LPostWriteElementBarrierO* lir =
new(alloc()) LPostWriteElementBarrierO(useConstantObject
? useOrConstant(ins->object())
: useRegister(ins->object()),
useRegister(ins->value()),
useRegister(ins->index()),
tmp);
add(lir, ins);
assignSafepoint(lir, ins);
break;
}
case MIRType_Value: {
LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
LPostWriteElementBarrierV* lir =
new(alloc()) LPostWriteElementBarrierV(useConstantObject
? useOrConstant(ins->object())
: useRegister(ins->object()),
useRegister(ins->index()),
tmp);
useBox(lir, LPostWriteElementBarrierV::Input, ins->value());
add(lir, ins);
assignSafepoint(lir, ins);
break;
}
default:
// Currently, only objects can be in the nursery. Other instruction
// types cannot hold nursery pointers.
break;
}
}
void
LIRGenerator::visitArrayLength(MArrayLength* ins)
{

View File

@ -186,6 +186,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitTypeBarrier(MTypeBarrier* ins);
void visitMonitorTypes(MMonitorTypes* ins);
void visitPostWriteBarrier(MPostWriteBarrier* ins);
void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins);
void visitArrayLength(MArrayLength* ins);
void visitSetArrayLength(MSetArrayLength* ins);
void visitTypedArrayLength(MTypedArrayLength* ins);

View File

@ -12829,6 +12829,53 @@ class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>::Dat
ALLOW_CLONE(MPostWriteBarrier)
};
// Given a value being written to another object's elements at the specified
// index, update the generational store buffer if the value is in the nursery
// and object is in the tenured heap.
class MPostWriteElementBarrier : public MTernaryInstruction
, public MixPolicy<ObjectPolicy<0>, IntPolicy<2>>::Data
{
MPostWriteElementBarrier(MDefinition* obj, MDefinition* value, MDefinition* index)
: MTernaryInstruction(obj, value, index)
{
setGuard();
}
public:
INSTRUCTION_HEADER(PostWriteElementBarrier)
static MPostWriteElementBarrier* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
MDefinition* index) {
return new(alloc) MPostWriteElementBarrier(obj, value, index);
}
MDefinition* object() const {
return getOperand(0);
}
MDefinition* value() const {
return getOperand(1);
}
MDefinition* index() const {
return getOperand(2);
}
AliasSet getAliasSet() const override {
return AliasSet::None();
}
#ifdef DEBUG
bool isConsistentFloat32Use(MUse* use) const override {
// During lowering, values that neither have object nor value MIR type
// are ignored, thus Float32 can show up at this point without any issue.
return use == getUseFor(1);
}
#endif
ALLOW_CLONE(MPostWriteElementBarrier)
};
class MNewDeclEnvObject : public MNullaryInstruction
{
CompilerGCPointer<DeclEnvObject*> templateObj_;

View File

@ -159,6 +159,7 @@ namespace jit {
_(TypeBarrier) \
_(MonitorTypes) \
_(PostWriteBarrier) \
_(PostWriteElementBarrier) \
_(GetPropertyCache) \
_(GetPropertyPolymorphic) \
_(SetPropertyPolymorphic) \

View File

@ -718,12 +718,12 @@ MacroAssembler::checkAllocatorState(Label* fail)
if (js::gc::TraceEnabled() || MemProfiler::enabled())
jump(fail);
# ifdef JS_GC_ZEAL
#ifdef JS_GC_ZEAL
// Don't execute the inline path if gc zeal or tracing are active.
branch32(Assembler::NotEqual,
AbsoluteAddress(GetJitContext()->runtime->addressOfGCZeal()), Imm32(0),
AbsoluteAddress(GetJitContext()->runtime->addressOfGCZealModeBits()), Imm32(0),
fail);
# endif
#endif
// Don't execute the inline path if the compartment has an object metadata callback,
// as the metadata to use for the object may vary between executions of the op.

View File

@ -1257,6 +1257,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
_(MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>>) \
_(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >) \
_(MixPolicy<ObjectPolicy<0>, IntPolicy<1> >) \
_(MixPolicy<ObjectPolicy<0>, IntPolicy<2> >) \
_(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >) \
_(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<2> >) \
_(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<3> >) \

View File

@ -6,6 +6,8 @@
#include "jit/VMFunctions.h"
#include "jsgc.h"
#include "builtin/TypedObject.h"
#include "frontend/BytecodeCompiler.h"
#include "jit/arm/Simulator-arm.h"
@ -614,6 +616,25 @@ PostWriteBarrier(JSRuntime* rt, JSObject* obj)
rt->gc.storeBuffer.putWholeCell(obj);
}
static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096;
void
PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, size_t index)
{
MOZ_ASSERT(!IsInsideNursery(obj));
if (obj->is<NativeObject>() &&
(obj->as<NativeObject>().getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
#ifdef JS_GC_ZEAL
|| rt->hasZealMode(gc::ZealMode::ElementsBarrier)
#endif
))
{
rt->gc.storeBuffer.putSlot(&obj->as<NativeObject>(), HeapSlot::Element, index, 1);
} else {
rt->gc.storeBuffer.putWholeCell(obj);
}
}
void
PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj)
{

View File

@ -640,6 +640,7 @@ bool CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, Muta
void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp);
void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, size_t index);
void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
uint32_t GetIndexFromString(JSString* str);

View File

@ -6597,6 +6597,75 @@ class LPostWriteBarrierV : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
}
};
// Generational write barrier used when writing an object to another object's
// elements.
class LPostWriteElementBarrierO : public LInstructionHelper<0, 3, 1>
{
public:
LIR_HEADER(PostWriteElementBarrierO)
LPostWriteElementBarrierO(const LAllocation& obj, const LAllocation& value,
const LAllocation& index, const LDefinition& temp) {
setOperand(0, obj);
setOperand(1, value);
setOperand(2, index);
setTemp(0, temp);
}
const MPostWriteElementBarrier* mir() const {
return mir_->toPostWriteElementBarrier();
}
const LAllocation* object() {
return getOperand(0);
}
const LAllocation* value() {
return getOperand(1);
}
const LAllocation* index() {
return getOperand(2);
}
const LDefinition* temp() {
return getTemp(0);
}
};
// Generational write barrier used when writing a value to another object's
// elements.
class LPostWriteElementBarrierV : public LInstructionHelper<0, 2 + BOX_PIECES, 1>
{
public:
LIR_HEADER(PostWriteElementBarrierV)
LPostWriteElementBarrierV(const LAllocation& obj, const LAllocation& index,
const LDefinition& temp) {
setOperand(0, obj);
setOperand(1, index);
setTemp(0, temp);
}
static const size_t Input = 2;
const MPostWriteElementBarrier* mir() const {
return mir_->toPostWriteElementBarrier();
}
const LAllocation* object() {
return getOperand(0);
}
const LAllocation* index() {
return getOperand(1);
}
const LDefinition* temp() {
return getTemp(0);
}
};
// Guard against an object's identity.
class LGuardObjectIdentity : public LInstructionHelper<0, 2, 0>
{

View File

@ -226,6 +226,8 @@
_(MonitorTypes) \
_(PostWriteBarrierO) \
_(PostWriteBarrierV) \
_(PostWriteElementBarrierO) \
_(PostWriteElementBarrierV) \
_(InitializedLength) \
_(SetInitializedLength) \
_(UnboxedArrayLength) \

View File

@ -424,19 +424,29 @@ class TestJSPrincipals : public JSPrincipals
class AutoLeaveZeal
{
JSContext* cx_;
uint8_t zeal_;
uint32_t zealBits_;
uint32_t frequency_;
public:
explicit AutoLeaveZeal(JSContext* cx) : cx_(cx) {
uint32_t dummy;
JS_GetGCZeal(cx_, &zeal_, &frequency_, &dummy);
JS_GetGCZealBits(cx_, &zealBits_, &frequency_, &dummy);
JS_SetGCZeal(cx_, 0, 0);
JS::PrepareForFullGC(JS_GetRuntime(cx_));
JS::GCForReason(JS_GetRuntime(cx_), GC_SHRINK, JS::gcreason::DEBUG_GC);
}
~AutoLeaveZeal() {
JS_SetGCZeal(cx_, zeal_, frequency_);
for (size_t i = 0; i < sizeof(zealBits_) * 8; i++) {
if (zealBits_ & (1 << i))
JS_SetGCZeal(cx_, i, frequency_);
}
#ifdef DEBUG
uint32_t zealBitsAfter, frequencyAfter, dummy;
JS_GetGCZealBits(cx_, &zealBitsAfter, &frequencyAfter, &dummy);
MOZ_ASSERT(zealBitsAfter == zealBits_);
MOZ_ASSERT(frequencyAfter == frequency_);
#endif
}
};
#endif /* JS_GC_ZEAL */

View File

@ -5753,9 +5753,9 @@ JS_AbortIfWrongThread(JSRuntime* rt)
#ifdef JS_GC_ZEAL
JS_PUBLIC_API(void)
JS_GetGCZeal(JSContext* cx, uint8_t* zeal, uint32_t* frequency, uint32_t* nextScheduled)
JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled)
{
cx->runtime()->gc.getZeal(zeal, frequency, nextScheduled);
cx->runtime()->gc.getZealBits(zealBits, frequency, nextScheduled);
}
JS_PUBLIC_API(void)

View File

@ -5239,7 +5239,7 @@ JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const JS::CallAr
#define JS_DEFAULT_ZEAL_FREQ 100
extern JS_PUBLIC_API(void)
JS_GetGCZeal(JSContext* cx, uint8_t* zeal, uint32_t* frequency, uint32_t* nextScheduled);
JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
extern JS_PUBLIC_API(void)
JS_SetGCZeal(JSContext* cx, uint8_t zeal, uint32_t frequency);

View File

@ -1164,7 +1164,7 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
objectsMarkedInDeadZones(0),
poked(false),
#ifdef JS_GC_ZEAL
zealMode(0),
zealModeBits(0),
zealFrequency(0),
nextScheduled(0),
deterministicOnly(false),
@ -1188,16 +1188,20 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
#ifdef JS_GC_ZEAL
void
GCRuntime::getZeal(uint8_t* zeal, uint32_t* frequency, uint32_t* scheduled)
GCRuntime::getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* scheduled)
{
*zeal = zealMode;
*zealBits = zealModeBits;
*frequency = zealFrequency;
*scheduled = nextScheduled;
}
const char* gc::ZealModeHelpText =
" Specifies how zealous the garbage collector should be. Values for level:\n"
" 0: Normal amount of collection\n"
" Specifies how zealous the garbage collector should be. Some of these modes can\n"
" be set simultaneously, e.g. in the shell, gczeal(2); gczeal(4); will activate\n"
" both modes 2 and 4.\n"
" \n"
" Values:\n"
" 0: Normal amount of collection (resets all modes)\n"
" 1: Collect when roots are added or removed\n"
" 2: Collect when every N allocations (default: 100)\n"
" 3: Collect when the window paints (browser only)\n"
@ -1209,26 +1213,42 @@ const char* gc::ZealModeHelpText =
" 9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
" 10: Incremental GC in multiple slices\n"
" 11: Verify incremental marking\n"
" 12: unused\n"
" 12: Always use the individual element post-write barrier, regardless of elements size\n"
" 13: Check internal hashtables on minor GC\n"
" 14: Perform a shrinking collection every N allocations\n";
void
GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
{
MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
if (verifyPreData)
VerifyBarriers(rt, PreBarrierVerifier);
if (zealMode == ZealGenerationalGCValue) {
if (zeal == 0 && hasZealMode(ZealMode::GenerationalGC)) {
evictNursery(JS::gcreason::DEBUG_GC);
nursery.leaveZealMode();
}
if (zeal == ZealGenerationalGCValue)
ZealMode zealMode = ZealMode(zeal);
if (zealMode == ZealMode::GenerationalGC)
nursery.enterZealMode();
bool schedule = zeal >= js::gc::ZealAllocValue;
zealMode = zeal;
// Zeal modes 8-10 are mutually exclusive. If we're setting one of those,
// we first reset all of them.
if (zealMode >= ZealMode::IncrementalRootsThenFinish &&
zealMode <= ZealMode::IncrementalMultipleSlices)
{
clearZealMode(ZealMode::IncrementalRootsThenFinish);
clearZealMode(ZealMode::IncrementalMarkAllThenFinish);
clearZealMode(ZealMode::IncrementalMultipleSlices);
}
bool schedule = zealMode >= ZealMode::Alloc;
if (zeal != 0)
zealModeBits |= 1 << unsigned(zeal);
else
zealModeBits = 0;
zealFrequency = frequency;
nextScheduled = schedule ? frequency : 0;
}
@ -1255,7 +1275,7 @@ GCRuntime::parseAndSetZeal(const char* str)
frequency = atoi(p + 1);
}
if (zeal < 0 || zeal > ZealLimit || frequency <= 0) {
if (zeal < 0 || zeal > int(ZealMode::Limit) || frequency <= 0) {
fprintf(stderr, "Format: JS_GC_ZEAL=level[,N]\n");
fputs(ZealModeHelpText, stderr);
return false;
@ -3251,7 +3271,7 @@ GCRuntime::triggerZoneGC(Zone* zone, JS::gcreason::Reason reason)
return false;
#ifdef JS_GC_ZEAL
if (zealMode == ZealAllocValue) {
if (hasZealMode(ZealMode::Alloc)) {
triggerGC(reason);
return true;
}
@ -3280,7 +3300,7 @@ GCRuntime::maybeGC(Zone* zone)
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
#ifdef JS_GC_ZEAL
if (zealMode == ZealAllocValue || zealMode == ZealPokeValue) {
if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::Poke)) {
JS::PrepareForFullGC(rt);
gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
return true;
@ -3680,7 +3700,7 @@ GCRuntime::shouldReleaseObservedTypes()
bool releaseTypes = false;
#ifdef JS_GC_ZEAL
if (zealMode != 0)
if (zealModeBits != 0)
releaseTypes = true;
#endif
@ -4504,7 +4524,7 @@ GCRuntime::computeNonIncrementalMarkingForValidation()
{
#ifdef JS_GC_ZEAL
MOZ_ASSERT(!markingValidator);
if (isIncremental && zeal() == ZealIncrementalMarkingValidator)
if (isIncremental && hasZealMode(ZealMode::IncrementalMarkingValidator))
markingValidator = js_new<MarkingValidator>(this);
if (markingValidator)
markingValidator->nonIncrementalMark();
@ -6033,7 +6053,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
gc::State initialState = incrementalState;
int zeal = 0;
bool useZeal = false;
#ifdef JS_GC_ZEAL
if (reason == JS::gcreason::DEBUG_GC && !budget.isUnlimited()) {
/*
@ -6041,14 +6061,16 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
* collection was triggered by runDebugGC() and incremental GC has not
* been cancelled by resetIncrementalGC().
*/
zeal = zealMode;
useZeal = true;
}
#endif
MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
isIncremental = !budget.isUnlimited();
if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
if (useZeal && (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish)))
{
/*
* Yields between slices occurs at predetermined points in these modes;
* the budget is not used.
@ -6078,7 +6100,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
incrementalState = MARK;
if (isIncremental && zeal == ZealIncrementalRootsThenFinish)
if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalRootsThenFinish))
break;
MOZ_FALLTHROUGH;
@ -6097,9 +6119,9 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
MOZ_ASSERT(marker.isDrained());
if (!lastMarkSlice && isIncremental &&
((initialState == MARK && zeal != ZealIncrementalRootsThenFinish) ||
zeal == ZealIncrementalMarkAllThenFinish))
if (!lastMarkSlice && isIncremental && useZeal &&
((initialState == MARK && !hasZealMode(ZealMode::IncrementalRootsThenFinish)) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish)))
{
/*
* Yield with the aim of starting the sweep in the next
@ -6124,7 +6146,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
* Always yield here when running in incremental multi-slice zeal
* mode, so RunDebugGC can reset the slice buget.
*/
if (isIncremental && zeal == ZealIncrementalMultipleSlices)
if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalMultipleSlices))
break;
MOZ_FALLTHROUGH;
@ -6550,12 +6572,10 @@ GCRuntime::notifyDidPaint()
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
#ifdef JS_GC_ZEAL
if (zealMode == ZealFrameVerifierPreValue) {
if (hasZealMode(ZealMode::FrameVerifierPre))
verifyPreBarriers();
return;
}
if (zealMode == ZealFrameGCValue) {
if (hasZealMode(ZealMode::FrameGC)) {
JS::PrepareForFullGC(rt);
gc(GC_NORMAL, JS::gcreason::REFRESH_FRAME);
return;
@ -6929,23 +6949,21 @@ void
GCRuntime::runDebugGC()
{
#ifdef JS_GC_ZEAL
int type = zealMode;
if (rt->mainThread.suppressGC)
return;
if (type == js::gc::ZealGenerationalGCValue)
if (hasZealMode(ZealMode::GenerationalGC))
return minorGC(JS::gcreason::DEBUG_GC);
PrepareForDebugGC(rt);
auto budget = SliceBudget::unlimited();
if (type == ZealIncrementalRootsThenFinish ||
type == ZealIncrementalMarkAllThenFinish ||
type == ZealIncrementalMultipleSlices)
if (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
hasZealMode(ZealMode::IncrementalMultipleSlices))
{
js::gc::State initialState = incrementalState;
if (type == ZealIncrementalMultipleSlices) {
if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
/*
* Start with a small slice limit and double it every slice. This
* ensure that we get multiple slices, and collection runs to
@ -6969,14 +6987,14 @@ GCRuntime::runDebugGC()
* For multi-slice zeal, reset the slice size when we get to the sweep
* or compact phases.
*/
if (type == ZealIncrementalMultipleSlices) {
if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
if ((initialState == MARK && incrementalState == SWEEP) ||
(initialState == SWEEP && incrementalState == COMPACT))
{
incrementalLimit = zealFrequency / 2;
}
}
} else if (type == ZealCompactValue) {
} else if (hasZealMode(ZealMode::Compact)) {
gc(GC_SHRINK, JS::gcreason::DEBUG_GC);
} else {
gc(GC_NORMAL, JS::gcreason::DEBUG_GC);

View File

@ -1251,20 +1251,23 @@ CheckValueAfterMovingGC(const JS::Value& value)
#endif // JSGC_HASH_TABLE_CHECKS
const int ZealPokeValue = 1;
const int ZealAllocValue = 2;
const int ZealFrameGCValue = 3;
const int ZealVerifierPreValue = 4;
const int ZealFrameVerifierPreValue = 5;
const int ZealStackRootingValue = 6;
const int ZealGenerationalGCValue = 7;
const int ZealIncrementalRootsThenFinish = 8;
const int ZealIncrementalMarkAllThenFinish = 9;
const int ZealIncrementalMultipleSlices = 10;
const int ZealIncrementalMarkingValidator = 11;
const int ZealCheckHashTablesOnMinorGC = 13;
const int ZealCompactValue = 14;
const int ZealLimit = 14;
enum class ZealMode {
Poke = 1,
Alloc = 2,
FrameGC = 3,
VerifierPre = 4,
FrameVerifierPre = 5,
StackRooting = 6,
GenerationalGC = 7,
IncrementalRootsThenFinish = 8,
IncrementalMarkAllThenFinish = 9,
IncrementalMultipleSlices = 10,
IncrementalMarkingValidator = 11,
ElementsBarrier = 12,
CheckHashTablesOnMinorGC = 13,
Compact = 14,
Limit = 14
};
enum VerifierType {
PreBarrierVerifier

View File

@ -33,7 +33,7 @@ GCRuntime::poke()
#ifdef JS_GC_ZEAL
/* Schedule a GC to happen "soon" after a GC poke. */
if (zealMode == ZealPokeValue)
if (hasZealMode(ZealMode::Poke))
nextScheduled = 1;
#endif
}

View File

@ -2570,61 +2570,7 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
return ok;
}
RootedNativeObject nobj(cx, obj.as<NativeObject>());
RootedShape shape(cx);
if (!NativeLookupOwnProperty<CanGC>(cx, nobj, id, &shape))
return false;
if (!shape) {
desc.object().set(nullptr);
return true;
}
desc.setAttributes(GetShapeAttributes(obj, shape));
if (desc.isAccessorDescriptor()) {
MOZ_ASSERT(desc.isShared());
// The result of GetOwnPropertyDescriptor() must be either undefined or
// a complete property descriptor (per ES6 draft rev 32 (2015 Feb 2)
// 6.1.7.3, Invariants of the Essential Internal Methods).
//
// It is an unfortunate fact that in SM, properties can exist that have
// JSPROP_GETTER or JSPROP_SETTER but not both. In these cases, rather
// than return true with desc incomplete, we fill out the missing
// getter or setter with a null, following CompletePropertyDescriptor.
if (desc.hasGetterObject()) {
desc.setGetterObject(shape->getterObject());
} else {
desc.setGetterObject(nullptr);
desc.attributesRef() |= JSPROP_GETTER;
}
if (desc.hasSetterObject()) {
desc.setSetterObject(shape->setterObject());
} else {
desc.setSetterObject(nullptr);
desc.attributesRef() |= JSPROP_SETTER;
}
desc.value().setUndefined();
} else {
// This is either a straight-up data property or (rarely) a
// property with a JSGetterOp/JSSetterOp. The latter must be
// reported to the caller as a plain data property, so clear
// desc.getter/setter, and mask away the SHARED bit.
desc.setGetter(nullptr);
desc.setSetter(nullptr);
desc.attributesRef() &= ~JSPROP_SHARED;
if (IsImplicitDenseOrTypedArrayElement(shape)) {
desc.value().set(nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
} else {
if (!NativeGetExistingProperty(cx, nobj, nobj, shape, desc.value()))
return false;
}
}
desc.object().set(nobj);
desc.assertComplete();
return true;
return NativeGetOwnPropertyDescriptor(cx, obj.as<NativeObject>(), id, desc);
}
bool

View File

@ -4528,6 +4528,9 @@ Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "Debugger.isCompilableUnit", 1))
return false;
if (!args[0].isString()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
JSMSG_NOT_EXPECTED_TYPE, "Debugger.isCompilableUnit",

View File

@ -1659,6 +1659,69 @@ js::NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool*
}
/*** [[GetOwnPropertyDescriptor]] ****************************************************************/
bool
js::NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
RootedShape shape(cx);
if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
return false;
if (!shape) {
desc.object().set(nullptr);
return true;
}
desc.setAttributes(GetShapeAttributes(obj, shape));
if (desc.isAccessorDescriptor()) {
MOZ_ASSERT(desc.isShared());
// The result of GetOwnPropertyDescriptor() must be either undefined or
// a complete property descriptor (per ES6 draft rev 32 (2015 Feb 2)
// 6.1.7.3, Invariants of the Essential Internal Methods).
//
// It is an unfortunate fact that in SM, properties can exist that have
// JSPROP_GETTER or JSPROP_SETTER but not both. In these cases, rather
// than return true with desc incomplete, we fill out the missing
// getter or setter with a null, following CompletePropertyDescriptor.
if (desc.hasGetterObject()) {
desc.setGetterObject(shape->getterObject());
} else {
desc.setGetterObject(nullptr);
desc.attributesRef() |= JSPROP_GETTER;
}
if (desc.hasSetterObject()) {
desc.setSetterObject(shape->setterObject());
} else {
desc.setSetterObject(nullptr);
desc.attributesRef() |= JSPROP_SETTER;
}
desc.value().setUndefined();
} else {
// This is either a straight-up data property or (rarely) a
// property with a JSGetterOp/JSSetterOp. The latter must be
// reported to the caller as a plain data property, so clear
// desc.getter/setter, and mask away the SHARED bit.
desc.setGetter(nullptr);
desc.setSetter(nullptr);
desc.attributesRef() &= ~JSPROP_SHARED;
if (IsImplicitDenseOrTypedArrayElement(shape)) {
desc.value().set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
} else {
if (!NativeGetExistingProperty(cx, obj, obj, shape, desc.value()))
return false;
}
}
desc.object().set(obj);
desc.assertComplete();
return true;
}
/*** [[Get]] *************************************************************************************/
static inline bool

View File

@ -160,7 +160,7 @@ ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
class ObjectElements
{
public:
enum Flags {
enum Flags: uint32_t {
// Integers written to these elements must be converted to doubles.
CONVERT_DOUBLE_ELEMENTS = 0x1,
@ -1339,6 +1339,10 @@ NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName*
extern bool
NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp);
extern bool
NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, HandleId id,
MutableHandle<PropertyDescriptor> desc);
extern bool
NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleValue receiver, HandleId id,
MutableHandleValue vp);

View File

@ -1092,7 +1092,7 @@ struct JSRuntime : public JS::shadow::Runtime,
/* Garbage collector state has been successfully initialized. */
bool gcInitialized;
int gcZeal() { return gc.zeal(); }
bool hasZealMode(js::gc::ZealMode mode) { return gc.hasZealMode(mode); }
void lockGC() {
assertCanLock(js::GCLock);

View File

@ -691,8 +691,20 @@ ModuleEnvironmentObject::setProperty(JSContext* cx, HandleObject obj, HandleId i
ModuleEnvironmentObject::getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
// We never call this hook on scope objects.
MOZ_CRASH();
const IndirectBindingMap& bindings = obj->as<ModuleEnvironmentObject>().importBindings();
Shape* shape;
ModuleEnvironmentObject* env;
if (bindings.lookup(id, &env, &shape)) {
desc.setAttributes(JSPROP_ENUMERATE | JSPROP_PERMANENT);
desc.object().set(obj);
RootedValue value(cx, env->getSlot(shape->slot()));
desc.setValue(value);
desc.assertComplete();
return true;
}
RootedNativeObject self(cx, &obj->as<NativeObject>());
return NativeGetOwnPropertyDescriptor(cx, self, id, desc);
}
/* static */ bool

View File

@ -1410,7 +1410,8 @@ JSStructuredCloneWriter::write(HandleValue v)
}
}
} else {
out.writePair(SCTAG_END_OF_KEYS, 0);
if (!out.writePair(SCTAG_END_OF_KEYS, 0))
return false;
objs.popBack();
counts.popBack();
}

View File

@ -135,7 +135,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
// We could also test DataView and Iterator here for completeness, but it's
// more trouble than it's worth.
SimpleTest.finish();
}
@ -148,6 +147,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
var isNightlyBuild = version.endsWith("a1");
var isReleaseBuild = !version.includes("a");
var gPrototypeProperties = {};
var gConstructorProperties = {};
function constructorProps(arr) {
// Some props live on all constructors
return arr.concat(["prototype", "length", "name"]);
}
gPrototypeProperties['Date'] =
["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear",
"getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay",
@ -160,30 +164,47 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
"toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
"toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
"toGMTString", Symbol.toPrimitive];
gConstructorProperties['Date'] = constructorProps(["UTC", "parse", "now"]);
gPrototypeProperties['Object'] =
["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
"unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
"__proto__"];
gConstructorProperties['Object'] =
constructorProps(["setPrototypeOf", "getOwnPropertyDescriptor", "keys",
"is", "defineProperty", "defineProperties", "create",
"getOwnPropertyNames", "getOwnPropertySymbols",
"preventExtensions", "freeze", "isFrozen", "seal",
"isSealed", "assign", "getPrototypeOf", "values",
"entries", "isExtensible"])
gPrototypeProperties['Array'] =
["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
"pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
"includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
"findIndex", "copyWithin", "fill", Symbol.iterator, "entries", "keys", "constructor"];
gConstructorProperties['Array'] =
constructorProps(["join", "reverse", "sort", "push", "pop", "shift",
"unshift", "splice", "concat", "slice", "isArray",
"lastIndexOf", "indexOf", "forEach", "map", "filter",
"every", "some", "reduce", "reduceRight", "from", "of"]);
for (var c of typedArrayClasses) {
gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
gConstructorProperties[c] = constructorProps(["BYTES_PER_ELEMENT"]);
}
gPrototypeProperties['TypedArray'] =
["length", "buffer", "byteLength", "byteOffset", Symbol.iterator, "subarray",
"set", "copyWithin", "find", "findIndex", "forEach","indexOf", "lastIndexOf", "includes",
"reverse", "join", "every", "some", "reduce", "reduceRight", "entries", "keys", "values",
"slice", "map", "filter"];
// There is no TypedArray constructor, looks like.
is(window.TypedArray, undefined, "If this ever changes, add to this test!");
for (var c of errorObjectClasses) {
gPrototypeProperties[c] = ["constructor", "name",
// We don't actually resolve these empty data properties
// onto the Xray prototypes, but we list them here to make
// the test happy.
"lineNumber", "columnNumber", "fileName", "message", "stack"];
gConstructorProperties[c] = constructorProps([]);
}
// toString and toSource only live on the parent proto (Error.prototype).
gPrototypeProperties['Error'].push('toString');
@ -192,14 +213,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
gPrototypeProperties['Function'] =
["constructor", "toSource", "toString", "apply", "call", "bind",
"isGenerator", "length", "name", "arguments", "caller"];
gPrototypeProperties['Function'] =
["constructor", "toSource", "toString", "apply", "call", "bind",
"isGenerator", "length", "name", "arguments", "caller"];
gConstructorProperties['Function'] = constructorProps([])
gPrototypeProperties['RegExp'] =
["constructor", "toSource", "toString", "compile", "exec", "test",
"flags", "global", "ignoreCase", "multiline", "source", "sticky", "unicode",
"lastIndex"];
gConstructorProperties['RegExp'] =
constructorProps(["input", "multiline", "lastMatch", "lastParen",
"leftContext", "rightContext", "$1", "$2", "$3", "$4",
"$5", "$6", "$7", "$8", "$9", "$_", "$*", "$&", "$+",
"$`", "$'"])
// Sort an array that may contain symbols as well as strings.
function sortProperties(arr) {
@ -213,6 +237,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
// again to sort them).
for (var c of Object.keys(gPrototypeProperties))
sortProperties(gPrototypeProperties[c]);
for (var c of Object.keys(gConstructorProperties))
sortProperties(gConstructorProperties[c]);
function filterOut(array, props) {
return array.filter(p => props.indexOf(p) == -1);
@ -265,7 +291,57 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
}
}
function testXray(classname, xray, xray2, propsToSkip) {
function testCtorCallables(ctorCallables, xrayCtor, localCtor) {
for (let name of ctorCallables) {
// Don't try to test Function.prototype, since that is in fact a callable
// but doesn't really do the things we expect callables to do here
// (e.g. it's in the wrong global, since it gets Xrayed itself).
if (name == "prototype" && localCtor.name == "Function") {
continue;
}
info(`Running tests for property: ${localCtor.name}.${name}`);
// Test both methods and getter properties.
function lookupCallable(obj) {
let desc = null;
do {
desc = Object.getOwnPropertyDescriptor(obj, name);
obj = Object.getPrototypeOf(obj);
} while (!desc);
return desc.get || desc.value;
};
ok(xrayCtor.hasOwnProperty(name), "ctor should have the property as own");
let method = lookupCallable(xrayCtor);
is(typeof method, 'function', "Methods from ctor Xrays are functions");
is(global(method), window, "Methods from ctor Xrays are local");
ok(method instanceof Function,
"instanceof works on methods from ctor Xrays");
is(lookupCallable(xrayCtor), method,
"Holder caching works properly on ctors");
let local = lookupCallable(localCtor);
is(method.length, local.length,
"Function.length identical for method from ctor");
// Don't try to do the return-value check on Date.now(), since there is
// absolutely no reason it should return the same value each time.
//
// Also don't try to do the return-value check on Regexp.lastMatch and
// Regexp["$&"] (which are aliases), because they get state off the global
// they live in, as far as I can tell, so testing them over Xrays will be
// wrong: on the Xray they will actaully get the lastMatch of _our_
// global, not the Xrayed one.
if (method.length == 0 &&
!(localCtor.name == "Date" && name == "now") &&
!(localCtor.name == "RegExp" && (name == "lastMatch" || name == "$&"))) {
is(method.call(xrayCtor) + "", local.call(xrayCtor) + "",
"Xray and local method results stringify identically on constructors");
is(method.call(xrayCtor) + "",
lookupCallable(xrayCtor.wrappedJSObject).call(xrayCtor.wrappedJSObject) + "",
"Xray and waived method results stringify identically");
}
}
}
function testXray(classname, xray, xray2, propsToSkip, ctorPropsToSkip = []) {
propsToSkip = propsToSkip || [];
let xrayProto = Object.getPrototypeOf(xray);
let localProto = window[classname].prototype;
@ -294,13 +370,39 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
protoProps.toSource(), "getOwnPropertyNames works");
is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(),
gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
protoProps.toSource(), "getOwnPropertySymbols works");
"getOwnPropertySymbols works");
is(xrayProto.constructor, iwin[classname], "constructor property works");
xrayProto.expando = 42;
is(xray.expando, 42, "Xrayed instances see proto expandos");
is(xray2.expando, 42, "Xrayed instances see proto expandos");
// Now test constructors
localCtor = window[classname];
xrayCtor = xrayProto.constructor;
// We already checked that this is the same as iwin[classname]
let desiredCtorProps =
Object.getOwnPropertyNames(localCtor).sort();
is(desiredCtorProps.toSource(),
gConstructorProperties[classname].filter(id => typeof id === "string").toSource(),
"A property on the " + classname +
" constructor has changed! You need a security audit from an XPConnect peer");
is(Object.getOwnPropertySymbols(localCtor).map(uneval).sort().toSource(),
gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
"A symbol-keyed property on the " + classname +
" constructor has been changed! You need a security audit from an XPConnect peer");
let ctorProps = filterOut(desiredCtorProps, ctorPropsToSkip);
let ctorCallables = ctorProps.filter(name => propertyIsGetter(localCtor, name, classname) ||
typeof localCtor[name] == 'function');
testCtorCallables(ctorCallables, xrayCtor, localCtor);
is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
ctorProps.toSource(), "getOwnPropertyNames works on Xrayed ctors");
is(Object.getOwnPropertySymbols(xrayCtor).map(uneval).sort().toSource(),
gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
"getOwnPropertySymbols works on Xrayed ctors");
}
function testDate() {
@ -357,7 +459,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
// |own| data property. So we add it to the ignore list here, and check it
// separately.
let propsToSkip = ['length'];
testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
// On the constructor, we want to skip all the non-standard "generic"
// functions. We're trying to remove them anyway; no point doing extra work
// to expose them over Xrays.
let ctorPropsToSkip = ["join", "reverse", "sort", "push", "pop", "shift",
"unshift", "splice", "concat", "slice"];
testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip,
ctorPropsToSkip);
let symbolProps = '';
uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
@ -593,7 +701,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
}
function testRegExp() {
testXray('RegExp', new iwin.RegExp('foo'), new iwin.RegExp());
// RegExp statics are very weird, and in particular RegExp has static
// properties that have to do with the last regexp execution in the global.
// Xraying those makes no sense, so we just skip constructor properties for
// RegExp xrays.
let ctorPropsToSkip = ["input", "multiline", "lastMatch", "lastParen",
"leftContext", "rightContext", "$1", "$2", "$3",
"$4", "$5", "$6", "$7", "$8", "$9", "$_", "$*", "$&",
"$+", "$`", "$'"];
testXray('RegExp', new iwin.RegExp('foo'), new iwin.RegExp(), [],
ctorPropsToSkip);
// Test the self-hosted |flags| property, toString, and toSource.
for (var flags of ["", "g", "i", "m", "y", "gimy"]) {

View File

@ -373,6 +373,112 @@ bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext* cx,
return true;
}
// Returns true on success (in the JSAPI sense), false on failure. If true is
// returned, desc.object() will indicate whether we actually resolved
// the property.
//
// id is the property id we're looking for.
// holder is the object to define the property on.
// fs is the relevant JSFunctionSpec*.
// ps is the relevant JSPropertySpec*.
// desc is the descriptor we're resolving into.
static bool
TryResolvePropertyFromSpecs(JSContext* cx, HandleId id, HandleObject holder,
const JSFunctionSpec* fs,
const JSPropertySpec* ps,
MutableHandle<PropertyDescriptor> desc)
{
// Scan through the functions.
const JSFunctionSpec* fsMatch = nullptr;
for ( ; fs && fs->name; ++fs) {
if (PropertySpecNameEqualsId(fs->name, id)) {
fsMatch = fs;
break;
}
}
if (fsMatch) {
// Generate an Xrayed version of the method.
RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id));
if (!fun)
return false;
// The generic Xray machinery only defines non-own properties of the target on
// the holder. This is broken, and will be fixed at some point, but for now we
// need to cache the value explicitly. See the corresponding call to
// JS_GetOwnPropertyDescriptorById at the top of
// JSXrayTraits::resolveOwnProperty.
RootedObject funObj(cx, JS_GetFunctionObject(fun));
return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
}
// Scan through the properties.
const JSPropertySpec* psMatch = nullptr;
for ( ; ps && ps->name; ++ps) {
if (PropertySpecNameEqualsId(ps->name, id)) {
psMatch = ps;
break;
}
}
if (psMatch) {
desc.value().setUndefined();
RootedFunction getterObj(cx);
RootedFunction setterObj(cx);
unsigned flags = psMatch->flags;
if (psMatch->isSelfHosted()) {
getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0);
if (!getterObj)
return false;
desc.setGetterObject(JS_GetFunctionObject(getterObj));
if (psMatch->setter.selfHosted.funname) {
MOZ_ASSERT(flags & JSPROP_SETTER);
setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0);
if (!setterObj)
return false;
desc.setSetterObject(JS_GetFunctionObject(setterObj));
}
} else {
desc.setGetter(JS_CAST_NATIVE_TO(psMatch->getter.native.op,
JSGetterOp));
desc.setSetter(JS_CAST_NATIVE_TO(psMatch->setter.native.op,
JSSetterOp));
}
desc.setAttributes(flags);
// The generic Xray machinery only defines non-own properties on the holder.
// This is broken, and will be fixed at some point, but for now we need to
// cache the value explicitly. See the corresponding call to
// JS_GetPropertyById at the top of JSXrayTraits::resolveOwnProperty.
//
// Note also that the public-facing API here doesn't give us a way to
// pass along JITInfo. It's probably ok though, since Xrays are already
// pretty slow.
return JS_DefinePropertyById(cx, holder, id,
UndefinedHandleValue,
// This particular descriptor, unlike most,
// actually stores JSNatives directly,
// since we just set it up. Do NOT pass
// JSPROP_PROPOP_ACCESSORS here!
desc.attributes(),
JS_PROPERTYOP_GETTER(desc.getter()),
JS_PROPERTYOP_SETTER(desc.setter())) &&
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
}
return true;
}
static bool
ShouldResolveStaticProperties(JSProtoKey key)
{
// Don't try to resolve static properties on RegExp, because they
// have issues. In particular, some of them grab state off the
// global of the RegExp constructor that describes the last regexp
// evaluation in that global, which is not a useful thing to do
// over Xrays.
return key != JSProto_RegExp;
}
bool
JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
HandleObject wrapper, HandleObject holder,
@ -385,6 +491,18 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
if (!ok || desc.object())
return ok;
// The non-HasPrototypes semantics implemented by traditional Xrays are kind
// of broken with respect to |own|-ness and the holder. The common code
// muddles through by only checking the holder for non-|own| lookups, but
// that doesn't work for us. So we do an explicit holder check here, and hope
// that this mess gets fixed up soon.
if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
return false;
if (desc.object()) {
desc.object().set(wrapper);
return true;
}
RootedObject target(cx, getTargetObject(wrapper));
JSProtoKey key = getProtoKey(holder);
if (!isPrototype(holder)) {
@ -432,22 +550,44 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
RootedString fname(cx, JS_GetFunctionId(JS_GetObjectFunction(target)));
FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
fname ? StringValue(fname) : JS_GetEmptyStringValue(cx));
} else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)) {
// Handle the 'prototype' property to make xrayedGlobal.StandardClass.prototype work.
} else {
// Look for various static properties/methods and the
// 'prototype' property.
JSProtoKey standardConstructor = constructorFor(holder);
if (standardConstructor != JSProto_Null) {
RootedObject standardProto(cx);
{
JSAutoCompartment ac(cx, target);
if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto))
// Handle the 'prototype' property to make
// xrayedGlobal.StandardClass.prototype work.
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)) {
RootedObject standardProto(cx);
{
JSAutoCompartment ac(cx, target);
if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto))
return false;
MOZ_ASSERT(standardProto);
}
if (!JS_WrapObject(cx, &standardProto))
return false;
MOZ_ASSERT(standardProto);
FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
ObjectValue(*standardProto));
return true;
}
if (ShouldResolveStaticProperties(standardConstructor)) {
const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
MOZ_ASSERT(clasp->spec.defined());
if (!TryResolvePropertyFromSpecs(cx, id, holder,
clasp->spec.constructorFunctions(),
clasp->spec.constructorProperties(), desc)) {
return false;
}
if (desc.object()) {
desc.object().set(wrapper);
return true;
}
}
if (!JS_WrapObject(cx, &standardProto))
return false;
FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
ObjectValue(*standardProto));
return true;
}
}
} else if (IsErrorObjectKey(key)) {
@ -486,18 +626,6 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
return true;
}
// The non-HasPrototypes semantics implemented by traditional Xrays are kind
// of broken with respect to |own|-ness and the holder. The common code
// muddles through by only checking the holder for non-|own| lookups, but
// that doesn't work for us. So we do an explicit holder check here, and hope
// that this mess gets fixed up soon.
if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
return false;
if (desc.object()) {
desc.object().set(wrapper);
return true;
}
// Handle the 'constructor' property.
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) {
RootedObject constructor(cx);
@ -532,80 +660,17 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
const js::Class* clasp = js::GetObjectClass(target);
MOZ_ASSERT(clasp->spec.defined());
// Scan through the functions. Indexed array properties are handled above.
const JSFunctionSpec* fsMatch = nullptr;
for (const JSFunctionSpec* fs = clasp->spec.prototypeFunctions(); fs && fs->name; ++fs) {
if (PropertySpecNameEqualsId(fs->name, id)) {
fsMatch = fs;
break;
}
}
if (fsMatch) {
// Generate an Xrayed version of the method.
RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id));
if (!fun)
return false;
// The generic Xray machinery only defines non-own properties of the target on
// the holder. This is broken, and will be fixed at some point, but for now we
// need to cache the value explicitly. See the corresponding call to
// JS_GetOwnPropertyDescriptorById at the top of this function.
RootedObject funObj(cx, JS_GetFunctionObject(fun));
return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
// Indexed array properties are handled above, so we can just work with the
// class spec here.
if (!TryResolvePropertyFromSpecs(cx, id, holder,
clasp->spec.prototypeFunctions(),
clasp->spec.prototypeProperties(),
desc)) {
return false;
}
// Scan through the properties.
const JSPropertySpec* psMatch = nullptr;
for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) {
if (PropertySpecNameEqualsId(ps->name, id)) {
psMatch = ps;
break;
}
}
if (psMatch) {
desc.value().setUndefined();
RootedFunction getterObj(cx);
RootedFunction setterObj(cx);
unsigned flags = psMatch->flags;
if (psMatch->isSelfHosted()) {
getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0);
if (!getterObj)
return false;
desc.setGetterObject(JS_GetFunctionObject(getterObj));
if (psMatch->setter.selfHosted.funname) {
MOZ_ASSERT(flags & JSPROP_SETTER);
setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0);
if (!setterObj)
return false;
desc.setSetterObject(JS_GetFunctionObject(setterObj));
}
} else {
desc.setGetter(JS_CAST_NATIVE_TO(psMatch->getter.native.op,
JSGetterOp));
desc.setSetter(JS_CAST_NATIVE_TO(psMatch->setter.native.op,
JSSetterOp));
}
desc.setAttributes(flags);
// The generic Xray machinery only defines non-own properties on the holder.
// This is broken, and will be fixed at some point, but for now we need to
// cache the value explicitly. See the corresponding call to
// JS_GetPropertyById at the top of this function.
//
// Note also that the public-facing API here doesn't give us a way to
// pass along JITInfo. It's probably ok though, since Xrays are already
// pretty slow.
return JS_DefinePropertyById(cx, holder, id,
UndefinedHandleValue,
// This particular descriptor, unlike most,
// actually stores JSNatives directly,
// since we just set it up. Do NOT pass
// JSPROP_PROPOP_ACCESSORS here!
desc.attributes(),
JS_PROPERTYOP_GETTER(desc.getter()),
JS_PROPERTYOP_SETTER(desc.setter())) &&
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
if (desc.object()) {
desc.object().set(wrapper);
}
return true;
@ -718,6 +783,33 @@ MaybeAppend(jsid id, unsigned flags, AutoIdVector& props)
return props.append(id);
}
// Append the names from the given function and property specs to props.
static bool
AppendNamesFromFunctionAndPropertySpecs(JSContext* cx,
const JSFunctionSpec* fs,
const JSPropertySpec* ps,
unsigned flags,
AutoIdVector& props)
{
// Convert the method and property names to jsids and pass them to the caller.
for ( ; fs && fs->name; ++fs) {
jsid id;
if (!PropertySpecNameToPermanentId(cx, fs->name, &id))
return false;
if (!MaybeAppend(id, flags, props))
return false;
}
for ( ; ps && ps->name; ++ps) {
jsid id;
if (!PropertySpecNameToPermanentId(cx, ps->name, &id))
return false;
if (!MaybeAppend(id, flags, props))
return false;
}
return true;
}
bool
JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
AutoIdVector& props)
@ -765,10 +857,23 @@ JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags
return false;
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_NAME)))
return false;
// Handle the .prototype property on standard constructors.
if (constructorFor(holder) != JSProto_Null) {
// Handle the .prototype property and static properties on standard
// constructors.
JSProtoKey standardConstructor = constructorFor(holder);
if (standardConstructor != JSProto_Null) {
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)))
return false;
if (ShouldResolveStaticProperties(standardConstructor)) {
const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
MOZ_ASSERT(clasp->spec.defined());
if (!AppendNamesFromFunctionAndPropertySpecs(
cx, clasp->spec.constructorFunctions(),
clasp->spec.constructorProperties(), flags, props)) {
return false;
}
}
}
} else if (IsErrorObjectKey(key)) {
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_FILENAME)) ||
@ -804,23 +909,9 @@ JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags
const js::Class* clasp = js::GetObjectClass(target);
MOZ_ASSERT(clasp->spec.defined());
// Convert the method and property names to jsids and pass them to the caller.
for (const JSFunctionSpec* fs = clasp->spec.prototypeFunctions(); fs && fs->name; ++fs) {
jsid id;
if (!PropertySpecNameToPermanentId(cx, fs->name, &id))
return false;
if (!MaybeAppend(id, flags, props))
return false;
}
for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) {
jsid id;
if (!PropertySpecNameToPermanentId(cx, ps->name, &id))
return false;
if (!MaybeAppend(id, flags, props))
return false;
}
return true;
return AppendNamesFromFunctionAndPropertySpecs(
cx, clasp->spec.prototypeFunctions(),
clasp->spec.prototypeProperties(), flags, props);
}
JSObject*

View File

@ -3346,9 +3346,32 @@ ScrollFrameHelper::DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
displayportBase =
nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
} else {
// Restrict the dirty rect to the scrollport, and make it relative to the
// scrollport for the displayport base.
// Make the displayport base equal to the dirty rect restricted to
// the scrollport and the root composition bounds, relative to the
// scrollport.
displayportBase = aDirtyRect->Intersect(mScrollPort);
const nsPresContext* rootPresContext =
pc->GetToplevelContentDocumentPresContext();
if (!rootPresContext) {
rootPresContext = pc->GetRootPresContext();
}
if (rootPresContext) {
const nsIPresShell* const rootPresShell = rootPresContext->PresShell();
nsIFrame* rootFrame = rootPresShell->GetRootScrollFrame();
if (!rootFrame) {
rootFrame = rootPresShell->GetRootFrame();
}
if (rootFrame) {
nsRect rootCompBounds =
nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootFrame));
nsLayoutUtils::TransformRect(rootFrame, mOuter, rootCompBounds);
displayportBase = displayportBase.Intersect(rootCompBounds);
}
}
displayportBase -= mScrollPort.TopLeft();
}

View File

@ -77,15 +77,15 @@ fuzzy-if(winWidget,1,36) == grid-auto-min-sizing-definite-001.html grid-auto-min
== grid-item-stretch-001.html grid-item-stretch-001-ref.html
== grid-align-content-001.html grid-align-content-001-ref.html
== grid-justify-content-001.html grid-justify-content-001-ref.html
== grid-justify-content-002.html grid-justify-content-002-ref.html
== grid-justify-content-003.html grid-justify-content-003-ref.html
== grid-column-gap-001.html grid-column-gap-001-ref.html
skip-if(Android&&isDebugBuild) == grid-justify-content-002.html grid-justify-content-002-ref.html # Bug 1245884 - slow
skip-if(Android&&isDebugBuild) == grid-justify-content-003.html grid-justify-content-003-ref.html # Bug 1245884 - slow
skip-if(Android&&isDebugBuild) == grid-column-gap-001.html grid-column-gap-001-ref.html # Bug 1245884 - slow
== grid-column-gap-002.html grid-column-gap-002-ref.html
== grid-column-gap-003.html grid-column-gap-003-ref.html
== grid-row-gap-001.html grid-row-gap-001-ref.html
== grid-row-gap-002.html grid-row-gap-002-ref.html
== grid-row-gap-003.html grid-row-gap-003-ref.html
== grid-row-gap-004.html grid-row-gap-004-ref.html
skip-if(Android&&isDebugBuild) == grid-row-gap-002.html grid-row-gap-002-ref.html # Bug 1245884 - slow
skip-if(Android&&isDebugBuild) == grid-row-gap-003.html grid-row-gap-003-ref.html # Bug 1245884 - slow
skip-if(Android&&isDebugBuild) == grid-row-gap-004.html grid-row-gap-004-ref.html # Bug 1245884 - slow
== grid-container-overflow-001.html grid-container-overflow-001-ref.html
== grid-item-margin-left-auto-001.html grid-item-margin-left-auto-001-ref.html
== grid-item-margin-left-auto-002.html grid-item-margin-left-auto-002-ref.html

View File

@ -366,10 +366,6 @@ CSS_PROP_ALIAS(-webkit-mask-image,
mask_image,
WebkitMaskImage,
WEBKIT_PREFIX_PREF)
CSS_PROP_ALIAS(-webkit-mask-mode,
mask_mode,
WebkitMaskMode,
WEBKIT_PREFIX_PREF)
CSS_PROP_ALIAS(-webkit-mask-origin,
mask_origin,
WebkitMaskOrigin,

View File

@ -7250,13 +7250,6 @@ if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) {
alias_for: "mask-image",
subproperties: [ "mask-image" ],
};
gCSSProperties["-webkit-mask-mode"] = {
domProp: "webkitMaskMode",
inherited: false,
type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
alias_for: "mask-mode",
subproperties: [ "mask-mode" ],
};
gCSSProperties["-webkit-mask-origin"] = {
domProp: "webkitMaskOrigin",
inherited: false,

View File

@ -359,6 +359,9 @@ void NrSocket::OnSocketReady(PRFileDesc *fd, int16_t outflags) {
fire_callback(NR_ASYNC_WAIT_READ);
if (outflags & PR_POLL_WRITE & poll_flags())
fire_callback(NR_ASYNC_WAIT_WRITE);
if (outflags & (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP))
// TODO: Bug 946423: how do we notify the upper layers about this?
close();
}
void NrSocket::OnSocketDetached(PRFileDesc *fd) {

View File

@ -490,6 +490,14 @@ static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_compon
if (ctx->turn_servers[j].turn_server.transport != IPPROTO_TCP)
continue;
if (ctx->turn_servers[j].turn_server.type == NR_ICE_STUN_SERVER_TYPE_ADDR &&
nr_transport_addr_cmp(&ctx->turn_servers[j].turn_server.u.addr,
&addrs[i].addr,
NR_TRANSPORT_ADDR_CMP_MODE_VERSION)) {
r_log(LOG_ICE,LOG_INFO,"ICE(%s): Skipping TURN server because of IP version mis-match (%u - %u)",ctx->label,addrs[i].addr.ip_version,ctx->turn_servers[j].turn_server.u.addr.ip_version);
continue;
}
if (!ice_tcp_disabled) {
/* Use TURN server to get srflx candidates */
if(r=nr_ice_candidate_create(ctx,component,

View File

@ -11,7 +11,8 @@
#include "mozilla/Attributes.h" // for MOZ_IMPLICIT
#include "mozilla/Move.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/RefCounted.h"
#include "mozilla/RefPtr.h"
// |Function<Signature>| is a wrapper that can hold any type of callable
// object that can be invoked in a way that's compatible with |Signature|.
@ -40,9 +41,11 @@ namespace mozilla {
namespace detail {
template<typename ReturnType, typename... Arguments>
class FunctionImplBase
class FunctionImplBase : public mozilla::RefCounted<FunctionImplBase<ReturnType, Arguments...>>
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FunctionImplBase)
virtual ~FunctionImplBase() {}
virtual ReturnType call(Arguments... aArguments) = 0;
};
@ -137,7 +140,12 @@ public:
// This constructor is implicit to match the interface of |std::function|.
template <typename Callable>
MOZ_IMPLICIT Function(const Callable& aCallable)
: mImpl(MakeUnique<detail::FunctionImpl<Callable, ReturnType, Arguments...>>(aCallable))
: mImpl(new detail::FunctionImpl<Callable, ReturnType, Arguments...>(aCallable))
{}
MOZ_IMPLICIT Function(const Function& aFunction)
: mImpl(aFunction.mImpl)
{}
MOZ_IMPLICIT Function(decltype(nullptr))
{}
// Move constructor and move assingment operator.
@ -151,7 +159,17 @@ public:
template <typename Callable>
Function& operator=(const Callable& aCallable)
{
mImpl = MakeUnique<detail::FunctionImpl<Callable, ReturnType, Arguments...>>(aCallable);
mImpl = new detail::FunctionImpl<Callable, ReturnType, Arguments...>(aCallable);
return *this;
}
Function& operator=(const Function& aFunction)
{
mImpl = aFunction.mImpl;
return *this;
}
Function& operator=(decltype(nullptr))
{
mImpl = nullptr;
return *this;
}
@ -161,9 +179,15 @@ public:
MOZ_ASSERT(mImpl);
return mImpl->call(Forward<Args>(aArguments)...);
}
explicit operator bool() const
{
return bool(mImpl);
}
private:
// TODO: Consider implementing a small object optimization.
UniquePtr<detail::FunctionImplBase<ReturnType, Arguments...>> mImpl;
RefPtr<detail::FunctionImplBase<ReturnType, Arguments...>> mImpl;
};
} // namespace mozilla

View File

@ -564,7 +564,7 @@ nsIOService::GetProtocolHandler(const char* scheme, nsIProtocolHandler* *result)
NS_IMETHODIMP
nsIOService::ExtractScheme(const nsACString &inURI, nsACString &scheme)
{
return net_ExtractURLScheme(inURI, nullptr, nullptr, &scheme);
return net_ExtractURLScheme(inURI, scheme);
}
NS_IMETHODIMP

View File

@ -2867,20 +2867,8 @@ nsStandardURL::Init(uint32_t urlType,
mOriginCharset = charset;
}
if (baseURI) {
uint32_t start, end;
// pull out the scheme and where it ends
nsresult rv = net_ExtractURLScheme(spec, &start, &end, nullptr);
if (NS_SUCCEEDED(rv) && spec.Length() > end+2) {
nsACString::const_iterator slash;
spec.BeginReading(slash);
slash.advance(end+1);
// then check if // follows
// if it follows, aSpec is really absolute ...
// ignore aBaseURI in this case
if (*slash == '/' && *(++slash) == '/')
baseURI = nullptr;
}
if (baseURI && net_IsAbsoluteURL(spec)) {
baseURI = nullptr;
}
if (!baseURI)

View File

@ -14,6 +14,7 @@
#include "nsNetCID.h"
#include "mozilla/Preferences.h"
#include "prnetdb.h"
#include "mozilla/Tokenizer.h"
using namespace mozilla;
@ -179,12 +180,12 @@ net_ParseFileURL(const nsACString &inURL,
const nsPromiseFlatCString &flatURL = PromiseFlatCString(inURL);
const char *url = flatURL.get();
uint32_t schemeBeg, schemeEnd;
rv = net_ExtractURLScheme(flatURL, &schemeBeg, &schemeEnd, nullptr);
nsAutoCString scheme;
rv = net_ExtractURLScheme(flatURL, scheme);
if (NS_FAILED(rv)) return rv;
if (strncmp(url + schemeBeg, "file", schemeEnd - schemeBeg) != 0) {
if (!scheme.EqualsLiteral("file")) {
NS_ERROR("must be a file:// url");
return NS_ERROR_UNEXPECTED;
}
@ -483,57 +484,55 @@ net_ResolveRelativePath(const nsACString &relativePath,
// scheme fu
//----------------------------------------------------------------------------
#if !defined(MOZILLA_XPCOMRT_API)
static bool isAsciiAlpha(char c) {
return nsCRT::IsAsciiAlpha(c);
}
static bool
net_IsValidSchemeChar(const char aChar)
{
if (nsCRT::IsAsciiAlpha(aChar) || nsCRT::IsAsciiDigit(aChar) ||
aChar == '+' || aChar == '.' || aChar == '-') {
return true;
}
return false;
}
#endif
/* Extract URI-Scheme if possible */
nsresult
net_ExtractURLScheme(const nsACString &inURI,
uint32_t *startPos,
uint32_t *endPos,
nsACString *scheme)
nsACString& scheme)
{
// search for something up to a colon, and call it the scheme
const nsPromiseFlatCString &flatURI = PromiseFlatCString(inURI);
const char* uri_start = flatURI.get();
const char* uri = uri_start;
#if defined(MOZILLA_XPCOMRT_API)
NS_WARNING("net_ExtractURLScheme not implemented");
return NS_ERROR_NOT_IMPLEMENTED;
#else
Tokenizer p(inURI, "\r\n\t");
if (!uri)
while (p.CheckWhite() || p.CheckChar(' ')) {
// Skip leading whitespace
}
p.Record();
if (!p.CheckChar(isAsciiAlpha)) {
// First char must be alpha
return NS_ERROR_MALFORMED_URI;
// skip leading white space
while (nsCRT::IsAsciiSpace(*uri))
uri++;
uint32_t start = uri - uri_start;
if (startPos) {
*startPos = start;
}
uint32_t length = 0;
char c;
while ((c = *uri++) != '\0') {
// First char must be Alpha
if (length == 0 && nsCRT::IsAsciiAlpha(c)) {
length++;
}
// Next chars can be alpha + digit + some special chars
else if (length > 0 && (nsCRT::IsAsciiAlpha(c) ||
nsCRT::IsAsciiDigit(c) || c == '+' ||
c == '.' || c == '-')) {
length++;
}
// stop if colon reached but not as first char
else if (c == ':' && length > 0) {
if (endPos) {
*endPos = start + length;
}
if (scheme)
scheme->Assign(Substring(inURI, start, length));
return NS_OK;
}
else
break;
while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
// Skip valid scheme characters or \r\n\t
}
return NS_ERROR_MALFORMED_URI;
if (!p.CheckChar(':')) {
return NS_ERROR_MALFORMED_URI;
}
p.Claim(scheme);
scheme.StripChars("\r\n\t");
return NS_OK;
#endif
}
bool
@ -556,87 +555,73 @@ net_IsValidScheme(const char *scheme, uint32_t schemeLen)
return true;
}
bool
net_IsAbsoluteURL(const nsACString& uri)
{
#if !defined(MOZILLA_XPCOMRT_API)
Tokenizer p(uri, "\r\n\t");
while (p.CheckWhite() || p.CheckChar(' ')) {
// Skip leading whitespace
}
// First char must be alpha
if (!p.CheckChar(isAsciiAlpha)) {
return false;
}
while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
// Skip valid scheme characters or \r\n\t
}
if (!p.CheckChar(':')) {
return false;
}
p.SkipWhites();
if (!p.CheckChar('/')) {
return false;
}
p.SkipWhites();
if (p.CheckChar('/')) {
// aSpec is really absolute. Ignore aBaseURI in this case
return true;
}
#endif
return false;
}
bool
net_FilterURIString(const char *str, nsACString& result)
{
NS_PRECONDITION(str, "Must have a non-null string!");
bool writing = false;
result.Truncate();
const char *p = str;
// Remove leading spaces, tabs, CR, LF if any.
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
writing = true;
str = p + 1;
p++;
}
// Don't strip from the scheme, because other code assumes everything
// up to the ':' is the scheme, and it's bad not to have it match.
// If there's no ':', strip.
bool found_colon = false;
const char *first = nullptr;
// Figure out if we need to filter anything.
bool writing = false;
while (*p) {
switch (*p) {
case '\t':
case '\r':
case '\n':
if (found_colon) {
writing = true;
// append chars up to but not including *p
if (p > str)
result.Append(str, p - str);
str = p + 1;
} else {
// remember where the first \t\r\n was in case we find no scheme
if (!first)
first = p;
}
break;
case ':':
found_colon = true;
break;
case '/':
case '@':
if (!found_colon) {
// colon also has to precede / or @ to be a scheme
found_colon = true; // not really, but means ok to strip
if (first) {
// go back and replace
p = first;
continue; // process *p again
}
}
break;
default:
break;
if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
writing = true;
break;
}
p++;
// At end, if there was no scheme, and we hit a control char, fix
// it up now.
if (!*p && first != nullptr && !found_colon) {
// TRICKY - to avoid duplicating code, we reset the loop back
// to the point we found something to do
p = first;
// This also stops us from looping after we finish
found_colon = true; // so we'll replace \t\r\n
}
}
// Remove trailing spaces if any
while (((p-1) >= str) && (*(p-1) == ' ')) {
writing = true;
p--;
if (!writing) {
// Nothing to strip or filter
return false;
}
if (writing && p > str)
result.Append(str, p - str);
nsAutoCString temp;
return writing;
temp.Assign(str);
temp.Trim("\r\n\t ");
temp.StripChars("\r\n\t");
result.Assign(temp);
return true;
}
#if defined(XP_WIN)

View File

@ -79,18 +79,22 @@ nsresult net_ResolveRelativePath(const nsACString &relativePath,
const nsACString &basePath,
nsACString &result);
/**
* Check if a URL is absolute
*
* @param inURL URL spec
* @return true if the given spec represents an absolute URL
*/
bool net_IsAbsoluteURL(const nsACString& inURL);
/**
* Extract URI-Scheme if possible
*
* @param inURI URI spec
* @param startPos start of scheme (may be null)
* @param endPos end of scheme; index of colon (may be null)
* @param scheme scheme copied to this buffer on return (may be null)
*/
nsresult net_ExtractURLScheme(const nsACString &inURI,
uint32_t *startPos,
uint32_t *endPos,
nsACString *scheme = nullptr);
nsACString &scheme);
/* check that the given scheme conforms to RFC 2396 */
bool net_IsValidScheme(const char *scheme, uint32_t schemeLen);
@ -109,8 +113,7 @@ inline bool net_IsValidScheme(const nsAFlatCString &scheme)
* This function strips out all whitespace at the beginning and end of the URL
* and strips out \r, \n, \t from the middle of the URL. This makes it safe to
* call on things like javascript: urls or data: urls, where we may in fact run
* into whitespace that is not properly encoded. Note that stripping does not
* occur in the scheme portion itself.
* into whitespace that is not properly encoded.
*
* @param str the pointer to the string to filter. Must be non-null.
* @param result the out param to write to if filtering happens

View File

@ -62,17 +62,21 @@ nsBaseURLParser::ParseURL(const char *spec, int32_t specLen,
const char *stop = nullptr;
const char *colon = nullptr;
const char *slash = nullptr;
const char *p;
const char *p = spec;
uint32_t offset = 0;
int32_t len = specLen;
for (p = spec; len && *p && !colon && !slash; ++p, --len) {
// skip leading whitespace
if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') {
spec++;
specLen--;
offset++;
continue;
}
// skip leading whitespace
while (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') {
spec++;
specLen--;
offset++;
p++;
len--;
}
for (; len && *p && !colon && !slash; ++p, --len) {
switch (*p) {
case ':':
if (!colon)

View File

@ -344,7 +344,7 @@ nsresult
Http2Stream::MakeOriginURL(const nsACString &origin, RefPtr<nsStandardURL> &url)
{
nsAutoCString scheme;
nsresult rv = net_ExtractURLScheme(origin, nullptr, nullptr, &scheme);
nsresult rv = net_ExtractURLScheme(origin, scheme);
NS_ENSURE_SUCCESS(rv, rv);
return MakeOriginURL(scheme, origin, url);
}

View File

@ -2102,6 +2102,13 @@ SpdySession31::WriteSegments(nsAHttpSegmentWriter *writer,
SpdyStream31 *stream = mInputFrameDataStream;
mSegmentWriter = writer;
rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
bool channelPipeFull = false;
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
LOG3(("SpdySession31::WriteSegments session=%p stream=%p 0x%X "
"stream channel pipe full\n",
this, stream, stream ? stream->StreamID() : 0));
channelPipeFull = mInputFrameDataStream->ChannelPipeFull();
}
mSegmentWriter = nullptr;
mLastDataReadEpoch = mLastReadEpoch;
@ -2137,10 +2144,13 @@ SpdySession31::WriteSegments(nsAHttpSegmentWriter *writer,
}
if (NS_FAILED(rv)) {
LOG3(("SpdySession31 %p data frame read failure %x\n", this, rv));
LOG3(("SpdySession31::WriteSegments session=%p stream=%p 0x%X "
"data frame read failure %x pipefull=%d\n",
this, stream, stream ? stream->StreamID() : 0, rv, channelPipeFull));
// maybe just blocked reading from network
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
if ((rv == NS_BASE_STREAM_WOULD_BLOCK) && !channelPipeFull) {
rv = NS_OK;
}
}
return rv;

View File

@ -229,6 +229,13 @@ SpdyStream31::WriteSegments(nsAHttpSegmentWriter *writer,
return rv;
}
bool
SpdyStream31::ChannelPipeFull()
{
nsHttpTransaction *trans = mTransaction ? mTransaction->QueryHttpTransaction() : nullptr;
return trans ? trans->ChannelPipeFull() : false;
}
void
SpdyStream31::CreatePushHashKey(const nsCString &scheme,
const nsCString &hostHeader,

View File

@ -88,6 +88,7 @@ public:
int64_t LocalWindow() { return mLocalWindow; }
bool BlockedOnRwin() { return mBlockedOnRwin; }
bool ChannelPipeFull();
// A pull stream has an implicit sink, a pushed stream has a sink
// once it is matched to a pull stream.

View File

@ -1765,23 +1765,27 @@ nsHttpConnection::OnSocketReadable()
break;
}
mSocketInCondition = NS_OK;
rv = mTransaction->WriteSegments(this, nsIOService::gDefaultSegmentSize, &n);
LOG(("nsHttpConnection::OnSocketReadable %p trans->ws rv=%x n=%d socketin=%x\n",
this, rv, n, mSocketInCondition));
if (NS_FAILED(rv)) {
// if the transaction didn't want to take any more data, then
// wait for the transaction to call ResumeRecv.
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
rv = NS_OK;
}
again = false;
}
else {
} else {
mCurrentBytesRead += n;
mTotalBytesRead += n;
if (NS_FAILED(mSocketInCondition)) {
// continue waiting for the socket if necessary...
if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK)
if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK) {
rv = ResumeRecv();
else
} else {
rv = mSocketInCondition;
}
again = false;
}
}

View File

@ -127,6 +127,7 @@ nsHttpTransaction::nsHttpTransaction()
, mContentDecoding(false)
, mContentDecodingCheck(false)
, mDeferredSendProgress(false)
, mWaitingOnPipeOut(false)
, mReportedStart(false)
, mReportedResponseHeader(false)
, mForTakeResponseHead(nullptr)
@ -832,9 +833,10 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
nsCOMPtr<nsIEventTarget> target;
gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
if (target)
if (target) {
mPipeOut->AsyncWait(this, 0, 0, target);
else {
mWaitingOnPipeOut = true;
} else {
NS_ERROR("no socket thread event target");
rv = NS_ERROR_UNEXPECTED;
}
@ -2179,6 +2181,7 @@ NS_IMPL_QUERY_INTERFACE(nsHttpTransaction,
NS_IMETHODIMP
nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (mConnection) {
mConnection->TransactionHasDataToWrite(this);
nsresult rv = mConnection->ResumeSend();
@ -2196,6 +2199,8 @@ nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
NS_IMETHODIMP
nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
mWaitingOnPipeOut = false;
if (mConnection) {
mConnection->TransactionHasDataToRecv(this);
nsresult rv = mConnection->ResumeRecv();

View File

@ -143,6 +143,7 @@ public:
}
void SetPushedStream(Http2PushedStream *push) { mPushedStream = push; }
uint32_t InitialRwin() const { return mInitialRwin; };
bool ChannelPipeFull() { return mWaitingOnPipeOut; }
// Locked methods to get and set timing info
const TimingStruct Timings();
@ -306,6 +307,7 @@ private:
bool mContentDecoding;
bool mContentDecodingCheck;
bool mDeferredSendProgress;
bool mWaitingOnPipeOut;
// mClosed := transaction has been explicitly closed
// mTransactionDone := transaction ran to completion or was interrupted

View File

@ -54,7 +54,7 @@ SubstitutingURL::EnsureFile()
return rv;
nsAutoCString scheme;
rv = net_ExtractURLScheme(spec, nullptr, nullptr, &scheme);
rv = net_ExtractURLScheme(spec, scheme);
if (NS_FAILED(rv))
return rv;

View File

@ -760,8 +760,10 @@ nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
// for some protocols, we expect the location to be absolute.
// if so, and if the location indeed appears to be a valid URI, then go
// ahead and treat it like one.
nsAutoCString scheme;
if (mExpectAbsLoc &&
NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) {
NS_SUCCEEDED(net_ExtractURLScheme(loc, scheme))) {
// escape as absolute
escFlags = esc_Forced | esc_AlwaysCopy | esc_Minimal;
}

View File

@ -21,6 +21,7 @@ var md5s = ['f1b708bba17f1ce948dc979f4d7092bc',
var bigListenerData = generateContent(128 * 1024);
var bigListenerMD5 = '8f607cfdd2c87d6a7eedb657dafbd836';
var hugeListenerData = generateContent(800 * 1024);
function checkIsSpdy(request) {
try {
@ -151,9 +152,6 @@ SpdyBigListener.prototype.onDataAvailable = function(request, ctx, stream, off,
this.onDataAvailableFired = true;
this.isSpdyConnection = checkIsSpdy(request);
this.buffer = this.buffer.concat(read_stream(stream, cnt));
// We know the server should send us the same data as our big post will be,
// so the md5 should be the same
do_check_eq(bigListenerMD5, request.getResponseHeader("X-Expected-MD5"));
};
SpdyBigListener.prototype.onStopRequest = function(request, ctx, status) {
@ -162,7 +160,14 @@ SpdyBigListener.prototype.onStopRequest = function(request, ctx, status) {
do_check_true(this.isSpdyConnection);
// Don't want to flood output, so don't use do_check_eq
do_check_true(this.buffer == bigListenerData);
if (request.originalURI.spec == "https://localhost:" + serverPort + "/big") {
// We know the server should send us the same data as our big post will be,
// so the md5 should be the same
do_check_eq(bigListenerMD5, request.getResponseHeader("X-Expected-MD5"));
do_check_true(this.buffer == bigListenerData);
} else {
do_check_true(this.buffer == hugeListenerData);
}
run_next_test();
do_test_finished();
@ -298,6 +303,15 @@ function test_spdy_big() {
chan.asyncOpen2(listener);
}
// Make sure we handle big GETs with suspend/resume
function test_spdy_big_and_slow() {
var chan = makeChan("https://localhost:" + serverPort + "/huge");
var listener = new SpdyBigListener();
chan.asyncOpen2(listener);
chan.suspend();
do_timeout(750, chan.resume);
}
// Support for doing a POST
function do_post(content, chan, listener) {
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
@ -348,6 +362,7 @@ var tests = [ test_spdy_post_big
, test_spdy_header
, test_spdy_multiplex
, test_spdy_big
, test_spdy_big_and_slow
, test_spdy_post
// cleanup
, test_complete

View File

@ -300,3 +300,10 @@ add_test(function test_hugeStringThrows()
run_next_test();
});
add_test(function test_filterWhitespace()
{
var url = stringToURL(" \r\n\th\nt\rt\tp://ex\r\n\tample.com/path\r\n\t/\r\n\tto the/fil\r\n\te.e\r\n\txt?que\r\n\try#ha\r\n\tsh \r\n\t ");
do_check_eq(url.spec, "http://example.com/path/to%20the/file.ext?query#hash");
run_next_test();
});

View File

@ -54,9 +54,8 @@ class CentOSBootstrapper(BaseBootstrapper):
self.dnf_groupinstall(*self.browser_group_packages)
self.dnf_install(*self.browser_packages)
kern = platform.uname()
yasm = 'http://pkgs.repoforge.org/yasm/yasm-1.1.0-1.el6.rf.i686.rpm'
if 'x86_64' in kern[2]:
if platform.architecture()[0] == '64bit':
yasm = 'http://pkgs.repoforge.org/yasm/yasm-1.1.0-1.el6.rf.x86_64.rpm'
self.run_as_root(['rpm', '-ivh', yasm])

View File

@ -729,7 +729,6 @@ class TreeMetadataEmitter(LoggingMixin):
'ANDROID_GENERATED_RESFILES',
'DISABLE_STL_WRAPPING',
'EXTRA_DSO_LDOPTS',
'USE_STATIC_LIBS',
'PYTHON_UNIT_TESTS',
'RCFILE',
'RESFILE',
@ -769,6 +768,21 @@ class TreeMetadataEmitter(LoggingMixin):
elif dist_install is False:
passthru.variables['NO_DIST_INSTALL'] = True
# Ideally, this should be done in templates, but this is difficult at
# the moment because USE_STATIC_LIBS can be set after a template
# returns. Eventually, with context-based templates, it will be
# possible.
if (context.config.substs.get('OS_ARCH') == 'WINNT' and
not context.config.substs.get('GNU_CC')):
use_static_lib = (context.get('USE_STATIC_LIBS') and
not context.config.substs.get('MOZ_ASAN'))
rtl_flag = '-MT' if use_static_lib else '-MD'
if (context.config.substs.get('MOZ_DEBUG') and
not context.config.substs.get('MOZ_NO_DEBUG_RTL')):
rtl_flag += 'd'
# Use a list, like MOZBUILD_*FLAGS variables
passthru.variables['RTL_FLAGS'] = [rtl_flag]
generated_files = set()
for obj in self._process_generated_files(context):
generated_files.add(obj.output)

View File

@ -11,8 +11,6 @@ RESFILE = 'bar.res'
RCINCLUDE = 'bar.rc'
DEFFILE = 'baz.def'
USE_STATIC_LIBS = True
CFLAGS += ['-fno-exceptions', '-w']
CXXFLAGS += ['-fcxx-exceptions', '-option with spaces']
LDFLAGS += ['-ld flag with spaces', '-x']

View File

@ -292,9 +292,6 @@ class TestRecursiveMakeBackend(BackendTester):
'DEFFILE': [
'DEFFILE := baz.def',
],
'USE_STATIC_LIBS': [
'USE_STATIC_LIBS := 1',
],
'MOZBUILD_CFLAGS': [
'MOZBUILD_CFLAGS += -fno-exceptions',
'MOZBUILD_CFLAGS += -w',

View File

@ -13,8 +13,6 @@ RESFILE = 'bar.res'
RCINCLUDE = 'bar.rc'
DEFFILE = 'baz.def'
USE_STATIC_LIBS = True
CFLAGS += ['-fno-exceptions', '-w']
CXXFLAGS += ['-fcxx-exceptions', '-include foo.h']
LDFLAGS += ['-framework Foo', '-x']

View File

@ -178,7 +178,6 @@ class TestEmitterBasic(unittest.TestCase):
'RESFILE': 'bar.res',
'RCINCLUDE': 'bar.rc',
'DEFFILE': 'baz.def',
'USE_STATIC_LIBS': True,
'MOZBUILD_CFLAGS': ['-fno-exceptions', '-w'],
'MOZBUILD_CXXFLAGS': ['-fcxx-exceptions', '-include foo.h'],
'MOZBUILD_LDFLAGS': ['-framework Foo', '-x', '-DELAYLOAD:foo.dll',

View File

@ -155,6 +155,8 @@ function handleRequest(req, res) {
hash.update(content);
var md5 = hash.digest('hex');
res.setHeader("X-Expected-MD5", md5);
} else if (u.pathname == "/huge") {
content = getHugeContent(800 * 1024);
} else if (u.pathname == "/post") {
if (req.method != "POST") {
res.writeHead(405);

View File

@ -688,53 +688,6 @@ var AddonManagerInternal = {
// Store telemetry details per addon provider
telemetryDetails: {},
// A read-only wrapper around the types dictionary
typesProxy: Proxy.create({
getOwnPropertyDescriptor: function(aName) {
if (!(aName in AddonManagerInternal.types))
return undefined;
return {
value: AddonManagerInternal.types[aName].type,
writable: false,
configurable: false,
enumerable: true
}
},
getPropertyDescriptor: function(aName) {
return this.getOwnPropertyDescriptor(aName);
},
getOwnPropertyNames: function() {
return Object.keys(AddonManagerInternal.types);
},
getPropertyNames: function() {
return this.getOwnPropertyNames();
},
delete: function(aName) {
// Not allowed to delete properties
return false;
},
defineProperty: function(aName, aProperty) {
// Ignore attempts to define properties
},
fix: function(){
return undefined;
},
// Despite MDC's claims to the contrary, it is required that this trap
// be defined
enumerate: function() {
// All properties are enumerable
return this.getPropertyNames();
}
}),
recordTimestamp: function(name, value) {
this.TelemetryTimestamps.add(name, value);
},
@ -2697,7 +2650,53 @@ var AddonManagerInternal = {
},
get addonTypes() {
return this.typesProxy;
// A read-only wrapper around the types dictionary
return new Proxy(this.types, {
defineProperty(target, property, descriptor) {
// Not allowed to define properties
return false;
},
deleteProperty(target, property) {
// Not allowed to delete properties
return false;
},
get(target, property, receiver) {
if (!target.hasOwnProperty(property))
return undefined;
return target[property].type;
},
getOwnPropertyDescriptor(target, property) {
if (!target.hasOwnProperty(property))
return undefined;
return {
value: target[property].type,
writable: false,
// Claim configurability to maintain the proxy invariants.
configurable: true,
enumerable: true
}
},
preventExtensions(target) {
// Not allowed to prevent adding new properties
return false;
},
set(target, property, value, receiver) {
// Not allowed to set properties
return false;
},
setPrototypeOf(target, prototype) {
// Not allowed to change prototype
return false;
}
});
},
get autoUpdateDefault() {

View File

@ -347,7 +347,9 @@ nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue)
return NS_OK;
}
RefPtr<File> domFile = File::CreateFromFile(mParent, localFile);
auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
RefPtr<File> domFile = File::CreateFromFile(innerParent, localFile);
domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder);
nsCOMPtr<nsIDOMBlob>(domFile).forget(aValue);
return NS_OK;

View File

@ -260,12 +260,12 @@ Tokenizer::Parse(Token& aToken) const
state = PARSE_WORD;
} else if (IsNumber(*next)) {
state = PARSE_INTEGER;
} else if (strchr(mWhitespaces, *next)) { // not UTF-8 friendly?
state = PARSE_WS;
} else if (*next == '\r') {
state = PARSE_CRLF;
} else if (*next == '\n') {
state = PARSE_LF;
} else if (strchr(mWhitespaces, *next)) { // not UTF-8 friendly?
state = PARSE_WS;
} else {
state = PARSE_CHAR;
}

View File

@ -526,6 +526,21 @@ TEST(Tokenizer, SkipWhites)
EXPECT_TRUE(p.CheckEOF());
}
TEST(Tokenizer, SkipCustomWhites)
{
Tokenizer p("Text1 \n\r\t.Text2 \n\r\t.", " \n\r\t.");
EXPECT_TRUE(p.CheckWord("Text1"));
p.SkipWhites();
EXPECT_TRUE(p.CheckWord("Text2"));
EXPECT_TRUE(p.CheckWhite());
EXPECT_TRUE(p.CheckWhite());
EXPECT_TRUE(p.CheckWhite());
EXPECT_TRUE(p.CheckWhite());
EXPECT_TRUE(p.CheckWhite());
EXPECT_TRUE(p.CheckEOF());
}
TEST(Tokenizer, IntegerReading)
{
#define INT_6_BITS 64U