mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1238859: ARM: MoveEmitter should only copying float32 instead of double during spilling, r=nbp (13c87beccb) - Bug 1233343: SharedStubs: Count frames pushed correctly on ARM when entering an ion stub frame, r=jandem (c2a1050f11) - Bug 1249493 - IonMonkey: MIPS: Fix crash after enable shared stubs. r=h4writer (a823579cae) - Bug 1239754 - Remove HashKeyRef now that all of its users are using stable hashing; r=jonco (d08fb10121) - Bug 1245485 - Only decommit newly unused Nursery Chunks; r=sfink (611cfa4d14) - Bug 1242691 - Merge overlapping SlotsEdges in the store buffer. r=terrence (a31875df9d) - Bug 1242072 - Continue using getPropertyDescriptor for get in ScriptedIndirectProxyHandler. r=jorendorff (e42f5a2ee2) - Bug 1235410 - Centralize StmtType enumeration in a higher-order macro, so as not to have types and string descriptions of them go out of sync. As they happen to be now. #_# r=arai (91c3b6497f) - Bug 1243793 - Fix handling of labels when emitting hoisted function definitions. (r=jorendorff) (34a36e7f5a) - Bug 1111386 - Support nested rest in destructuring assignment; r=jorendorff (aab810e579) - Bug 1247789: Fix comment for js::Fifo DONTBUILD r=fitzgen (e6947b44ae) - Bug 1231965 - Change references to ./mach build-docs to ./mach doc. r=gps (fbcfdedeef) - Bug 1247666 - Do not require all functions to have a PEdgeCallInstance, r=jonco (e06fbf58c8) - Bug 1247666 - Correctly test isSuppressConstructor, r=jonco (9ef1760f0f) - Bug 1247813 - Add a very specific annotation for paramBuffer usage to avoid changing a whole bunch of platform-specific code just to avoid a false positive, r=terrence (b3d1918239) - Bug 1249448 - Handled unified (C4) constructors, r=terrence (3b1b1a8060) - Bug 1237230 - Fix ctypes MOZ_WARN_UNUSED_RESULT warnings for Vector methods. r=jorendorff (fd7d0d8675) - minor update (e0a035fd89)
This commit is contained in:
@@ -2715,7 +2715,8 @@ IntegerToString(IntegerType i, int radix, mozilla::Vector<CharType, N, AP>& resu
|
||||
*--cp = '-';
|
||||
|
||||
MOZ_ASSERT(cp >= buffer);
|
||||
result.append(cp, end);
|
||||
if (!result.append(cp, end))
|
||||
return;
|
||||
}
|
||||
|
||||
template<class CharType>
|
||||
@@ -3766,12 +3767,10 @@ BuildDataSource(JSContext* cx,
|
||||
double fp = *static_cast<type*>(data); \
|
||||
ToCStringBuf cbuf; \
|
||||
char* str = NumberToCString(cx, &cbuf, fp); \
|
||||
if (!str) { \
|
||||
if (!str || !result.append(str, strlen(str))) { \
|
||||
JS_ReportOutOfMemory(cx); \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
result.append(str, strlen(str)); \
|
||||
break; \
|
||||
}
|
||||
CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
|
||||
|
||||
@@ -77,7 +77,8 @@ template <class T, size_t N, size_t M, class AP>
|
||||
void
|
||||
AppendString(mozilla::Vector<T, N, AP>& v, mozilla::Vector<T, M, AP>& w)
|
||||
{
|
||||
v.append(w.begin(), w.length());
|
||||
if (!v.append(w.begin(), w.length()))
|
||||
return;
|
||||
}
|
||||
|
||||
template <size_t N, class AP>
|
||||
@@ -89,10 +90,13 @@ AppendString(mozilla::Vector<char16_t, N, AP>& v, JSString* str)
|
||||
if (!linear)
|
||||
return;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
if (linear->hasLatin1Chars())
|
||||
v.append(linear->latin1Chars(nogc), linear->length());
|
||||
else
|
||||
v.append(linear->twoByteChars(nogc), linear->length());
|
||||
if (linear->hasLatin1Chars()) {
|
||||
if (!v.append(linear->latin1Chars(nogc), linear->length()))
|
||||
return;
|
||||
} else {
|
||||
if (!v.append(linear->twoByteChars(nogc), linear->length()))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N, class AP>
|
||||
|
||||
@@ -37,7 +37,7 @@ function isMatchingDestructor(constructor, edge)
|
||||
return false;
|
||||
var variable = callee.Variable;
|
||||
assert(variable.Kind == "Func");
|
||||
if (!/::~/.test(variable.Name[0]))
|
||||
if (variable.Name[1].charAt(0) != '~')
|
||||
return false;
|
||||
|
||||
var constructExp = constructor.PEdgeCallInstance.Exp;
|
||||
@@ -50,8 +50,11 @@ function isMatchingDestructor(constructor, edge)
|
||||
return sameVariable(constructExp.Variable, destructExp.Variable);
|
||||
}
|
||||
|
||||
// Return all calls within the RAII scope of the constructor matched by
|
||||
// isConstructor()
|
||||
// Return all calls within the RAII scope of any constructor matched by
|
||||
// isConstructor(). (Note that this would be insufficient if you needed to
|
||||
// treat each instance separately, such as when different regions of a function
|
||||
// body were guarded by these constructors and you needed to do something
|
||||
// different with each.)
|
||||
function allRAIIGuardedCallPoints(body, isConstructor)
|
||||
{
|
||||
if (!("PEdge" in body))
|
||||
@@ -67,7 +70,9 @@ function allRAIIGuardedCallPoints(body, isConstructor)
|
||||
continue;
|
||||
var variable = callee.Variable;
|
||||
assert(variable.Kind == "Func");
|
||||
if (!isConstructor(variable.Name[0]))
|
||||
if (!isConstructor(variable.Name))
|
||||
continue;
|
||||
if (!("PEdgeCallInstance" in edge))
|
||||
continue;
|
||||
if (edge.PEdgeCallInstance.Exp.Kind != "Var")
|
||||
continue;
|
||||
|
||||
@@ -140,7 +140,7 @@ function isReturningImmobileValue(edge, variable)
|
||||
//
|
||||
function edgeUsesVariable(edge, variable, body)
|
||||
{
|
||||
if (ignoreEdgeUse(edge, variable))
|
||||
if (ignoreEdgeUse(edge, variable, body))
|
||||
return 0;
|
||||
|
||||
if (variable.Kind == "Return" && body.Index[1] == edge.Index[1] && body.BlockId.Kind == "Function")
|
||||
@@ -192,9 +192,9 @@ function expressionIsVariableAddress(exp, variable)
|
||||
return exp.Kind == "Var" && sameVariable(exp.Variable, variable);
|
||||
}
|
||||
|
||||
function edgeTakesVariableAddress(edge, variable)
|
||||
function edgeTakesVariableAddress(edge, variable, body)
|
||||
{
|
||||
if (ignoreEdgeUse(edge, variable))
|
||||
if (ignoreEdgeUse(edge, variable, body))
|
||||
return false;
|
||||
if (ignoreEdgeAddressTaken(edge))
|
||||
return false;
|
||||
@@ -492,7 +492,7 @@ function unsafeVariableAddressTaken(suppressed, variable)
|
||||
if (!("PEdge" in body))
|
||||
continue;
|
||||
for (var edge of body.PEdge) {
|
||||
if (edgeTakesVariableAddress(edge, variable)) {
|
||||
if (edgeTakesVariableAddress(edge, variable, body)) {
|
||||
if (edge.Kind == "Assign" || (!suppressed && edgeCanGC(edge)))
|
||||
return {body:body, ppoint:edge.Index[0]};
|
||||
}
|
||||
@@ -721,6 +721,11 @@ for (var nameIndex = start; nameIndex <= end; nameIndex++) {
|
||||
var data = xdb.read_entry(name);
|
||||
xdb.free_string(name);
|
||||
var json = data.readString();
|
||||
process(functionName, json);
|
||||
try {
|
||||
process(functionName, json);
|
||||
} catch (e) {
|
||||
printErr("Exception caught while handling " + functionName);
|
||||
throw(e);
|
||||
}
|
||||
xdb.free_string(data);
|
||||
}
|
||||
|
||||
@@ -91,8 +91,27 @@ function fieldCallCannotGC(csu, fullfield)
|
||||
return false;
|
||||
}
|
||||
|
||||
function ignoreEdgeUse(edge, variable)
|
||||
function ignoreEdgeUse(edge, variable, body)
|
||||
{
|
||||
// Horrible special case for ignoring a false positive in xptcstubs: there
|
||||
// is a local variable 'paramBuffer' holding an array of nsXPTCMiniVariant
|
||||
// on the stack, which appears to be live across a GC call because its
|
||||
// constructor is called when the array is initialized, even though the
|
||||
// constructor is a no-op. So we'll do a very narrow exclusion for the use
|
||||
// that incorrectly started the live range, which was basically "__temp_1 =
|
||||
// paramBuffer".
|
||||
//
|
||||
// By scoping it so narrowly, we can detect most hazards that would be
|
||||
// caused by modifications in the PrepareAndDispatch code. It just barely
|
||||
// avoids having a hazard already.
|
||||
if (('Name' in variable) && (variable.Name[0] == 'paramBuffer')) {
|
||||
if (body.BlockId.Kind == 'Function' && body.BlockId.Variable.Name[0] == 'PrepareAndDispatch')
|
||||
if (edge.Kind == 'Assign' && edge.Type.Kind == 'Pointer')
|
||||
if (edge.Exp[0].Kind == 'Var' && edge.Exp[1].Kind == 'Var')
|
||||
if (edge.Exp[1].Variable.Kind == 'Local' && edge.Exp[1].Variable.Name[0] == 'paramBuffer')
|
||||
return true;
|
||||
}
|
||||
|
||||
// Functions which should not be treated as using variable.
|
||||
if (edge.Kind == "Call") {
|
||||
var callee = edge.Exp[0];
|
||||
@@ -292,13 +311,16 @@ function isUnsafeStorage(typeName)
|
||||
return typeName.startsWith('UniquePtr<');
|
||||
}
|
||||
|
||||
function isSuppressConstructor(name)
|
||||
function isSuppressConstructor(varName)
|
||||
{
|
||||
return name.indexOf("::AutoSuppressGC") != -1
|
||||
|| name.indexOf("::AutoAssertGCCallback") != -1
|
||||
|| name.indexOf("::AutoEnterAnalysis") != -1
|
||||
|| name.indexOf("::AutoSuppressGCAnalysis") != -1
|
||||
|| name.indexOf("::AutoIgnoreRootingHazards") != -1;
|
||||
// varName[1] contains the unqualified name
|
||||
return [
|
||||
"AutoSuppressGC",
|
||||
"AutoAssertGCCallback",
|
||||
"AutoEnterAnalysis",
|
||||
"AutoSuppressGCAnalysis",
|
||||
"AutoIgnoreRootingHazards"
|
||||
].indexOf(varName[1]) != -1;
|
||||
}
|
||||
|
||||
// nsISupports subclasses' methods may be scriptable (or overridden
|
||||
|
||||
@@ -303,6 +303,43 @@ for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) {
|
||||
}
|
||||
}
|
||||
|
||||
// Further note: from http://mentorembedded.github.io/cxx-abi/abi.html the
|
||||
// different kinds of constructors/destructors are:
|
||||
// C1 # complete object constructor
|
||||
// C2 # base object constructor
|
||||
// C3 # complete object allocating constructor
|
||||
// D0 # deleting destructor
|
||||
// D1 # complete object destructor
|
||||
// D2 # base object destructor
|
||||
//
|
||||
// In actual practice, I have observed a C4 constructor generated by gcc
|
||||
// 4.9.3 (but not 4.7.3). The gcc source code says:
|
||||
//
|
||||
// /* This is the old-style "[unified]" constructor.
|
||||
// In some cases, we may emit this function and call
|
||||
// it from the clones in order to share code and save space. */
|
||||
//
|
||||
// Unfortunately, that "call... from the clones" does not seem to appear in
|
||||
// the CFG we get from GCC. So if we see a C4 constructor, inject an edge
|
||||
// to it from C1, C2, and C3. (Note that C3 isn't even used in current GCC,
|
||||
// but add the edge anyway just in case.)
|
||||
if (functionName.indexOf("C4E") != -1) {
|
||||
var [ mangled, unmangled ] = splitFunction(functionName);
|
||||
// E terminates the method name (and precedes the method parameters).
|
||||
if (mangled.indexOf("C4E") != -1) {
|
||||
// If "C4E" shows up in the mangled name for another reason, this
|
||||
// will create bogus edges in the callgraph. But that shouldn't
|
||||
// matter too much, and is somewhat difficult to avoid, so we will
|
||||
// live with it.
|
||||
var C1 = mangled.replace("C4E", "C1E");
|
||||
var C2 = mangled.replace("C4E", "C2E");
|
||||
var C3 = mangled.replace("C4E", "C3E");
|
||||
print("D " + memo(C1) + " " + memo(mangled));
|
||||
print("D " + memo(C2) + " " + memo(mangled));
|
||||
print("D " + memo(C3) + " " + memo(mangled));
|
||||
}
|
||||
}
|
||||
|
||||
xdb.free_string(name);
|
||||
xdb.free_string(data);
|
||||
}
|
||||
|
||||
@@ -155,6 +155,6 @@ invoke the following commands:
|
||||
This ought to be integrated with mach
|
||||
-------------------------------------
|
||||
|
||||
Indeed. It should somehow be unified with 'mach build-docs', which seems to
|
||||
Indeed. It should somehow be unified with 'mach doc', which seems to
|
||||
format in-tree docs of a different kind, and use a different markup
|
||||
language (ReStructuredText) and a different formatter (Sphinx).
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ class Fifo
|
||||
// An element A is "younger" than an element B if B was inserted into the
|
||||
// |Fifo| before A was.
|
||||
//
|
||||
// Invariant 1: Every element within |front_| is younger than every element
|
||||
// Invariant 1: Every element within |front_| is older than every element
|
||||
// within |rear_|.
|
||||
// Invariant 2: Entries within |front_| are sorted from younger to older.
|
||||
// Invariant 3: Entries within |rear_| are sorted from older to younger.
|
||||
|
||||
@@ -355,38 +355,22 @@ BytecodeEmitter::emitDupAt(unsigned slotFromTop)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
|
||||
const char js_with_statement_str[] = "with statement";
|
||||
const char js_finally_block_str[] = "finally block";
|
||||
|
||||
static const char * const statementName[] = {
|
||||
"label statement", /* LABEL */
|
||||
"if statement", /* IF */
|
||||
"else statement", /* ELSE */
|
||||
"destructuring body", /* BODY */
|
||||
"switch statement", /* SWITCH */
|
||||
"block", /* BLOCK */
|
||||
js_with_statement_str, /* WITH */
|
||||
"catch block", /* CATCH */
|
||||
"try block", /* TRY */
|
||||
js_finally_block_str, /* FINALLY */
|
||||
js_finally_block_str, /* SUBROUTINE */
|
||||
"do loop", /* DO_LOOP */
|
||||
"for loop", /* FOR_LOOP */
|
||||
"for/in loop", /* FOR_IN_LOOP */
|
||||
"for/of loop", /* FOR_OF_LOOP */
|
||||
"while loop", /* WHILE_LOOP */
|
||||
"spread", /* SPREAD */
|
||||
};
|
||||
|
||||
static_assert(MOZ_ARRAY_LENGTH(statementName) == uint16_t(StmtType::LIMIT),
|
||||
"statementName array and StmtType enum must be consistent");
|
||||
|
||||
static const char*
|
||||
StatementName(StmtInfoBCE* stmt)
|
||||
{
|
||||
if (!stmt)
|
||||
return js_script_str;
|
||||
|
||||
/* XXX too many "... statement" L10N gaffes -- fix via js.msg! */
|
||||
static const char* const statementName[] = {
|
||||
#define STATEMENT_TYPE_NAME(name, desc) desc,
|
||||
FOR_EACH_STATEMENT_TYPE(STATEMENT_TYPE_NAME)
|
||||
#undef STATEMENT_TYPE_NAME
|
||||
};
|
||||
|
||||
static_assert(MOZ_ARRAY_LENGTH(statementName) == uint16_t(StmtType::LIMIT),
|
||||
"statementName array and StmtType enum must be consistent");
|
||||
|
||||
return statementName[uint16_t(stmt->type)];
|
||||
}
|
||||
|
||||
@@ -3784,7 +3768,6 @@ BytecodeEmitter::emitDestructuringDeclsWithEmitter(JSOp prologueOp, ParseNode* p
|
||||
continue;
|
||||
ParseNode* target = element;
|
||||
if (element->isKind(PNK_SPREAD)) {
|
||||
MOZ_ASSERT(element->pn_kid->isKind(PNK_NAME));
|
||||
target = element->pn_kid;
|
||||
}
|
||||
if (target->isKind(PNK_ASSIGN))
|
||||
@@ -5400,15 +5383,17 @@ BytecodeEmitter::emitHoistedFunctionsInList(ParseNode* list)
|
||||
MOZ_ASSERT(list->pn_xflags & PNX_FUNCDEFS);
|
||||
|
||||
for (ParseNode* pn = list->pn_head; pn; pn = pn->pn_next) {
|
||||
ParseNode* maybeFun = pn;
|
||||
|
||||
if (!sc->strict()) {
|
||||
while (pn->isKind(PNK_LABEL))
|
||||
pn = pn->as<LabeledStatement>().statement();
|
||||
while (maybeFun->isKind(PNK_LABEL))
|
||||
maybeFun = maybeFun->as<LabeledStatement>().statement();
|
||||
}
|
||||
|
||||
if (pn->isKind(PNK_ANNEXB_FUNCTION) ||
|
||||
(pn->isKind(PNK_FUNCTION) && pn->functionIsHoisted()))
|
||||
if (maybeFun->isKind(PNK_ANNEXB_FUNCTION) ||
|
||||
(maybeFun->isKind(PNK_FUNCTION) && maybeFun->functionIsHoisted()))
|
||||
{
|
||||
if (!emitTree(pn))
|
||||
if (!emitTree(maybeFun))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1691,6 +1691,7 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(kind == Statement);
|
||||
#ifdef DEBUG
|
||||
if (options().selfHostingMode && !pc->sc->isFunctionBox()) {
|
||||
isGlobalSelfHostedBuiltin = true;
|
||||
@@ -4347,11 +4348,6 @@ Parser<FullParseHandler>::checkDestructuringArray(BindData<FullParseHandler>* da
|
||||
return false;
|
||||
}
|
||||
target = element->pn_kid;
|
||||
|
||||
if (handler.isUnparenthesizedDestructuringPattern(target)) {
|
||||
report(ParseError, false, target, JSMSG_BAD_DESTRUCT_TARGET);
|
||||
return false;
|
||||
}
|
||||
} else if (handler.isUnparenthesizedAssignment(element)) {
|
||||
target = element->pn_left;
|
||||
} else {
|
||||
|
||||
@@ -474,34 +474,33 @@ SharedContext::allLocalsAliased()
|
||||
return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator());
|
||||
}
|
||||
|
||||
// NOTE: If you add a new type of statement that is a scope, add it between
|
||||
// WITH and CATCH, or you'll break StmtInfoBase::linksScope. If you add
|
||||
// a non-looping statement type, add it before DO_LOOP or you'll break
|
||||
// StmtInfoBase::isLoop().
|
||||
#define FOR_EACH_STATEMENT_TYPE(macro) \
|
||||
macro(LABEL, "label statement") \
|
||||
macro(IF, "if statement") \
|
||||
macro(ELSE, "else statement") \
|
||||
macro(SEQ, "destructuring body") \
|
||||
macro(BLOCK, "block") \
|
||||
macro(SWITCH, "switch statement") \
|
||||
macro(WITH, "with statement") \
|
||||
macro(CATCH, "catch block") \
|
||||
macro(TRY, "try block") \
|
||||
macro(FINALLY, "finally block") \
|
||||
macro(SUBROUTINE, "finally block") \
|
||||
macro(DO_LOOP, "do loop") \
|
||||
macro(FOR_LOOP, "for loop") \
|
||||
macro(FOR_IN_LOOP, "for/in loop") \
|
||||
macro(FOR_OF_LOOP, "for/of loop") \
|
||||
macro(WHILE_LOOP, "while loop") \
|
||||
macro(SPREAD, "spread")
|
||||
|
||||
/*
|
||||
* NB: If you add a new type of statement that is a scope, add it between
|
||||
* STMT_WITH and STMT_CATCH, or you will break StmtInfoBase::linksScope. If you
|
||||
* add a non-looping statement type, add it before STMT_DO_LOOP or you will
|
||||
* break StmtInfoBase::isLoop().
|
||||
*
|
||||
* Also remember to keep the statementName array in BytecodeEmitter.cpp in
|
||||
* sync.
|
||||
*/
|
||||
enum class StmtType : uint16_t {
|
||||
LABEL, /* labeled statement: L: s */
|
||||
IF, /* if (then) statement */
|
||||
ELSE, /* else clause of if statement */
|
||||
SEQ, /* synthetic sequence of statements */
|
||||
BLOCK, /* compound statement: { s1[;... sN] } */
|
||||
SWITCH, /* switch statement */
|
||||
WITH, /* with statement */
|
||||
CATCH, /* catch block */
|
||||
TRY, /* try block */
|
||||
FINALLY, /* finally block */
|
||||
SUBROUTINE, /* gosub-target subroutine body */
|
||||
DO_LOOP, /* do/while loop statement */
|
||||
FOR_LOOP, /* for loop statement */
|
||||
FOR_IN_LOOP, /* for/in loop statement */
|
||||
FOR_OF_LOOP, /* for/of loop statement */
|
||||
WHILE_LOOP, /* while loop statement */
|
||||
SPREAD, /* spread operator (pseudo for/of) */
|
||||
#define DECLARE_STMTTYPE_ENUM(name, desc) name,
|
||||
FOR_EACH_STATEMENT_TYPE(DECLARE_STMTTYPE_ENUM)
|
||||
#undef DECLARE_STMTTYPE_ENUM
|
||||
LIMIT
|
||||
};
|
||||
|
||||
|
||||
@@ -413,27 +413,6 @@ IsNullTaggedPointer(void* p)
|
||||
return uintptr_t(p) <= LargestTaggedNullCellPointer;
|
||||
}
|
||||
|
||||
// HashKeyRef represents a reference to a HashMap key. This should normally
|
||||
// be used through the HashTableWriteBarrierPost function.
|
||||
template <typename Map, typename Key>
|
||||
class HashKeyRef : public BufferableRef
|
||||
{
|
||||
Map* map;
|
||||
Key key;
|
||||
|
||||
public:
|
||||
HashKeyRef(Map* m, const Key& k) : map(m), key(k) {}
|
||||
|
||||
void trace(JSTracer* trc) override {
|
||||
Key prior = key;
|
||||
typename Map::Ptr p = map->lookup(key);
|
||||
if (!p)
|
||||
return;
|
||||
TraceManuallyBarrieredEdge(trc, &key, "HashKeyRef");
|
||||
map->rekeyIfMoved(prior, key);
|
||||
}
|
||||
};
|
||||
|
||||
// Wrap a GC thing pointer into a new Value or jsid. The type system enforces
|
||||
// that the thing pointer is a wrappable type.
|
||||
template <typename S, typename T>
|
||||
|
||||
+29
-26
@@ -82,10 +82,10 @@ js::Nursery::init(uint32_t maxNurseryBytes)
|
||||
heapStart_ = uintptr_t(heap);
|
||||
heapEnd_ = heapStart_ + nurserySize();
|
||||
currentStart_ = start();
|
||||
numActiveChunks_ = 1;
|
||||
numActiveChunks_ = numNurseryChunks_;
|
||||
JS_POISON(heap, JS_FRESH_NURSERY_PATTERN, nurserySize());
|
||||
updateNumActiveChunks(1);
|
||||
setCurrentChunk(0);
|
||||
updateDecommittedRegion();
|
||||
|
||||
char* env = getenv("JS_GC_PROFILE_NURSERY");
|
||||
if (env) {
|
||||
@@ -110,24 +110,6 @@ js::Nursery::~Nursery()
|
||||
js_delete(freeMallocedBuffersTask);
|
||||
}
|
||||
|
||||
void
|
||||
js::Nursery::updateDecommittedRegion()
|
||||
{
|
||||
#ifndef JS_GC_ZEAL
|
||||
if (numActiveChunks_ < numNurseryChunks_) {
|
||||
// Bug 994054: madvise on MacOS is too slow to make this
|
||||
// optimization worthwhile.
|
||||
# ifndef XP_DARWIN
|
||||
uintptr_t decommitStart = chunk(numActiveChunks_).start();
|
||||
uintptr_t decommitSize = heapEnd() - decommitStart;
|
||||
MOZ_ASSERT(decommitStart == AlignBytes(decommitStart, Alignment));
|
||||
MOZ_ASSERT(decommitSize == AlignBytes(decommitStart, Alignment));
|
||||
MarkPagesUnused((void*)decommitStart, decommitSize);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
js::Nursery::enable()
|
||||
{
|
||||
@@ -135,7 +117,7 @@ js::Nursery::enable()
|
||||
MOZ_ASSERT(!runtime()->gc.isVerifyPreBarriersEnabled());
|
||||
if (isEnabled())
|
||||
return;
|
||||
numActiveChunks_ = 1;
|
||||
updateNumActiveChunks(1);
|
||||
setCurrentChunk(0);
|
||||
currentStart_ = position();
|
||||
#ifdef JS_GC_ZEAL
|
||||
@@ -150,9 +132,8 @@ js::Nursery::disable()
|
||||
MOZ_ASSERT(isEmpty());
|
||||
if (!isEnabled())
|
||||
return;
|
||||
numActiveChunks_ = 0;
|
||||
updateNumActiveChunks(0);
|
||||
currentEnd_ = 0;
|
||||
updateDecommittedRegion();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -703,7 +684,7 @@ js::Nursery::growAllocableSpace()
|
||||
MOZ_ASSERT_IF(runtime()->hasZealMode(ZealMode::GenerationalGC),
|
||||
numActiveChunks_ == numNurseryChunks_);
|
||||
#endif
|
||||
numActiveChunks_ = Min(numActiveChunks_ * 2, numNurseryChunks_);
|
||||
updateNumActiveChunks(Min(numActiveChunks_ * 2, numNurseryChunks_));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -713,6 +694,28 @@ js::Nursery::shrinkAllocableSpace()
|
||||
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
|
||||
return;
|
||||
#endif
|
||||
numActiveChunks_ = Max(numActiveChunks_ - 1, 1);
|
||||
updateDecommittedRegion();
|
||||
updateNumActiveChunks(Max(numActiveChunks_ - 1, 1));
|
||||
}
|
||||
|
||||
void
|
||||
js::Nursery::updateNumActiveChunks(int newCount)
|
||||
{
|
||||
#ifndef JS_GC_ZEAL
|
||||
int priorChunks = numActiveChunks_;
|
||||
#endif
|
||||
numActiveChunks_ = newCount;
|
||||
|
||||
// In zeal mode, we want to keep the unused memory poisoned so that we
|
||||
// will crash sooner. Avoid decommit in that case to avoid having the
|
||||
// system zero the pages.
|
||||
#ifndef JS_GC_ZEAL
|
||||
if (numActiveChunks_ < priorChunks) {
|
||||
uintptr_t decommitStart = chunk(numActiveChunks_).start();
|
||||
uintptr_t decommitSize = chunk(priorChunks - 1).start() + ChunkSize - decommitStart;
|
||||
MOZ_ASSERT(decommitSize != 0);
|
||||
MOZ_ASSERT(decommitStart == AlignBytes(decommitStart, Alignment));
|
||||
MOZ_ASSERT(decommitSize == AlignBytes(decommitSize, Alignment));
|
||||
MarkPagesUnused((void*)decommitStart, decommitSize);
|
||||
}
|
||||
#endif // !defined(JS_GC_ZEAL)
|
||||
}
|
||||
|
||||
+3
-3
@@ -303,8 +303,8 @@ class Nursery
|
||||
struct NurseryChunkLayout {
|
||||
char data[NurseryChunkUsableSize];
|
||||
gc::ChunkTrailer trailer;
|
||||
uintptr_t start() { return uintptr_t(&data); }
|
||||
uintptr_t end() { return uintptr_t(&trailer); }
|
||||
uintptr_t start() const { return uintptr_t(&data); }
|
||||
uintptr_t end() const { return uintptr_t(&trailer); }
|
||||
};
|
||||
static_assert(sizeof(NurseryChunkLayout) == gc::ChunkSize,
|
||||
"Nursery chunk size must match gc::Chunk size.");
|
||||
@@ -328,7 +328,7 @@ class Nursery
|
||||
initChunk(chunkno);
|
||||
}
|
||||
|
||||
void updateDecommittedRegion();
|
||||
void updateNumActiveChunks(int newCount);
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t allocationEnd() const {
|
||||
MOZ_ASSERT(numActiveChunks_ > 0);
|
||||
|
||||
+36
-1
@@ -11,6 +11,8 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/ReentrancyGuard.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "jsalloc.h"
|
||||
|
||||
#include "ds/LifoAlloc.h"
|
||||
@@ -288,6 +290,35 @@ class StoreBuffer
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// True if this SlotsEdge range overlaps with the other SlotsEdge range,
|
||||
// false if they do not overlap.
|
||||
bool overlaps(const SlotsEdge& other) const {
|
||||
if (objectAndKind_ != other.objectAndKind_)
|
||||
return false;
|
||||
|
||||
// Widen our range by one on each side so that we consider
|
||||
// adjacent-but-not-actually-overlapping ranges as overlapping. This
|
||||
// is particularly useful for coalescing a series of increasing or
|
||||
// decreasing single index writes 0, 1, 2, ..., N into a SlotsEdge
|
||||
// range of elements [0, N].
|
||||
auto end = start_ + count_ + 1;
|
||||
auto start = start_ - 1;
|
||||
|
||||
auto otherEnd = other.start_ + other.count_;
|
||||
return (start <= other.start_ && other.start_ <= end) ||
|
||||
(start <= otherEnd && otherEnd <= end);
|
||||
}
|
||||
|
||||
// Destructively make this SlotsEdge range the union of the other
|
||||
// SlotsEdge range and this one. A precondition is that the ranges must
|
||||
// overlap.
|
||||
void merge(const SlotsEdge& other) {
|
||||
MOZ_ASSERT(overlaps(other));
|
||||
auto end = std::max(start_ + count_, other.start_ + other.count_);
|
||||
start_ = std::min(start_, other.start_);
|
||||
count_ = end - start_;
|
||||
}
|
||||
|
||||
bool maybeInRememberedSet(const Nursery& n) const {
|
||||
return !IsInsideNursery(reinterpret_cast<Cell*>(object()));
|
||||
}
|
||||
@@ -404,7 +435,11 @@ class StoreBuffer
|
||||
void putCell(Cell** cellp) { put(bufferCell, CellPtrEdge(cellp)); }
|
||||
void unputCell(Cell** cellp) { unput(bufferCell, CellPtrEdge(cellp)); }
|
||||
void putSlot(NativeObject* obj, int kind, int32_t start, int32_t count) {
|
||||
put(bufferSlot, SlotsEdge(obj, kind, start, count));
|
||||
SlotsEdge edge(obj, kind, start, count);
|
||||
if (bufferSlot.last_.overlaps(edge))
|
||||
bufferSlot.last_.merge(edge);
|
||||
else
|
||||
put(bufferSlot, edge);
|
||||
}
|
||||
void putWholeCell(Cell* cell) {
|
||||
MOZ_ASSERT(cell->isTenured());
|
||||
|
||||
@@ -49,6 +49,17 @@ assertIterable([5,5,4,4],
|
||||
it => { var [,,...rest] = it; return rest; },
|
||||
[3,4]);
|
||||
|
||||
// the iterator should be exhausted before any error is thrown
|
||||
assertIterable([5,5,4,4],
|
||||
it => {
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
[...{0: "".x}] = it;
|
||||
}, TypeError);
|
||||
return [];
|
||||
},
|
||||
[]);
|
||||
|
||||
var arraycalls = 0;
|
||||
var ArrayIterator = Array.prototype[Symbol.iterator];
|
||||
Array.prototype[Symbol.iterator] = function () {
|
||||
@@ -58,7 +69,11 @@ Array.prototype[Symbol.iterator] = function () {
|
||||
// [...rest] should not call Array#@@iterator for the LHS
|
||||
var [...rest] = iterable;
|
||||
assertEq(arraycalls, 0, 'calls to Array#@@iterator');
|
||||
|
||||
// [...[...rest]] should do so, since it creates an implicit array for the
|
||||
// first rest pattern, then destructures that again using @@iterator() for the
|
||||
// second rest pattern.
|
||||
var [...[...rest]] = iterable;
|
||||
assertEq(arraycalls, 1, 'calls to Array#@@iterator');
|
||||
|
||||
// loop `fn` a few times, to get it JIT-compiled
|
||||
function loop(fn) {
|
||||
@@ -67,7 +82,7 @@ function loop(fn) {
|
||||
}
|
||||
|
||||
loop(() => { doneafter = 4; var [a] = iterable; return a; });
|
||||
loop(() => { doneafter = 4; var [a,b,...rest] = iterable; return rest; });
|
||||
loop(() => { doneafter = 4; var [a,b,...[...rest]] = iterable; return rest; });
|
||||
|
||||
|
||||
// destructuring assignment should always use iterators and not optimize
|
||||
|
||||
@@ -4,8 +4,6 @@ load(libdir + 'eqArrayHelper.js');
|
||||
|
||||
assertThrowsInstanceOf(() => new Function('[...a, ,] = []'), SyntaxError, 'trailing elision');
|
||||
assertThrowsInstanceOf(() => new Function('[a, ...b, c] = []'), SyntaxError, 'trailing param');
|
||||
assertThrowsInstanceOf(() => new Function('[...[a]] = []'), SyntaxError, 'nested arraypattern');
|
||||
assertThrowsInstanceOf(() => new Function('[...{a}] = []'), SyntaxError, 'nested objectpattern');
|
||||
assertThrowsInstanceOf(() => new Function('[...a=b] = []'), SyntaxError, 'assignment expression');
|
||||
assertThrowsInstanceOf(() => new Function('[...a()] = []'), SyntaxError, 'call expression');
|
||||
assertThrowsInstanceOf(() => new Function('[...(a,b)] = []'), SyntaxError, 'comma expression');
|
||||
@@ -22,6 +20,14 @@ assertThrowsInstanceOf(() =>
|
||||
assertThrowsInstanceOf(() => new Function('[...b,] = []'), SyntaxError)
|
||||
, Error);
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
try {
|
||||
eval('let [...[...x]] = (() => { throw "foo"; } )();');
|
||||
} catch(e) {
|
||||
assertEq(e, "foo");
|
||||
}
|
||||
x;
|
||||
}, ReferenceError);
|
||||
|
||||
var inputArray = [1, 2, 3];
|
||||
var inputDeep = [1, inputArray];
|
||||
@@ -45,6 +51,11 @@ function testAll(fn) {
|
||||
assertEqArray(fn('[, ...(o.prop)]', inputArray, 'o.prop'), expected);
|
||||
o.prop = null;
|
||||
assertEqArray(fn('[, ...(o.call().prop)]', inputArray, 'o.prop'), expected);
|
||||
|
||||
o.prop = null;
|
||||
assertEqArray(fn('[, ...[...(o.prop)]]', inputArray, 'o.prop'), expected);
|
||||
o.prop = null;
|
||||
assertEqArray(fn('[, ...[...(o.call().prop)]]', inputArray, 'o.prop'), expected);
|
||||
}
|
||||
function testDeclaration(fn) {
|
||||
testStr(fn);
|
||||
@@ -53,10 +64,24 @@ function testDeclaration(fn) {
|
||||
assertEqArray(fn('[, ...rest]', inputGenerator()), expected);
|
||||
assertEqArray(fn('[, [, ...rest]]', inputDeep), expected);
|
||||
assertEqArray(fn('{a: [, ...rest]}', inputObject), expected);
|
||||
|
||||
assertEqArray(fn('[, ...[...rest]]', inputArray), expected);
|
||||
assertEqArray(fn('[, ...[...rest]]', inputGenerator()), expected);
|
||||
assertEqArray(fn('[, [, ...[...rest]]]', inputDeep), expected);
|
||||
assertEqArray(fn('{a: [, ...[...rest]]}', inputObject), expected);
|
||||
|
||||
assertEqArray(fn('[, ...{0: a, 1: b}]', inputArray, '[a, b]'), expected);
|
||||
assertEqArray(fn('[, ...{0: a, 1: b}]', inputGenerator(), '[a, b]'), expected);
|
||||
assertEqArray(fn('[, [, ...{0: a, 1: b}]]', inputDeep, '[a, b]'), expected);
|
||||
assertEqArray(fn('{a: [, ...{0: a, 1: b}]}', inputObject, '[a, b]'), expected);
|
||||
}
|
||||
|
||||
function testStr(fn) {
|
||||
assertEqArray(fn('[, ...rest]', inputStr), expectedStr);
|
||||
|
||||
assertEqArray(fn('[, ...[...rest]]', inputStr), expectedStr);
|
||||
|
||||
assertEqArray(fn('[, ...{0: a, 1: b}]', inputStr, '[a, b]'), expectedStr);
|
||||
}
|
||||
|
||||
function testForIn(pattern, input, binding) {
|
||||
@@ -88,8 +113,9 @@ testAll(testGlobal);
|
||||
|
||||
function testClosure(pattern, input, binding) {
|
||||
binding = binding || 'rest';
|
||||
const decl = binding.replace('[', '').replace(']', '');
|
||||
return new Function('input',
|
||||
'var ' + binding + '; (function () {' +
|
||||
'var ' + decl + '; (function () {' +
|
||||
'(' + pattern + ' = input);' +
|
||||
'})();' +
|
||||
'return ' + binding
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
function addRemove() {
|
||||
dbg.addDebuggee(g);
|
||||
f = dbg.getNewestFrame().older;
|
||||
}
|
||||
function removeAdd() {
|
||||
dbg.addDebuggee(g);
|
||||
var f = dbg.getNewestFrame();
|
||||
while (f) {
|
||||
f = f.older;
|
||||
}
|
||||
}
|
||||
function testInterrupt() {
|
||||
g = newGlobal();
|
||||
dbg = new Debugger;
|
||||
g.eval("" + function f() {
|
||||
return g();
|
||||
});
|
||||
g.eval("" + function g() {
|
||||
return h();
|
||||
});
|
||||
g.eval("" + function h() {
|
||||
for (var i = 0; i < 100; i++) {
|
||||
interruptIf(5);
|
||||
}
|
||||
});
|
||||
setInterruptCallback(function() {
|
||||
toggleSeq();
|
||||
return true;
|
||||
});
|
||||
g.f();
|
||||
}
|
||||
toggleSeq = addRemove;
|
||||
testInterrupt();
|
||||
toggleSeq = removeAdd;
|
||||
testInterrupt();
|
||||
|
||||
@@ -170,14 +170,27 @@ MoveEmitterARM::completeCycle(const MoveOperand& from, const MoveOperand& to, Mo
|
||||
// saved value of B, to A.
|
||||
switch (type) {
|
||||
case MoveOp::FLOAT32:
|
||||
case MoveOp::DOUBLE:
|
||||
MOZ_ASSERT(!to.isGeneralRegPair());
|
||||
if (to.isMemory()) {
|
||||
ScratchDoubleScope scratch(masm);
|
||||
ScratchFloat32Scope scratch(masm);
|
||||
masm.ma_vldr(cycleSlot(slotId, 0), scratch);
|
||||
masm.ma_vstr(scratch, toAddress(to));
|
||||
} else if (to.isGeneralReg()) {
|
||||
MOZ_ASSERT(type == MoveOp::FLOAT32);
|
||||
masm.ma_ldr(toAddress(from), to.reg());
|
||||
} else {
|
||||
uint32_t offset = 0;
|
||||
if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1)
|
||||
offset = sizeof(float);
|
||||
masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg());
|
||||
}
|
||||
break;
|
||||
case MoveOp::DOUBLE:
|
||||
MOZ_ASSERT(!to.isGeneralReg());
|
||||
if (to.isMemory()) {
|
||||
ScratchDoubleScope scratch(masm);
|
||||
masm.ma_vldr(cycleSlot(slotId, 0), scratch);
|
||||
masm.ma_vstr(scratch, toAddress(to));
|
||||
} else if (to.isGeneralRegPair()) {
|
||||
MOZ_ASSERT(type == MoveOp::DOUBLE);
|
||||
ScratchDoubleScope scratch(masm);
|
||||
|
||||
@@ -200,7 +200,13 @@ inline void
|
||||
EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
|
||||
{
|
||||
MOZ_ASSERT(ICTailCallReg == lr);
|
||||
masm.Push(ICTailCallReg);
|
||||
|
||||
// In arm the link register contains the return address,
|
||||
// but in jit frames we expect it to be on the stack. As a result
|
||||
// push the link register (which is actually part of the previous frame.
|
||||
// Therefore using push instead of Push).
|
||||
masm.push(ICTailCallReg);
|
||||
|
||||
masm.Push(ICStubReg);
|
||||
}
|
||||
|
||||
@@ -235,7 +241,7 @@ inline void
|
||||
EmitIonLeaveStubFrame(MacroAssembler& masm)
|
||||
{
|
||||
masm.Pop(ICStubReg);
|
||||
masm.Pop(ICTailCallReg);
|
||||
masm.pop(ICTailCallReg); // See EmitIonEnterStubFrame for explanation on pop/Pop.
|
||||
}
|
||||
|
||||
inline void
|
||||
|
||||
@@ -108,7 +108,18 @@ EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
|
||||
inline void
|
||||
EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
|
||||
{
|
||||
MOZ_CRASH("Not implemented yet.");
|
||||
Register scratch = R2.scratchReg();
|
||||
|
||||
masm.loadPtr(Address(sp, stackSize), scratch);
|
||||
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch);
|
||||
masm.addPtr(Imm32(stackSize + JitStubFrameLayout::Size() - sizeof(intptr_t)), scratch);
|
||||
|
||||
// Push frame descriptor and perform the tail call.
|
||||
MOZ_ASSERT(ICTailCallReg == ra);
|
||||
masm.makeFrameDescriptor(scratch, JitFrame_IonJS, ExitFrameLayout::Size());
|
||||
masm.push(scratch);
|
||||
masm.push(ICTailCallReg);
|
||||
masm.branch(target);
|
||||
}
|
||||
|
||||
inline void
|
||||
@@ -135,7 +146,17 @@ EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
|
||||
inline void
|
||||
EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm)
|
||||
{
|
||||
MOZ_CRASH("Not implemented yet.");
|
||||
uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonStub,
|
||||
ExitFrameLayout::Size());
|
||||
masm.Push(Imm32(descriptor));
|
||||
masm.callJit(target);
|
||||
|
||||
// Remove rest of the frame left on the stack. We remove the return address
|
||||
// which is implicitly popped when returning.
|
||||
size_t framePop = sizeof(ExitFrameLayout) - sizeof(void*);
|
||||
|
||||
// Pop arguments from framePushed.
|
||||
masm.implicitPop(stackSlots * sizeof(void*) + framePop);
|
||||
}
|
||||
|
||||
struct BaselineStubFrame {
|
||||
@@ -184,7 +205,15 @@ EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
|
||||
inline void
|
||||
EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
|
||||
{
|
||||
MOZ_CRASH("Not implemented yet.");
|
||||
MOZ_ASSERT(ICTailCallReg == ra);
|
||||
|
||||
// In MIPS the ra register contains the return address,
|
||||
// but in jit frames we expect it to be on the stack. As a result
|
||||
// push the link register (which is actually part of the previous frame.
|
||||
// Therefore using push instead of Push).
|
||||
masm.push(ICTailCallReg);
|
||||
|
||||
masm.Push(ICStubReg);
|
||||
}
|
||||
|
||||
inline void
|
||||
@@ -219,7 +248,8 @@ EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
|
||||
inline void
|
||||
EmitIonLeaveStubFrame(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_CRASH("Not implemented yet.");
|
||||
masm.Pop(ICStubReg);
|
||||
masm.pop(ICTailCallReg); // See EmitIonEnterStubFrame for explanation on pop/Pop.
|
||||
}
|
||||
|
||||
inline void
|
||||
|
||||
@@ -299,10 +299,42 @@ ScriptedIndirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue
|
||||
if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
|
||||
return false;
|
||||
if (!IsCallable(fval))
|
||||
return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
|
||||
return derivedGet(cx, proxy, receiver, id, vp);
|
||||
return Trap(cx, handler, fval, 2, argv.begin(), vp);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::derivedGet(JSContext* cx, HandleObject proxy, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp) const
|
||||
{
|
||||
// This uses getPropertyDescriptor for backward compatibility reasons.
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!getPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
desc.assertCompleteIfFound();
|
||||
|
||||
if (!desc.object()) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (desc.isDataDescriptor()) {
|
||||
vp.set(desc.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(desc.isAccessorDescriptor());
|
||||
RootedObject getter(cx, desc.getterObject());
|
||||
|
||||
if (!getter) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
return InvokeGetter(cx, receiver, ObjectValue(*getter), vp);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result) const
|
||||
|
||||
@@ -55,6 +55,8 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler
|
||||
static const ScriptedIndirectProxyHandler singleton;
|
||||
|
||||
private:
|
||||
bool derivedGet(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
|
||||
MutableHandleValue vp) const;
|
||||
bool derivedSet(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result) const;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,18 @@
|
||||
// Annex B still works.
|
||||
assertEq(f(), 4);
|
||||
|
||||
// The same thing with labels.
|
||||
{
|
||||
assertEq(f(), 4);
|
||||
function f() { return 3; }
|
||||
assertEq(f(), 4);
|
||||
l: function f() { return 4; }
|
||||
assertEq(f(), 4);
|
||||
}
|
||||
|
||||
// Annex B still works.
|
||||
assertEq(f(), 4);
|
||||
|
||||
function test() {
|
||||
{
|
||||
assertEq(f(), 2);
|
||||
|
||||
@@ -910,7 +910,7 @@ LoginManagerPrompter.prototype = {
|
||||
_removeLoginNotifications : function () {
|
||||
var popupNote = this._getPopupNote();
|
||||
if (popupNote)
|
||||
popupNote = popupNote.getNotification("password-save");
|
||||
popupNote = popupNote.getNotification("password");
|
||||
if (popupNote)
|
||||
popupNote.remove();
|
||||
|
||||
|
||||
@@ -39,7 +39,11 @@ function getPopupNotifications(aWindow) {
|
||||
*/
|
||||
function getPopup(aPopupNote, aKind) {
|
||||
ok(true, "Looking for " + aKind + " popup notification");
|
||||
return aPopupNote.getNotification(aKind);
|
||||
var notification = aPopupNote.getNotification("password");
|
||||
if (notification) {
|
||||
is(notification.options.passwordNotificationType, aKind);
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4206,6 +4206,24 @@
|
||||
"n_buckets": 20,
|
||||
"description": "Session restore: Number of characters in DOM Storage for a tab. Pages without DOM Storage or with an empty DOM Storage are ignored."
|
||||
},
|
||||
"FX_SESSION_RESTORE_AUTO_RESTORE_DURATION_UNTIL_EAGER_TABS_RESTORED_MS": {
|
||||
"alert_emails": ["session-restore-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "default",
|
||||
"kind": "exponential",
|
||||
"low": "100",
|
||||
"high": "100000",
|
||||
"n_buckets": 20,
|
||||
"description": "Session restore: If the browser is setup to auto-restore tabs, this probe measures the time elapsed between the instant we start Session Restore and the instant we have finished restoring tabs eagerly. At this stage, the tabs that are restored on demand are not restored yet."
|
||||
},
|
||||
"FX_SESSION_RESTORE_MANUAL_RESTORE_DURATION_UNTIL_EAGER_TABS_RESTORED_MS": {
|
||||
"alert_emails": ["session-restore-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "default",
|
||||
"kind": "exponential",
|
||||
"low": "100",
|
||||
"high": "100000",
|
||||
"n_buckets": 20,
|
||||
"description": "Session restore: If a session is restored by the user clicking on 'Restore Session', this probe measures the time elapsed between the instant the user has clicked and the instant we have finished restoring tabs eagerly. At this stage, the tabs that are restored on demand are not restored yet."
|
||||
},
|
||||
"FX_TABLETMODE_PAGE_LOAD": {
|
||||
"expires_in_version": "47",
|
||||
"kind": "exponential",
|
||||
@@ -5108,12 +5126,6 @@
|
||||
"keyed": true,
|
||||
"description": "Record the search counts for search engines"
|
||||
},
|
||||
"SEARCH_DEFAULT_ENGINE": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "flag",
|
||||
"keyed": true,
|
||||
"description": "Record the default search engine."
|
||||
},
|
||||
"SEARCH_SERVICE_INIT_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
@@ -7860,26 +7872,26 @@
|
||||
"description": "Cache hits on the tile-info metadata database"
|
||||
},
|
||||
"DISPLAY_SCALING_OSX" : {
|
||||
"expires_in_version": "40",
|
||||
"expires_in_version": "never",
|
||||
"kind": "linear",
|
||||
"high": "500",
|
||||
"n_buckets": "100",
|
||||
"high": 500,
|
||||
"n_buckets": 100,
|
||||
"description": "Scaling percentage for the display where the first window is opened (OS X only)",
|
||||
"cpp_guard": "XP_MACOSX"
|
||||
},
|
||||
"DISPLAY_SCALING_MSWIN" : {
|
||||
"expires_in_version": "40",
|
||||
"expires_in_version": "never",
|
||||
"kind": "linear",
|
||||
"high": "500",
|
||||
"n_buckets": "100",
|
||||
"high": 500,
|
||||
"n_buckets": 100,
|
||||
"description": "Scaling percentage for the display where the first window is opened (MS Windows only)",
|
||||
"cpp_guard": "XP_WIN"
|
||||
},
|
||||
"DISPLAY_SCALING_LINUX" : {
|
||||
"expires_in_version": "40",
|
||||
"expires_in_version": "never",
|
||||
"kind": "linear",
|
||||
"high": "500",
|
||||
"n_buckets": "100",
|
||||
"high": 500,
|
||||
"n_buckets": 100,
|
||||
"description": "Scaling percentage for the display where the first window is opened (Linux only)",
|
||||
"cpp_guard": "XP_LINUX"
|
||||
},
|
||||
|
||||
@@ -21,8 +21,8 @@ Managing Documentation
|
||||
This documentation is generated via the
|
||||
`Sphinx <http://sphinx-doc.org/>`_ tool from sources in the tree.
|
||||
|
||||
To build the documentation, run ``mach build-docs``. Run
|
||||
``mach help build-docs`` to see configurable options.
|
||||
To build the documentation, run ``mach doc``. Run
|
||||
``mach help doc`` to see configurable options.
|
||||
|
||||
Adding Documentation
|
||||
--------------------
|
||||
|
||||
Reference in New Issue
Block a user