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 types = new List(); private final Map cachedStackTypes = new Map(); 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; } }