mirror of
https://github.com/corda/corda.git
synced 2025-01-06 05:04:20 +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)/process.cpp \
|
||||||
$(src)/$(asm).cpp
|
$(src)/$(asm).cpp
|
||||||
|
|
||||||
ifeq ($(heapdump),true)
|
|
||||||
vm-sources += $(src)/heapdump.cpp
|
|
||||||
cflags += -DAVIAN_HEAPDUMP
|
|
||||||
endif
|
|
||||||
|
|
||||||
vm-asm-sources = $(src)/$(asm).S
|
vm-asm-sources = $(src)/$(asm).S
|
||||||
|
|
||||||
ifeq ($(process),compile)
|
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-asm-objects = $(call asm-objects,$(vm-asm-sources),$(src),$(native-build))
|
||||||
vm-objects = $(vm-cpp-objects) $(vm-asm-objects)
|
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-source = $(src)/main.cpp
|
||||||
driver-object = $(native-build)/main.o
|
driver-object = $(native-build)/main.o
|
||||||
driver-dynamic-object = $(native-build)/main-dynamic.o
|
driver-dynamic-object = $(native-build)/main-dynamic.o
|
||||||
@ -259,7 +269,7 @@ args = $(flags) $(input)
|
|||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: $(static-library) $(executable) $(dynamic-library) \
|
build: $(static-library) $(executable) $(dynamic-library) \
|
||||||
$(executable-dynamic) $(classpath-dep) $(test-dep)
|
$(executable-dynamic) $(classpath-dep) $(test-dep) $(bootimage)
|
||||||
|
|
||||||
$(test-classes): $(classpath-dep)
|
$(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
|
$(vm-asm-objects): $(native-build)/%-asm.o: $(src)/%.S
|
||||||
$(compile-asm-object)
|
$(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)
|
$(driver-object): $(driver-source)
|
||||||
$(compile-object)
|
$(compile-object)
|
||||||
|
|
||||||
@ -395,7 +411,20 @@ $(static-library): $(vm-objects) $(jni-objects)
|
|||||||
|
|
||||||
$(executable): \
|
$(executable): \
|
||||||
$(vm-objects) $(classpath-object) $(jni-objects) $(driver-object) \
|
$(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 $(@)"
|
@echo "linking $(@)"
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
$(dlltool) -z $(@).def $(^)
|
$(dlltool) -z $(@).def $(^)
|
||||||
@ -408,7 +437,7 @@ endif
|
|||||||
|
|
||||||
$(dynamic-library): \
|
$(dynamic-library): \
|
||||||
$(vm-objects) $(classpath-object) $(dynamic-object) $(jni-objects) \
|
$(vm-objects) $(classpath-object) $(dynamic-object) $(jni-objects) \
|
||||||
$(boot-object)
|
$(vm-heapwalk-objects) $(boot-object)
|
||||||
@echo "linking $(@)"
|
@echo "linking $(@)"
|
||||||
$(cc) $(^) $(shared) $(lflags) -o $(@)
|
$(cc) $(^) $(shared) $(lflags) -o $(@)
|
||||||
$(strip) $(strip-all) $(@)
|
$(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 {
|
class Element {
|
||||||
public:
|
public:
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
virtual const char* next(unsigned* size) = 0;
|
||||||
|
virtual void dispose() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
Element(): next(0) { }
|
Element(): next(0) { }
|
||||||
|
|
||||||
|
virtual Iterator* iterator() = 0;
|
||||||
virtual System::Region* find(const char* name) = 0;
|
virtual System::Region* find(const char* name) = 0;
|
||||||
virtual bool exists(const char* name) = 0;
|
virtual bool exists(const char* name) = 0;
|
||||||
virtual void dispose() = 0;
|
virtual void dispose() = 0;
|
||||||
@ -70,10 +77,45 @@ class Element {
|
|||||||
|
|
||||||
class DirectoryElement: public Element {
|
class DirectoryElement: public Element {
|
||||||
public:
|
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):
|
DirectoryElement(System* s, const char* name):
|
||||||
s(s), name(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) {
|
virtual System::Region* find(const char* name) {
|
||||||
const char* file = append(s, this->name, "/", name);
|
const char* file = append(s, this->name, "/", name);
|
||||||
System::Region* region;
|
System::Region* region;
|
||||||
@ -91,7 +133,7 @@ class DirectoryElement: public Element {
|
|||||||
const char* file = append(s, this->name, "/", name);
|
const char* file = append(s, this->name, "/", name);
|
||||||
System::FileType type = s->identify(file);
|
System::FileType type = s->identify(file);
|
||||||
s->free(file);
|
s->free(file);
|
||||||
return type != System::DoesNotExist;
|
return type != System::TypeDoesNotExist;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void dispose() {
|
virtual void dispose() {
|
||||||
@ -379,10 +421,37 @@ class JarIndex {
|
|||||||
|
|
||||||
class JarElement: public Element {
|
class JarElement: public Element {
|
||||||
public:
|
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):
|
JarElement(System* s, const char* name):
|
||||||
s(s), name(name), region(0), index(0)
|
s(s), name(name), region(0), index(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
virtual Element::Iterator* iterator() {
|
||||||
|
return new (allocate(s, sizeof(Iterator))) Iterator(s, index);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void init() {
|
virtual void init() {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
System::Region* r;
|
System::Region* r;
|
||||||
@ -512,11 +581,11 @@ parsePath(System* s, const char* path, const char* bootLibrary)
|
|||||||
name[token.length] = 0;
|
name[token.length] = 0;
|
||||||
|
|
||||||
switch (s->identify(name)) {
|
switch (s->identify(name)) {
|
||||||
case System::File: {
|
case System::TypeFile: {
|
||||||
e = new (allocate(s, sizeof(JarElement))) JarElement(s, name);
|
e = new (allocate(s, sizeof(JarElement))) JarElement(s, name);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case System::Directory: {
|
case System::TypeDirectory: {
|
||||||
e = new (allocate(s, sizeof(DirectoryElement)))
|
e = new (allocate(s, sizeof(DirectoryElement)))
|
||||||
DirectoryElement(s, name);
|
DirectoryElement(s, name);
|
||||||
} break;
|
} break;
|
||||||
@ -541,6 +610,38 @@ parsePath(System* s, const char* path, const char* bootLibrary)
|
|||||||
return first;
|
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 {
|
class MyFinder: public Finder {
|
||||||
public:
|
public:
|
||||||
MyFinder(System* system, const char* path, const char* bootLibrary):
|
MyFinder(System* system, const char* path, const char* bootLibrary):
|
||||||
@ -549,6 +650,11 @@ class MyFinder: public Finder {
|
|||||||
pathString(copy(system, path))
|
pathString(copy(system, path))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
virtual IteratorImp* iterator() {
|
||||||
|
return new (allocate(system, sizeof(MyIterator)))
|
||||||
|
MyIterator(system, path_);
|
||||||
|
}
|
||||||
|
|
||||||
virtual System::Region* find(const char* name) {
|
virtual System::Region* find(const char* name) {
|
||||||
for (Element* e = path_; e; e = e->next) {
|
for (Element* e = path_; e; e = e->next) {
|
||||||
System::Region* r = e->find(name);
|
System::Region* r = e->find(name);
|
||||||
|
38
src/finder.h
38
src/finder.h
@ -19,6 +19,44 @@ namespace vm {
|
|||||||
|
|
||||||
class Finder {
|
class Finder {
|
||||||
public:
|
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 System::Region* find(const char* name) = 0;
|
||||||
virtual bool exists(const char* name) = 0;
|
virtual bool exists(const char* name) = 0;
|
||||||
virtual const char* path() = 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
|
There is NO WARRANTY for this software. See license.txt for
|
||||||
details. */
|
details. */
|
||||||
|
|
||||||
#include "machine.h"
|
#include "heapwalk.h"
|
||||||
|
|
||||||
using namespace vm;
|
using namespace vm;
|
||||||
|
|
||||||
namespace {
|
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 {
|
enum {
|
||||||
Root,
|
Root,
|
||||||
Size,
|
Size,
|
||||||
@ -205,100 +22,30 @@ enum {
|
|||||||
Pop
|
Pop
|
||||||
};
|
};
|
||||||
|
|
||||||
inline object
|
void
|
||||||
get(object o, unsigned offsetInWords)
|
write1(FILE* out, uint8_t v)
|
||||||
{
|
{
|
||||||
return static_cast<object>
|
fwrite(&v, 1, 1, out);
|
||||||
(mask(cast<void*>(o, offsetInWords * BytesPerWord)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
write1(Context* c, uint8_t v)
|
write4(FILE* out, uint32_t v)
|
||||||
{
|
|
||||||
fwrite(&v, 1, 1, c->out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
write4(Context* c, uint32_t v)
|
|
||||||
{
|
{
|
||||||
uint8_t b[] = { v >> 24, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF };
|
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
|
void
|
||||||
writeString(Context* c, int8_t* p, unsigned size)
|
writeString(FILE* out, int8_t* p, unsigned size)
|
||||||
{
|
{
|
||||||
write4(c, size);
|
write4(out, size);
|
||||||
fwrite(p, size, 1, c->out);
|
fwrite(p, size, 1, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
objectSize(Thread* t, object o)
|
objectSize(Thread* t, object o)
|
||||||
{
|
{
|
||||||
unsigned n = baseSize(t, o, objectClass(t, o));
|
return extendedSize(t, o, 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -308,22 +55,56 @@ namespace vm {
|
|||||||
void
|
void
|
||||||
dumpHeap(Thread* t, FILE* out)
|
dumpHeap(Thread* t, FILE* out)
|
||||||
{
|
{
|
||||||
Context context(t, out);
|
class Walker: public HeapWalker {
|
||||||
|
|
||||||
class Visitor : public Heap::Visitor {
|
|
||||||
public:
|
public:
|
||||||
Visitor(Context* c): c(c) { }
|
Walker(Thread* t, FILE* out): t(t), out(out), nextNumber(1) { }
|
||||||
|
|
||||||
virtual void visit(void* p) {
|
virtual void root() {
|
||||||
::visit(c, static_cast<object>(mask(*static_cast<void**>(p))));
|
write1(out, Root);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context* c;
|
virtual unsigned visitNew(object p) {
|
||||||
} v(&context);
|
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
|
} // 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
|
void
|
||||||
finalizerTargetUnreachable(Thread* t, Heap::Visitor* v, object* p)
|
finalizerTargetUnreachable(Thread* t, Heap::Visitor* v, object* p)
|
||||||
{
|
{
|
||||||
@ -770,12 +738,8 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool)
|
|||||||
PROTECT(t, interfaceTable);
|
PROTECT(t, interfaceTable);
|
||||||
|
|
||||||
unsigned i = 0;
|
unsigned i = 0;
|
||||||
object it = hashMapIterator(t, map);
|
for (HashMapIterator it(t, map); it.hasMore();) {
|
||||||
PROTECT(t, it);
|
object interface = resolveClass(t, tripleFirst(t, it.next()));
|
||||||
|
|
||||||
for (; it; it = hashMapIteratorNext(t, it)) {
|
|
||||||
object interface = resolveClass
|
|
||||||
(t, tripleFirst(t, hashMapIteratorNode(t, it)));
|
|
||||||
if (UNLIKELY(t->exception)) return;
|
if (UNLIKELY(t->exception)) return;
|
||||||
|
|
||||||
set(t, interfaceTable, ArrayBody + (i * BytesPerWord), interface);
|
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) {
|
if (classFlags(t, class_) & ACC_INTERFACE) {
|
||||||
PROTECT(t, vtable);
|
PROTECT(t, vtable);
|
||||||
|
|
||||||
for (object it = hashMapIterator(t, virtualMap); it;
|
for (HashMapIterator it(t, virtualMap); it.hasMore();) {
|
||||||
it = hashMapIteratorNext(t, it))
|
object method = tripleFirst(t, it.next());
|
||||||
{
|
|
||||||
object method = tripleFirst(t, hashMapIteratorNode(t, it));
|
|
||||||
assert(t, arrayBody(t, vtable, methodOffset(t, method)) == 0);
|
assert(t, arrayBody(t, vtable, methodOffset(t, method)) == 0);
|
||||||
set(t, vtable, ArrayBody + (methodOffset(t, method) * BytesPerWord),
|
set(t, vtable, ArrayBody + (methodOffset(t, method) * BytesPerWord),
|
||||||
method);
|
method);
|
||||||
@ -2769,6 +2731,38 @@ collect(Thread* t, Heap::CollectionType type)
|
|||||||
#endif
|
#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
|
int
|
||||||
walkNext(Thread* t, object o, int previous)
|
walkNext(Thread* t, object o, int previous)
|
||||||
{
|
{
|
||||||
|
@ -1128,7 +1128,7 @@ class Machine {
|
|||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const unsigned HeapPoolSize = 8;
|
static const unsigned HeapPoolSize = 64;
|
||||||
|
|
||||||
static const unsigned FixedFootprintThresholdInBytes = 256 * 1024;
|
static const unsigned FixedFootprintThresholdInBytes = 256 * 1024;
|
||||||
|
|
||||||
@ -2208,6 +2208,9 @@ intern(Thread* t, object s);
|
|||||||
void
|
void
|
||||||
exit(Thread* t);
|
exit(Thread* t);
|
||||||
|
|
||||||
|
void
|
||||||
|
walk(Thread* t, Heap::Walker* w, object o, unsigned start);
|
||||||
|
|
||||||
int
|
int
|
||||||
walkNext(Thread* t, object o, int previous);
|
walkNext(Thread* t, object o, int previous);
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "signal.h"
|
#include "signal.h"
|
||||||
#include "ucontext.h"
|
#include "ucontext.h"
|
||||||
#include "stdint.h"
|
#include "stdint.h"
|
||||||
|
#include "dirent.h"
|
||||||
|
|
||||||
#include "x86.h"
|
#include "x86.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
@ -463,6 +464,31 @@ class MySystem: public System {
|
|||||||
size_t length_;
|
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 {
|
class Library: public System::Library {
|
||||||
public:
|
public:
|
||||||
Library(System* s, void* p, const char* name, unsigned nameLength,
|
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) {
|
virtual Status map(System::Region** region, const char* name) {
|
||||||
Status status = 1;
|
Status status = 1;
|
||||||
|
|
||||||
int fd = open(name, O_RDONLY);
|
int fd = ::open(name, O_RDONLY);
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
struct stat s;
|
struct stat s;
|
||||||
int r = fstat(fd, &s);
|
int r = fstat(fd, &s);
|
||||||
@ -676,19 +702,31 @@ class MySystem: public System {
|
|||||||
return status;
|
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) {
|
virtual FileType identify(const char* name) {
|
||||||
struct stat s;
|
struct stat s;
|
||||||
int r = stat(name, &s);
|
int r = stat(name, &s);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
if (S_ISREG(s.st_mode)) {
|
if (S_ISREG(s.st_mode)) {
|
||||||
return File;
|
return TypeFile;
|
||||||
} else if (S_ISDIR(s.st_mode)) {
|
} else if (S_ISDIR(s.st_mode)) {
|
||||||
return Directory;
|
return TypeDirectory;
|
||||||
} else {
|
} else {
|
||||||
return Unknown;
|
return TypeUnknown;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return DoesNotExist;
|
return TypeDoesNotExist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
src/system.h
15
src/system.h
@ -20,10 +20,10 @@ class System {
|
|||||||
typedef intptr_t Status;
|
typedef intptr_t Status;
|
||||||
|
|
||||||
enum FileType {
|
enum FileType {
|
||||||
Unknown,
|
TypeUnknown,
|
||||||
DoesNotExist,
|
TypeDoesNotExist,
|
||||||
File,
|
TypeFile,
|
||||||
Directory
|
TypeDirectory
|
||||||
};
|
};
|
||||||
|
|
||||||
class Thread {
|
class Thread {
|
||||||
@ -79,6 +79,12 @@ class System {
|
|||||||
virtual void dispose() = 0;
|
virtual void dispose() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Directory {
|
||||||
|
public:
|
||||||
|
virtual const char* next() = 0;
|
||||||
|
virtual void dispose() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class Library {
|
class Library {
|
||||||
public:
|
public:
|
||||||
virtual void* resolve(const char* function) = 0;
|
virtual void* resolve(const char* function) = 0;
|
||||||
@ -128,6 +134,7 @@ class System {
|
|||||||
unsigned returnType) = 0;
|
unsigned returnType) = 0;
|
||||||
virtual Status map(Region**, const char* name) = 0;
|
virtual Status map(Region**, const char* name) = 0;
|
||||||
virtual FileType identify(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 Status load(Library**, const char* name, bool mapName) = 0;
|
||||||
virtual char pathSeparator() = 0;
|
virtual char pathSeparator() = 0;
|
||||||
virtual int64_t now() = 0;
|
virtual int64_t now() = 0;
|
||||||
|
@ -73,11 +73,6 @@
|
|||||||
(type weakHashMap
|
(type weakHashMap
|
||||||
(extends hashMap))
|
(extends hashMap))
|
||||||
|
|
||||||
(type hashMapIterator
|
|
||||||
(object map)
|
|
||||||
(object node)
|
|
||||||
(unsigned index))
|
|
||||||
|
|
||||||
(type list
|
(type list
|
||||||
(uint32_t size)
|
(uint32_t size)
|
||||||
(object front)
|
(object front)
|
||||||
|
34
src/util.cpp
34
src/util.cpp
@ -442,40 +442,6 @@ hashMapRemove(Thread* t, object map, object key,
|
|||||||
return o;
|
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
|
void
|
||||||
listAppend(Thread* t, object list, object value)
|
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,
|
object sentinal,
|
||||||
intptr_t (*compare)(Thread* t, intptr_t key, object b));
|
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
|
} // vm
|
||||||
|
|
||||||
#endif//UTIL_H
|
#endif//UTIL_H
|
||||||
|
Loading…
Reference in New Issue
Block a user