314 lines
9.2 KiB
C++
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 */
|