1647 lines
46 KiB
Plaintext
1647 lines
46 KiB
Plaintext
|
package slangc.api;
|
||
|
|
||
|
import slang.data.Map;
|
||
|
import slangc.bytecode.FieldSignature;
|
||
|
import slangc.bytecode.MethodSignature;
|
||
|
import slangc.bytecode.TypeSignature;
|
||
|
import slangc.sdk.SimpleBytecodeOutput;
|
||
|
|
||
|
public class BytecodeHeap {
|
||
|
private Record[] records = new Record[0];
|
||
|
private int numberFree;
|
||
|
private final int maximumRecords;
|
||
|
private Map<String, ConstString> constStringMap = new Map<String, ConstString>(99999);
|
||
|
private Map<String, ObjectLike> constStringUnprocessedMap = new Map<String, ObjectLike>();
|
||
|
private Map<String, ObjectLike> packageMap = new Map<String, ObjectLike>(99);
|
||
|
private Map<TypeSignature, ObjectLike> typeSignatureMap = new Map<TypeSignature, ObjectLike>(999);
|
||
|
private Map<FieldSignature, ObjectLike> fieldSignatureMap = new Map<FieldSignature, ObjectLike>(9999);
|
||
|
private Map<MethodSignature, ObjectLike> methodSignatureMap = new Map<MethodSignature, ObjectLike>(12345);
|
||
|
private Map<String, ObjectLike> constStringInt32Map = new Map<String, ObjectLike>();
|
||
|
private Map<String, ObjectLike> constStringInt64Map = new Map<String, ObjectLike>();
|
||
|
private Map<String, ObjectLike> constStringUint32Map = new Map<String, ObjectLike>();
|
||
|
private Map<String, ObjectLike> constStringUint64Map = new Map<String, ObjectLike>();
|
||
|
private Map<String, ObjectLike> constStringFloat32Map = new Map<String, ObjectLike>();
|
||
|
private Map<String, ObjectLike> constStringFloat64Map = new Map<String, ObjectLike>();
|
||
|
|
||
|
public enum RecordType {
|
||
|
INVALID,
|
||
|
|
||
|
STRING_UTF8,
|
||
|
ARRAY_INT8,
|
||
|
ARRAY_INT16,
|
||
|
ARRAY_INT32,
|
||
|
ARRAY_INT64,
|
||
|
ARRAY_FLOAT32,
|
||
|
ARRAY_FLOAT64,
|
||
|
|
||
|
OBJECT_LIKE,
|
||
|
|
||
|
CONST_INT32,
|
||
|
CONST_INT64,
|
||
|
CONST_FLOAT32,
|
||
|
CONST_FLOAT64
|
||
|
}
|
||
|
|
||
|
/** This is used to specify the "class" of an encoded OBJECT_LIKE typeName */
|
||
|
public enum StandardClass {
|
||
|
NULL,
|
||
|
FALSE,
|
||
|
TRUE,
|
||
|
SIMPLEARRAY,
|
||
|
SIMPLETYPEREF,
|
||
|
ARRAYTYPEREF, // TODO...
|
||
|
SPECIALTYPEREF, // TODO...
|
||
|
STATICFIELDREF,
|
||
|
STATICMETHODREF,
|
||
|
INSTANCEFIELDREF,
|
||
|
INSTANCEMETHODREF,
|
||
|
INTERFACEMETHODREF, // TODO...
|
||
|
|
||
|
/** Used to defer number-processing to a secondary stage. This may allow future versions of the
|
||
|
* compiler to work on platforms that don't have full number stack such as JavaScript, as well as
|
||
|
* being a reference encoding for future e.g. bigint/bigdecimal types) */
|
||
|
STRING_INT32,
|
||
|
STRING_INT64,
|
||
|
STRING_FLOAT32,
|
||
|
STRING_FLOAT64,
|
||
|
|
||
|
STRING_UTF8, // Processed UTF-8 string data
|
||
|
STRING_UNPROCESSED, // A string with arbitrary \escape sequences encoded as written in source
|
||
|
CONST_INT32,
|
||
|
CONST_INT64,
|
||
|
CONST_FLOAT32,
|
||
|
CONST_FLOAT64,
|
||
|
|
||
|
ARRAY_INT8,
|
||
|
ARRAY_INT16,
|
||
|
ARRAY_INT32,
|
||
|
ARRAY_INT64,
|
||
|
ARRAY_FLOAT32,
|
||
|
ARRAY_FLOAT64,
|
||
|
ARRAY_BYTECODE, // TODO...
|
||
|
ARRAY_DEBUG16, // TODO...
|
||
|
ARRAY_LABELS, // TODO...
|
||
|
|
||
|
PACKAGE,
|
||
|
TYPE,
|
||
|
STATIC_FIELD,
|
||
|
INSTANCE_FIELD,
|
||
|
STATIC_METHOD,
|
||
|
INSTANCE_METHOD,
|
||
|
INTERFACE_METHOD,
|
||
|
|
||
|
FILE,
|
||
|
FILE_BYTES,
|
||
|
DEPENDS,
|
||
|
PROVIDES,
|
||
|
ENTRYPOINT,
|
||
|
RUNTIMEDATA,
|
||
|
METADATA,
|
||
|
|
||
|
CONSTRUCTORMETHODREF,
|
||
|
STATICINITMETHODREF,
|
||
|
INSTANCE_CONSTRUCTOR,
|
||
|
STATIC_INIT,
|
||
|
|
||
|
STRING_UINT32,
|
||
|
STRING_UINT64
|
||
|
}
|
||
|
|
||
|
public static abstract class Record {
|
||
|
public final BytecodeHeap heap;
|
||
|
public final int recordIndex;
|
||
|
|
||
|
public Record(BytecodeHeap heap, int recordIndex) {
|
||
|
super();
|
||
|
this.heap = heap;
|
||
|
this.recordIndex = recordIndex;
|
||
|
heap.assignRecord(this);
|
||
|
}
|
||
|
|
||
|
public boolean usingCompressedFormat() {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public BytecodeHeap getHeap() {
|
||
|
return heap;
|
||
|
}
|
||
|
|
||
|
public int getRecordIndex() {
|
||
|
return recordIndex;
|
||
|
}
|
||
|
|
||
|
public abstract RecordType getRecordType();
|
||
|
public abstract StandardClass getStandardClass();
|
||
|
|
||
|
public void writeArrayPart(BytecodeOutput o) {
|
||
|
/* No array part by default. */
|
||
|
}
|
||
|
|
||
|
public int elementsLength() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
private int cachedSize = -1;
|
||
|
public void clearCachedSize() {
|
||
|
cachedSize = -1;
|
||
|
}
|
||
|
|
||
|
public int getArraySizeInBytes() {
|
||
|
if (cachedSize >= 0) {
|
||
|
return cachedSize;
|
||
|
}
|
||
|
if (isEmbeddedData()) {
|
||
|
return 0;
|
||
|
}
|
||
|
/*if (elementsLength() == 0) {
|
||
|
return 0;
|
||
|
}*/
|
||
|
BytecodeOutput o = new SimpleBytecodeOutput();
|
||
|
writeArrayPart(o);
|
||
|
int result = o.getCount();
|
||
|
o.endOfFile();
|
||
|
cachedSize = result;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public void writeObjectHead(BytecodeOutput output) {
|
||
|
output.write8((byte) getRecordType().value);
|
||
|
output.write8((byte) getStandardClass().value);
|
||
|
output.write8((byte) 0);
|
||
|
output.write8((byte) 0);
|
||
|
output.write32(0);
|
||
|
}
|
||
|
|
||
|
public boolean isEmbeddedData() {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
protected void innerWriteEmbeddedData(BytecodeOutput output) {
|
||
|
throw new Error("innerWriteEmbeddedData should be overridden for types with embedded data");
|
||
|
}
|
||
|
|
||
|
public final void writeEmbeddedData(BytecodeOutput output) {
|
||
|
if (!isEmbeddedData()) {
|
||
|
throw new Error("This record doesn't have embedded data (a pointer should be written instead)");
|
||
|
}
|
||
|
int countBefore = output.getCount();
|
||
|
|
||
|
innerWriteEmbeddedData(output);
|
||
|
|
||
|
/* Automatically pad if necessary. */
|
||
|
while (output.getCount() < countBefore + 8) {
|
||
|
output.write8((byte)0);
|
||
|
}
|
||
|
|
||
|
if (output.getCount() != countBefore + 8) {
|
||
|
throw new Error("Embedded data was too large! Expected 8 bytes but got " + (output.getCount() - countBefore));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int getTableEntrySize(int compressionLevel) {
|
||
|
switch (compressionLevel) {
|
||
|
case 0:
|
||
|
return 16;
|
||
|
case 1:
|
||
|
return 8;
|
||
|
case 2:
|
||
|
return 4;
|
||
|
case 3:
|
||
|
if (getArraySizeInBytes() < 256) {
|
||
|
return 2;
|
||
|
} else {
|
||
|
return 4;
|
||
|
}
|
||
|
default:
|
||
|
throw new Error("Invalid compression level: " + compressionLevel + " (expecting 0-3)");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public String innerDebugString() {
|
||
|
return "TODO";
|
||
|
}
|
||
|
|
||
|
public String debugString() {
|
||
|
return getRecordType().name() + "#" + recordIndex + "(" + innerDebugString() + ")";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ConstString extends Record {
|
||
|
private final String value;
|
||
|
|
||
|
public ConstString(BytecodeHeap heap, int recordIndex, String value) {
|
||
|
super(heap, recordIndex);
|
||
|
if (value == null) {
|
||
|
throw new Error("Bad string (got null)");
|
||
|
}
|
||
|
this.value = value;
|
||
|
}
|
||
|
|
||
|
public String getStringValue() {
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
public String innerDebugString() {
|
||
|
return "\"" + getStringValue() + "\"";
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.STRING_UTF8;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return StandardClass.STRING_UTF8;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeArrayPart(BytecodeOutput output) {
|
||
|
try {
|
||
|
byte[] bytes = getStringValue().signedBytes();
|
||
|
for (int i = 0; i < bytes.length; i++) {
|
||
|
output.write8(bytes[i]);
|
||
|
}
|
||
|
} catch (Error e) {
|
||
|
throw new Error("Unable to encode string as UTF-8", e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ConstInt32 extends Record {
|
||
|
private final int value;
|
||
|
|
||
|
public ConstInt32(BytecodeHeap heap, int recordIndex, int value) {
|
||
|
super(heap, recordIndex);
|
||
|
this.value = value;
|
||
|
}
|
||
|
|
||
|
public int getValue() {
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.CONST_INT32;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return StandardClass.CONST_INT32;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ConstInt64 extends Record {
|
||
|
private final long value;
|
||
|
|
||
|
public ConstInt64(BytecodeHeap heap, int recordIndex, long value) {
|
||
|
super(heap, recordIndex);
|
||
|
this.value = value;
|
||
|
}
|
||
|
|
||
|
public long getValue() {
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.CONST_INT64;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return StandardClass.CONST_INT64;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ConstFloat32 extends Record {
|
||
|
private final float value;
|
||
|
|
||
|
public ConstFloat32(BytecodeHeap heap, int recordIndex, float value) {
|
||
|
super(heap, recordIndex);
|
||
|
this.value = value;
|
||
|
}
|
||
|
|
||
|
public float getValue() {
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.CONST_FLOAT32;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return StandardClass.CONST_FLOAT32;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ConstFloat64 extends Record {
|
||
|
private final double value;
|
||
|
|
||
|
public ConstFloat64(BytecodeHeap heap, int recordIndex, double value) {
|
||
|
super(heap, recordIndex);
|
||
|
this.value = value;
|
||
|
}
|
||
|
|
||
|
public double getValue() {
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.CONST_FLOAT64;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return StandardClass.CONST_FLOAT64;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ObjectLike extends Record {
|
||
|
private final StandardClass standardClass;
|
||
|
private Record[] elements;
|
||
|
|
||
|
public ObjectLike(BytecodeHeap heap, int recordIndex, StandardClass standardClass, int size) {
|
||
|
super(heap, recordIndex);
|
||
|
this.standardClass = standardClass;
|
||
|
elements = new Record[size];
|
||
|
for (int i = 0; i < size; i++) {
|
||
|
elements[i] = heap.getNull();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public String innerDebugString() {
|
||
|
String result = "";
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
if (i > 0) {
|
||
|
result = result + ",";
|
||
|
}
|
||
|
result = result+elements[i].debugString();
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean usingCompressedFormat() {
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
if (elements[i].getRecordIndex() >= 65536) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public void resizeElements(int newSize) {
|
||
|
Record[] newElements = new Record[newSize];
|
||
|
for (int i = 0; i < newElements.length; i++) {
|
||
|
if (i < elements.length) {
|
||
|
newElements[i] = elements[i];
|
||
|
} else {
|
||
|
newElements[i] = getHeap().getNull();
|
||
|
}
|
||
|
}
|
||
|
elements = newElements;
|
||
|
}
|
||
|
|
||
|
public void appendElement(Record r) {
|
||
|
int idx = elementsLength();
|
||
|
resizeElements(idx + 1);
|
||
|
setElement(idx, r);
|
||
|
}
|
||
|
|
||
|
public StandardClass getStandardClass() {
|
||
|
return standardClass;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int elementsLength() {
|
||
|
return elements.length;
|
||
|
}
|
||
|
|
||
|
public Record getElement(int i) {
|
||
|
return elements[i];
|
||
|
}
|
||
|
|
||
|
public void setElement(int i, Record r) {
|
||
|
clearCachedSize();
|
||
|
elements[i] = r;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.OBJECT_LIKE;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeArrayPart(BytecodeOutput output) {
|
||
|
if (usingCompressedFormat()) {
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
output.write16((short) elements[i].getRecordIndex());
|
||
|
}
|
||
|
} else {
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
output.write32(elements[i].getRecordIndex());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ArrayInt8 extends Record {
|
||
|
private final StandardClass standardClass;
|
||
|
private byte[] elements;
|
||
|
|
||
|
public ArrayInt8(BytecodeHeap heap, int recordIndex, StandardClass standardClass, int size) {
|
||
|
super(heap, recordIndex);
|
||
|
this.standardClass = standardClass;
|
||
|
elements = new byte[size];
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int elementsLength() {
|
||
|
return elements.length;
|
||
|
}
|
||
|
|
||
|
public byte getElement(int i) {
|
||
|
return elements[i];
|
||
|
}
|
||
|
|
||
|
public void setElement(int i, byte r) {
|
||
|
clearCachedSize();
|
||
|
elements[i] = r;
|
||
|
}
|
||
|
|
||
|
public void resizeElements(int newSize) {
|
||
|
byte[] newElements = new byte[newSize];
|
||
|
for (int i = 0; i < newElements.length; i++) {
|
||
|
if (i < elements.length) {
|
||
|
newElements[i] = elements[i];
|
||
|
} else {
|
||
|
//newElements[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
elements = newElements;
|
||
|
}
|
||
|
|
||
|
public void appendElement(byte r) {
|
||
|
int idx = elementsLength();
|
||
|
resizeElements(idx + 1);
|
||
|
setElement(idx, r);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.ARRAY_INT8;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return standardClass;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeArrayPart(BytecodeOutput output) {
|
||
|
boolean cmp = usingCompressedFormat();
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
output.write8(elements[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean usingCompressedFormat() {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ArrayInt16 extends Record {
|
||
|
private final StandardClass standardClass;
|
||
|
private short[] elements;
|
||
|
|
||
|
public ArrayInt16(BytecodeHeap heap, int recordIndex, StandardClass standardClass, int size) {
|
||
|
super(heap, recordIndex);
|
||
|
this.standardClass = standardClass;
|
||
|
elements = new short[size];
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int elementsLength() {
|
||
|
return elements.length;
|
||
|
}
|
||
|
|
||
|
public short getElement(int i) {
|
||
|
return elements[i];
|
||
|
}
|
||
|
|
||
|
public void setElement(int i, short r) {
|
||
|
clearCachedSize();
|
||
|
elements[i] = r;
|
||
|
}
|
||
|
|
||
|
public void resizeElements(int newSize) {
|
||
|
short[] newElements = new short[newSize];
|
||
|
for (int i = 0; i < newElements.length; i++) {
|
||
|
if (i < elements.length) {
|
||
|
newElements[i] = elements[i];
|
||
|
} else {
|
||
|
//newElements[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
elements = newElements;
|
||
|
}
|
||
|
|
||
|
public void appendElement(short r) {
|
||
|
int idx = elementsLength();
|
||
|
resizeElements(idx + 1);
|
||
|
setElement(idx, r);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.ARRAY_INT16;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return standardClass;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeArrayPart(BytecodeOutput output) {
|
||
|
boolean cmp = usingCompressedFormat();
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
if (cmp) {
|
||
|
output.write8((byte)elements[i]);
|
||
|
} else {
|
||
|
output.write16(elements[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean usingCompressedFormat() {
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
byte tmp = (byte) elements[i];
|
||
|
if ((((short) tmp) & 0xFF) != elements[i]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class ArrayInt32 extends Record {
|
||
|
private final StandardClass standardClass;
|
||
|
private int[] elements;
|
||
|
|
||
|
public ArrayInt32(BytecodeHeap heap, int recordIndex, StandardClass standardClass, int size) {
|
||
|
super(heap, recordIndex);
|
||
|
this.standardClass = standardClass;
|
||
|
elements = new int[size];
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int elementsLength() {
|
||
|
return elements.length;
|
||
|
}
|
||
|
|
||
|
public int getElement(int i) {
|
||
|
return elements[i];
|
||
|
}
|
||
|
|
||
|
public void setElement(int i, int r) {
|
||
|
//if ((r & 0xFFFF) != r) throw new Error("XXX temporary error: Can't compress " + r);
|
||
|
clearCachedSize();
|
||
|
elements[i] = r;
|
||
|
}
|
||
|
|
||
|
public void resizeElements(int newSize) {
|
||
|
int[] newElements = new int[newSize];
|
||
|
for (int i = 0; i < newElements.length; i++) {
|
||
|
if (i < elements.length) {
|
||
|
newElements[i] = elements[i];
|
||
|
} else {
|
||
|
//newElements[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
elements = newElements;
|
||
|
}
|
||
|
|
||
|
public void appendElement(int r) {
|
||
|
int idx = elementsLength();
|
||
|
resizeElements(idx + 1);
|
||
|
setElement(idx, r);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public RecordType getRecordType() {
|
||
|
return RecordType.ARRAY_INT32;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public StandardClass getStandardClass() {
|
||
|
return standardClass;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeArrayPart(BytecodeOutput output) {
|
||
|
boolean cmp = usingCompressedFormat();
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
if (cmp) {
|
||
|
output.write16((short)elements[i]);
|
||
|
} else {
|
||
|
output.write32(elements[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean usingCompressedFormat() {
|
||
|
for (int i = 0; i < elements.length; i++) {
|
||
|
int tmp = elements[i];
|
||
|
if ((tmp & 0xFFFF) != elements[i]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public BytecodeHeap(int maximumRecords, boolean initialise) {
|
||
|
this.maximumRecords = maximumRecords;
|
||
|
|
||
|
if (initialise) {
|
||
|
initialiseHeap();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void initialiseHeap() {
|
||
|
int nullId = nextFreeRecordIndex();
|
||
|
if (nullId != 0) {
|
||
|
throw new Error("Can't initialise heap, null id already seems to exist!");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.NULL, 0).getRecordIndex() != 0) {
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.FALSE, 0).getRecordIndex() != 1) {
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.TRUE, 0).getRecordIndex() != 2) {
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.SIMPLEARRAY, 0).getRecordIndex() != 3) { // Package array
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.SIMPLEARRAY, 0).getRecordIndex() != 4) { // Provides/depends array
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.SIMPLEARRAY, 0).getRecordIndex() != 5) { // Entrypoints array
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.SIMPLEARRAY, 32).getRecordIndex() != 6) { // Runtimedata array
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.SIMPLEARRAY, 0).getRecordIndex() != 7) { // Important metadata array
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
if (newObjectLike(StandardClass.SIMPLEARRAY, 0).getRecordIndex() != 8) { // Other metadata array
|
||
|
throw new Error("Failed to create initial records");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public ObjectLike getNull() {
|
||
|
ObjectLike result = (ObjectLike)records[0];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid null record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getFalse() {
|
||
|
ObjectLike result = (ObjectLike)records[1];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid false record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getTrue() {
|
||
|
ObjectLike result = (ObjectLike)records[2];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid true record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getPackageArray() {
|
||
|
ObjectLike result = (ObjectLike)records[3];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid package-array record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getDependsArray() {
|
||
|
ObjectLike result = (ObjectLike)records[4];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid depends-array record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getEntrypointsArray() {
|
||
|
ObjectLike result = (ObjectLike)records[5];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid entrypoints-array record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getRuntimedataArray() {
|
||
|
ObjectLike result = (ObjectLike)records[6];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid runtimeinfo-array record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getImportantMetadataArray() {
|
||
|
ObjectLike result = (ObjectLike)records[7];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid importantmetadata-array record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getOtherMetadataArray() {
|
||
|
ObjectLike result = (ObjectLike)records[8];
|
||
|
if (result == null) {
|
||
|
throw new Error("Invalid othermetadata-array record");
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public int getCurrentHeapSize() {
|
||
|
return records.length;
|
||
|
}
|
||
|
|
||
|
public int getHeapAllocationGranularity() {
|
||
|
return 1024;
|
||
|
}
|
||
|
|
||
|
public void expandHeap(int atLeast) {
|
||
|
int newLimit = getCurrentHeapSize() + atLeast;
|
||
|
while (newLimit < getMaximumRecords() && (newLimit % getHeapAllocationGranularity()) != 0) {
|
||
|
newLimit++;
|
||
|
}
|
||
|
if (newLimit > getMaximumRecords()) {
|
||
|
throw new Error("Unable to expand heap (hit the maximum of " + getMaximumRecords() + " records)");
|
||
|
}
|
||
|
numberFree += (newLimit - getCurrentHeapSize());
|
||
|
Record[] newRecords = new Record[newLimit];
|
||
|
for (int i = 0; i < getCurrentHeapSize(); i++) {
|
||
|
newRecords[i] = records[i];
|
||
|
}
|
||
|
records = newRecords;
|
||
|
}
|
||
|
|
||
|
public int objectTableSize(int compressionLevel) {
|
||
|
return objectTableSize(compressionLevel, getCurrentHeapSize());
|
||
|
}
|
||
|
|
||
|
public int objectTableSize(int compressionLevel, int upto) {
|
||
|
switch (compressionLevel) {
|
||
|
case 0:
|
||
|
return (highestUsedRecordIndex() + 1) * 16;
|
||
|
case 1:
|
||
|
return (highestUsedRecordIndex() + 1) * 8;
|
||
|
case 2:
|
||
|
return (highestUsedRecordIndex() + 1) * 4;
|
||
|
default: {
|
||
|
int sz = 0;
|
||
|
for (int i = 0; i < upto; i++) {
|
||
|
if (records[i] != null) {
|
||
|
sz += records[i].getTableEntrySize(compressionLevel);
|
||
|
}
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void writeObjectTable(BytecodeOutput output, int compressionLevel, int dataGranularity) {
|
||
|
int dataOffset = 0;
|
||
|
for (int i = 0; i <= highestUsedRecordIndex(); i++) {
|
||
|
Record r = records[i];
|
||
|
if (r == null) {
|
||
|
switch (compressionLevel) {
|
||
|
case 0:
|
||
|
output.write64(0);
|
||
|
output.write64(0);
|
||
|
break;
|
||
|
case 1:
|
||
|
output.write64(0);
|
||
|
break;
|
||
|
case 2:
|
||
|
output.write32(0);
|
||
|
break;
|
||
|
case 3:
|
||
|
output.write16((short) 0);
|
||
|
break;
|
||
|
default:
|
||
|
throw new Error("Bad compression level " + compressionLevel);
|
||
|
}
|
||
|
} else {
|
||
|
int cl = r.getStandardClass().value;
|
||
|
if (r.usingCompressedFormat()) {
|
||
|
cl |= (1<<7);
|
||
|
}
|
||
|
int len = r.getArraySizeInBytes();
|
||
|
switch (compressionLevel) {
|
||
|
case 0:
|
||
|
output.write32(cl);
|
||
|
output.write32(len);
|
||
|
output.write64(dataOffset);
|
||
|
break;
|
||
|
case 1:
|
||
|
output.write32((len << 8) | (cl & 0xFF));
|
||
|
output.write32(dataOffset);
|
||
|
break;
|
||
|
case 2:
|
||
|
output.write32((len << 8) | (cl & 0xFF));
|
||
|
break;
|
||
|
case 3:
|
||
|
if (r.getTableEntrySize(compressionLevel) == 2) {
|
||
|
output.write16((short) ((len << 8) | ((cl | (1<<6)) & 0xFF)));
|
||
|
} else {
|
||
|
output.write32((len << 8) | (cl & 0xFF));
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
throw new Error("Bad compression level " + compressionLevel);
|
||
|
}
|
||
|
|
||
|
dataOffset += records[i].getArraySizeInBytes();
|
||
|
while ((dataOffset % dataGranularity) != 0) {
|
||
|
dataOffset++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int objectTableIndexSize(int compressionLevel, int maxpool) {
|
||
|
switch (compressionLevel) {
|
||
|
case 3: {
|
||
|
int sz = 0;
|
||
|
int top = this.highestUsedRecordIndex();
|
||
|
for (int i = 0; i <= top; i++) {
|
||
|
if (i % maxpool == 0) {
|
||
|
sz += 16;
|
||
|
}
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
default:
|
||
|
return 0; // An index is not required unless the object table is compressed
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int writeObjectTableIndex(BytecodeOutput output, int compressionLevel, int maxpool, int dataGranularity) {
|
||
|
switch (compressionLevel) {
|
||
|
case 3: {
|
||
|
int sz = 0;
|
||
|
int tableoffset = 0;
|
||
|
int top = this.highestUsedRecordIndex();
|
||
|
boolean writtenDots = false;
|
||
|
for (int i = 0; i <= top; i++) {
|
||
|
Record r = records[i];
|
||
|
if (i % maxpool == 0) {
|
||
|
output.write64(tableoffset);
|
||
|
output.write64(dataOffset(i, dataGranularity));
|
||
|
if (i > maxpool*4 && i < top-maxpool*4) {
|
||
|
if (!writtenDots) {
|
||
|
Log.line("...");
|
||
|
writtenDots = true;
|
||
|
}
|
||
|
} else {
|
||
|
Log.line("Writing index #" + (i/maxpool) + ": table offset " + tableoffset + " data offset " + dataOffset(i, dataGranularity));
|
||
|
}
|
||
|
sz += 16;
|
||
|
}
|
||
|
tableoffset += r.getTableEntrySize(compressionLevel);
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
default:
|
||
|
return 0; // An index is not required unless the object table is compressed
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void writeData(BytecodeOutput output, int granularity) {
|
||
|
int offset = 0;
|
||
|
for (int i = 0; i <= highestUsedRecordIndex(); i++) {
|
||
|
Record r = records[i];
|
||
|
if (r != null) {
|
||
|
r.writeArrayPart(output);
|
||
|
offset += r.getArraySizeInBytes();
|
||
|
while ((offset % granularity) != 0) {
|
||
|
output.write8((byte)0);
|
||
|
offset++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int fileHeaderSize(int compressionLevel, int maxpool, int dataGranularity, int sectionGranularity) {
|
||
|
return 64 * 4;
|
||
|
}
|
||
|
|
||
|
private void writeStringTag(BytecodeOutput output, String data, int size) {
|
||
|
try {
|
||
|
byte[] x = data.signedBytes();
|
||
|
if (x.length > size) {
|
||
|
throw new Error("String is too long (expected maximum of " + size + " bytes but got " + x.length + " bytes)");
|
||
|
}
|
||
|
for (int i = 0; i < size; i++) {
|
||
|
output.write8((byte) (i < x.length ? x[i] : 0));
|
||
|
}
|
||
|
} catch (Error e) {
|
||
|
throw new Error("Unable to encode string (system error?)", e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void writeFileHeader(BytecodeOutput output, String tagdata, int compressionLevel, int maxpool,
|
||
|
int dataGranularity, int sectionGranularity) {
|
||
|
// Section 0 (file top header)
|
||
|
writeStringTag(output, tagdata, 32); // Optional user-defined tag, may reference interpreter on Unix-like systems
|
||
|
writeStringTag(output, "BYTECODE FILE01", 16); // Magic number
|
||
|
output.write8((byte)1); // File format version
|
||
|
output.write8((byte)4); // Number of file headers (including this top-header part)
|
||
|
output.write16((short)64);
|
||
|
output.write32(0); // Reserved for checksum
|
||
|
output.write64(fileSize(compressionLevel, maxpool, dataGranularity, sectionGranularity));
|
||
|
|
||
|
int sectionDataOffset = fileHeaderSize(compressionLevel, maxpool, dataGranularity, sectionGranularity);
|
||
|
while ((sectionDataOffset % sectionGranularity) != 0) {
|
||
|
sectionDataOffset++;
|
||
|
}
|
||
|
|
||
|
// Section 1 (index, if provided)
|
||
|
if (objectTableIndexSize(compressionLevel, maxpool) == 0) {
|
||
|
for (int i = 0; i < 64; i++) {
|
||
|
output.write8((byte)0);
|
||
|
}
|
||
|
} else {
|
||
|
output.write32(maxpool);
|
||
|
output.write32(compressionLevel);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
|
||
|
writeStringTag(output, "INDEX", 8);
|
||
|
output.write64(0);
|
||
|
output.write64(sectionDataOffset);
|
||
|
output.write64(objectTableIndexSize(compressionLevel, maxpool));
|
||
|
sectionDataOffset += objectTableIndexSize(compressionLevel, maxpool);
|
||
|
while ((sectionDataOffset % sectionGranularity) != 0) {
|
||
|
sectionDataOffset++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Section 2 (object table)
|
||
|
output.write32(highestUsedRecordIndex() + 1);
|
||
|
output.write32(compressionLevel);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
|
||
|
writeStringTag(output, "TABLE", 8);
|
||
|
output.write64(0);
|
||
|
output.write64(sectionDataOffset);
|
||
|
output.write64(objectTableSize(compressionLevel));
|
||
|
sectionDataOffset += objectTableSize(compressionLevel);
|
||
|
while ((sectionDataOffset % sectionGranularity) != 0) {
|
||
|
sectionDataOffset++;
|
||
|
}
|
||
|
|
||
|
// Section 3 (object data)
|
||
|
|
||
|
output.write32(dataGranularity);//sectionDataOffset += objectTableIndexSize(compressionLevel, maxpool);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
output.write32(0);
|
||
|
|
||
|
writeStringTag(output, "DATA", 8);
|
||
|
output.write64(0);
|
||
|
output.write64(sectionDataOffset);
|
||
|
output.write64(dataSize(dataGranularity));
|
||
|
sectionDataOffset += dataSize(dataGranularity);
|
||
|
while ((sectionDataOffset % sectionGranularity) != 0) {
|
||
|
sectionDataOffset++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int fileSize(int compressionLevel, int maxpool, int dataGranularity, int sectionGranularity) {
|
||
|
int sz = fileHeaderSize(compressionLevel, maxpool, dataGranularity, sectionGranularity);
|
||
|
|
||
|
while (sz % sectionGranularity != 0) {
|
||
|
sz++;
|
||
|
}
|
||
|
|
||
|
sz += objectTableIndexSize(compressionLevel, maxpool);
|
||
|
|
||
|
while (sz % sectionGranularity != 0) {
|
||
|
sz++;
|
||
|
}
|
||
|
|
||
|
sz += objectTableSize(compressionLevel);
|
||
|
|
||
|
while (sz % sectionGranularity != 0) {
|
||
|
sz++;
|
||
|
}
|
||
|
|
||
|
sz += dataSize(dataGranularity);
|
||
|
|
||
|
while (sz % sectionGranularity != 0) {
|
||
|
sz++;
|
||
|
}
|
||
|
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
public void writeAll(BytecodeOutput output, String tagdata, int compressionLevel, int maxpool, int dataGranularity, int sectionGranularity) {
|
||
|
Log.line("aA");
|
||
|
int base = output.getCount();
|
||
|
int expectedOffset = 0;
|
||
|
|
||
|
Log.line("A");
|
||
|
writeFileHeader(output, tagdata, compressionLevel, maxpool, dataGranularity, sectionGranularity);
|
||
|
Log.line("B");
|
||
|
expectedOffset = fileHeaderSize(compressionLevel, maxpool, dataGranularity, sectionGranularity);
|
||
|
Log.line("C");
|
||
|
while (expectedOffset % sectionGranularity != 0) {
|
||
|
output.write8((byte)0);
|
||
|
expectedOffset++;
|
||
|
}
|
||
|
Log.line("D");
|
||
|
|
||
|
writeObjectTableIndex(output, compressionLevel, maxpool, dataGranularity);
|
||
|
expectedOffset += objectTableIndexSize(compressionLevel, maxpool);
|
||
|
while (expectedOffset % sectionGranularity != 0) {
|
||
|
output.write8((byte)0);
|
||
|
expectedOffset++;
|
||
|
}
|
||
|
Log.line("E");
|
||
|
|
||
|
writeObjectTable(output, compressionLevel, dataGranularity);
|
||
|
expectedOffset += objectTableSize(compressionLevel);
|
||
|
while (expectedOffset % sectionGranularity != 0) {
|
||
|
output.write8((byte)0);
|
||
|
expectedOffset++;
|
||
|
}
|
||
|
Log.line("F");
|
||
|
|
||
|
writeData(output, dataGranularity);
|
||
|
expectedOffset += dataSize(dataGranularity);
|
||
|
while (expectedOffset % sectionGranularity != 0) {
|
||
|
output.write8((byte)0);
|
||
|
expectedOffset++;
|
||
|
}
|
||
|
Log.line("G");
|
||
|
}
|
||
|
|
||
|
public int dataOffset(int index, int granularity) {
|
||
|
int total = 0;
|
||
|
for (int i = 0; i < index; i++) {
|
||
|
if (records[i] != null) {
|
||
|
total += records[i].getArraySizeInBytes();
|
||
|
while ((total % granularity) != 0) {
|
||
|
total++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return total;
|
||
|
}
|
||
|
|
||
|
public int dataSize(int granularity) {
|
||
|
return dataOffset(getCurrentHeapSize(), granularity);
|
||
|
}
|
||
|
|
||
|
public int highestUsedRecordIndex() {
|
||
|
return highestUsedIndex;
|
||
|
///*
|
||
|
int highestUsed = -1;
|
||
|
for (int i = 0; i < getCurrentHeapSize(); i++) {
|
||
|
if (records[i] != null) {
|
||
|
highestUsed = i;
|
||
|
}
|
||
|
}
|
||
|
return highestUsed;
|
||
|
//*/
|
||
|
}
|
||
|
|
||
|
private int nextFreeHint = 0;
|
||
|
|
||
|
public int nextFreeRecordIndex() {
|
||
|
if (numberFree < 1) {
|
||
|
expandHeap(1);
|
||
|
}
|
||
|
if (numberFree < 1) {
|
||
|
throw new Error("Heap expansion failed unexpectedly");
|
||
|
}
|
||
|
|
||
|
for (int i = nextFreeHint; i < getCurrentHeapSize(); i++) {
|
||
|
if (records[i] == null) {
|
||
|
nextFreeHint = i;
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
throw new Error("Internal free count doesn't match - no free records found");
|
||
|
}
|
||
|
|
||
|
private int highestUsedIndex = -1;
|
||
|
|
||
|
/* This is called internally from the Record constructor, so the creator only needs to find a free index. */
|
||
|
private void assignRecord(Record r) {
|
||
|
assert r.heap == this;
|
||
|
assert records[r.recordIndex] == null;
|
||
|
records[r.recordIndex] = r;
|
||
|
if (r.recordIndex > highestUsedIndex) {
|
||
|
highestUsedIndex = r.recordIndex;
|
||
|
}
|
||
|
numberFree--;
|
||
|
}
|
||
|
|
||
|
public ObjectLike newObjectLike(StandardClass cl, int size) {
|
||
|
int idx = nextFreeRecordIndex();
|
||
|
return new ObjectLike(this, idx, cl, size);
|
||
|
}
|
||
|
|
||
|
public ArrayInt8 newArrayInt8(StandardClass standardClass, int size) {
|
||
|
int idx = nextFreeRecordIndex();
|
||
|
return new ArrayInt8(this, idx, standardClass, size);
|
||
|
}
|
||
|
|
||
|
public ArrayInt16 newArrayInt16(StandardClass standardClass, int size) {
|
||
|
int idx = nextFreeRecordIndex();
|
||
|
return new ArrayInt16(this, idx, standardClass, size);
|
||
|
}
|
||
|
|
||
|
public ArrayInt32 newArrayInt32(StandardClass standardClass, int size) {
|
||
|
int idx = nextFreeRecordIndex();
|
||
|
return new ArrayInt32(this, idx, standardClass, size);
|
||
|
}
|
||
|
|
||
|
public ArrayInt32 newInstructions(int size) {
|
||
|
return newArrayInt32(StandardClass.ARRAY_BYTECODE, size);
|
||
|
}
|
||
|
|
||
|
public ArrayInt32 newLabels(int size) {
|
||
|
return newArrayInt32(StandardClass.ARRAY_LABELS, size);
|
||
|
}
|
||
|
|
||
|
public ArrayInt16 newDebug(int size) {
|
||
|
return newArrayInt16(StandardClass.ARRAY_DEBUG16, size);
|
||
|
}
|
||
|
|
||
|
public ConstString getConstString(String value) {
|
||
|
if (constStringMap.hasKey(value)) {
|
||
|
return constStringMap.get(value);
|
||
|
}
|
||
|
int idx = nextFreeRecordIndex();
|
||
|
ConstString str = new ConstString(this, idx, value);
|
||
|
constStringMap.set(value, str);
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/** Used for encoding quoted strings which may contain escape codes. If the
|
||
|
* string doesn't contain any fancy formatting, then it's encoded directly as
|
||
|
* a regular string constant (in UTF-8 format). Otherwise it's encoded as
|
||
|
* STRING_UNPROCESSED object containing a reference to the string exactly
|
||
|
* as written in the source code (including quotes, since future versions
|
||
|
* may allow string modifiers to specify a certain encoding or even regex
|
||
|
* syntax etc.).
|
||
|
*/
|
||
|
public Record getConstStringLiteral(String value) {
|
||
|
if (value.startsWith("\"") && value.endsWith("\"") && !value.search("\\")) {
|
||
|
return getConstString(value.sub(1, value.ints().length - 1));
|
||
|
}
|
||
|
if (constStringUnprocessedMap.hasKey(value)) {
|
||
|
return constStringUnprocessedMap.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.STRING_UNPROCESSED, 1);
|
||
|
result.setElement(0, strval);
|
||
|
|
||
|
constStringUnprocessedMap.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public Record getConstInt32(String value) {
|
||
|
if (constStringInt32Map.hasKey(value)) {
|
||
|
return constStringInt32Map.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.STRING_INT32, 1);
|
||
|
result.setElement(0, strval);
|
||
|
|
||
|
constStringInt32Map.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public Record getConstInt64(String value) {
|
||
|
if (constStringInt64Map.hasKey(value)) {
|
||
|
return constStringInt64Map.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.STRING_INT64, 1);
|
||
|
result.setElement(0, strval);
|
||
|
|
||
|
constStringInt64Map.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public Record getConstUint32(String value) {
|
||
|
if (constStringUint32Map.hasKey(value)) {
|
||
|
return constStringUint32Map.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.STRING_UINT32, 1);
|
||
|
result.setElement(0, strval);
|
||
|
|
||
|
constStringUint32Map.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public Record getConstUint64(String value) {
|
||
|
if (constStringUint64Map.hasKey(value)) {
|
||
|
return constStringUint64Map.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.STRING_UINT64, 1);
|
||
|
result.setElement(0, strval);
|
||
|
|
||
|
constStringUint64Map.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public Record getConstFloat32(String value) {
|
||
|
if (constStringFloat32Map.hasKey(value)) {
|
||
|
return constStringFloat32Map.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.STRING_FLOAT32, 1);
|
||
|
result.setElement(0, strval);
|
||
|
|
||
|
constStringFloat32Map.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public Record getConstFloat64(String value) {
|
||
|
if (constStringFloat64Map.hasKey(value)) {
|
||
|
return constStringFloat64Map.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.STRING_FLOAT64, 1);
|
||
|
result.setElement(0, strval);
|
||
|
|
||
|
constStringFloat64Map.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getPackage(String value) {
|
||
|
if (packageMap.hasKey(value)) {
|
||
|
return packageMap.get(value);
|
||
|
}
|
||
|
ConstString strval = getConstString(value);
|
||
|
ObjectLike result = newObjectLike(StandardClass.PACKAGE, 3);
|
||
|
result.setElement(0, strval);
|
||
|
result.setElement(1, getNull());
|
||
|
result.setElement(2, newObjectLike(StandardClass.SIMPLEARRAY, 0));
|
||
|
getPackageArray().appendElement(result);
|
||
|
|
||
|
packageMap.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getTypeSignature(TypeSignature value) {
|
||
|
//Log.line("Looking up type signature " + value.toString());
|
||
|
if (typeSignatureMap.hasKey(value)) {
|
||
|
//Log.line("Using " + typeSignatureMap.get(value).debugString());
|
||
|
return typeSignatureMap.get(value);
|
||
|
}
|
||
|
ObjectLike result = newObjectLike(StandardClass.SIMPLETYPEREF, 2);
|
||
|
result.setElement(0, value.packageName == null ? getNull() : (ObjectLike) getConstString(value.packageName));
|
||
|
result.setElement(1, getConstString(value.typeName));
|
||
|
|
||
|
typeSignatureMap.set(value, result);
|
||
|
|
||
|
//Log.line("Created " + typeSignatureMap.get(value).debugString());
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getFieldSignature(FieldSignature value) {
|
||
|
if (fieldSignatureMap.hasKey(value)) {
|
||
|
return fieldSignatureMap.get(value);
|
||
|
}
|
||
|
ObjectLike result = newObjectLike(value.isStatic ? StandardClass.STATICFIELDREF : StandardClass.INSTANCEFIELDREF, 3);
|
||
|
result.setElement(0, getTypeSignature(value.owner));
|
||
|
result.setElement(1, getTypeSignature(value.storageType));
|
||
|
result.setElement(2, getConstString(value.name));
|
||
|
|
||
|
fieldSignatureMap.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getMethodSignature(MethodSignature value) {
|
||
|
if (methodSignatureMap.hasKey(value)) {
|
||
|
return methodSignatureMap.get(value);
|
||
|
}
|
||
|
StandardClass cl;
|
||
|
switch (value.kind) {
|
||
|
case MethodSignature.Kind.CONSTRUCTOR:
|
||
|
cl = StandardClass.CONSTRUCTORMETHODREF;
|
||
|
break;
|
||
|
case MethodSignature.Kind.STATIC_INIT:
|
||
|
cl = StandardClass.STATICINITMETHODREF;
|
||
|
break;
|
||
|
case MethodSignature.Kind.STATIC_METHOD:
|
||
|
cl = StandardClass.STATICMETHODREF;
|
||
|
break;
|
||
|
case MethodSignature.Kind.INSTANCE_METHOD:
|
||
|
cl = StandardClass.INSTANCEMETHODREF;
|
||
|
break;
|
||
|
case MethodSignature.Kind.INTERFACE_METHOD:
|
||
|
cl = StandardClass.INTERFACEMETHODREF;
|
||
|
break;
|
||
|
default:
|
||
|
throw new Error("Internal error: Unrecognised method kind " + value.kind);
|
||
|
}
|
||
|
ObjectLike result = newObjectLike(cl, 4);
|
||
|
result.setElement(0, getTypeSignature(value.owner));
|
||
|
result.setElement(1, getTypeSignature(value.returnType));
|
||
|
result.setElement(2, getConstString(value.name));
|
||
|
if (value.argumentTypes != null && value.argumentTypes.length > 0) {
|
||
|
ObjectLike argtypes = newObjectLike(StandardClass.SIMPLEARRAY, value.argumentTypes.length);
|
||
|
result.setElement(3, argtypes);
|
||
|
for (int i = 0; i < value.argumentTypes.length; i++) {
|
||
|
argtypes.setElement(i, getTypeSignature(value.argumentTypes[i]));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
methodSignatureMap.set(value, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public static enum FileFields {
|
||
|
PACKAGENAME,
|
||
|
INNERNAME,
|
||
|
DATA,
|
||
|
META,
|
||
|
DEBUGNAME
|
||
|
}
|
||
|
|
||
|
public ObjectLike newFile(String packagename, String innername, byte[] data, String typeinfo, String debugname) {
|
||
|
ObjectLike result = newObjectLike(StandardClass.FILE, FileFields.lookupCount(FileFields.class));
|
||
|
result.setElement(FileFields.PACKAGENAME.value, packagename == null ? getNull() : (ObjectLike) getConstString(packagename));
|
||
|
result.setElement(FileFields.INNERNAME.value, getConstString(innername));
|
||
|
result.setElement(FileFields.META.value, typeinfo == null ? getNull() : (ObjectLike) getConstString(typeinfo));
|
||
|
result.setElement(FileFields.DEBUGNAME.value, debugname == null ? getNull() : (ObjectLike) getConstString(debugname));
|
||
|
|
||
|
ArrayInt8 dataarray = newArrayInt8(StandardClass.FILE_BYTES, data.length);
|
||
|
for (int i = 0; i < data.length; i++) {
|
||
|
dataarray.setElement(i, data[i]);
|
||
|
}
|
||
|
result.setElement(FileFields.DATA.value, dataarray);
|
||
|
|
||
|
ObjectLike pkgtypes = (ObjectLike) getPackage(packagename).getElement(2);
|
||
|
pkgtypes.appendElement(result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public static enum DependsFields {
|
||
|
NAME,
|
||
|
VERSION,
|
||
|
HINT,
|
||
|
HASH
|
||
|
}
|
||
|
|
||
|
public ObjectLike newDepends(String name, String version, String hint, String hash) {
|
||
|
ObjectLike result = newObjectLike(StandardClass.DEPENDS, DependsFields.lookupCount(DependsFields.class));
|
||
|
result.setElement(DependsFields.NAME.value, name == null ? getNull() : (ObjectLike) getConstString(name));
|
||
|
result.setElement(DependsFields.VERSION.value, version == null ? getNull() : (ObjectLike) getConstString(version));
|
||
|
result.setElement(DependsFields.HINT.value, hint == null ? getNull() : (ObjectLike) getConstString(hint));
|
||
|
result.setElement(DependsFields.HASH.value, hash == null ? getNull() : (ObjectLike) getConstString(hash));
|
||
|
|
||
|
getDependsArray().appendElement(result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public static enum ProvidesFields {
|
||
|
NAME,
|
||
|
VERSION
|
||
|
}
|
||
|
|
||
|
public ObjectLike newProvides(String name, String version) {
|
||
|
ObjectLike result = newObjectLike(StandardClass.PROVIDES, ProvidesFields.lookupCount(ProvidesFields.class));
|
||
|
result.setElement(ProvidesFields.NAME.value, name == null ? getNull() : (ObjectLike) getConstString(name));
|
||
|
result.setElement(ProvidesFields.VERSION.value, version == null ? getNull() : (ObjectLike) getConstString(version));
|
||
|
|
||
|
getDependsArray().appendElement(result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public static enum MetadataFields {
|
||
|
KEY,
|
||
|
VALUE,
|
||
|
}
|
||
|
|
||
|
public ObjectLike newMetadata(boolean important, String key, String value) {
|
||
|
ObjectLike result = newObjectLike(StandardClass.METADATA, MetadataFields.lookupCount(MetadataFields.class));
|
||
|
result.setElement(MetadataFields.KEY.value, key == null ? getNull() : (ObjectLike) getConstString(key));
|
||
|
result.setElement(MetadataFields.VALUE.value, value == null ? getNull() : (ObjectLike) getConstString(value));
|
||
|
|
||
|
if (important) {
|
||
|
getImportantMetadataArray().appendElement(result);
|
||
|
} else {
|
||
|
getOtherMetadataArray().appendElement(result);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public static enum RuntimedataFields {
|
||
|
TYPE,
|
||
|
FORMATOPTS,
|
||
|
FLAGS
|
||
|
}
|
||
|
|
||
|
public ObjectLike registerRuntimeType(int indexOrNeg, int kind, TypeSignature type, String formatopts, boolean isBuiltin, boolean isNumber, boolean isFloat, boolean isSigned, int nbits) {
|
||
|
int flags = kind;
|
||
|
if (isBuiltin) {
|
||
|
flags |= (1 << 8);
|
||
|
}
|
||
|
if (isNumber) {
|
||
|
flags |= (1 << 9);
|
||
|
}
|
||
|
if (isFloat) {
|
||
|
flags |= (1 << 10);
|
||
|
}
|
||
|
if (isSigned) {
|
||
|
flags |= (1 << 11);
|
||
|
}
|
||
|
flags |= (nbits << 16);
|
||
|
|
||
|
ObjectLike result = newObjectLike(StandardClass.RUNTIMEDATA, RuntimedataFields.lookupCount(RuntimedataFields.class));
|
||
|
//System.err.println("Adding runtime data #" + indexOrNeg + " at index #" + result.getRecordIndex());
|
||
|
result.setElement(RuntimedataFields.TYPE.value, type == null ? getNull() : getTypeSignature(type));
|
||
|
result.setElement(RuntimedataFields.FORMATOPTS.value, formatopts == null ? getNull() : (ObjectLike) getConstString(formatopts));
|
||
|
result.setElement(RuntimedataFields.FLAGS.value, getConstInt32("" + flags));
|
||
|
|
||
|
if (indexOrNeg < 0) {
|
||
|
getRuntimedataArray().appendElement(result);
|
||
|
} else {
|
||
|
getRuntimedataArray().setElement(indexOrNeg, result);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public static enum TypeFields {
|
||
|
SIGNATURE,
|
||
|
FLAGS,
|
||
|
BASE,
|
||
|
INTERFACES,
|
||
|
MEMBERS,
|
||
|
FILENAME
|
||
|
}
|
||
|
|
||
|
public ObjectLike newType(int flags, TypeSignature sig) {
|
||
|
ObjectLike result = newObjectLike(StandardClass.TYPE, TypeFields.lookupCount(TypeFields.class));
|
||
|
result.setElement(0, getTypeSignature(sig));
|
||
|
result.setElement(1, getNull());//newObjectLike(StandardClass.SIMPLEARRAY, 0));
|
||
|
result.setElement(2, getNull());
|
||
|
result.setElement(3, getNull());//newObjectLike(StandardClass.SIMPLEARRAY, 0));
|
||
|
result.setElement(4, getNull());
|
||
|
result.setElement(TypeFields.FLAGS.value, getConstInt32("" + flags));
|
||
|
|
||
|
ObjectLike pkgtypes = (ObjectLike) getPackage(sig.packageName).getElement(2);
|
||
|
pkgtypes.appendElement(result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public void addMember(ObjectLike type, Record member) {
|
||
|
if (type.getElement(TypeFields.MEMBERS.value) == getNull()) {
|
||
|
type.setElement(TypeFields.MEMBERS.value, newObjectLike(StandardClass.SIMPLEARRAY, 0));
|
||
|
}
|
||
|
ObjectLike memberArray = (ObjectLike) type.getElement(TypeFields.MEMBERS.value);
|
||
|
memberArray.appendElement(member);
|
||
|
}
|
||
|
|
||
|
public static enum FieldFields {
|
||
|
SIGNATURE,
|
||
|
FLAGS
|
||
|
}
|
||
|
|
||
|
|
||
|
public ObjectLike newField(ObjectLike type, int flags, FieldSignature sig) {
|
||
|
ObjectLike result = newObjectLike(sig.isStatic ? StandardClass.STATIC_FIELD : StandardClass.INSTANCE_FIELD, FieldFields.lookupCount(FieldFields.class));
|
||
|
result.setElement(FieldFields.SIGNATURE.value, getFieldSignature(sig));
|
||
|
result.setElement(FieldFields.FLAGS.value, getConstInt32("" + flags));
|
||
|
|
||
|
addMember(type, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public static enum MethodFields {
|
||
|
SIGNATURE,
|
||
|
FLAGS,
|
||
|
BYTECODE,
|
||
|
EXCEPTIONS,
|
||
|
DEBUG,
|
||
|
LOCALS,
|
||
|
MAXSTACK,
|
||
|
THROWS
|
||
|
}
|
||
|
static int methodFieldsCount = MethodFields.lookupCount(MethodFields.class);
|
||
|
|
||
|
public ObjectLike newMethod(ObjectLike type, int flags, MethodSignature sig, int nlocals) {
|
||
|
StandardClass t;
|
||
|
switch (sig.kind) {
|
||
|
case MethodSignature.Kind.INSTANCE_METHOD:
|
||
|
t = StandardClass.INSTANCE_METHOD;
|
||
|
break;
|
||
|
case MethodSignature.Kind.STATIC_METHOD:
|
||
|
t = StandardClass.STATIC_METHOD;
|
||
|
break;
|
||
|
case MethodSignature.Kind.CONSTRUCTOR:
|
||
|
t = StandardClass.INSTANCE_CONSTRUCTOR;
|
||
|
break;
|
||
|
case MethodSignature.Kind.STATIC_INIT:
|
||
|
t = StandardClass.STATIC_INIT;
|
||
|
break;
|
||
|
case MethodSignature.Kind.INTERFACE_METHOD:
|
||
|
t = StandardClass.INTERFACE_METHOD;
|
||
|
break;
|
||
|
default:
|
||
|
throw new Error("Unexpected method signature kind: " + sig.kind);
|
||
|
}
|
||
|
ObjectLike result = newObjectLike(t, methodFieldsCount);
|
||
|
result.setElement(MethodFields.SIGNATURE.value, getMethodSignature(sig));
|
||
|
result.setElement(MethodFields.FLAGS.value, getConstInt32("" + flags));
|
||
|
result.setElement(MethodFields.LOCALS.value, getConstInt32("" + nlocals));
|
||
|
|
||
|
addMember(type, result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public int getMaximumRecords() {
|
||
|
return maximumRecords;
|
||
|
}
|
||
|
|
||
|
public ObjectLike getMemberExtendedFlags(ObjectLike member) {
|
||
|
if (member.getElement(MethodFields.FLAGS.value).getStandardClass() != StandardClass.SIMPLEARRAY) {
|
||
|
ObjectLike result = newObjectLike(StandardClass.SIMPLEARRAY, 1);
|
||
|
result.setElement(0, member.getElement(MethodFields.FLAGS.value));
|
||
|
member.setElement(MethodFields.FLAGS.value, result);
|
||
|
}
|
||
|
return (ObjectLike) member.getElement(MethodFields.FLAGS.value);
|
||
|
}
|
||
|
|
||
|
public void addMemberTranslation(ObjectLike member, String language, String name) {
|
||
|
ObjectLike tr = newObjectLike(StandardClass.METADATA, MetadataFields.lookupCount(MetadataFields.class));
|
||
|
tr.setElement(MetadataFields.KEY.value, language == null ? getNull() : (ObjectLike) getConstString(language));
|
||
|
tr.setElement(MetadataFields.VALUE.value, name == null ? getNull() : (ObjectLike) getConstString(name));
|
||
|
getMemberExtendedFlags(member).appendElement(tr);
|
||
|
}
|
||
|
}
|