340 lines
13 KiB
Plaintext
340 lines
13 KiB
Plaintext
package slangc.api;
|
|
|
|
import slang.data.List;
|
|
import slang.data.Map;
|
|
import slangc.api.BytecodeHeap;
|
|
import slangc.bytecode.MethodSignature;
|
|
import slangc.bytecode.TypeSignature;
|
|
import slangc.model.FieldModel;
|
|
import slangc.model.Flags;
|
|
import slangc.model.MethodModel;
|
|
import slangc.model.ParameterModel;
|
|
import slangc.model.UserTypeModel;
|
|
import slangc.model.statements.BlockStatement;
|
|
import slangc.model.BuiltinTypeModel;
|
|
|
|
public class BytecodeTarget extends CompilerTarget {
|
|
final BytecodeHeap heap;
|
|
final BytecodeOutput output;
|
|
private final List<ReprocessType> types = new List<ReprocessType>();
|
|
|
|
private final Map<TypeSignature,BytecodeInstructionWriter.StackType> cachedStackTypes = new Map<TypeSignature,BytecodeInstructionWriter.StackType>();
|
|
|
|
|
|
|
|
public BytecodeInstructionWriter.StackType stackType(TypeSignature type) {
|
|
if (!cachedStackTypes.hasKey(type)) {
|
|
BytecodeInstructionWriter.StackType t = calculateStackType(type);
|
|
cachedStackTypes.set(type, t);
|
|
return t;
|
|
}
|
|
return cachedStackTypes.get(type);
|
|
}
|
|
|
|
public BytecodeInstructionWriter.StackType calculateStackType(TypeSignature type) {
|
|
if (type.mappableEquals(TypeSignature.VOID)) {
|
|
return BytecodeInstructionWriter.StackType.VOID;
|
|
} /*else if (type.mappableEquals(getSystem().getDefaultBooleanType().getTypeSignature())) {
|
|
return BytecodeInstructionWriter.StackType.BIT;
|
|
} else if (type.mappableEquals(getSystem().getDefaultFloat32Type().getTypeSignature())) {
|
|
return BytecodeInstructionWriter.StackType.FLOAT32;
|
|
} else if (type.mappableEquals(getSystem().getDefaultFloat64Type().getTypeSignature())) {
|
|
return BytecodeInstructionWriter.StackType.FLOAT64;
|
|
} else if (type.mappableEquals(getSystem().getDefaultInt64Type().getTypeSignature())) {
|
|
return BytecodeInstructionWriter.StackType.INT64;
|
|
} else if (getSystem().matchesTypeOption(TypeOption.Kind.DUCK, type)) {
|
|
return BytecodeInstructionWriter.StackType.DUCK;
|
|
}*/ else if (getSystem().getType(type) instanceof BuiltinTypeModel) {
|
|
for (int i = 0; i < BytecodeInstructionWriter.StackType.lookupCount(BytecodeInstructionWriter.StackType.class); i++) {
|
|
if (getSystem().matchesTypeOption((TypeOption.Kind)TypeOption.Kind.lookup(TypeOption.Kind.class, i), type)) {
|
|
return (BytecodeInstructionWriter.StackType)BytecodeInstructionWriter.StackType.lookup(BytecodeInstructionWriter.StackType.class, i);
|
|
}
|
|
}
|
|
return BytecodeInstructionWriter.StackType.INT32;
|
|
} else {
|
|
for (int i = 0; i < BytecodeInstructionWriter.StackType.lookupCount(BytecodeInstructionWriter.StackType.class); i++) {
|
|
if (getSystem().matchesTypeOption((TypeOption.Kind)TypeOption.Kind.lookup(TypeOption.Kind.class, i), type)) {
|
|
return (BytecodeInstructionWriter.StackType)BytecodeInstructionWriter.StackType.lookup(BytecodeInstructionWriter.StackType.class, i);
|
|
}
|
|
}
|
|
return BytecodeInstructionWriter.StackType.OBJECT;
|
|
}
|
|
}
|
|
|
|
private static class ReprocessType {
|
|
UserTypeModel type;
|
|
boolean addToMainList;
|
|
|
|
ReprocessType(UserTypeModel type, boolean addToMainList) {
|
|
this.type = type;
|
|
this.addToMainList = addToMainList;
|
|
}
|
|
}
|
|
|
|
public BytecodeTarget(BytecodeOutput output, BytecodeHeap heap) {
|
|
this.output = output;
|
|
this.heap = heap;
|
|
}
|
|
|
|
public BytecodeTarget(BytecodeOutput output) {
|
|
this(output, new BytecodeHeap(1000000000, true));
|
|
}
|
|
|
|
public void generateFieldInitialisers(UserTypeModel t, BytecodeInstructionWriter w, boolean isStaticInit) {
|
|
for (int i = 0; i < t.countFieldMembers(); i++) {
|
|
FieldModel f = t.getFieldMember(i);
|
|
if (isStaticInit && f.isEnumField()) {
|
|
w.genNewObject(t.getDefaultInstanceConstructor().getMethodSignature());
|
|
w.genStore(f.getFieldSignature());
|
|
} else if (f.hasInitialiser() && f.isStatic() == isStaticInit) {
|
|
TypeSignature storing = f.getInitialisationExpression().generate(w);
|
|
if (!storing.mappableEquals(f.getStorageType().getTypeSignature())) {
|
|
w.genConvert(storing, f.getStorageType().getTypeSignature());
|
|
storing = f.getStorageType().getTypeSignature();
|
|
}
|
|
if (!f.isStatic()) {
|
|
w.genThis();
|
|
}
|
|
w.genStore(f.getFieldSignature());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void visitField(BytecodeHeap.ObjectLike typ, FieldModel m) {
|
|
BytecodeHeap.ObjectLike member = heap.newField(typ, m.getFlags(), m.getFieldSignature());
|
|
for (int i = 0; i < m.getAttributes().countTranslations(); i++) {
|
|
heap.addMemberTranslation(member, m.getAttributes().getTranslation(i).language, m.getAttributes().getTranslation(i).name);
|
|
}
|
|
}
|
|
|
|
private void visitMethod(BytecodeHeap.ObjectLike typ, MethodModel m) {
|
|
BytecodeHeap.ObjectLike member = heap.newMethod(typ, m.getFlags(), m.getMethodSignature(), m.countLocals());
|
|
for (int i = 0; i < m.getAttributes().countTranslations(); i++) {
|
|
heap.addMemberTranslation(member, m.getAttributes().getTranslation(i).language, m.getAttributes().getTranslation(i).name);
|
|
}
|
|
if (m.hasBody() || m.isConstructor()) {
|
|
BytecodeInstructionWriter w = new BytecodeInstructionWriter(this, m.getOwner(), member);
|
|
w.pushSource(m.getSource());
|
|
boolean initAfterFirstStatement = false;
|
|
if (m.isStaticInitialisation()) {
|
|
generateFieldInitialisers((UserTypeModel)m.getOwner(), w, true);
|
|
} else if (m.isConstructor()) {
|
|
if (m.checkExplicitBaseConstructorCall(true)) {
|
|
// This will have "approved" the constructor call location,
|
|
// But we need to check if it was actually a super(...) call
|
|
// (and not a call to another this(...) constructor)
|
|
// before we go around re-initialising any already-initialised fields!
|
|
if (m.checkExplicitBaseConstructorCall(false)) {
|
|
initAfterFirstStatement = true;
|
|
}
|
|
} else {
|
|
if (m.hasBody()) {
|
|
w.genDefaultSuperConstructorCall();
|
|
} else {
|
|
// This handles synthetic constructors
|
|
// These naturally pass all arguments to the super-constructor
|
|
for (int i = 0; i < m.countParameters(); i++) {
|
|
ParameterModel p = m.getParameter(i);
|
|
w.genLoadLocalOrParam(p.getStorageType().getTypeSignature(), p.getIndex());
|
|
}
|
|
w.genNestedConstructorCall(m.getPrototype().getMethodSignature());
|
|
}
|
|
generateFieldInitialisers((UserTypeModel)m.getOwner(), w, false);
|
|
}
|
|
}
|
|
if (m.countExceptions() > 0) {
|
|
BytecodeHeap.ObjectLike o = getHeap().newObjectLike(BytecodeHeap.StandardClass.SIMPLEARRAY, m.countExceptions());
|
|
for (int i = 0; i < m.countExceptions(); i++) {
|
|
o.setElement(i, getHeap().getTypeSignature(m.getException(i).getTypeSignature()));
|
|
}
|
|
member.setElement(BytecodeHeap.MethodFields.THROWS.value, o);
|
|
}
|
|
if (m.hasBody()) {
|
|
/* Manually loop through statements to ensure fields are initialised if needed. */
|
|
if (m.getBody() instanceof BlockStatement) {
|
|
BlockStatement block = (BlockStatement) m.getBody();
|
|
w.pushSource(block.getSource());
|
|
if (block.countInnerStatements() == 0 && initAfterFirstStatement) {
|
|
generateFieldInitialisers((UserTypeModel)m.getOwner(), w, false);
|
|
}
|
|
for (int i = 0; i < block.countInnerStatements(); i++) {
|
|
block.getInnerStatement(i).generate(w);
|
|
if (i == 0 && initAfterFirstStatement) {
|
|
generateFieldInitialisers((UserTypeModel)m.getOwner(), w, false);
|
|
}
|
|
}
|
|
w.popSource();
|
|
} else {
|
|
m.getBody().generate(w);
|
|
if (initAfterFirstStatement) {
|
|
generateFieldInitialisers((UserTypeModel)m.getOwner(), w, false);
|
|
}
|
|
}
|
|
}
|
|
w.genEnd();
|
|
w.popSource(m.getSource());
|
|
}
|
|
}
|
|
|
|
void generateImplicitStaticInit(UserTypeModel m, BytecodeHeap.ObjectLike typ) {
|
|
int flags = Flags.MASK_METHOD | Flags.MASK_CONSTRUCTOR | Flags.MASK_STATIC | Flags.MASK_SYNTHETIC;
|
|
BytecodeHeap.ObjectLike member = heap.newMethod(typ, flags, new MethodSignature(m.getTypeSignature(), MethodSignature.Kind.STATIC_INIT, TypeSignature.VOID, "static-init", new TypeSignature[0]), 0);
|
|
BytecodeInstructionWriter w = new BytecodeInstructionWriter(this, m, member);
|
|
w.pushSource(m.getParsed());
|
|
generateFieldInitialisers(m, w, true);
|
|
w.genEnd();
|
|
w.popSource(m.getParsed());
|
|
}
|
|
|
|
@Override
|
|
public void visitProvidesOrDepends(ProvidesOrDepends x) {
|
|
if (x.provides) {
|
|
heap.newProvides(x.name, x.version);
|
|
} else {
|
|
heap.newDepends(x.name, x.version, null, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitMeta(boolean important, String key, String value) {
|
|
heap.newMetadata(important, key, value);
|
|
}
|
|
|
|
@Override
|
|
public void visitTypeOption(TypeOption o) {
|
|
heap.registerRuntimeType(o.kind.value, o.kind.value, o.getTypeSignature(), o.formatopt, o.isBuiltin, o.isNumber, o.isFloat, o.isSigned, o.nbits);
|
|
}
|
|
|
|
@Override
|
|
public void visitDataFile(DataFile dataFile) {
|
|
heap.newFile(dataFile.packagename, dataFile.innername, dataFile.data, dataFile.typeinfo, dataFile.debugname);
|
|
}
|
|
|
|
@Override
|
|
public void visitType(UserTypeModel m, boolean addToMainList) {
|
|
types.append(new ReprocessType(m, addToMainList));
|
|
dryRun(m, addToMainList);
|
|
}
|
|
|
|
private boolean firstDryRun = true;
|
|
private void dryRun(UserTypeModel m, boolean addToMainList) {
|
|
if (firstDryRun) {
|
|
for (int i = 0; i <= 1024; i++) {
|
|
//heap.getConstInt32(""+i);
|
|
}
|
|
firstDryRun = false;
|
|
}
|
|
heap.getTypeSignature(m.getTypeSignature());
|
|
|
|
for (int i = 0; i < m.countFieldMembers(); i++) {
|
|
FieldModel f = m.getFieldMember(i);
|
|
heap.getFieldSignature(f.getFieldSignature());
|
|
}
|
|
|
|
for (int i = 0; i < m.countMethodMembers(); i++) {
|
|
MethodModel mt = m.getMethodMember(i);
|
|
if (!mt.isStaticInitialisation()) {
|
|
//Log.line("For " + mt.toString() + "[" + mt.getMethodSignature().toString() + "] got " + heap.getMethodSignature(mt.getMethodSignature()).debugString());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void processType(UserTypeModel m, boolean addToMainList) {
|
|
BytecodeHeap.ObjectLike typ = heap.newType(m.getFlags(), m.getTypeSignature());
|
|
if (m.getBaseClass() != null) {
|
|
typ.setElement(2, heap.getTypeSignature(m.getBaseClass().getTypeSignature()));
|
|
}
|
|
if (m.countInterfaces() > 0) {
|
|
BytecodeHeap.ObjectLike ifcs = heap.newObjectLike(BytecodeHeap.StandardClass.SIMPLEARRAY, m.countInterfaces());
|
|
typ.setElement(3, ifcs);
|
|
for (int i = 0; i < m.countInterfaces(); i++) {
|
|
ifcs.setElement(i, heap.getTypeSignature(m.getInterface(i).getTypeSignature()));
|
|
}
|
|
}
|
|
|
|
if (m.getSource() != null) {
|
|
typ.setElement(BytecodeHeap.TypeFields.FILENAME.value, heap.getConstString(m.getSource().getFilename()));
|
|
}
|
|
|
|
for (int i = 0; i < m.countFieldMembers(); i++) {
|
|
FieldModel f = m.getFieldMember(i);
|
|
visitField(typ, f);
|
|
}
|
|
|
|
boolean explicitStaticInit = false;
|
|
|
|
for (int i = 0; i < m.countMethodMembers(); i++) {
|
|
MethodModel mt = m.getMethodMember(i);
|
|
if (mt.isStaticInitialisation()) {
|
|
explicitStaticInit = true;
|
|
}
|
|
visitMethod(typ, mt);
|
|
}
|
|
|
|
if (!explicitStaticInit) {
|
|
generateImplicitStaticInit(m, typ);
|
|
}
|
|
|
|
if (addToMainList) {
|
|
getHeap().getEntrypointsArray().appendElement(typ);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void finish() {
|
|
for (int i = 0; i < types.count(); i++) {
|
|
Log.line("Reprocessing type #" + i);
|
|
ReprocessType t = types.get(i);
|
|
Log.line("Reprocessing type " + t.type.toString());
|
|
processType(t.type, t.addToMainList);
|
|
}
|
|
Log.line("FINISHING: " + quickReport());
|
|
Log.line("in BytecodeTaget...");
|
|
if (output != null) {
|
|
heap.writeAll(output, "#!/usr/bin/env testvm", 3, 256, 1, 1);
|
|
output.endOfFile();
|
|
} else {
|
|
Log.line("NO OUTPUT??");
|
|
}
|
|
}
|
|
|
|
public String strbytes(int size) {
|
|
int orig = size;
|
|
String result = "";
|
|
|
|
if (size > 1024*1024) {
|
|
result += (size/(1024*1024)) + " MB";
|
|
size %= (1024*1024);
|
|
}
|
|
|
|
if (size > 1024) {
|
|
if (!result.equals("")) {
|
|
result += " ";
|
|
}
|
|
result += (size/1024) + " KB";
|
|
size %= 1024;
|
|
}
|
|
|
|
if (size != 0 || result.equals("")) {
|
|
if (!result.equals("")) {
|
|
result += " ";
|
|
}
|
|
result += size + " bytes";
|
|
size = 0;
|
|
}
|
|
|
|
result += " (" + /*String.format("%.4f",*/ (orig / (1024.0*1024.0)) + "MB)";
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public String quickReport() {
|
|
return "Object table " + strbytes(heap.objectTableSize(3)) + " (" + (heap.highestUsedRecordIndex() + 1) + " entries), index size " + strbytes(heap.objectTableIndexSize(3, 256)) + ", data section " + strbytes(heap.dataSize(1)) + ": total file size " + strbytes(heap.fileSize(3, 256, 1, 1)) /*heap.objectTableSize(3) + heap.objectTableIndexSize(3, 256) + heap.dataSize(1))*/;
|
|
}
|
|
|
|
public BytecodeHeap getHeap() {
|
|
return heap;
|
|
}
|
|
}
|