Mypal/js/src/vm/Caches.h

314 lines
9.2 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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_Caches_h
#define vm_Caches_h
#include <new>
#include "jsatom.h"
#include "jsbytecode.h"
#include "jsobj.h"
#include "jsscript.h"
#include "ds/FixedSizeHash.h"
#include "frontend/SourceNotes.h"
#include "gc/Tracer.h"
#include "js/RootingAPI.h"
#include "js/UniquePtr.h"
#include "vm/NativeObject.h"
namespace js {
/*
* GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
* given pc in a script. We use the script->code pointer to tag the cache,
* instead of the script address itself, so that source notes are always found
* by offset from the bytecode with which they were generated.
*/
struct GSNCache {
typedef HashMap<jsbytecode*,
jssrcnote*,
PointerHasher<jsbytecode*, 0>,
SystemAllocPolicy> Map;
jsbytecode* code;
Map map;
GSNCache() : code(nullptr) { }
void purge();
};
/*
* EnvironmentCoordinateName cache to avoid O(n^2) growth in finding the name
* associated with a given aliasedvar operation.
*/
struct EnvironmentCoordinateNameCache {
typedef HashMap<uint32_t,
jsid,
DefaultHasher<uint32_t>,
SystemAllocPolicy> Map;
Shape* shape;
Map map;
EnvironmentCoordinateNameCache() : shape(nullptr) {}
void purge();
};
struct EvalCacheEntry
{
JSLinearString* str;
JSScript* script;
JSScript* callerScript;
jsbytecode* pc;
};
struct EvalCacheLookup
{
explicit EvalCacheLookup(JSContext* cx) : str(cx), callerScript(cx) {}
RootedLinearString str;
RootedScript callerScript;
JSVersion version;
jsbytecode* pc;
};
struct EvalCacheHashPolicy
{
typedef EvalCacheLookup Lookup;
static HashNumber hash(const Lookup& l);
static bool match(const EvalCacheEntry& entry, const EvalCacheLookup& l);
};
typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
struct LazyScriptHashPolicy
{
struct Lookup {
JSContext* cx;
LazyScript* lazy;
Lookup(JSContext* cx, LazyScript* lazy)
: cx(cx), lazy(lazy)
{}
};
static const size_t NumHashes = 3;
static void hash(const Lookup& lookup, HashNumber hashes[NumHashes]);
static bool match(JSScript* script, const Lookup& lookup);
// Alternate methods for use when removing scripts from the hash without an
// explicit LazyScript lookup.
static void hash(JSScript* script, HashNumber hashes[NumHashes]);
static bool match(JSScript* script, JSScript* lookup) { return script == lookup; }
static void clear(JSScript** pscript) { *pscript = nullptr; }
static bool isCleared(JSScript* script) { return !script; }
};
typedef FixedSizeHashSet<JSScript*, LazyScriptHashPolicy, 769> LazyScriptCache;
class PropertyIteratorObject;
class NativeIterCache
{
static const size_t SIZE = size_t(1) << 8;
/* Cached native iterators. */
PropertyIteratorObject* data[SIZE];
static size_t getIndex(uint32_t key) {
return size_t(key) % SIZE;
}
public:
NativeIterCache() {
mozilla::PodArrayZero(data);
}
void purge() {
mozilla::PodArrayZero(data);
}
PropertyIteratorObject* get(uint32_t key) const {
return data[getIndex(key)];
}
void set(uint32_t key, PropertyIteratorObject* iterobj) {
data[getIndex(key)] = iterobj;
}
};
/*
* Cache for speeding up repetitive creation of objects in the VM.
* When an object is created which matches the criteria in the 'key' section
* below, an entry is filled with the resulting object.
*/
class NewObjectCache
{
/* Statically asserted to be equal to sizeof(JSObject_Slots16) */
static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value);
static void staticAsserts() {
JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
JS_STATIC_ASSERT(gc::AllocKind::OBJECT_LAST == gc::AllocKind::OBJECT16_BACKGROUND);
}
struct Entry
{
/* Class of the constructed object. */
const Class* clasp;
/*
* Key with one of three possible values:
*
* - Global for the object. The object must have a standard class for
* which the global's prototype can be determined, and the object's
* parent will be the global.
*
* - Prototype for the object (cannot be global). The object's parent
* will be the prototype's parent.
*
* - Type for the object. The object's parent will be the type's
* prototype's parent.
*/
gc::Cell* key;
/* Allocation kind for the constructed object. */
gc::AllocKind kind;
/* Number of bytes to copy from the template object. */
uint32_t nbytes;
/*
* Template object to copy from, with the initial values of fields,
* fixed slots (undefined) and private data (nullptr).
*/
char templateObject[MAX_OBJ_SIZE];
};
using EntryArray = Entry[41]; // TODO: reconsider size;
EntryArray entries;
public:
using EntryIndex = int;
NewObjectCache()
: entries{} // zeroes out the array
{}
void purge() {
new (&entries) EntryArray{}; // zeroes out the array
}
/* Remove any cached items keyed on moved objects. */
void clearNurseryObjects(JSRuntime* rt);
/*
* Get the entry index for the given lookup, return whether there was a hit
* on an existing entry.
*/
inline bool lookupProto(const Class* clasp, JSObject* proto, gc::AllocKind kind, EntryIndex* pentry);
inline bool lookupGlobal(const Class* clasp, js::GlobalObject* global, gc::AllocKind kind,
EntryIndex* pentry);
bool lookupGroup(js::ObjectGroup* group, gc::AllocKind kind, EntryIndex* pentry) {
return lookup(group->clasp(), group, kind, pentry);
}
/*
* Return a new object from a cache hit produced by a lookup method, or
* nullptr if returning the object could possibly trigger GC (does not
* indicate failure).
*/
inline NativeObject* newObjectFromHit(JSContext* cx, EntryIndex entry, js::gc::InitialHeap heap);
/* Fill an entry after a cache miss. */
void fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto,
gc::AllocKind kind, NativeObject* obj);
inline void fillGlobal(EntryIndex entry, const Class* clasp, js::GlobalObject* global,
gc::AllocKind kind, NativeObject* obj);
void fillGroup(EntryIndex entry, js::ObjectGroup* group, gc::AllocKind kind,
NativeObject* obj)
{
MOZ_ASSERT(obj->group() == group);
return fill(entry, group->clasp(), group, kind, obj);
}
/* Invalidate any entries which might produce an object with shape/proto. */
void invalidateEntriesForShape(JSContext* cx, HandleShape shape, HandleObject proto);
private:
EntryIndex makeIndex(const Class* clasp, gc::Cell* key, gc::AllocKind kind) {
uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + size_t(kind);
return hash % mozilla::ArrayLength(entries);
}
bool lookup(const Class* clasp, gc::Cell* key, gc::AllocKind kind, EntryIndex* pentry) {
*pentry = makeIndex(clasp, key, kind);
Entry* entry = &entries[*pentry];
/* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
return entry->clasp == clasp && entry->key == key;
}
void fill(EntryIndex entry_, const Class* clasp, gc::Cell* key, gc::AllocKind kind,
NativeObject* obj) {
MOZ_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
MOZ_ASSERT(entry_ == makeIndex(clasp, key, kind));
Entry* entry = &entries[entry_];
entry->clasp = clasp;
entry->key = key;
entry->kind = kind;
entry->nbytes = gc::Arena::thingSize(kind);
js_memcpy(&entry->templateObject, obj, entry->nbytes);
}
static void copyCachedToObject(NativeObject* dst, NativeObject* src, gc::AllocKind kind) {
js_memcpy(dst, src, gc::Arena::thingSize(kind));
Shape::writeBarrierPost(&dst->shape_, nullptr, dst->shape_);
ObjectGroup::writeBarrierPost(&dst->group_, nullptr, dst->group_);
}
};
class MathCache;
class ContextCaches
{
UniquePtr<js::MathCache> mathCache_;
js::MathCache* createMathCache(JSContext* cx);
public:
js::GSNCache gsnCache;
js::EnvironmentCoordinateNameCache envCoordinateNameCache;
js::NewObjectCache newObjectCache;
js::NativeIterCache nativeIterCache;
js::UncompressedSourceCache uncompressedSourceCache;
js::EvalCache evalCache;
js::LazyScriptCache lazyScriptCache;
bool init();
js::MathCache* getMathCache(JSContext* cx) {
return mathCache_ ? mathCache_.get() : createMathCache(cx);
}
js::MathCache* maybeGetMathCache() {
return mathCache_.get();
}
};
} // namespace js
#endif /* vm_Caches_h */