From 837e751094e93f4c33c477d875829eb0a407a7b2 Mon Sep 17 00:00:00 2001 From: i22007 Date: Wed, 19 Jun 2024 12:53:14 -0400 Subject: [PATCH] Generate jar with first class as main --- .../java/ast/type/AccessModifierNode.java | 2 +- src/main/java/ast/type/TypeNode.java | 2 +- src/main/java/bytecode/ByteCodeGenerator.java | 56 ++++++++++++++++--- src/main/java/bytecode/ClassCodeGen.java | 25 +++++++-- src/main/java/bytecode/Mapper.java | 15 +++-- src/main/java/bytecode/MethodCodeGen.java | 2 +- 6 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/main/java/ast/type/AccessModifierNode.java b/src/main/java/ast/type/AccessModifierNode.java index 14081db..45f9172 100644 --- a/src/main/java/ast/type/AccessModifierNode.java +++ b/src/main/java/ast/type/AccessModifierNode.java @@ -1,7 +1,7 @@ package ast.type; public class AccessModifierNode { - EnumAccessModifierNode accessType; + public EnumAccessModifierNode accessType; public AccessModifierNode(String accessModifier) { setModifier(accessModifier); diff --git a/src/main/java/ast/type/TypeNode.java b/src/main/java/ast/type/TypeNode.java index 6336208..8a831cc 100644 --- a/src/main/java/ast/type/TypeNode.java +++ b/src/main/java/ast/type/TypeNode.java @@ -1,7 +1,7 @@ package ast.type; public class TypeNode { - EnumTypeNode type; + public EnumTypeNode type; public TypeNode(String type) { setType(type); diff --git a/src/main/java/bytecode/ByteCodeGenerator.java b/src/main/java/bytecode/ByteCodeGenerator.java index bef6e79..3782a09 100644 --- a/src/main/java/bytecode/ByteCodeGenerator.java +++ b/src/main/java/bytecode/ByteCodeGenerator.java @@ -4,17 +4,57 @@ import ast.ProgramNode; import ast.ClassNode; import bytecode.visitor.ProgramVisitor; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + public class ByteCodeGenerator implements ProgramVisitor { - @Override - public void visit(ProgramNode programNode) { - //ASMCodeGenerator asmCodeGenerator = new ASMCodeGenerator(); - //asmCodeGenerator.test(); + private JarOutputStream jarOutputStream; + private ByteArrayOutputStream byteArrayOutputStream; - for (ClassNode classDeclarationNode : programNode.classes) { - ClassCodeGen classCodeGen = new ClassCodeGen(); - classDeclarationNode.accept(classCodeGen); + + @Override + public void visit(ProgramNode programNode) { + byteArrayOutputStream = new ByteArrayOutputStream(); + try { + Manifest manifest = new Manifest(); + manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); + manifest.getMainAttributes().putValue("Main-Class", programNode.classes.get(0).identifier); + + jarOutputStream = new JarOutputStream(byteArrayOutputStream, manifest); + } catch (IOException e) { + throw new RuntimeException(e); + } + + for (ClassNode classDeclarationNode : programNode.classes) { + ClassCodeGen classCodeGen = new ClassCodeGen(jarOutputStream); + classDeclarationNode.accept(classCodeGen); + } + + try { + jarOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + saveJarFile(byteArrayOutputStream.toByteArray(), "output.jar"); } - } + private void saveJarFile(byte[] jarBytes, String jarFileName) { + File directory = new File("src/main/resources/classFileOutput"); + if (!directory.exists()) { + directory.mkdirs(); + } + + File jarFile = new File(directory, jarFileName); + try (FileOutputStream fos = new FileOutputStream(jarFile)) { + fos.write(jarBytes); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/bytecode/ClassCodeGen.java b/src/main/java/bytecode/ClassCodeGen.java index 6bddd61..79011b8 100644 --- a/src/main/java/bytecode/ClassCodeGen.java +++ b/src/main/java/bytecode/ClassCodeGen.java @@ -4,22 +4,27 @@ import ast.ClassNode; import ast.member.FieldNode; import ast.member.MemberNode; import ast.member.MethodNode; -import ast.type.BaseTypeNode; import bytecode.visitor.ClassVisitor; + import java.io.File; + import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import java.io.FileOutputStream; import java.io.IOException; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; public class ClassCodeGen implements ClassVisitor { private Mapper mapper; private ClassWriter classWriter; + private JarOutputStream jarOutputStream; - public ClassCodeGen() { + public ClassCodeGen(JarOutputStream jarOutputStream) { mapper = new Mapper(); + this.jarOutputStream = jarOutputStream; } @Override @@ -38,6 +43,7 @@ public class ClassCodeGen implements ClassVisitor { } classWriter.visitEnd(); + writeToJar(classWriter.toByteArray(), classNode.identifier); printIntoClassFile(classWriter.toByteArray(), classNode.identifier); classWriter.visitEnd(); @@ -45,9 +51,7 @@ public class ClassCodeGen implements ClassVisitor { @Override public void visit(FieldNode fieldNode) { - if(fieldNode.type instanceof BaseTypeNode baseTypeNode){ - classWriter.visitField(mapper.mapAccessTypeToOpcode(fieldNode.accessTypeNode), fieldNode.identifier, mapper.getTypeChar(baseTypeNode.enumType), null, null ); - } + classWriter.visitField(mapper.mapAccessTypeToOpcode(fieldNode.accessTypeNode), fieldNode.identifier, mapper.getTypeChar(fieldNode.type.type), null, null); classWriter.visitEnd(); } @@ -67,4 +71,15 @@ public class ClassCodeGen implements ClassVisitor { e.printStackTrace(); } } + + private void writeToJar(byte[] byteCode, String className) { + try { + JarEntry jarEntry = new JarEntry(className + ".class"); + jarOutputStream.putNextEntry(jarEntry); + jarOutputStream.write(byteCode); + jarOutputStream.closeEntry(); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/bytecode/Mapper.java b/src/main/java/bytecode/Mapper.java index faa4a67..7421708 100644 --- a/src/main/java/bytecode/Mapper.java +++ b/src/main/java/bytecode/Mapper.java @@ -3,20 +3,23 @@ package bytecode; import ast.parameter.ParameterNode; import ast.type.*; import org.objectweb.asm.Opcodes; -import ast.type.BaseTypeNode; public class Mapper { - public int mapAccessTypeToOpcode(AccessTypeNode type) { - switch (type.enumAccessTypeNode) { - case EnumAccessTypeNode.PUBLIC: + public int mapAccessTypeToOpcode(AccessModifierNode accessModifierNode) { + switch (accessModifierNode.accessType) { + case EnumAccessModifierNode.PUBLIC: return Opcodes.ACC_PUBLIC; - case EnumAccessTypeNode.PRIVATE: + case EnumAccessModifierNode.PRIVATE: return Opcodes.ACC_PRIVATE; + case EnumAccessModifierNode.PUBLIC_STATIC: + return Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; + case EnumAccessModifierNode.PRIVATE_STATIC: + return Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC; } return 0; } - public String generateMethodDescriptor(BaseTypeNode baseTypeNode, ParameterListNode parameterListNode) { + public String generateMethodDescriptor(TypeNode typeNode, ParameterListNode parameterListNode) { String descriptor = "("; for(ParameterNode parameterNode : parameterListNode.parameters) { descriptor += getTypeChar(EnumTypeNode.INT); diff --git a/src/main/java/bytecode/MethodCodeGen.java b/src/main/java/bytecode/MethodCodeGen.java index 6e9c247..37b8d7a 100644 --- a/src/main/java/bytecode/MethodCodeGen.java +++ b/src/main/java/bytecode/MethodCodeGen.java @@ -35,7 +35,7 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor { @Override public void visit(ConstructorNode constructorNode) { methodVisitor = - classWriter.visitMethod(mapper.mapAccessTypeToOpcode(constructorNode.visibility), + classWriter.visitMethod(mapper.mapAccessTypeToOpcode(constructorNode.accessType), "", "()V", null,