diff --git a/js/src/frontend/ParseMaps-inl.h b/js/src/frontend/ParseMaps-inl.h index 16404812fa37..280cfb3763a7 100644 --- a/js/src/frontend/ParseMaps-inl.h +++ b/js/src/frontend/ParseMaps-inl.h @@ -105,8 +105,7 @@ AtomDecls::lookupMulti(JSAtom *atom) DefnOrHeader &doh = p.value(); if (doh.isHeader()) return MultiDeclRange(doh.header()); - else - return MultiDeclRange(doh.defn()); + return MultiDeclRange(doh.defn()); } inline bool diff --git a/js/src/jsarena.h b/js/src/jsarena.h index 866d8f5777b9..f38b1055d813 100644 --- a/js/src/jsarena.h +++ b/js/src/jsarena.h @@ -286,6 +286,18 @@ ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d, cons return v ? new (v) T(a, b, c, d, e) : NULL; } +inline uintN +ArenaAllocatedSize(const JSArenaPool &pool) +{ + uintN res = 0; + const JSArena *a = &pool.first; + while (a) { + res += (a->limit - (jsuword)a); + a = a->next; + } + return res; +} + } /* namespace js */ #endif /* __cplusplus */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 83098394e277..51e0e053ed50 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -85,6 +85,7 @@ JSCompartment::JSCompartment(JSRuntime *rt) propertyTree(thisForCtor()), emptyArgumentsShape(NULL), emptyBlockShape(NULL), + emptyCallShape(NULL), emptyDeclEnvShape(NULL), emptyEnumeratorShape(NULL), emptyWithShape(NULL), @@ -515,6 +516,8 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) emptyArgumentsShape = NULL; if (emptyBlockShape && IsAboutToBeFinalized(cx, emptyBlockShape)) emptyBlockShape = NULL; + if (emptyCallShape && IsAboutToBeFinalized(cx, emptyCallShape)) + emptyCallShape = NULL; if (emptyDeclEnvShape && IsAboutToBeFinalized(cx, emptyDeclEnvShape)) emptyDeclEnvShape = NULL; if (emptyEnumeratorShape && IsAboutToBeFinalized(cx, emptyEnumeratorShape)) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 94a22e11991a..7d486c3f0f27 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -394,6 +394,18 @@ struct JS_FRIEND_API(JSCompartment) { bool condenseTypes(JSContext *cx); + /* Data for tracking analysis/inference memory usage. */ + struct TypeInferenceMemoryStats + { + int64 scriptMain; + int64 scriptSets; + int64 objectMain; + int64 objectSets; + int64 poolMain; + }; + + void getTypeInferenceMemoryStats(TypeInferenceMemoryStats *stats); + #ifdef JS_TRACER private: /* @@ -452,10 +464,11 @@ struct JS_FRIEND_API(JSCompartment) { /* * Runtime-shared empty scopes for well-known built-in objects that lack - * class prototypes (the usual locus of an emptyShape). Mnemonic: ABDEW + * class prototypes (the usual locus of an emptyShape). Mnemonic: ABCDEW */ js::EmptyShape *emptyArgumentsShape; js::EmptyShape *emptyBlockShape; + js::EmptyShape *emptyCallShape; js::EmptyShape *emptyDeclEnvShape; js::EmptyShape *emptyEnumeratorShape; js::EmptyShape *emptyWithShape; diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index a545614def24..c58b733b94d3 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -121,6 +121,7 @@ JSCodeGenerator::JSCodeGenerator(Parser *parser, constMap(parser->context), constList(parser->context), upvarIndices(parser->context), + upvarMap(parser->context), globalUses(parser->context), globalMap(parser->context), closedArgs(parser->context), @@ -134,7 +135,6 @@ JSCodeGenerator::JSCodeGenerator(Parser *parser, current = &main; firstLine = prolog.currentLine = main.currentLine = lineno; prolog.noteMask = main.noteMask = SRCNOTE_CHUNK - 1; - memset(&upvarMap, 0, sizeof upvarMap); } bool @@ -154,9 +154,6 @@ JSCodeGenerator::~JSCodeGenerator() /* NB: non-null only after OOM. */ if (spanDeps) cx->free_(spanDeps); - - if (upvarMap.vector) - cx->free_(upvarMap.vector); } static ptrdiff_t @@ -2339,17 +2336,13 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!cg->upvarIndices->add(p, atom, index)) return JS_FALSE; - UpvarCookie *vector = cg->upvarMap.vector; - uint32 length = cg->roLexdeps->count(); - if (!vector || cg->upvarMap.length != length) { - vector = (UpvarCookie *) cx->realloc_(vector, length * sizeof *vector); - if (!vector) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - cg->upvarMap.vector = vector; - cg->upvarMap.length = length; - } + UpvarCookies &upvarMap = cg->upvarMap; + /* upvarMap should have the same number of UpvarCookies as there are lexdeps. */ + size_t lexdepCount = cg->roLexdeps->count(); + + JS_ASSERT_IF(!upvarMap.empty(), lexdepCount == upvarMap.length()); + if (upvarMap.empty() && !upvarMap.appendN(UpvarCookie(), lexdepCount)) + return JS_FALSE; uintN slot = cookie.slot(); if (slot != UpvarCookie::CALLEE_SLOT && dn_kind != JSDefinition::ARG) { @@ -2361,8 +2354,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) slot += tc->fun()->nargs; } - JS_ASSERT(index < cg->upvarMap.length); - vector[index].set(skip, slot); + JS_ASSERT(index < upvarMap.length()); + upvarMap[index].set(skip, slot); } pn->pn_op = JSOP_GETFCSLOT; diff --git a/js/src/jsemit.h b/js/src/jsemit.h index ffbe31bf04d0..ab498d02793b 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -644,7 +644,8 @@ struct JSCodeGenerator : public JSTreeContext cloned during execution */ js::OwnedAtomIndexMapPtr upvarIndices; /* map of atoms to upvar indexes */ - JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */ + + js::UpvarCookies upvarMap; /* indexed upvar slot locations */ typedef js::Vector GlobalUseVector; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 6fe0c173e561..87689346ebd3 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -516,6 +516,8 @@ public: } TypeObject * persistentObject() { return object; } + + size_t allocatedSize() { return sizeof(TypeConstraintBaseSubset); } }; void @@ -556,6 +558,8 @@ public: void newObjectState(JSContext *cx, TypeObject*, bool) { checkAnalysis(cx); } bool condensed() { return true; } + + size_t allocatedSize() { return sizeof(TypeConstraintCondensed); } }; bool @@ -1880,8 +1884,10 @@ TypeCompartment::init(JSContext *cx) #endif typeEmpty.flags = OBJECT_FLAG_UNKNOWN_MASK; +#ifndef JS_CPU_ARM if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE) inferenceEnabled = true; +#endif } TypeObject * @@ -4296,6 +4302,8 @@ class TypeIntermediateClearDefinite : public TypeIntermediate { return object->marked; } + + size_t allocatedSize() { return sizeof(TypeIntermediateClearDefinite); } }; static bool @@ -4564,7 +4572,7 @@ types::CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *scrip Vector initializerList(cx); AnalyzeNewScriptProperties(cx, type, script, &baseobj, &initializerList); - if (!baseobj || baseobj->slotSpan() == 0 && type->newScriptCleared) + if (!baseobj || (baseobj->slotSpan() == 0 && type->newScriptCleared)) return; gc::FinalizeKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); @@ -4837,6 +4845,8 @@ class TypeIntermediatePushed : public TypeIntermediate return false; } + + size_t allocatedSize() { return sizeof(TypeIntermediatePushed); } }; void @@ -5464,6 +5474,9 @@ SweepTypeObjectList(JSContext *cx, TypeObject *&objects) if (count >= 2) cx->free_(object->propertySet); + if (object->newScript) + cx->free_(object->newScript); + cx->delete_(object); } } @@ -5639,3 +5652,76 @@ JSScript::sweepAnalysis(JSContext *cx) analysis_ = NULL; } } + +size_t +TypeSet::dynamicSize() +{ + size_t res = 0; + + if (objectCount >= 2) + res += HashSetCapacity(objectCount) * sizeof(TypeObject *); + + /* Get the total size of any heap-allocated constraints on this set. */ + TypeConstraint *constraint = constraintList; + while (constraint) { + res += constraint->allocatedSize(); + constraint = constraint->next; + } + + return res; +} + +static void +GetObjectListMemoryStats(TypeObject *object, JSCompartment::TypeInferenceMemoryStats *stats) +{ + while (object) { + stats->objectMain += sizeof(TypeObject); + + if (object->propertyCount >= 2) + stats->objectMain += HashSetCapacity(object->propertyCount) * sizeof(Property *); + + unsigned count = object->getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + Property *prop = object->getProperty(i); + if (prop) { + stats->objectMain += sizeof(Property); + stats->objectSets += prop->types.dynamicSize(); + } + } + + object = object->next; + } +} + +static void +GetScriptMemoryStats(JSScript *script, JSCompartment::TypeInferenceMemoryStats *stats) +{ + GetObjectListMemoryStats(script->types.typeObjects, stats); + + if (!script->types.typeArray) + return; + + unsigned count = script->types.numTypeSets(); + stats->scriptMain += count * sizeof(TypeSet); + for (unsigned i = 0; i < count; i++) + stats->scriptSets += script->types.typeArray[i].dynamicSize(); + + TypeIntermediate *intermediate = script->types.intermediateList; + while (intermediate) { + stats->scriptMain += intermediate->allocatedSize(); + intermediate = intermediate->next; + } +} + +void +JSCompartment::getTypeInferenceMemoryStats(TypeInferenceMemoryStats *stats) +{ + GetObjectListMemoryStats(types.objects, stats); + + for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { + JSScript *script = reinterpret_cast(cursor); + GetScriptMemoryStats(script, stats); + } + + stats->poolMain += ArenaAllocatedSize(pool); +} diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 5f4274bf760f..de27d656da02 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -209,6 +209,8 @@ public: * of any script. */ virtual TypeObject * persistentObject() { return NULL; } + + virtual size_t allocatedSize() { return 0; } }; /* Coarse flags for the contents of a type set. */ @@ -313,6 +315,7 @@ class TypeSet void print(JSContext *cx); inline void destroy(JSContext *cx); + size_t dynamicSize(); /* Whether this set contains a specific type. */ inline bool hasType(jstype type); @@ -487,6 +490,8 @@ class TypeIntermediate /* Whether this subsumes a dynamic type pushed by the bytecode at offset. */ virtual bool hasDynamicResult(uint32 offset, jstype type) { return false; } + + virtual size_t allocatedSize() = 0; }; /* diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 8744c212fbc9..f1b8d0d5ac47 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -1551,21 +1551,14 @@ Parser::functionBody() return pn; } -/* - * Creates a placeholder JSDefinition node for |atom| and adds it to the - * current lexdeps. - */ +/* Create a placeholder JSDefinition node for |atom|. */ static JSDefinition * -MakePlaceholder(AtomDefnAddPtr &p, JSParseNode *pn, JSTreeContext *tc) +MakePlaceholder(JSParseNode *pn, JSTreeContext *tc) { - JSAtom *atom = pn->pn_atom; - JSDefinition *dn = (JSDefinition *) NameNode::create(atom, tc); + JSDefinition *dn = (JSDefinition *) NameNode::create(pn->pn_atom, tc); if (!dn) return NULL; - if (!tc->lexdeps->add(p, atom, dn)) - return NULL; - dn->pn_type = TOK_NAME; dn->pn_op = JSOP_NOP; dn->pn_defn = true; @@ -1896,23 +1889,20 @@ struct BindData { }; static bool -BindLocalVariable(JSContext *cx, JSTreeContext *tc, JSAtom *atom, BindingKind kind, bool isArg) +BindLocalVariable(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, BindingKind kind) { JS_ASSERT(kind == VARIABLE || kind == CONSTANT); - /* - * Don't bind a variable with the hidden name 'arguments', per ECMA-262. - * Instead 'var arguments' always restates the predefined property of the - * activation objects whose name is 'arguments'. Assignment to such a - * variable must be handled specially. - * - * Special case: an argument named 'arguments' *does* shadow the predefined - * arguments property. - */ - if (atom == cx->runtime->atomState.argumentsAtom && !isArg) - return true; + /* 'arguments' can be bound as a local only via a destructuring formal parameter. */ + JS_ASSERT_IF(pn->pn_atom == cx->runtime->atomState.argumentsAtom, kind == VARIABLE); - return tc->bindings.add(cx, atom, kind); + uintN index = tc->bindings.countVars(); + if (!tc->bindings.add(cx, pn->pn_atom, kind)) + return false; + + pn->pn_cookie.set(tc->staticLevel, index); + pn->pn_dflags |= PND_BOUND; + return true; } #if JS_HAS_DESTRUCTURING @@ -2785,8 +2775,8 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL, * inherited lexdeps into uses of a new outer definition * allows us to handle both these cases in a natural way. */ - outer_dn = MakePlaceholder(p, dn, tc); - if (!outer_dn) + outer_dn = MakePlaceholder(dn, tc); + if (!outer_dn || !tc->lexdeps->add(p, atom, outer_dn)) return false; } } @@ -3184,10 +3174,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, FunctionSyntaxKind kind) if (apn->pn_op != JSOP_SETLOCAL) continue; - uint16 index = funtc.bindings.countVars(); - if (!BindLocalVariable(context, &funtc, apn->pn_atom, VARIABLE, true)) + if (!BindLocalVariable(context, &funtc, apn, VARIABLE)) return NULL; - apn->pn_cookie.set(funtc.staticLevel, index); } } #endif @@ -3773,7 +3761,7 @@ DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom) } static bool -BindTopLevelVar(JSContext *cx, BindData *data, JSParseNode *pn, JSAtom *varname, JSTreeContext *tc) +BindTopLevelVar(JSContext *cx, BindData *data, JSParseNode *pn, JSTreeContext *tc) { JS_ASSERT(pn->pn_op == JSOP_NAME); JS_ASSERT(!tc->inFunction()); @@ -3826,11 +3814,19 @@ BindTopLevelVar(JSContext *cx, BindData *data, JSParseNode *pn, JSAtom *varname, } static bool -BindFunctionLocal(JSContext *cx, BindData *data, MultiDeclRange &mdl, JSParseNode *pn, - JSAtom *name, JSTreeContext *tc) +BindFunctionLocal(JSContext *cx, BindData *data, MultiDeclRange &mdl, JSTreeContext *tc) { JS_ASSERT(tc->inFunction()); + JSParseNode *pn = data->pn; + JSAtom *name = pn->pn_atom; + + /* + * Don't create a local variable with the name 'arguments', per ECMA-262. + * Instead, 'var arguments' always restates that predefined binding of the + * lexical environment for function activations. Assignments to arguments + * must be handled specially -- see NoteLValue. + */ if (name == cx->runtime->atomState.argumentsAtom) { pn->pn_op = JSOP_ARGUMENTS; pn->pn_dflags |= PND_BOUND; @@ -3848,12 +3844,9 @@ BindFunctionLocal(JSContext *cx, BindData *data, MultiDeclRange &mdl, JSParseNod */ kind = (data->op == JSOP_DEFCONST) ? CONSTANT : VARIABLE; - uintN index = tc->bindings.countVars(); - if (!BindLocalVariable(cx, tc, name, kind, false)) + if (!BindLocalVariable(cx, tc, pn, kind)) return false; pn->pn_op = JSOP_GETLOCAL; - pn->pn_cookie.set(tc->staticLevel, index); - pn->pn_dflags |= PND_BOUND; return true; } @@ -4012,9 +4005,9 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) pn->pn_dflags |= PND_CONST; if (tc->inFunction()) - return BindFunctionLocal(cx, data, mdl, pn, atom, tc); + return BindFunctionLocal(cx, data, mdl, tc); - return BindTopLevelVar(cx, data, pn, atom, tc); + return BindTopLevelVar(cx, data, pn, tc); } static bool @@ -6917,8 +6910,7 @@ CompExprTransplanter::transplant(JSParseNode *pn) * generator) a use of a new placeholder in the generator's * lexdeps. */ - AtomDefnAddPtr p = tc->lexdeps->lookupForAdd(atom); - JSDefinition *dn2 = MakePlaceholder(p, pn, tc); + JSDefinition *dn2 = MakePlaceholder(pn, tc); if (!dn2) return false; dn2->pn_pos = root->pn_pos; @@ -6937,6 +6929,8 @@ CompExprTransplanter::transplant(JSParseNode *pn) dn2->dn_uses = dn->dn_uses; dn->dn_uses = *pnup; *pnup = NULL; + if (!tc->lexdeps->put(atom, dn2)) + return false; } else if (dn->isPlaceholder()) { /* * The variable first occurs free in the 'yield' expression; @@ -7101,8 +7095,9 @@ Parser::comprehensionTail(JSParseNode *kid, uintN blockid, bool isGenexp, if (isGenexp) { if (!guard.checkValidBody(pn2)) return NULL; - } else if (!guard.maybeNoteGenerator()) { - return NULL; + } else { + if (!guard.maybeNoteGenerator()) + return NULL; } switch (tt) { @@ -8761,8 +8756,8 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) * - Be left as a free variable definition if we never * see the real definition. */ - dn = MakePlaceholder(p, pn, tc); - if (!dn) + dn = MakePlaceholder(pn, tc); + if (!dn || !tc->lexdeps->add(p, dn->pn_atom, dn)) return NULL; /* diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 0a3c8e3c1999..478050eb9efb 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -155,6 +155,7 @@ class TokenStream; struct Token; struct TokenPos; struct TokenPtr; +class UpvarCookie; class TempAllocPolicy; @@ -192,9 +193,10 @@ class Bindings; class MultiDeclRange; class ParseMapPool; class DefnOrHeader; -typedef js::InlineMap AtomDefnMap; -typedef js::InlineMap AtomIndexMap; -typedef js::InlineMap AtomDOHMap; +typedef InlineMap AtomDefnMap; +typedef InlineMap AtomIndexMap; +typedef InlineMap AtomDOHMap; +typedef Vector UpvarCookies; } /* namespace js */ diff --git a/js/src/jsscope.h b/js/src/jsscope.h index d79b56f4d582..e4efbb599a24 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -636,6 +636,7 @@ struct EmptyShape : public js::Shape static inline EmptyShape *getEmptyArgumentsShape(JSContext *cx); static inline EmptyShape *getEmptyBlockShape(JSContext *cx); + static inline EmptyShape *getEmptyCallShape(JSContext *cx); static inline EmptyShape *getEmptyDeclEnvShape(JSContext *cx); static inline EmptyShape *getEmptyEnumeratorShape(JSContext *cx); static inline EmptyShape *getEmptyWithShape(JSContext *cx); diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index e49f9ce1256f..6e888c110098 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -364,6 +364,12 @@ EmptyShape::getEmptyBlockShape(JSContext *cx) return ensure(cx, &js_BlockClass, &cx->compartment->emptyBlockShape); } +/* static */ inline EmptyShape * +EmptyShape::getEmptyCallShape(JSContext *cx) +{ + return ensure(cx, &js_CallClass, &cx->compartment->emptyCallShape); +} + /* static */ inline EmptyShape * EmptyShape::getEmptyDeclEnvShape(JSContext *cx) { diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ea999c340154..1e11fef8c632 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1312,12 +1312,11 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) script->hasSingletons = true; if (cg->hasUpvarIndices()) { - JS_ASSERT(cg->upvarIndices->count() <= cg->upvarMap.length); - memcpy(script->upvars()->vector, cg->upvarMap.vector, - cg->upvarIndices->count() * sizeof(uint32)); + JS_ASSERT(cg->upvarIndices->count() <= cg->upvarMap.length()); + memcpy(script->upvars()->vector, cg->upvarMap.begin(), + cg->upvarIndices->count() * sizeof(cg->upvarMap[0])); cg->upvarIndices->clear(); - cx->free_(cg->upvarMap.vector); - cg->upvarMap.vector = NULL; + cg->upvarMap.clear(); } /* Set global for compileAndGo scripts. */ diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index 13d1c90a18f1..a6b91733aa08 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -50,6 +50,8 @@ #include "jsscope.h" #include "vm/GlobalObject.h" +#include "jsscopeinlines.h" + namespace js { inline @@ -102,7 +104,7 @@ bool Bindings::ensureShape(JSContext *cx) { if (!lastBinding) { - lastBinding = EmptyShape::create(cx, &js_CallClass); + lastBinding = EmptyShape::getEmptyCallShape(cx); if (!lastBinding) return false; } diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 03aaec5fcf08..f8df75fbe012 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -646,7 +646,18 @@ class SetPropCompiler : public PICStubCompiler shape->setterOp() != SetCallVar) { return disable("setter"); } + JS_ASSERT(obj->isCall()); if (pic.typeMonitored) { + /* + * Update the types of the locals/args in the script according + * to the possible RHS types of the assignment. Note that the + * shape guards we have performed do not by themselves + * guarantee that future call objects hit will be for the same + * script. We also depend on the fact that the scope chains hit + * at the same bytecode are all isomorphic: the same scripts, + * in the same order (though the properties on their call + * objects may differ due to eval(), DEFFUN, etc.). + */ RecompilationMonitor monitor(cx); JSScript *script = obj->getCallObjCalleeFunction()->script(); uint16 slot = uint16(shape->shortid); diff --git a/js/src/tests/ecma_5/RegExp/jstests.list b/js/src/tests/ecma_5/RegExp/jstests.list index b6507a2e1dfc..b36a6cb167c4 100644 --- a/js/src/tests/ecma_5/RegExp/jstests.list +++ b/js/src/tests/ecma_5/RegExp/jstests.list @@ -6,5 +6,8 @@ script empty-lookahead.js script exec.js script exec-lastIndex-ToInteger.js script regress-576828.js +script regress-613820-1.js +script regress-613820-2.js +script regress-613820-3.js silentfail skip-if(!xulRuntime.shell&&(Android||xulRuntime.OS=="WINNT")) script regress-617935.js script instance-property-storage-introspection.js diff --git a/js/src/tests/ecma_5/RegExp/regress-613820-1.js b/js/src/tests/ecma_5/RegExp/regress-613820-1.js new file mode 100644 index 000000000000..e5e755b24aa3 --- /dev/null +++ b/js/src/tests/ecma_5/RegExp/regress-613820-1.js @@ -0,0 +1,9 @@ +/* Back reference is actually a forwards reference. */ +var re = /(\2(a)){2}/; +var str = 'aaa'; +var actual = re.exec(str); +var expected = makeExpectedMatch(['aa', 'a', 'a'], 0, str); +checkRegExpMatch(actual, expected); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/ecma_5/RegExp/regress-613820-2.js b/js/src/tests/ecma_5/RegExp/regress-613820-2.js new file mode 100644 index 000000000000..5db7244ba273 --- /dev/null +++ b/js/src/tests/ecma_5/RegExp/regress-613820-2.js @@ -0,0 +1,9 @@ +/* Resetting of inner capture groups across quantified capturing parens. */ +var re = /(?:(f)(o)(o)|(b)(a)(r))*/; +var str = 'foobar'; +var actual = re.exec(str); +var expected = makeExpectedMatch(['foobar', undefined, undefined, undefined, 'b', 'a', 'r'], 0, str); +checkRegExpMatch(actual, expected); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/ecma_5/RegExp/regress-613820-3.js b/js/src/tests/ecma_5/RegExp/regress-613820-3.js new file mode 100644 index 000000000000..126e838ff272 --- /dev/null +++ b/js/src/tests/ecma_5/RegExp/regress-613820-3.js @@ -0,0 +1,9 @@ +/* Capture group reset to undefined during second iteration, so backreference doesn't see prior result. */ +var re = /(?:^(a)|\1(a)|(ab)){2}/; +var str = 'aab'; +var actual = re.exec(str); +var expected = makeExpectedMatch(['aa', undefined, 'a', undefined], 0, str); +checkRegExpMatch(actual, expected); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/js1_8_5/extensions/jstests.list b/js/src/tests/js1_8_5/extensions/jstests.list index d6c6322cfaa2..23a3f39c9795 100644 --- a/js/src/tests/js1_8_5/extensions/jstests.list +++ b/js/src/tests/js1_8_5/extensions/jstests.list @@ -44,5 +44,6 @@ skip-if(!xulRuntime.shell) script regress-636818.js script regress-636697.js script is-generator.js script weakmap.js -script regress-650753.js script regress-645160.js +script regress-650753.js +script regress-668438.js diff --git a/js/src/tests/js1_8_5/extensions/regress-668438.js b/js/src/tests/js1_8_5/extensions/regress-668438.js new file mode 100644 index 000000000000..645237d02d0f --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/regress-668438.js @@ -0,0 +1,12 @@ +// -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +// Don't assert trying to parse this. +let(x = (x(( function() { + return { e: function() { e in x } }; + })) + for (x in []))) +true; + +reportCompare(true, true); diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 5a7d01945f2d..2f81f6c3433e 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1348,6 +1348,7 @@ private: PRInt64 tjitDataAllocatorsMain; PRInt64 tjitDataAllocatorsReserve; #endif + JSCompartment::TypeInferenceMemoryStats typeInferenceMemory; }; struct IterateData @@ -1428,6 +1429,13 @@ private: : 0; } + static void + GetCompartmentTypeInferenceMemorySize(JSCompartment *c, + JSCompartment::TypeInferenceMemoryStats *stats) + { + c->getTypeInferenceMemoryStats(stats); + } + #endif // JS_TRACER static void @@ -1451,6 +1459,7 @@ private: curr->tjitDataAllocatorsMain = GetCompartmentTjitDataAllocatorsMainSize(compartment); curr->tjitDataAllocatorsReserve = GetCompartmentTjitDataAllocatorsReserveSize(compartment); #endif + GetCompartmentTypeInferenceMemorySize(compartment, &curr->typeInferenceMemory); } static void @@ -1638,6 +1647,34 @@ public: "Memory used by the trace JIT and held in reserve for VMAllocators " "in case of OOM."); #endif + + DO(mkPath(name, "type-inference/script-main"), + nsIMemoryReporter::KIND_HEAP, stats->typeInferenceMemory.scriptMain, + "Memory used during type inference to store type sets of variables " + "and dynamically observed types."); + + DO(mkPath(name, "type-inference/script-typesets"), + nsIMemoryReporter::KIND_HEAP, stats->typeInferenceMemory.scriptSets, + "Memory used during type inference to hold the contents of type " + "sets associated with scripts."); + + DO(mkPath(name, "type-inference/object-main"), + nsIMemoryReporter::KIND_HEAP, stats->typeInferenceMemory.objectMain, + "Memory used during type inference to store types and possible " + "property types of JS objects."); + + DO(mkPath(name, "type-inference/object-typesets"), + nsIMemoryReporter::KIND_HEAP, stats->typeInferenceMemory.objectSets, + "Memory used during type inference to hold the contents of type " + "sets associated with objects."); + + /* + * This is in a different category from the rest of type inference + * data as this can be large but is volatile and cleared on GC. + */ + DO(mkPath(name, "type-inference-pools"), + nsIMemoryReporter::KIND_HEAP, stats->typeInferenceMemory.poolMain, + "Memory used during type inference to hold transient analysis information."); } JS_ASSERT(gcHeapChunkTotal % js::GC_CHUNK_SIZE == 0);