Bug 1108941 - Implement the per-global template literal registry. (r=arai,jonco)

This commit is contained in:
Shu-yu Guo 2017-03-08 12:00:54 -08:00
Родитель 9fa58ed270
Коммит c91e65240b
10 изменённых файлов: 128 добавлений и 26 удалений

Просмотреть файл

@ -767,6 +767,7 @@ struct CompartmentStats
macro(Other, MallocHeap, savedStacksSet) \
macro(Other, MallocHeap, varNamesSet) \
macro(Other, MallocHeap, nonSyntacticLexicalScopesTable) \
macro(Other, MallocHeap, templateLiteralMap) \
macro(Other, MallocHeap, jitCompartment) \
macro(Other, MallocHeap, privateData)

Просмотреть файл

@ -1657,10 +1657,8 @@ BaselineCompiler::emit_JSOP_CALLSITEOBJ()
RootedObject raw(cx, script->getObject(GET_UINT32_INDEX(pc) + 1));
if (!cso || !raw)
return false;
RootedValue rawValue(cx);
rawValue.setObject(*raw);
if (!ProcessCallSiteObjOperation(cx, cso, raw, rawValue))
if (!cx->compartment()->getTemplateLiteralObject(cx, raw, &cso))
return false;
frame.push(ObjectValue(*cso));

Просмотреть файл

@ -2185,6 +2185,7 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_regexp(info().getRegExp(pc));
case JSOP_CALLSITEOBJ:
// TODO this is wrong, need threadsafe way to get unique template obj
pushConstant(ObjectValue(*(info().getObject(pc))));
return Ok();

Просмотреть файл

@ -155,7 +155,7 @@ JSCompartment::init(JSContext* maybecx)
if (!enumerators)
return false;
if (!savedStacks_.init() || !varNames_.init()) {
if (!savedStacks_.init() || !varNames_.init() || !templateLiteralMap_.init()) {
if (maybecx)
ReportOutOfMemory(maybecx);
return false;
@ -599,6 +599,64 @@ JSCompartment::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
return false;
}
/* static */ HashNumber
TemplateRegistryHashPolicy::hash(const Lookup& lookup)
{
size_t length = GetAnyBoxedOrUnboxedInitializedLength(lookup);
HashNumber hash = 0;
for (uint32_t i = 0; i < length; i++) {
JSAtom& lookupAtom = GetAnyBoxedOrUnboxedDenseElement(lookup, i).toString()->asAtom();
hash = mozilla::AddToHash(hash, lookupAtom.hash());
}
return hash;
}
/* static */ bool
TemplateRegistryHashPolicy::match(const Key& key, const Lookup& lookup)
{
size_t length = GetAnyBoxedOrUnboxedInitializedLength(lookup);
if (GetAnyBoxedOrUnboxedInitializedLength(key) != length)
return false;
for (uint32_t i = 0; i < length; i++) {
JSAtom* a = &GetAnyBoxedOrUnboxedDenseElement(key, i).toString()->asAtom();
JSAtom* b = &GetAnyBoxedOrUnboxedDenseElement(lookup, i).toString()->asAtom();
if (a != b)
return false;
}
return true;
}
bool
JSCompartment::getTemplateLiteralObject(JSContext* cx, HandleObject rawStrings,
MutableHandleObject templateObj)
{
if (TemplateRegistry::AddPtr p = templateLiteralMap_.lookupForAdd(rawStrings)) {
templateObj.set(p->value());
// The template object must have been frozen when it was added to the
// registry.
MOZ_ASSERT(!templateObj->nonProxyIsExtensible());
} else {
// Add the template object to the registry before freezing to avoid
// needing to call relookupOrAdd.
if (!templateLiteralMap_.add(p, rawStrings, templateObj))
return false;
MOZ_ASSERT(templateObj->nonProxyIsExtensible());
RootedValue rawValue(cx, ObjectValue(*rawStrings));
if (!DefineProperty(cx, templateObj, cx->names().raw, rawValue, nullptr, nullptr, 0))
return false;
if (!FreezeObject(cx, rawStrings))
return false;
if (!FreezeObject(cx, templateObj))
return false;
}
return true;
}
void
JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
{
@ -636,6 +694,10 @@ JSCompartment::trace(JSTracer* trc)
{
savedStacks_.trace(trc);
// The template registry strongly holds everything in it by design and
// spec.
templateLiteralMap_.trace(trc);
// Atoms are always tenured.
if (!JS::CurrentThreadIsHeapMinorCollecting())
varNames_.trace(trc);
@ -976,6 +1038,7 @@ JSCompartment::clearTables()
MOZ_ASSERT(!debugEnvs);
MOZ_ASSERT(enumerators->next() == enumerators);
MOZ_ASSERT(regExps.empty());
MOZ_ASSERT(templateLiteralMap_.empty());
objectGroups.clearTables();
if (savedStacks_.initialized())
@ -1243,6 +1306,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* savedStacksSet,
size_t* varNamesSet,
size_t* nonSyntacticLexicalEnvironmentsArg,
size_t* templateLiteralMap,
size_t* jitCompartment,
size_t* privateData)
{
@ -1263,6 +1327,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
if (nonSyntacticLexicalEnvironments_)
*nonSyntacticLexicalEnvironmentsArg +=
nonSyntacticLexicalEnvironments_->sizeOfIncludingThis(mallocSizeOf);
*templateLiteralMap += templateLiteralMap_.sizeOfExcludingThis(mallocSizeOf);
if (jitCompartment_)
*jitCompartment += jitCompartment_->sizeOfIncludingThis(mallocSizeOf);

Просмотреть файл

@ -21,6 +21,7 @@
#include "vm/GlobalObject.h"
#include "vm/PIC.h"
#include "vm/SavedStacks.h"
#include "vm/TemplateRegistry.h"
#include "vm/Time.h"
#include "wasm/WasmCompartment.h"
@ -545,6 +546,7 @@ struct JSCompartment
size_t* savedStacksSet,
size_t* varNamesSet,
size_t* nonSyntacticLexicalScopes,
size_t* templateLiteralMap,
size_t* jitCompartment,
size_t* privateData);
@ -586,6 +588,12 @@ struct JSCompartment
// to use the same lexical environment to persist lexical bindings.
js::ObjectWeakMap* nonSyntacticLexicalEnvironments_;
// The realm's [[TemplateMap]], used for mapping template literals to
// unique template objects used in evaluation of tagged template literals.
//
// See ES 12.2.9.3.
js::TemplateRegistry templateLiteralMap_;
public:
/* During GC, stores the index of this compartment in rt->compartments. */
unsigned gcIndex;
@ -743,6 +751,13 @@ struct JSCompartment
return varNames_.has(name);
}
// Get a unique template object given a JS array of raw template strings
// and a template object. If a template object is found in template
// registry, that object is returned. Otherwise, the passed-in templateObj
// is added to the registry.
bool getTemplateLiteralObject(JSContext* cx, js::HandleObject rawStrings,
js::MutableHandleObject templateObj);
void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
MOZ_MUST_USE bool findDeadProxyZoneEdges(bool* foundAny);

Просмотреть файл

@ -664,25 +664,6 @@ InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t
return true;
}
static MOZ_ALWAYS_INLINE bool
ProcessCallSiteObjOperation(JSContext* cx, RootedObject& cso, RootedObject& raw,
RootedValue& rawValue)
{
bool extensible;
if (!IsExtensible(cx, cso, &extensible))
return false;
if (extensible) {
JSAtom* name = cx->names().raw;
if (!DefineProperty(cx, cso, name->asPropertyName(), rawValue, nullptr, nullptr, 0))
return false;
if (!FreezeObject(cx, raw))
return false;
if (!FreezeObject(cx, cso))
return false;
}
return true;
}
#define RELATIONAL_OP(OP) \
JS_BEGIN_MACRO \
/* Optimize for two int-tagged operands (typical loop control). */ \

