mirror of
https://github.com/corda/corda.git
synced 2025-01-01 02:36:44 +00:00
initial work to support boot image creation and use
This commit is contained in:
parent
15ea90cd0c
commit
6500f1eff6
45
makefile
45
makefile
@ -201,11 +201,6 @@ vm-sources = \
|
||||
$(src)/process.cpp \
|
||||
$(src)/$(asm).cpp
|
||||
|
||||
ifeq ($(heapdump),true)
|
||||
vm-sources += $(src)/heapdump.cpp
|
||||
cflags += -DAVIAN_HEAPDUMP
|
||||
endif
|
||||
|
||||
vm-asm-sources = $(src)/$(asm).S
|
||||
|
||||
ifeq ($(process),compile)
|
||||
@ -222,6 +217,21 @@ vm-cpp-objects = $(call cpp-objects,$(vm-sources),$(src),$(native-build))
|
||||
vm-asm-objects = $(call asm-objects,$(vm-asm-sources),$(src),$(native-build))
|
||||
vm-objects = $(vm-cpp-objects) $(vm-asm-objects)
|
||||
|
||||
heapwalk-sources = $(src)/heapwalk.cpp
|
||||
heapwalk-objects = \
|
||||
$(call cpp-objects,$(heapwalk-sources),$(src),$(native-build))
|
||||
|
||||
ifeq ($(heapdump),true)
|
||||
vm-sources += $(src)/heapdump.cpp
|
||||
vm-heapwalk-objects = $(heapwalk-objects)
|
||||
cflags += -DAVIAN_HEAPDUMP
|
||||
endif
|
||||
|
||||
bootimage-sources = $(src)/bootimage.cpp
|
||||
bootimage-objects = \
|
||||
$(call cpp-objects,$(bootimage-sources),$(src),$(native-build))
|
||||
bootimage = $(native-build)/bootimage
|
||||
|
||||
driver-source = $(src)/main.cpp
|
||||
driver-object = $(native-build)/main.o
|
||||
driver-dynamic-object = $(native-build)/main-dynamic.o
|
||||
@ -259,7 +269,7 @@ args = $(flags) $(input)
|
||||
|
||||
.PHONY: build
|
||||
build: $(static-library) $(executable) $(dynamic-library) \
|
||||
$(executable-dynamic) $(classpath-dep) $(test-dep)
|
||||
$(executable-dynamic) $(classpath-dep) $(test-dep) $(bootimage)
|
||||
|
||||
$(test-classes): $(classpath-dep)
|
||||
|
||||
@ -346,6 +356,12 @@ $(vm-cpp-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends)
|
||||
$(vm-asm-objects): $(native-build)/%-asm.o: $(src)/%.S
|
||||
$(compile-asm-object)
|
||||
|
||||
$(bootimage-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends)
|
||||
$(compile-object)
|
||||
|
||||
$(heapwalk-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends)
|
||||
$(compile-object)
|
||||
|
||||
$(driver-object): $(driver-source)
|
||||
$(compile-object)
|
||||
|
||||
@ -395,7 +411,20 @@ $(static-library): $(vm-objects) $(jni-objects)
|
||||
|
||||
$(executable): \
|
||||
$(vm-objects) $(classpath-object) $(jni-objects) $(driver-object) \
|
||||
$(boot-object)
|
||||
$(vm-heapwalk-objects) $(boot-object)
|
||||
@echo "linking $(@)"
|
||||
ifeq ($(platform),windows)
|
||||
$(dlltool) -z $(@).def $(^)
|
||||
$(dlltool) -d $(@).def -e $(@).exp
|
||||
$(cc) $(@).exp $(^) $(lflags) -o $(@)
|
||||
else
|
||||
$(cc) $(^) $(rdynamic) $(lflags) -o $(@)
|
||||
endif
|
||||
$(strip) $(strip-all) $(@)
|
||||
|
||||
$(bootimage): \
|
||||
$(vm-objects) $(classpath-object) $(jni-objects) $(heapwalk-objects) \
|
||||
$(bootimage-objects)
|
||||
@echo "linking $(@)"
|
||||
ifeq ($(platform),windows)
|
||||
$(dlltool) -z $(@).def $(^)
|
||||
@ -408,7 +437,7 @@ endif
|
||||
|
||||
$(dynamic-library): \
|
||||
$(vm-objects) $(classpath-object) $(dynamic-object) $(jni-objects) \
|
||||
$(boot-object)
|
||||
$(vm-heapwalk-objects) $(boot-object)
|
||||
@echo "linking $(@)"
|
||||
$(cc) $(^) $(shared) $(lflags) -o $(@)
|
||||
$(strip) $(strip-all) $(@)
|
||||
|
240
src/bootimage.cpp
Normal file
240
src/bootimage.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
#include "bootimage.h"
|
||||
#include "heapwalk.h"
|
||||
#include "common.h"
|
||||
#include "machine.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace vm;
|
||||
|
||||
namespace {
|
||||
|
||||
bool
|
||||
endsWith(const char* suffix, const char* s, unsigned length)
|
||||
{
|
||||
unsigned suffixLength = strlen(suffix);
|
||||
return length >= suffixLength
|
||||
and memcmp(suffix, s + (length - suffixLength), suffixLength) == 0;
|
||||
}
|
||||
|
||||
unsigned
|
||||
codeMapSize(unsigned codeSize)
|
||||
{
|
||||
return ceiling(codeSize, BitsPerWord) * BytesPerWord;
|
||||
}
|
||||
|
||||
object
|
||||
makeCodeImage(Thread* t, BootImage* image, uint8_t* code, unsigned capacity)
|
||||
{
|
||||
unsigned size;
|
||||
compileThunks(t, code, &size, image);
|
||||
|
||||
unsigned fixupCount = 0;
|
||||
object table = makeHashMap(t, 0, 0);
|
||||
PROTECT(t, table);
|
||||
|
||||
for (Finder::Iterator it(t->m->finder); it.hasMore();) {
|
||||
unsigned nameSize;
|
||||
const char* name = it.next(&nameSize);
|
||||
|
||||
if (endsWith(".class", name, nameSize)) {
|
||||
object c = resolveClass
|
||||
(t, makeByteArray(t, "%*s", nameSize - 5, name));
|
||||
PROTECT(t, c);
|
||||
|
||||
for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) {
|
||||
object method = arrayBody(t, classMethodTable(t, c), i);
|
||||
if (methodCode(t, method)) {
|
||||
compileMethod(t, method, code, &size, capacity,
|
||||
&table, &fixupCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image->codeSize = size;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
unsigned
|
||||
heapMapSize(unsigned heapSize)
|
||||
{
|
||||
return ceiling(heapSize, BitsPerWord * 8) * BytesPerWord;
|
||||
}
|
||||
|
||||
unsigned
|
||||
objectSize(Thread* t, object o)
|
||||
{
|
||||
assert(t, not objectExtended(t, o));
|
||||
return baseSize(t, o, objectClass(t, o));
|
||||
}
|
||||
|
||||
HeapMap*
|
||||
makeHeapImage(Thread* t, BootImage* image, uintptr_t* heap, uintptr_t* map,
|
||||
unsigned capacity)
|
||||
{
|
||||
class Walker: public HeapWalker {
|
||||
public:
|
||||
Walker(Thread* t, uintptr_t* heap, uintptr_t* map, unsigned capacity):
|
||||
t(t), currentObject(0), currentOffset(0), heap(heap), map(map),
|
||||
position(0), capacity(capacity)
|
||||
{ }
|
||||
|
||||
void visit(object p, unsigned number) {
|
||||
if (currentObject) {
|
||||
markBit(map, (currentObject - heap) + currentOffset);
|
||||
currentObject[currentOffset] = number;
|
||||
}
|
||||
|
||||
currentObject = reinterpret_cast<uintptr_t*>(p);
|
||||
}
|
||||
|
||||
virtual void root() {
|
||||
currentObject = 0;
|
||||
}
|
||||
|
||||
virtual unsigned visitNew(object p) {
|
||||
if (p) {
|
||||
unsigned size = objectSize(t, p);
|
||||
assert(t, position + size < capacity);
|
||||
|
||||
memcpy(heap + position, p, size * BytesPerWord);
|
||||
|
||||
unsigned number = position + 1;
|
||||
position += size;
|
||||
|
||||
visit(p, number);
|
||||
|
||||
return number;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visitOld(object, unsigned number) {
|
||||
visit(0, number);
|
||||
}
|
||||
|
||||
virtual void push(unsigned offset) {
|
||||
currentOffset = offset;
|
||||
}
|
||||
|
||||
virtual void pop() {
|
||||
currentObject = 0;
|
||||
}
|
||||
|
||||
Thread* t;
|
||||
uintptr_t* currentObject;
|
||||
unsigned currentOffset;
|
||||
uintptr_t* heap;
|
||||
uintptr_t* map;
|
||||
unsigned position;
|
||||
unsigned capacity;
|
||||
} walker(t, heap, map, capacity / BytesPerWord);
|
||||
|
||||
HeapMap* table = walk(t, &walker);
|
||||
|
||||
image->heapSize = walker.position * BytesPerWord;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void
|
||||
updateCodeTable(Thread* t, object codeTable, uint8_t* code, uintptr_t* codeMap,
|
||||
HeapMap* heapTable)
|
||||
{
|
||||
intptr_t i = 0;
|
||||
for (HashMapIterator it(t, codeTable); it.hasMore(); ++i) {
|
||||
object mapEntry = it.next();
|
||||
intptr_t target = heapTable->find(tripleFirst(t, mapEntry));
|
||||
assert(t, target >= 0);
|
||||
|
||||
for (object fixup = tripleSecond(t, mapEntry);
|
||||
fixup;
|
||||
fixup = pairSecond(t, fixup))
|
||||
{
|
||||
int32_t v = intValue(t, pairFirst(t, fixup));
|
||||
memcpy(code + v, &target, BytesPerWord);
|
||||
markBit(codeMap, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
offset(object a, uintptr_t* b)
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(b) - reinterpret_cast<uintptr_t>(a);
|
||||
}
|
||||
|
||||
void
|
||||
writeBootImage(Thread* t, FILE* out)
|
||||
{
|
||||
BootImage image;
|
||||
|
||||
const unsigned CodeCapacity = 32 * 1024 * 1024;
|
||||
uint8_t* code = static_cast<uint8_t*>(t->m->heap->allocate(CodeCapacity));
|
||||
uintptr_t* codeMap = static_cast<uintptr_t*>
|
||||
(t->m->heap->allocate(codeMapSize(CodeCapacity)));
|
||||
memset(codeMap, 0, codeMapSize(CodeCapacity));
|
||||
|
||||
object codeTable = makeCodeImage(t, &image, code, CodeCapacity);
|
||||
|
||||
const unsigned HeapCapacity = 32 * 1024 * 1024;
|
||||
uintptr_t* heap = static_cast<uintptr_t*>
|
||||
(t->m->heap->allocate(HeapCapacity));
|
||||
uintptr_t* heapMap = static_cast<uintptr_t*>
|
||||
(t->m->heap->allocate(heapMapSize(HeapCapacity)));
|
||||
memset(heapMap, 0, heapMapSize(HeapCapacity));
|
||||
|
||||
HeapMap* heapTable = makeHeapImage(t, &image, heap, heapMap, HeapCapacity);
|
||||
|
||||
updateCodeTable(t, codeTable, code, codeMap, heapTable);
|
||||
|
||||
image.magic = BootImage::Magic;
|
||||
|
||||
image.codeTable = offset(codeTable, heap);
|
||||
|
||||
image.loader = offset(t->m->loader, heap);
|
||||
image.bootstrapClassMap = offset(t->m->bootstrapClassMap, heap);
|
||||
image.stringMap = offset(t->m->stringMap, heap);
|
||||
image.types = offset(t->m->types, heap);
|
||||
image.jniMethodTable = offset(t->m->jniMethodTable, heap);
|
||||
image.finalizers = offset(t->m->finalizers, heap);
|
||||
image.tenuredFinalizers = offset(t->m->tenuredFinalizers, heap);
|
||||
image.finalizeQueue = offset(t->m->finalizeQueue, heap);
|
||||
image.weakReferences = offset(t->m->weakReferences, heap);
|
||||
image.tenuredWeakReferences = offset(t->m->tenuredWeakReferences, heap);
|
||||
|
||||
fwrite(&image, sizeof(BootImage), 1, out);
|
||||
|
||||
fwrite(heapMap, pad(heapMapSize(image.heapSize)), 1, out);
|
||||
fwrite(heap, pad(image.heapSize), 1, out);
|
||||
|
||||
fwrite(codeMap, pad(codeMapSize(image.codeSize)), 1, out);
|
||||
fwrite(code, pad(image.codeSize), 1, out);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int
|
||||
main(int ac, const char** av)
|
||||
{
|
||||
if (ac != 2) {
|
||||
fprintf(stderr, "usage: %s <classpath>\n", av[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
System* s = makeSystem(0);
|
||||
Heap* h = makeHeap(s, 128 * 1024 * 1024);
|
||||
Finder* f = makeFinder(s, av[0], 0);
|
||||
Processor* p = makeProcessor(s, h);
|
||||
Machine* m = new (h->allocate(sizeof(Machine))) Machine(s, h, f, p, 0, 0);
|
||||
Thread* t = p->makeThread(m, 0, 0);
|
||||
|
||||
enter(t, Thread::ActiveState);
|
||||
enter(t, Thread::IdleState);
|
||||
|
||||
writeBootImage(t, stdout);
|
||||
|
||||
return 0;
|
||||
}
|
34
src/bootimage.h
Normal file
34
src/bootimage.h
Normal file
@ -0,0 +1,34 @@
|
||||
namespace vm {
|
||||
|
||||
class BootImage {
|
||||
public:
|
||||
static const unsigned Magic = 0x22377322;
|
||||
|
||||
unsigned magic;
|
||||
|
||||
unsigned heapSize;
|
||||
unsigned codeSize;
|
||||
|
||||
unsigned codeTable;
|
||||
|
||||
unsigned loader;
|
||||
unsigned bootstrapClassMap;
|
||||
unsigned stringMap;
|
||||
unsigned types;
|
||||
unsigned jniMethodTable;
|
||||
unsigned finalizers;
|
||||
unsigned tenuredFinalizers;
|
||||
unsigned finalizeQueue;
|
||||
unsigned weakReferences;
|
||||
unsigned tenuredWeakReferences;
|
||||
|
||||
unsigned defaultThunk;
|
||||
unsigned nativeThunk;
|
||||
unsigned aioobThunk;
|
||||
|
||||
#define THUNK(s) unsigned s##Thunk;
|
||||
#include "thunks.cpp"
|
||||
#undef THUNK
|
||||
};
|
||||
|
||||
} // namespace vm
|
112
src/finder.cpp
112
src/finder.cpp
@ -59,8 +59,15 @@ equal(const void* a, unsigned al, const void* b, unsigned bl)
|
||||
|
||||
class Element {
|
||||
public:
|
||||
class Iterator {
|
||||
public:
|
||||
virtual const char* next(unsigned* size) = 0;
|
||||
virtual void dispose() = 0;
|
||||
};
|
||||
|
||||
Element(): next(0) { }
|
||||
|
||||
virtual Iterator* iterator() = 0;
|
||||
virtual System::Region* find(const char* name) = 0;
|
||||
virtual bool exists(const char* name) = 0;
|
||||
virtual void dispose() = 0;
|
||||
@ -70,10 +77,45 @@ class Element {
|
||||
|
||||
class DirectoryElement: public Element {
|
||||
public:
|
||||
class Iterator: public Element::Iterator {
|
||||
public:
|
||||
Iterator(System* s, const char* name):
|
||||
s(s), directory(0)
|
||||
{
|
||||
if (not s->success(s->open(&directory, name))) {
|
||||
directory = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* next(unsigned* size) {
|
||||
if (directory) {
|
||||
for (const char* v = directory->next(); v; v = directory->next()) {
|
||||
if (v[0] != '.') {
|
||||
*size = strlen(v);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void dispose() {
|
||||
directory->dispose();
|
||||
s->free(this);
|
||||
}
|
||||
|
||||
System* s;
|
||||
System::Directory* directory;
|
||||
};
|
||||
|
||||
DirectoryElement(System* s, const char* name):
|
||||
s(s), name(name)
|
||||
{ }
|
||||
|
||||
virtual Element::Iterator* iterator() {
|
||||
return new (allocate(s, sizeof(Iterator))) Iterator(s, name);
|
||||
}
|
||||
|
||||
virtual System::Region* find(const char* name) {
|
||||
const char* file = append(s, this->name, "/", name);
|
||||
System::Region* region;
|
||||
@ -91,7 +133,7 @@ class DirectoryElement: public Element {
|
||||
const char* file = append(s, this->name, "/", name);
|
||||
System::FileType type = s->identify(file);
|
||||
s->free(file);
|
||||
return type != System::DoesNotExist;
|
||||
return type != System::TypeDoesNotExist;
|
||||
}
|
||||
|
||||
virtual void dispose() {
|
||||
@ -379,10 +421,37 @@ class JarIndex {
|
||||
|
||||
class JarElement: public Element {
|
||||
public:
|
||||
class Iterator: public Element::Iterator {
|
||||
public:
|
||||
Iterator(System* s, JarIndex* index): s(s), index(index), position(0) { }
|
||||
|
||||
virtual const char* next(unsigned* size) {
|
||||
if (position < index->position) {
|
||||
JarIndex::Node* n = index->nodes + (position++);
|
||||
*size = JarIndex::fileNameLength(n->entry);
|
||||
return reinterpret_cast<const char*>(JarIndex::fileName(n->entry));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void dispose() {
|
||||
s->free(this);
|
||||
}
|
||||
|
||||
System* s;
|
||||
JarIndex* index;
|
||||
unsigned position;
|
||||
};
|
||||
|
||||
JarElement(System* s, const char* name):
|
||||
s(s), name(name), region(0), index(0)
|
||||
{ }
|
||||
|
||||
virtual Element::Iterator* iterator() {
|
||||
return new (allocate(s, sizeof(Iterator))) Iterator(s, index);
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
if (index == 0) {
|
||||
System::Region* r;
|
||||
@ -512,11 +581,11 @@ parsePath(System* s, const char* path, const char* bootLibrary)
|
||||
name[token.length] = 0;
|
||||
|
||||
switch (s->identify(name)) {
|
||||
case System::File: {
|
||||
case System::TypeFile: {
|
||||
e = new (allocate(s, sizeof(JarElement))) JarElement(s, name);
|
||||
} break;
|
||||
|
||||
case System::Directory: {
|
||||
case System::TypeDirectory: {
|
||||
e = new (allocate(s, sizeof(DirectoryElement)))
|
||||
DirectoryElement(s, name);
|
||||
} break;
|
||||
@ -541,6 +610,38 @@ parsePath(System* s, const char* path, const char* bootLibrary)
|
||||
return first;
|
||||
}
|
||||
|
||||
class MyIterator: public Finder::IteratorImp {
|
||||
public:
|
||||
MyIterator(System* s, Element* path):
|
||||
s(s), e(path ? path->next : 0), it(path ? path->iterator() : 0)
|
||||
{ }
|
||||
|
||||
virtual const char* next(unsigned* size) {
|
||||
while (it) {
|
||||
const char* v = it->next(size);
|
||||
if (v) {
|
||||
return v;
|
||||
} else {
|
||||
it->dispose();
|
||||
if (e) {
|
||||
it = e->iterator();
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void dispose() {
|
||||
if (it) it->dispose();
|
||||
s->free(this);
|
||||
}
|
||||
|
||||
System* s;
|
||||
Element* e;
|
||||
Element::Iterator* it;
|
||||
};
|
||||
|
||||
class MyFinder: public Finder {
|
||||
public:
|
||||
MyFinder(System* system, const char* path, const char* bootLibrary):
|
||||
@ -549,6 +650,11 @@ class MyFinder: public Finder {
|
||||
pathString(copy(system, path))
|
||||
{ }
|
||||
|
||||
virtual IteratorImp* iterator() {
|
||||
return new (allocate(system, sizeof(MyIterator)))
|
||||
MyIterator(system, path_);
|
||||
}
|
||||
|
||||
virtual System::Region* find(const char* name) {
|
||||
for (Element* e = path_; e; e = e->next) {
|
||||
System::Region* r = e->find(name);
|
||||
|
38
src/finder.h
38
src/finder.h
@ -19,6 +19,44 @@ namespace vm {
|
||||
|
||||
class Finder {
|
||||
public:
|
||||
class IteratorImp {
|
||||
public:
|
||||
virtual const char* next(unsigned* size) = 0;
|
||||
virtual void dispose() = 0;
|
||||
};
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(Finder* finder):
|
||||
it(finder->iterator()),
|
||||
current(it->next(¤tSize))
|
||||
{ }
|
||||
|
||||
~Iterator() {
|
||||
it->dispose();
|
||||
}
|
||||
|
||||
bool hasMore() {
|
||||
return current != 0;
|
||||
}
|
||||
|
||||
const char* next(unsigned* size) {
|
||||
if (current) {
|
||||
const char* v = current;
|
||||
*size = currentSize;
|
||||
current = it->next(¤tSize);
|
||||
return v;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
IteratorImp* it;
|
||||
const char* current;
|
||||
unsigned currentSize;
|
||||
};
|
||||
|
||||
virtual IteratorImp* iterator();
|
||||
virtual System::Region* find(const char* name) = 0;
|
||||
virtual bool exists(const char* name) = 0;
|
||||
virtual const char* path() = 0;
|
||||
|
327
src/heapdump.cpp
327
src/heapdump.cpp
@ -8,195 +8,12 @@
|
||||
There is NO WARRANTY for this software. See license.txt for
|
||||
details. */
|
||||
|
||||
#include "machine.h"
|
||||
#include "heapwalk.h"
|
||||
|
||||
using namespace vm;
|
||||
|
||||
namespace {
|
||||
|
||||
const uintptr_t PointerShift = log(BytesPerWord);
|
||||
|
||||
class Set {
|
||||
public:
|
||||
class Entry {
|
||||
public:
|
||||
object value;
|
||||
uint32_t number;
|
||||
int next;
|
||||
};
|
||||
|
||||
static unsigned footprint(unsigned capacity) {
|
||||
return sizeof(Set)
|
||||
+ pad(sizeof(int) * capacity)
|
||||
+ pad(sizeof(Set::Entry) * capacity);
|
||||
}
|
||||
|
||||
Set(unsigned capacity):
|
||||
size(0),
|
||||
capacity(capacity),
|
||||
index(reinterpret_cast<int*>
|
||||
(reinterpret_cast<uint8_t*>(this)
|
||||
+ sizeof(Set))),
|
||||
entries(reinterpret_cast<Entry*>
|
||||
(reinterpret_cast<uint8_t*>(index)
|
||||
+ pad(sizeof(int) * capacity)))
|
||||
{ }
|
||||
|
||||
unsigned size;
|
||||
unsigned capacity;
|
||||
int* index;
|
||||
Entry* entries;
|
||||
};
|
||||
|
||||
class Stack {
|
||||
public:
|
||||
class Entry {
|
||||
public:
|
||||
object value;
|
||||
int offset;
|
||||
};
|
||||
|
||||
static const unsigned Capacity = 4096;
|
||||
|
||||
Stack(Stack* next): next(next), entryCount(0) { }
|
||||
|
||||
Stack* next;
|
||||
unsigned entryCount;
|
||||
Entry entries[Capacity];
|
||||
};
|
||||
|
||||
class Context {
|
||||
public:
|
||||
Context(Thread* thread, FILE* out):
|
||||
thread(thread), out(out), objects(0), stack(0), nextNumber(1)
|
||||
{ }
|
||||
|
||||
~Context() {
|
||||
if (objects) {
|
||||
thread->m->heap->free(objects, Set::footprint(objects->capacity));
|
||||
}
|
||||
while (stack) {
|
||||
Stack* dead = stack;
|
||||
stack = dead->next;
|
||||
thread->m->heap->free(stack, sizeof(Stack));
|
||||
}
|
||||
}
|
||||
|
||||
Thread* thread;
|
||||
FILE* out;
|
||||
Set* objects;
|
||||
Stack* stack;
|
||||
uint32_t nextNumber;
|
||||
};
|
||||
|
||||
void
|
||||
push(Context* c, object p, int offset)
|
||||
{
|
||||
if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) {
|
||||
c->stack = new (c->thread->m->heap->allocate(sizeof(Stack)))
|
||||
Stack(c->stack);
|
||||
}
|
||||
Stack::Entry* e = c->stack->entries + (c->stack->entryCount++);
|
||||
e->value = p;
|
||||
e->offset = offset;
|
||||
}
|
||||
|
||||
bool
|
||||
pop(Context* c, object* p, int* offset)
|
||||
{
|
||||
if (c->stack) {
|
||||
if (c->stack->entryCount == 0) {
|
||||
if (c->stack->next) {
|
||||
Stack* dead = c->stack;
|
||||
c->stack = dead->next;
|
||||
c->thread->m->heap->free(dead, sizeof(Stack));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Stack::Entry* e = c->stack->entries + (--c->stack->entryCount);
|
||||
*p = e->value;
|
||||
*offset = e->offset;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
hash(object p, unsigned capacity)
|
||||
{
|
||||
return (reinterpret_cast<uintptr_t>(p) >> PointerShift)
|
||||
& (capacity - 1);
|
||||
}
|
||||
|
||||
Set::Entry*
|
||||
find(Context* c, object p)
|
||||
{
|
||||
if (c->objects == 0) return 0;
|
||||
|
||||
for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) {
|
||||
Set::Entry* e = c->objects->entries + i;
|
||||
if (e->value == p) {
|
||||
return e;
|
||||
}
|
||||
i = e->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Set::Entry*
|
||||
add(Context* c UNUSED, Set* set, object p, uint32_t number)
|
||||
{
|
||||
assert(c->thread, set->size < set->capacity);
|
||||
|
||||
unsigned index = hash(p, set->capacity);
|
||||
|
||||
int offset = set->size++;
|
||||
Set::Entry* e = set->entries + offset;
|
||||
e->value = p;
|
||||
e->number = number;
|
||||
e->next = set->index[index];
|
||||
set->index[index] = offset;
|
||||
return e;
|
||||
}
|
||||
|
||||
Set::Entry*
|
||||
add(Context* c, object p)
|
||||
{
|
||||
if (c->objects == 0 or c->objects->size == c->objects->capacity) {
|
||||
unsigned capacity;
|
||||
if (c->objects) {
|
||||
capacity = c->objects->capacity * 2;
|
||||
} else {
|
||||
capacity = 4096; // must be power of two
|
||||
}
|
||||
|
||||
Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity)))
|
||||
Set(capacity);
|
||||
|
||||
memset(set->index, 0xFF, sizeof(int) * capacity);
|
||||
|
||||
if (c->objects) {
|
||||
for (unsigned i = 0; i < c->objects->capacity; ++i) {
|
||||
for (int j = c->objects->index[i]; j >= 0;) {
|
||||
Set::Entry* e = c->objects->entries + j;
|
||||
add(c, set, e->value, e->number);
|
||||
j = e->next;
|
||||
}
|
||||
}
|
||||
|
||||
c->thread->m->heap->free
|
||||
(c->objects, Set::footprint(c->objects->capacity));
|
||||
}
|
||||
|
||||
c->objects = set;
|
||||
}
|
||||
|
||||
return add(c, c->objects, p, 0);
|
||||
}
|
||||
|
||||
enum {
|
||||
Root,
|
||||
Size,
|
||||
@ -205,100 +22,30 @@ enum {
|
||||
Pop
|
||||
};
|
||||
|
||||
inline object
|
||||
get(object o, unsigned offsetInWords)
|
||||
void
|
||||
write1(FILE* out, uint8_t v)
|
||||
{
|
||||
return static_cast<object>
|
||||
(mask(cast<void*>(o, offsetInWords * BytesPerWord)));
|
||||
fwrite(&v, 1, 1, out);
|
||||
}
|
||||
|
||||
void
|
||||
write1(Context* c, uint8_t v)
|
||||
{
|
||||
fwrite(&v, 1, 1, c->out);
|
||||
}
|
||||
|
||||
void
|
||||
write4(Context* c, uint32_t v)
|
||||
write4(FILE* out, uint32_t v)
|
||||
{
|
||||
uint8_t b[] = { v >> 24, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF };
|
||||
fwrite(b, 4, 1, c->out);
|
||||
fwrite(b, 4, 1, out);
|
||||
}
|
||||
|
||||
void
|
||||
writeString(Context* c, int8_t* p, unsigned size)
|
||||
writeString(FILE* out, int8_t* p, unsigned size)
|
||||
{
|
||||
write4(c, size);
|
||||
fwrite(p, size, 1, c->out);
|
||||
write4(out, size);
|
||||
fwrite(p, size, 1, out);
|
||||
}
|
||||
|
||||
unsigned
|
||||
objectSize(Thread* t, object o)
|
||||
{
|
||||
unsigned n = baseSize(t, o, objectClass(t, o));
|
||||
if (objectExtended(t, o)) {
|
||||
++ n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
visit(Context* c, object p)
|
||||
{
|
||||
Thread* t = c->thread;
|
||||
int nextChildOffset;
|
||||
|
||||
write1(c, Root);
|
||||
|
||||
visit: {
|
||||
Set::Entry* e = find(c, p);
|
||||
if (e) {
|
||||
write4(c, e->number);
|
||||
} else {
|
||||
e = add(c, p);
|
||||
e->number = c->nextNumber++;
|
||||
|
||||
write4(c, e->number);
|
||||
|
||||
write1(c, Size);
|
||||
write4(c, objectSize(t, p));
|
||||
|
||||
if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType)) {
|
||||
object name = className(t, p);
|
||||
if (name) {
|
||||
write1(c, ClassName);
|
||||
writeString(c, &byteArrayBody(t, name, 0),
|
||||
byteArrayLength(t, name) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
nextChildOffset = walkNext(t, p, -1);
|
||||
if (nextChildOffset != -1) {
|
||||
goto children;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goto pop;
|
||||
|
||||
children: {
|
||||
write1(c, Push);
|
||||
push(c, p, nextChildOffset);
|
||||
p = get(p, nextChildOffset);
|
||||
goto visit;
|
||||
}
|
||||
|
||||
pop: {
|
||||
if (pop(c, &p, &nextChildOffset)) {
|
||||
write1(c, Pop);
|
||||
nextChildOffset = walkNext(t, p, nextChildOffset);
|
||||
if (nextChildOffset >= 0) {
|
||||
goto children;
|
||||
} else {
|
||||
goto pop;
|
||||
}
|
||||
}
|
||||
}
|
||||
return extendedSize(t, o, baseSize(t, o, objectClass(t, o)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -308,22 +55,56 @@ namespace vm {
|
||||
void
|
||||
dumpHeap(Thread* t, FILE* out)
|
||||
{
|
||||
Context context(t, out);
|
||||
|
||||
class Visitor : public Heap::Visitor {
|
||||
class Walker: public HeapWalker {
|
||||
public:
|
||||
Visitor(Context* c): c(c) { }
|
||||
Walker(Thread* t, FILE* out): t(t), out(out), nextNumber(1) { }
|
||||
|
||||
virtual void visit(void* p) {
|
||||
::visit(c, static_cast<object>(mask(*static_cast<void**>(p))));
|
||||
virtual void root() {
|
||||
write1(out, Root);
|
||||
}
|
||||
|
||||
Context* c;
|
||||
} v(&context);
|
||||
virtual unsigned visitNew(object p) {
|
||||
if (p) {
|
||||
unsigned number = nextNumber++;
|
||||
write4(out, number);
|
||||
|
||||
add(&context, 0)->number = 0;
|
||||
write1(out, Size);
|
||||
write4(out, objectSize(t, p));
|
||||
|
||||
visitRoots(t->m, &v);
|
||||
if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType))
|
||||
{
|
||||
object name = className(t, p);
|
||||
if (name) {
|
||||
write1(out, ClassName);
|
||||
writeString(out, &byteArrayBody(t, name, 0),
|
||||
byteArrayLength(t, name) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return number;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visitOld(object, unsigned number) {
|
||||
write4(out, number);
|
||||
}
|
||||
|
||||
virtual void push(unsigned) {
|
||||
write1(out, Push);
|
||||
}
|
||||
|
||||
virtual void pop() {
|
||||
write1(out, Pop);
|
||||
}
|
||||
|
||||
Thread* t;
|
||||
FILE* out;
|
||||
unsigned nextNumber;
|
||||
} walker(t, out);
|
||||
|
||||
walk(t, &walker)->dispose();
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
|
300
src/heapwalk.cpp
Normal file
300
src/heapwalk.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
#include "heapwalk.h"
|
||||
|
||||
using namespace vm;
|
||||
|
||||
namespace {
|
||||
|
||||
const uintptr_t PointerShift = log(BytesPerWord);
|
||||
|
||||
class Context;
|
||||
|
||||
class Set: public HeapMap {
|
||||
public:
|
||||
class Entry {
|
||||
public:
|
||||
object value;
|
||||
uint32_t number;
|
||||
int next;
|
||||
};
|
||||
|
||||
static unsigned footprint(unsigned capacity) {
|
||||
return sizeof(Set)
|
||||
+ pad(sizeof(int) * capacity)
|
||||
+ pad(sizeof(Set::Entry) * capacity);
|
||||
}
|
||||
|
||||
Set(Context* context, unsigned capacity):
|
||||
context(context),
|
||||
index(reinterpret_cast<int*>
|
||||
(reinterpret_cast<uint8_t*>(this)
|
||||
+ sizeof(Set))),
|
||||
entries(reinterpret_cast<Entry*>
|
||||
(reinterpret_cast<uint8_t*>(index)
|
||||
+ pad(sizeof(int) * capacity))),
|
||||
size(0),
|
||||
capacity(capacity)
|
||||
{ }
|
||||
|
||||
virtual int find(object value);
|
||||
|
||||
virtual void dispose();
|
||||
|
||||
Context* context;
|
||||
int* index;
|
||||
Entry* entries;
|
||||
unsigned size;
|
||||
unsigned capacity;
|
||||
};
|
||||
|
||||
class Stack {
|
||||
public:
|
||||
class Entry {
|
||||
public:
|
||||
object value;
|
||||
int offset;
|
||||
};
|
||||
|
||||
static const unsigned Capacity = 4096;
|
||||
|
||||
Stack(Stack* next): next(next), entryCount(0) { }
|
||||
|
||||
Stack* next;
|
||||
unsigned entryCount;
|
||||
Entry entries[Capacity];
|
||||
};
|
||||
|
||||
class Context {
|
||||
public:
|
||||
Context(Thread* thread):
|
||||
thread(thread), objects(0), stack(0)
|
||||
{ }
|
||||
|
||||
~Context() {
|
||||
while (stack) {
|
||||
Stack* dead = stack;
|
||||
stack = dead->next;
|
||||
thread->m->heap->free(stack, sizeof(Stack));
|
||||
}
|
||||
}
|
||||
|
||||
Thread* thread;
|
||||
Set* objects;
|
||||
Stack* stack;
|
||||
};
|
||||
|
||||
void
|
||||
push(Context* c, object p, int offset)
|
||||
{
|
||||
if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) {
|
||||
c->stack = new (c->thread->m->heap->allocate(sizeof(Stack)))
|
||||
Stack(c->stack);
|
||||
}
|
||||
Stack::Entry* e = c->stack->entries + (c->stack->entryCount++);
|
||||
e->value = p;
|
||||
e->offset = offset;
|
||||
}
|
||||
|
||||
bool
|
||||
pop(Context* c, object* p, int* offset)
|
||||
{
|
||||
if (c->stack) {
|
||||
if (c->stack->entryCount == 0) {
|
||||
if (c->stack->next) {
|
||||
Stack* dead = c->stack;
|
||||
c->stack = dead->next;
|
||||
c->thread->m->heap->free(dead, sizeof(Stack));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Stack::Entry* e = c->stack->entries + (--c->stack->entryCount);
|
||||
*p = e->value;
|
||||
*offset = e->offset;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
hash(object p, unsigned capacity)
|
||||
{
|
||||
return (reinterpret_cast<uintptr_t>(p) >> PointerShift)
|
||||
& (capacity - 1);
|
||||
}
|
||||
|
||||
Set::Entry*
|
||||
find(Context* c, object p)
|
||||
{
|
||||
if (c->objects == 0) return 0;
|
||||
|
||||
for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) {
|
||||
Set::Entry* e = c->objects->entries + i;
|
||||
if (e->value == p) {
|
||||
return e;
|
||||
}
|
||||
i = e->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Set::find(object value)
|
||||
{
|
||||
Set::Entry* e = ::find(context, value);
|
||||
if (e) {
|
||||
return e->number;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Set::dispose()
|
||||
{
|
||||
context->thread->m->heap->free(this, footprint(capacity));
|
||||
}
|
||||
|
||||
Set::Entry*
|
||||
add(Context* c UNUSED, Set* set, object p, uint32_t number)
|
||||
{
|
||||
assert(c->thread, set->size < set->capacity);
|
||||
|
||||
unsigned index = hash(p, set->capacity);
|
||||
|
||||
int offset = set->size++;
|
||||
Set::Entry* e = set->entries + offset;
|
||||
e->value = p;
|
||||
e->number = number;
|
||||
e->next = set->index[index];
|
||||
set->index[index] = offset;
|
||||
return e;
|
||||
}
|
||||
|
||||
Set::Entry*
|
||||
add(Context* c, object p)
|
||||
{
|
||||
if (c->objects == 0 or c->objects->size == c->objects->capacity) {
|
||||
unsigned capacity;
|
||||
if (c->objects) {
|
||||
capacity = c->objects->capacity * 2;
|
||||
} else {
|
||||
capacity = 4096; // must be power of two
|
||||
}
|
||||
|
||||
Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity)))
|
||||
Set(c, capacity);
|
||||
|
||||
memset(set->index, 0xFF, sizeof(int) * capacity);
|
||||
|
||||
if (c->objects) {
|
||||
for (unsigned i = 0; i < c->objects->capacity; ++i) {
|
||||
for (int j = c->objects->index[i]; j >= 0;) {
|
||||
Set::Entry* e = c->objects->entries + j;
|
||||
add(c, set, e->value, e->number);
|
||||
j = e->next;
|
||||
}
|
||||
}
|
||||
|
||||
c->thread->m->heap->free
|
||||
(c->objects, Set::footprint(c->objects->capacity));
|
||||
}
|
||||
|
||||
c->objects = set;
|
||||
}
|
||||
|
||||
return add(c, c->objects, p, 0);
|
||||
}
|
||||
|
||||
inline object
|
||||
get(object o, unsigned offsetInWords)
|
||||
{
|
||||
return static_cast<object>
|
||||
(mask(cast<void*>(o, offsetInWords * BytesPerWord)));
|
||||
}
|
||||
|
||||
unsigned
|
||||
objectSize(Thread* t, object o)
|
||||
{
|
||||
unsigned n = baseSize(t, o, objectClass(t, o));
|
||||
if (objectExtended(t, o)) {
|
||||
++ n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
walk(Context* c, HeapWalker* w, object p)
|
||||
{
|
||||
Thread* t = c->thread;
|
||||
int nextChildOffset;
|
||||
|
||||
w->root();
|
||||
|
||||
visit: {
|
||||
Set::Entry* e = find(c, p);
|
||||
if (e) {
|
||||
w->visitOld(p, e->number);
|
||||
} else {
|
||||
e = add(c, p);
|
||||
e->number = w->visitNew(p);
|
||||
|
||||
nextChildOffset = walkNext(t, p, -1);
|
||||
if (nextChildOffset != -1) {
|
||||
goto children;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goto pop;
|
||||
|
||||
children: {
|
||||
w->push(nextChildOffset);
|
||||
push(c, p, nextChildOffset);
|
||||
p = get(p, nextChildOffset);
|
||||
goto visit;
|
||||
}
|
||||
|
||||
pop: {
|
||||
if (pop(c, &p, &nextChildOffset)) {
|
||||
w->pop();
|
||||
nextChildOffset = walkNext(t, p, nextChildOffset);
|
||||
if (nextChildOffset >= 0) {
|
||||
goto children;
|
||||
} else {
|
||||
goto pop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace vm {
|
||||
|
||||
HeapMap*
|
||||
walk(Thread* t, HeapWalker* w)
|
||||
{
|
||||
Context context(t);
|
||||
|
||||
class Visitor: public Heap::Visitor {
|
||||
public:
|
||||
Visitor(Context* c, HeapWalker* w): c(c), w(w) { }
|
||||
|
||||
virtual void visit(void* p) {
|
||||
walk(c, w, static_cast<object>(mask(*static_cast<void**>(p))));
|
||||
}
|
||||
|
||||
Context* c;
|
||||
HeapWalker* w;
|
||||
} v(&context, w);
|
||||
|
||||
add(&context, 0)->number = w->visitNew(0);
|
||||
|
||||
visitRoots(t->m, &v);
|
||||
|
||||
return context.objects;
|
||||
}
|
||||
|
||||
} // namespace vm
|
23
src/heapwalk.h
Normal file
23
src/heapwalk.h
Normal file
@ -0,0 +1,23 @@
|
||||
#include "machine.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
class HeapMap {
|
||||
public:
|
||||
virtual int find(object value) = 0;
|
||||
virtual void dispose() = 0;
|
||||
};
|
||||
|
||||
class HeapWalker {
|
||||
public:
|
||||
virtual void root() = 0;
|
||||
virtual unsigned visitNew(object value) = 0;
|
||||
virtual void visitOld(object value, unsigned number) = 0;
|
||||
virtual void push(unsigned offset) = 0;
|
||||
virtual void pop() = 0;
|
||||
};
|
||||
|
||||
HeapMap*
|
||||
walk(Thread* t, HeapWalker* w);
|
||||
|
||||
} // namespace vm
|
@ -248,38 +248,6 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
walk(Thread* t, Heap::Walker* w, object o, unsigned start)
|
||||
{
|
||||
object class_ = static_cast<object>(t->m->heap->follow(objectClass(t, o)));
|
||||
object objectMask = static_cast<object>
|
||||
(t->m->heap->follow(classObjectMask(t, class_)));
|
||||
|
||||
if (objectMask) {
|
||||
unsigned fixedSize = classFixedSize(t, class_);
|
||||
unsigned arrayElementSize = classArrayElementSize(t, class_);
|
||||
unsigned arrayLength
|
||||
= (arrayElementSize ?
|
||||
cast<uintptr_t>(o, fixedSize - BytesPerWord) : 0);
|
||||
|
||||
uint32_t mask[intArrayLength(t, objectMask)];
|
||||
memcpy(mask, &intArrayBody(t, objectMask, 0),
|
||||
intArrayLength(t, objectMask) * 4);
|
||||
|
||||
walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start);
|
||||
} else if (classVmFlags(t, class_) & SingletonFlag) {
|
||||
unsigned length = singletonLength(t, o);
|
||||
if (length) {
|
||||
walk(t, w, singletonMask(t, o),
|
||||
(singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start);
|
||||
} else if (start == 0) {
|
||||
w->visit(0);
|
||||
}
|
||||
} else if (start == 0) {
|
||||
w->visit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
finalizerTargetUnreachable(Thread* t, Heap::Visitor* v, object* p)
|
||||
{
|
||||
@ -770,12 +738,8 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool)
|
||||
PROTECT(t, interfaceTable);
|
||||
|
||||
unsigned i = 0;
|
||||
object it = hashMapIterator(t, map);
|
||||
PROTECT(t, it);
|
||||
|
||||
for (; it; it = hashMapIteratorNext(t, it)) {
|
||||
object interface = resolveClass
|
||||
(t, tripleFirst(t, hashMapIteratorNode(t, it)));
|
||||
for (HashMapIterator it(t, map); it.hasMore();) {
|
||||
object interface = resolveClass(t, tripleFirst(t, it.next()));
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
set(t, interfaceTable, ArrayBody + (i * BytesPerWord), interface);
|
||||
@ -1243,10 +1207,8 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool)
|
||||
if (classFlags(t, class_) & ACC_INTERFACE) {
|
||||
PROTECT(t, vtable);
|
||||
|
||||
for (object it = hashMapIterator(t, virtualMap); it;
|
||||
it = hashMapIteratorNext(t, it))
|
||||
{
|
||||
object method = tripleFirst(t, hashMapIteratorNode(t, it));
|
||||
for (HashMapIterator it(t, virtualMap); it.hasMore();) {
|
||||
object method = tripleFirst(t, it.next());
|
||||
assert(t, arrayBody(t, vtable, methodOffset(t, method)) == 0);
|
||||
set(t, vtable, ArrayBody + (methodOffset(t, method) * BytesPerWord),
|
||||
method);
|
||||
@ -2769,6 +2731,38 @@ collect(Thread* t, Heap::CollectionType type)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
walk(Thread* t, Heap::Walker* w, object o, unsigned start)
|
||||
{
|
||||
object class_ = static_cast<object>(t->m->heap->follow(objectClass(t, o)));
|
||||
object objectMask = static_cast<object>
|
||||
(t->m->heap->follow(classObjectMask(t, class_)));
|
||||
|
||||
if (objectMask) {
|
||||
unsigned fixedSize = classFixedSize(t, class_);
|
||||
unsigned arrayElementSize = classArrayElementSize(t, class_);
|
||||
unsigned arrayLength
|
||||
= (arrayElementSize ?
|
||||
cast<uintptr_t>(o, fixedSize - BytesPerWord) : 0);
|
||||
|
||||
uint32_t mask[intArrayLength(t, objectMask)];
|
||||
memcpy(mask, &intArrayBody(t, objectMask, 0),
|
||||
intArrayLength(t, objectMask) * 4);
|
||||
|
||||
::walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start);
|
||||
} else if (classVmFlags(t, class_) & SingletonFlag) {
|
||||
unsigned length = singletonLength(t, o);
|
||||
if (length) {
|
||||
::walk(t, w, singletonMask(t, o),
|
||||
(singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start);
|
||||
} else if (start == 0) {
|
||||
w->visit(0);
|
||||
}
|
||||
} else if (start == 0) {
|
||||
w->visit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
walkNext(Thread* t, object o, int previous)
|
||||
{
|
||||
|
@ -1128,7 +1128,7 @@ class Machine {
|
||||
dispose();
|
||||
}
|
||||
|
||||
static const unsigned HeapPoolSize = 8;
|
||||
static const unsigned HeapPoolSize = 64;
|
||||
|
||||
static const unsigned FixedFootprintThresholdInBytes = 256 * 1024;
|
||||
|
||||
@ -2208,6 +2208,9 @@ intern(Thread* t, object s);
|
||||
void
|
||||
exit(Thread* t);
|
||||
|
||||
void
|
||||
walk(Thread* t, Heap::Walker* w, object o, unsigned start);
|
||||
|
||||
int
|
||||
walkNext(Thread* t, object o, int previous);
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "signal.h"
|
||||
#include "ucontext.h"
|
||||
#include "stdint.h"
|
||||
#include "dirent.h"
|
||||
|
||||
#include "x86.h"
|
||||
#include "system.h"
|
||||
@ -463,6 +464,31 @@ class MySystem: public System {
|
||||
size_t length_;
|
||||
};
|
||||
|
||||
class Directory: public System::Directory {
|
||||
public:
|
||||
Directory(System* s, DIR* directory): s(s), directory(directory) { }
|
||||
|
||||
virtual const char* next() {
|
||||
if (directory) {
|
||||
dirent* e = readdir(directory);
|
||||
if (e) {
|
||||
return e->d_name;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void dispose() {
|
||||
if (directory) {
|
||||
closedir(directory);
|
||||
}
|
||||
s->free(this);
|
||||
}
|
||||
|
||||
System* s;
|
||||
DIR* directory;
|
||||
};
|
||||
|
||||
class Library: public System::Library {
|
||||
public:
|
||||
Library(System* s, void* p, const char* name, unsigned nameLength,
|
||||
@ -658,7 +684,7 @@ class MySystem: public System {
|
||||
virtual Status map(System::Region** region, const char* name) {
|
||||
Status status = 1;
|
||||
|
||||
int fd = open(name, O_RDONLY);
|
||||
int fd = ::open(name, O_RDONLY);
|
||||
if (fd != -1) {
|
||||
struct stat s;
|
||||
int r = fstat(fd, &s);
|
||||
@ -676,19 +702,31 @@ class MySystem: public System {
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual Status open(System::Directory** directory, const char* name) {
|
||||
Status status = 1;
|
||||
|
||||
DIR* d = opendir(name);
|
||||
if (d) {
|
||||
*directory = new (allocate(this, sizeof(Directory))) Directory(this, d);
|
||||
status = 0;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual FileType identify(const char* name) {
|
||||
struct stat s;
|
||||
int r = stat(name, &s);
|
||||
if (r == 0) {
|
||||
if (S_ISREG(s.st_mode)) {
|
||||
return File;
|
||||
return TypeFile;
|
||||
} else if (S_ISDIR(s.st_mode)) {
|
||||
return Directory;
|
||||
return TypeDirectory;
|
||||
} else {
|
||||
return Unknown;
|
||||
return TypeUnknown;
|
||||
}
|
||||
} else {
|
||||
return DoesNotExist;
|
||||
return TypeDoesNotExist;
|
||||
}
|
||||
}
|
||||
|
||||
|
15
src/system.h
15
src/system.h
@ -20,10 +20,10 @@ class System {
|
||||
typedef intptr_t Status;
|
||||
|
||||
enum FileType {
|
||||
Unknown,
|
||||
DoesNotExist,
|
||||
File,
|
||||
Directory
|
||||
TypeUnknown,
|
||||
TypeDoesNotExist,
|
||||
TypeFile,
|
||||
TypeDirectory
|
||||
};
|
||||
|
||||
class Thread {
|
||||
@ -79,6 +79,12 @@ class System {
|
||||
virtual void dispose() = 0;
|
||||
};
|
||||
|
||||
class Directory {
|
||||
public:
|
||||
virtual const char* next() = 0;
|
||||
virtual void dispose() = 0;
|
||||
};
|
||||
|
||||
class Library {
|
||||
public:
|
||||
virtual void* resolve(const char* function) = 0;
|
||||
@ -128,6 +134,7 @@ class System {
|
||||
unsigned returnType) = 0;
|
||||
virtual Status map(Region**, const char* name) = 0;
|
||||
virtual FileType identify(const char* name) = 0;
|
||||
virtual Status open(Directory**, const char* name) = 0;
|
||||
virtual Status load(Library**, const char* name, bool mapName) = 0;
|
||||
virtual char pathSeparator() = 0;
|
||||
virtual int64_t now() = 0;
|
||||
|
@ -73,11 +73,6 @@
|
||||
(type weakHashMap
|
||||
(extends hashMap))
|
||||
|
||||
(type hashMapIterator
|
||||
(object map)
|
||||
(object node)
|
||||
(unsigned index))
|
||||
|
||||
(type list
|
||||
(uint32_t size)
|
||||
(object front)
|
||||
|
34
src/util.cpp
34
src/util.cpp
@ -442,40 +442,6 @@ hashMapRemove(Thread* t, object map, object key,
|
||||
return o;
|
||||
}
|
||||
|
||||
object
|
||||
hashMapIterator(Thread* t, object map)
|
||||
{
|
||||
object array = hashMapArray(t, map);
|
||||
if (array) {
|
||||
for (unsigned i = 0; i < arrayLength(t, array); ++i) {
|
||||
if (arrayBody(t, array, i)) {
|
||||
return makeHashMapIterator(t, map, arrayBody(t, array, i), i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
object
|
||||
hashMapIteratorNext(Thread* t, object it)
|
||||
{
|
||||
object map = hashMapIteratorMap(t, it);
|
||||
object node = hashMapIteratorNode(t, it);
|
||||
unsigned index = hashMapIteratorIndex(t, it);
|
||||
|
||||
if (tripleThird(t, node)) {
|
||||
return makeHashMapIterator(t, map, tripleThird(t, node), index);
|
||||
} else {
|
||||
object array = hashMapArray(t, map);
|
||||
for (unsigned i = index; i < arrayLength(t, array); ++i) {
|
||||
if (arrayBody(t, array, i)) {
|
||||
return makeHashMapIterator(t, map, arrayBody(t, array, i), i + 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
listAppend(Thread* t, object list, object value)
|
||||
{
|
||||
|
46
src/util.h
46
src/util.h
@ -92,6 +92,52 @@ treeInsertNode(Thread* t, object tree, intptr_t key, object node,
|
||||
object sentinal,
|
||||
intptr_t (*compare)(Thread* t, intptr_t key, object b));
|
||||
|
||||
class HashMapIterator: public Thread::Protector {
|
||||
public:
|
||||
HashMapIterator(Thread* t, object map):
|
||||
Protector(t), map(map), node(0), index(0)
|
||||
{
|
||||
find();
|
||||
}
|
||||
|
||||
void find() {
|
||||
object array = hashMapArray(t, map);
|
||||
for (unsigned i = index; i < arrayLength(t, array); ++i) {
|
||||
if (arrayBody(t, array, i)) {
|
||||
node = arrayBody(t, array, i);
|
||||
index = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasMore() {
|
||||
return node != 0;
|
||||
}
|
||||
|
||||
object next() {
|
||||
if (node) {
|
||||
object n = node;
|
||||
if (tripleThird(t, node)) {
|
||||
node = tripleThird(t, node);
|
||||
} else {
|
||||
find();
|
||||
}
|
||||
return n;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(Heap::Visitor* v) {
|
||||
v->visit(&map);
|
||||
v->visit(&node);
|
||||
}
|
||||
|
||||
object map;
|
||||
object node;
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
} // vm
|
||||
|
||||
#endif//UTIL_H
|
||||
|
Loading…
Reference in New Issue
Block a user