Bug 1660699 - Store JSAtom in CompilationInfo. r=tcampbell

ParserAtom now holds either AtomIndex that is an index into
CompilationInfo.atoms, or WellKnownAtomId that maps to cx->names() fields.

ParserAtoms in WellKnownParserAtoms holds WellKnownAtomId,
and ParserAtoms in CompilationInfo.parserAtoms holds AtomIndex.

GetWellKnownAtom relies on the struct layout of JSAtomState, to
quickly map WellKnownParserAtoms to its field.

Differential Revision: https://phabricator.services.mozilla.com/D88203
This commit is contained in:
Tooru Fujisawa 2020-08-26 22:14:48 +00:00
parent 3742f3b6f0
commit 90a2e27cb6
9 changed files with 147 additions and 71 deletions

View File

@ -58,7 +58,7 @@ bool js::frontend::EmitScriptThingsVector(JSContext* cx,
mozilla::Span<JS::GCCellPtr>& output;
bool operator()(const ScriptAtom& data) {
auto maybeAtom = data->toJSAtom(cx);
auto maybeAtom = data->toJSAtom(cx, compilationInfo);
if (maybeAtom.isErr()) {
return false;
}

View File

@ -165,6 +165,8 @@ struct MOZ_RAII CompilationInfo {
// Table of parser atoms for this compilation.
ParserAtomsTable parserAtoms;
JS::RootedVector<JSAtom*> atoms;
Directives directives;
ScopeContext scopeContext;
@ -238,6 +240,7 @@ struct MOZ_RAII CompilationInfo {
options(options),
keepAtoms(cx),
parserAtoms(cx),
atoms(cx),
directives(options.forceStrictMode()),
scopeContext(cx, enclosingScope, enclosingEnv),
script(cx),
@ -286,10 +289,10 @@ struct MOZ_RAII CompilationInfo {
MOZ_MUST_USE bool instantiateStencils();
JSAtom* liftParserAtomToJSAtom(const ParserAtom* parserAtom) {
return parserAtom->toJSAtom(cx).unwrapOr(nullptr);
return parserAtom->toJSAtom(cx, *this).unwrapOr(nullptr);
}
const ParserAtom* lowerJSAtomToParserAtom(JSAtom* atom) {
auto result = parserAtoms.internJSAtom(cx, atom);
auto result = parserAtoms.internJSAtom(cx, *this, atom);
return result.unwrapOr(nullptr);
}

View File

@ -407,7 +407,7 @@ bool ParseContext::isVarRedeclaredInEval(const ParserName* name,
MOZ_ASSERT(sc()->isEvalContext());
// TODO-Stencil: After scope snapshotting, this can be done away with.
auto mbNameAtom = name->toJSAtom(sc()->cx_);
auto mbNameAtom = name->toJSAtom(sc()->cx_, sc()->compilationInfo());
if (mbNameAtom.isErr()) {
return false;
}

View File

@ -832,7 +832,8 @@ bool PerHandlerParser<ParseHandler>::
// TODO-Stencil
// After closed-over-bindings are snapshotted in the handler,
// remove this.
auto mbNameId = compilationInfo_.parserAtoms.internJSAtom(cx_, name);
auto mbNameId = compilationInfo_.parserAtoms.internJSAtom(
cx_, this->getCompilationInfo(), name);
if (mbNameId.isErr()) {
return false;
}

View File

@ -10,6 +10,7 @@
#include "jsnum.h"
#include "frontend/CompilationInfo.h"
#include "frontend/NameCollections.h"
#include "vm/JSContext.h"
#include "vm/Printer.h"
@ -24,6 +25,27 @@ namespace frontend {
static JS::OOM PARSER_ATOMS_OOM;
static JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId kind) {
#define ASSERT_OFFSET_(idpart, id, text) \
static_assert(offsetof(JSAtomState, id) == \
int32_t(WellKnownAtomId::id) * \
sizeof(js::ImmutablePropertyNamePtr));
FOR_EACH_COMMON_PROPERTYNAME(ASSERT_OFFSET_);
#undef ASSERT_OFFSET_
#define ASSERT_OFFSET_(name, clasp) \
static_assert(offsetof(JSAtomState, name) == \
int32_t(WellKnownAtomId::name) * \
sizeof(js::ImmutablePropertyNamePtr));
JS_FOR_EACH_PROTOTYPE(ASSERT_OFFSET_);
#undef ASSERT_OFFSET_
static_assert(int32_t(WellKnownAtomId::abort) == 0,
"Unexpected order of WellKnownAtom");
return (&cx->names().abort)[int32_t(kind)];
}
mozilla::GenericErrorResult<OOM&> RaiseParserAtomsOOMError(JSContext* cx) {
js::ReportOutOfMemory(cx);
return mozilla::Err(PARSER_ATOMS_OOM);
@ -66,12 +88,6 @@ ParserAtomEntry::allocateInline(JSContext* cx,
}
bool ParserAtomEntry::equalsJSAtom(JSAtom* other) const {
// If this parser-atom has already been atomized, or been constructed
// from an existing js-atom, just compare against that.
if (jsatom_) {
return other == jsatom_;
}
// Compare hashes and lengths first.
if (hash_ != other->hash() || length_ != other->length()) {
return false;
@ -127,20 +143,30 @@ bool ParserAtomEntry::isIndex(uint32_t* indexp) const {
js::CheckStringIsIndex(twoByteChars(), len, indexp);
}
JS::Result<JSAtom*, OOM&> ParserAtomEntry::toJSAtom(JSContext* cx) const {
if (jsatom_) {
return jsatom_;
JS::Result<JSAtom*, OOM&> ParserAtomEntry::toJSAtom(
JSContext* cx, CompilationInfo& compilationInfo) const {
if (atomIndex_.constructed<AtomIndex>()) {
return compilationInfo.atoms[atomIndex_.ref<AtomIndex>()].get();
}
if (atomIndex_.constructed<WellKnownAtomId>()) {
return GetWellKnownAtom(cx, atomIndex_.ref<WellKnownAtomId>());
}
JSAtom* atom;
if (hasLatin1Chars()) {
jsatom_ = AtomizeChars(cx, latin1Chars(), length());
atom = AtomizeChars(cx, latin1Chars(), length());
} else {
jsatom_ = AtomizeChars(cx, twoByteChars(), length());
atom = AtomizeChars(cx, twoByteChars(), length());
}
if (!jsatom_) {
if (!atom) {
return RaiseParserAtomsOOMError(cx);
}
return jsatom_;
auto index = compilationInfo.atoms.length();
if (!compilationInfo.atoms.append(atom)) {
return mozilla::Err(PARSER_ATOMS_OOM);
}
atomIndex_.construct<AtomIndex>(index);
return atom;
}
bool ParserAtomEntry::toNumber(JSContext* cx, double* result) const {
@ -302,18 +328,40 @@ JS::Result<const ParserAtom*, OOM&> ParserAtomsTable::internUtf8(
}
JS::Result<const ParserAtom*, OOM&> ParserAtomsTable::internJSAtom(
JSContext* cx, JSAtom* atom) {
JS::AutoCheckCannotGC nogc;
JSContext* cx, CompilationInfo& compilationInfo, JSAtom* atom) {
const ParserAtom* id;
{
JS::AutoCheckCannotGC nogc;
auto result =
atom->hasLatin1Chars()
? internLatin1(cx, atom->latin1Chars(nogc), atom->length())
: internChar16(cx, atom->twoByteChars(nogc), atom->length());
if (result.isErr()) {
return result;
auto result =
atom->hasLatin1Chars()
? internLatin1(cx, atom->latin1Chars(nogc), atom->length())
: internChar16(cx, atom->twoByteChars(nogc), atom->length());
if (result.isErr()) {
return result;
}
id = result.unwrap();
}
if (id->atomIndex_.empty()) {
MOZ_ASSERT(id->equalsJSAtom(atom));
auto index = AtomIndex(compilationInfo.atoms.length());
if (!compilationInfo.atoms.append(atom)) {
return mozilla::Err(PARSER_ATOMS_OOM);
}
id->setAtomIndex(index);
} else {
#ifdef DEBUG
if (id->atomIndex_.constructed<AtomIndex>()) {
MOZ_ASSERT(compilationInfo.atoms[id->atomIndex_.ref<AtomIndex>()] ==
atom);
} else {
MOZ_ASSERT(GetWellKnownAtom(cx, id->atomIndex_.ref<WellKnownAtomId>()) ==
atom);
}
#endif
}
const ParserAtom* id = result.unwrap();
id->setAtom(atom);
return id;
}
@ -439,7 +487,7 @@ const ParserAtom* WellKnownParserAtoms::lookupChar16Seq(
}
bool WellKnownParserAtoms::initSingle(JSContext* cx, const ParserName** name,
const char* str, JSAtom* jsatom) {
const char* str, WellKnownAtomId kind) {
MOZ_ASSERT(name != nullptr);
unsigned int len = strlen(str);
@ -476,7 +524,7 @@ bool WellKnownParserAtoms::initSingle(JSContext* cx, const ParserName** name,
}
entry = maybeEntry.unwrap();
}
entry->jsatom_ = jsatom;
entry->setWellKnownAtomId(kind);
// Save name for returning after moving entry into set.
const ParserName* nm = entry.get()->asName();
@ -489,16 +537,16 @@ bool WellKnownParserAtoms::initSingle(JSContext* cx, const ParserName** name,
}
bool WellKnownParserAtoms::init(JSContext* cx) {
#define COMMON_NAME_INIT_(idpart, id, text) \
if (!initSingle(cx, &(id), text, cx->names().id)) { \
return false; \
#define COMMON_NAME_INIT_(idpart, id, text) \
if (!initSingle(cx, &(id), text, WellKnownAtomId::id)) { \
return false; \
}
FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INIT_)
#undef COMMON_NAME_INIT_
#define COMMON_NAME_INIT_(name, clasp) \
if (!initSingle(cx, &(name), #name, cx->names().name)) { \
return false; \
#define COMMON_NAME_INIT_(name, clasp) \
if (!initSingle(cx, &(name), #name, WellKnownAtomId::name)) { \
return false; \
}
JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INIT_)
#undef COMMON_NAME_INIT_

View File

@ -9,19 +9,22 @@
#include "mozilla/DebugOnly.h" // mozilla::DebugOnly
#include "mozilla/HashFunctions.h" // HashString
#include "mozilla/MaybeOneOf.h" // mozilla::MaybeOneOf
#include "mozilla/Range.h" // mozilla::Range
#include "mozilla/Variant.h" // mozilla::Variant
#include "ds/LifoAlloc.h" // LifoAlloc
#include "js/HashTable.h" // HashSet
#include "js/UniquePtr.h" // js::UniquePtr
#include "js/Vector.h" // Vector
#include "ds/LifoAlloc.h" // LifoAlloc
#include "frontend/TypedIndex.h" // TypedIndex
#include "js/HashTable.h" // HashSet
#include "js/UniquePtr.h" // js::UniquePtr
#include "js/Vector.h" // Vector
#include "vm/CommonPropertyNames.h"
#include "vm/StringType.h" // CompareChars, StringEqualsAscii
namespace js {
namespace frontend {
struct CompilationInfo;
class ParserAtom;
class ParserName;
@ -32,6 +35,25 @@ class ParserAtomsTable;
mozilla::GenericErrorResult<OOM&> RaiseParserAtomsOOMError(JSContext* cx);
// An index into CompilationInfo.atoms.
// This is local to the current compilation.
using AtomIndex = TypedIndex<JSAtom*>;
// An index to map WellKnownParserAtoms to cx->names().
// This is consistent across multiple compilation.
//
// GetWellKnownAtom in ParserAtom.cpp relies on the fact that
// JSAtomState fields and this enum variants use the same order.
enum class WellKnownAtomId : uint32_t {
#define ENUM_ENTRY_(idpart, id, text) id,
FOR_EACH_COMMON_PROPERTYNAME(ENUM_ENTRY_)
#undef ENUM_ENTRY_
#define ENUM_ENTRY_(name, clasp) name,
JS_FOR_EACH_PROTOTYPE(ENUM_ENTRY_)
#undef ENUM_ENTRY_
};
/**
* A ParserAtomEntry is an in-parser representation of an interned atomic
* string. It mostly mirrors the information carried by a JSAtom*.
@ -190,13 +212,13 @@ class alignas(alignof(void*)) ParserAtomEntry {
HashNumber hash_;
// Used to dynamically optimize the mapping of ParserAtoms to JSAtom*s.
// If the entry comes from an atom or has been mapped to an
// atom previously, the atom reference is kept here.
//
// Note: if/when this field is removed, remove the comment
// in front of the call to `rt->initializeParserAtoms()` in
// `JS::InitSelfHostedCode`.
mutable JSAtom* jsatom_ = nullptr;
// If this ParserAtomEntry is a part of WellKnownParserAtoms, this should
// hold WellKnownAtomId that maps to an item in cx->names().
//
// Otherwise, this should hold AtomIndex into CompilationInfo.atoms,
// or empty if the JSAtom isn't yet allocated.
mutable mozilla::MaybeOneOf<AtomIndex, WellKnownAtomId> atomIndex_;
public:
static const uint32_t MAX_LENGTH = JSString::MAX_LENGTH;
@ -276,19 +298,18 @@ class alignas(alignof(void*)) ParserAtomEntry {
template <typename CharT>
bool equalsSeq(HashNumber hash, InflatedChar16Sequence<CharT> seq) const;
void setAtom(JSAtom* atom) const {
MOZ_ASSERT(atom != nullptr);
if (jsatom_ != nullptr) {
MOZ_ASSERT(jsatom_ == atom);
return;
}
MOZ_ASSERT(equalsJSAtom(atom));
jsatom_ = atom;
void setAtomIndex(AtomIndex index) const {
atomIndex_.construct<AtomIndex>(index);
}
void setWellKnownAtomId(WellKnownAtomId kind) const {
atomIndex_.construct<WellKnownAtomId>(kind);
}
// Convert this entry to a js-atom. The first time this method is called
// the entry will cache the JSAtom pointer to return later.
JS::Result<JSAtom*, OOM&> toJSAtom(JSContext* cx) const;
JS::Result<JSAtom*, OOM&> toJSAtom(JSContext* cx,
CompilationInfo& compilationInfo) const;
// Convert this entry to a number.
bool toNumber(JSContext* cx, double* result) const;
@ -368,7 +389,7 @@ class WellKnownParserAtoms {
EntrySet entrySet_;
bool initSingle(JSContext* cx, const ParserName** name, const char* str,
JSAtom* jsatom);
WellKnownAtomId kind);
public:
explicit WellKnownParserAtoms(JSContext* cx) : entrySet_(cx) {}
@ -459,7 +480,8 @@ class ParserAtomsTable {
JS::Result<const ParserAtom*, OOM&> internUtf8(
JSContext* cx, const mozilla::Utf8Unit* utf8Ptr, uint32_t length);
JS::Result<const ParserAtom*, OOM&> internJSAtom(JSContext* cx, JSAtom* atom);
JS::Result<const ParserAtom*, OOM&> internJSAtom(
JSContext* cx, CompilationInfo& compilationInfo, JSAtom* atom);
JS::Result<const ParserAtom*, OOM&> concatAtoms(
JSContext* cx, mozilla::Range<const ParserAtom*> atoms);

View File

@ -465,8 +465,6 @@ JS_PUBLIC_API bool JS::InitSelfHostedCode(JSContext* cx) {
return false;
}
// Initialization of well-known ParserAtoms must happen AFTER
// initialization of the coresponding JSAtoms.
if (!rt->initializeParserAtoms(cx)) {
return false;
}

View File

@ -150,8 +150,9 @@ Shape* js::CreateEnvironmentShape(JSContext* cx, BindingIter& bi,
}
Shape* js::CreateEnvironmentShape(
JSContext* cx, AbstractBindingIter<const frontend::ParserAtom>& bi,
const JSClass* cls, uint32_t numSlots, uint32_t baseShapeFlags) {
JSContext* cx, frontend::CompilationInfo& compilationInfo,
AbstractBindingIter<const frontend::ParserAtom>& bi, const JSClass* cls,
uint32_t numSlots, uint32_t baseShapeFlags) {
RootedShape shape(cx,
EmptyEnvironmentShape(cx, cls, numSlots, baseShapeFlags));
if (!shape) {
@ -163,7 +164,7 @@ Shape* js::CreateEnvironmentShape(
for (; bi; bi++) {
BindingLocation loc = bi.location();
if (loc.kind() == BindingLocation::Kind::Environment) {
auto mbJSAtom = bi.name()->toJSAtom(cx);
auto mbJSAtom = bi.name()->toJSAtom(cx, compilationInfo);
if (mbJSAtom.isErr()) {
return nullptr;
}
@ -295,7 +296,8 @@ static UniquePtr<AbstractScopeData<ConcreteScope, AtomT>> NewEmptyScopeData(
template <typename ConcreteScope>
static UniquePtr<typename ConcreteScope::Data> LiftParserScopeData(
JSContext* cx, ParserScopeData<ConcreteScope>* data) {
JSContext* cx, frontend::CompilationInfo& compilationInfo,
ParserScopeData<ConcreteScope>* data) {
using ConcreteData = typename ConcreteScope::Data;
// Convert all scope ParserAtoms to rooted JSAtoms.
@ -309,7 +311,7 @@ static UniquePtr<typename ConcreteScope::Data> LiftParserScopeData(
for (size_t i = 0; i < length; i++) {
JSAtom* jsatom = nullptr;
if (names[i].name()) {
jsatom = names[i].name()->toJSAtom(cx).unwrapOr(nullptr);
jsatom = names[i].name()->toJSAtom(cx, compilationInfo).unwrapOr(nullptr);
if (jsatom == nullptr) {
return nullptr;
}
@ -2168,7 +2170,8 @@ bool ScopeStencil::createForWithScope(
template <typename SpecificScopeT>
UniquePtr<typename SpecificScopeT::Data> ScopeStencil::createSpecificScopeData(
JSContext* cx, CompilationInfo& compilationInfo) {
return LiftParserScopeData<SpecificScopeT>(cx, &data<SpecificScopeT>());
return LiftParserScopeData<SpecificScopeT>(cx, compilationInfo,
&data<SpecificScopeT>());
}
template <>
@ -2176,8 +2179,8 @@ UniquePtr<FunctionScope::Data>
ScopeStencil::createSpecificScopeData<FunctionScope>(
JSContext* cx, CompilationInfo& compilationInfo) {
// Allocate a new vm function-scope.
UniquePtr<FunctionScope::Data> data =
LiftParserScopeData<FunctionScope>(cx, &this->data<FunctionScope>());
UniquePtr<FunctionScope::Data> data = LiftParserScopeData<FunctionScope>(
cx, compilationInfo, &this->data<FunctionScope>());
if (!data) {
return nullptr;
}
@ -2192,8 +2195,8 @@ template <>
UniquePtr<ModuleScope::Data> ScopeStencil::createSpecificScopeData<ModuleScope>(
JSContext* cx, CompilationInfo& compilationInfo) {
// Allocate a new vm module-scope.
UniquePtr<ModuleScope::Data> data =
LiftParserScopeData<ModuleScope>(cx, &this->data<ModuleScope>());
UniquePtr<ModuleScope::Data> data = LiftParserScopeData<ModuleScope>(
cx, compilationInfo, &this->data<ModuleScope>());
if (!data) {
return nullptr;
}

View File

@ -1633,8 +1633,9 @@ Shape* CreateEnvironmentShape(JSContext* cx, BindingIter& bi,
uint32_t baseShapeFlags);
Shape* CreateEnvironmentShape(
JSContext* cx, AbstractBindingIter<const frontend::ParserAtom>& bi,
const JSClass* cls, uint32_t numSlots, uint32_t baseShapeFlags);
JSContext* cx, frontend::CompilationInfo& compilationInfo,
AbstractBindingIter<const frontend::ParserAtom>& bi, const JSClass* cls,
uint32_t numSlots, uint32_t baseShapeFlags);
Shape* EmptyEnvironmentShape(JSContext* cx, const JSClass* cls,
uint32_t numSlots, uint32_t baseShapeFlags);