Просмотреть файл

@ -3187,12 +3187,10 @@ END_CASE(JSOP_OBJECT)
CASE(JSOP_CALLSITEOBJ)
{
ReservedRooted<JSObject*> cso(&rootObject0, script->getObject(REGS.pc));
ReservedRooted<JSObject*> raw(&rootObject1, script->getObject(GET_UINT32_INDEX(REGS.pc) + 1));
ReservedRooted<Value> rawValue(&rootValue0, ObjectValue(*raw));
if (!ProcessCallSiteObjOperation(cx, cso, raw, rawValue))
if (!cx->compartment()->getTemplateLiteralObject(cx, raw, &cso))
goto error;
PUSH_OBJECT(*cso);

Просмотреть файл

@ -357,6 +357,7 @@ StatsCompartmentCallback(JSContext* cx, void* data, JSCompartment* compartment)
&cStats.savedStacksSet,
&cStats.varNamesSet,
&cStats.nonSyntacticLexicalScopesTable,
&cStats.templateLiteralMap,
&cStats.jitCompartment,
&cStats.privateData);
}

Просмотреть файл

@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_TemplateRegistry_h
#define vm_TemplateRegistry_h
#include "jsobj.h"
#include "js/GCHashTable.h"
#include "gc/Marking.h"
namespace js {
// Data structures to maintain unique template objects mapped to by lists of
// raw strings.
//
// See ES 12.2.9.3.
struct TemplateRegistryHashPolicy
{
// For use as HashPolicy. Expects keys as arrays of atoms.
using Key = JSObject*;
using Lookup = JSObject*;
static HashNumber hash(const Lookup& lookup);
static bool match(const Key& key, const Lookup& lookup);
};
using TemplateRegistry = JS::GCHashMap<JSObject*,
JSObject*,
TemplateRegistryHashPolicy,
SystemAllocPolicy>;
} // namespace js
#endif // vm_TemplateRegistery_h

Просмотреть файл

@ -2338,6 +2338,10 @@ ReportCompartmentStats(const JS::CompartmentStats& cStats,
cStats.nonSyntacticLexicalScopesTable,
"The non-syntactic lexical scopes table.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("template-literal-map"),
cStats.templateLiteralMap,
"The template literal registry.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("jit-compartment"),
cStats.jitCompartment,
"The JIT compartment.");