mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to f-t
This commit is contained in:
commit
80c8b4cc56
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -306,7 +306,7 @@ private:
|
||||
|
||||
class nsGlobalWindow : public mozilla::dom::EventTarget,
|
||||
public nsPIDOMWindow<nsISupports>,
|
||||
public nsIDOMWindowInternal,
|
||||
private nsIDOMWindowInternal,
|
||||
public nsIScriptGlobalObject,
|
||||
public nsIScriptObjectPrincipal,
|
||||
public nsSupportsWeakReference,
|
||||
|
@ -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,
|
||||
|
@ -2001,7 +2001,7 @@ void
|
||||
Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
||||
PromiseCallback* aRejectCallback)
|
||||
{
|
||||
if (mGlobal->IsDying()) {
|
||||
if (!mGlobal || mGlobal->IsDying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
14
dom/svg/crashtests/1244898-1.xhtml
Normal file
14
dom/svg/crashtests/1244898-1.xhtml
Normal 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>
|
@ -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
|
||||
|
21
editor/libeditor/crashtests/1244894.xhtml
Normal file
21
editor/libeditor/crashtests/1244894.xhtml
Normal 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>
|
@ -66,3 +66,4 @@ needs-focus load 1128787.html
|
||||
load 1134545.html
|
||||
load 1158452.html
|
||||
load 1158651.html
|
||||
load 1244894.xhtml
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -123,6 +123,7 @@ BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const
|
||||
group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
|
||||
return group;
|
||||
}
|
||||
aContext->Restore();
|
||||
}
|
||||
|
||||
Matrix maskTransform;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 };
|
||||
|
@ -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);
|
||||
|
5
js/src/jit-test/tests/gc/bug-1245520.js
Normal file
5
js/src/jit-test/tests/gc/bug-1245520.js
Normal file
@ -0,0 +1,5 @@
|
||||
if (!('oomTest' in this))
|
||||
quit();
|
||||
|
||||
var t = {};
|
||||
oomTest(() => serialize(t));
|
27
js/src/jit-test/tests/gc/elements-post-write-barrier.js
Normal file
27
js/src/jit-test/tests/gc/elements-post-write-barrier.js
Normal 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();
|
5
js/src/jit-test/tests/gc/gczeal-range.js
Normal file
5
js/src/jit-test/tests/gc/gczeal-range.js
Normal file
@ -0,0 +1,5 @@
|
||||
try {
|
||||
gczeal(123);
|
||||
} catch(e) {
|
||||
assertEq(e.toString().includes("out of range"), true);
|
||||
}
|
15
js/src/jit-test/tests/modules/bug-1245518.js
Normal file
15
js/src/jit-test/tests/modules/bug-1245518.js
Normal 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();
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -53,7 +53,7 @@ class CompileRuntime
|
||||
const void* addressOfLastCachedNativeIterator();
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
const void* addressOfGCZeal();
|
||||
const void* addressOfGCZealModeBits();
|
||||
#endif
|
||||
|
||||
const void* addressOfInterruptUint32();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
@ -159,6 +159,7 @@ namespace jit {
|
||||
_(TypeBarrier) \
|
||||
_(MonitorTypes) \
|
||||
_(PostWriteBarrier) \
|
||||
_(PostWriteElementBarrier) \
|
||||
_(GetPropertyCache) \
|
||||
_(GetPropertyPolymorphic) \
|
||||
_(SetPropertyPolymorphic) \
|
||||
|
@ -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.
|
||||
|
@ -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> >) \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -226,6 +226,8 @@
|
||||
_(MonitorTypes) \
|
||||
_(PostWriteBarrierO) \
|
||||
_(PostWriteBarrierV) \
|
||||
_(PostWriteElementBarrierO) \
|
||||
_(PostWriteElementBarrierV) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(UnboxedArrayLength) \
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"]) {
|
||||
|
@ -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*
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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])
|
||||
|
@ -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)
|
||||
|
@ -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']
|
||||
|
@ -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',
|
||||
|
@ -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']
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user