Compare commits

..

10 commits

Author SHA1 Message Date
Sander Hautvast
008bd06680 * removed unused code
* added more javadoc
* more renaming
2021-01-18 22:11:21 +01:00
Sander Hautvast
22a6930973 first commit i a long while. Should have committed earlier, but tests were failing.
* updated bejava lang
* bug fixes
2021-01-18 20:42:24 +01:00
Sander Hautvast
5e7267c170 added info to README.md 2020-11-20 17:57:27 +01:00
Sander Hautvast
958a5b3fd7 added testassertions and fixed more stuff 2020-11-20 17:53:19 +01:00
Sander Hautvast
33400c7268 added test and fixed stuff 2020-11-19 22:58:49 +01:00
Sander Hautvast
adc54f0042 new javap output 2020-11-19 22:29:22 +01:00
Sander Hautvast
138f14b8f3 added a parser to read text to BeeSource. Will require a lexer for the actual opcode 2020-11-19 22:29:09 +01:00
Sander Hautvast
68c5b9e9a9 removed duplicate maven plugin 2020-11-18 20:51:13 +01:00
Sander Hautvast
60860490b8 removed linenumbers 2020-11-18 20:36:23 +01:00
Sander Hautvast
dd4f52f9cf added latest capability to readme 2020-11-18 18:56:55 +01:00
110 changed files with 2928 additions and 1887 deletions

View file

@ -1,9 +1,11 @@
# beejava
Beejava is a code creation library, somewhat comparable to javassist or bytebuddy.
* It let's you compile java 'opcode' to bytecode.
# bejava compiler
Bejava (backend java) compiler is a code creation library, somewhat comparable to javassist or bytebuddy.
* It let's you compile bejava-lang' to bytecode. As an alternative a BeSource object can be created programmatically and then compiled.
* In this way it is a backend compiler.
* Handles creation of the constant pool
* It does not inspect or enhance existing bytecode, though it could be part of such functionality.
What is 'opcode'?
What is 'bejava lang'?
The goal of the project is to let developers dynamically create classes, using a simplified version of standard java opcodes. For instance:
instead of having to choose between:
@ -37,17 +39,49 @@ BeeConstructor createDefaultConstructor() {
return BeeConstructor.builder()
.withAccessFlags(MethodAccessFlags.PUBLIC)
.withCode(
line(0, LD_VAR, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(5, RETURN))
line(LD_VAR, Ref.THIS),
line(INVOKE, Ref.SUPER, "<init>", "()"),
line(RETURN))
.build();
}
```
and
```
BeeMethod print2 = BeeMethod.builder()
.withName("print2")
.withAccessFlags(MethodAccessFlags.PUBLIC)
.withCode(
line(GET, "java.lang.System","out"),
line(LD_CONST, "2"),
line(INVOKE, "java.io.PrintStream", "println", "java.lang.String"),
line(RETURN))
.build();
```
The latest commits are centered around the new feature that let's you write code like this:
```
class com.acme.SimpleBean(V15)
field private int value
constructor public()
INVOKE super()
RETURN
method public getValue() -> int
RETURN this.value
method public setValue(int newValue)
LOAD newValue
PUT this.value
RETURN
```
* text based instead of programmatic, although that will still be possible
* abstracts away most of the intricacies of working with a stack (although like bytecode it will still be stack based)
* indent-aware like python ;)
*Ideas about what's next*
* MORE opcodes
* invoke dynamic support (also in constant pool)
* support for exceptions, class attributes
* figure out a nicer, better api, drop the line numbers
* figure out a nicer, better api
* or instead drop this idea and let the developer write the raw bytecode. The constant pool would then be the only thing Beejava adds.
* create a readable file format for opcode files

719
out Normal file
View file

@ -0,0 +1,719 @@
Classfile /Users/Shautvast/IdeaProjects/beejava/target/classes/nl/sander/beejava/Compiler.class
Last modified 19 Nov 2020; size 6572 bytes
SHA-256 checksum 0fb294ea8b397e1710dc3017a3c9c31e9bbda5f5219ef646af238a9a0aa62e0a
Compiled from "Compiler.java"
public class nl.sander.beejava.Compiler
minor version: 0
major version: 59
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #11 // nl/sander/beejava/Compiler
super_class: #2 // java/lang/Object
interfaces: 0, fields: 4, methods: 9, attributes: 3
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // nl/sander/beejava/ConstantPoolCreator
#8 = Utf8 nl/sander/beejava/ConstantPoolCreator
#9 = Methodref #7.#3 // nl/sander/beejava/ConstantPoolCreator."<init>":()V
#10 = Fieldref #11.#12 // nl/sander/beejava/Compiler.constantPoolCreator:Lnl/sander/beejava/ConstantPoolCreator;
#11 = Class #13 // nl/sander/beejava/Compiler
#12 = NameAndType #14:#15 // constantPoolCreator:Lnl/sander/beejava/ConstantPoolCreator;
#13 = Utf8 nl/sander/beejava/Compiler
#14 = Utf8 constantPoolCreator
#15 = Utf8 Lnl/sander/beejava/ConstantPoolCreator;
#16 = Fieldref #11.#17 // nl/sander/beejava/Compiler.compiledClass:Lnl/sander/beejava/CompiledClass;
#17 = NameAndType #18:#19 // compiledClass:Lnl/sander/beejava/CompiledClass;
#18 = Utf8 compiledClass
#19 = Utf8 Lnl/sander/beejava/CompiledClass;
#20 = Class #21 // nl/sander/beejava/ConstantPoolEntryCreator
#21 = Utf8 nl/sander/beejava/ConstantPoolEntryCreator
#22 = Methodref #20.#23 // nl/sander/beejava/ConstantPoolEntryCreator."<init>":(Lnl/sander/beejava/CompiledClass;)V
#23 = NameAndType #5:#24 // "<init>":(Lnl/sander/beejava/CompiledClass;)V
#24 = Utf8 (Lnl/sander/beejava/CompiledClass;)V
#25 = Fieldref #11.#26 // nl/sander/beejava/Compiler.constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
#26 = NameAndType #27:#28 // constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
#27 = Utf8 constantPoolEntryCreator
#28 = Utf8 Lnl/sander/beejava/ConstantPoolEntryCreator;
#29 = Class #30 // nl/sander/beejava/CompiledClass
#30 = Utf8 nl/sander/beejava/CompiledClass
#31 = Methodref #29.#32 // nl/sander/beejava/CompiledClass."<init>":(Lnl/sander/beejava/api/BeeSource;)V
#32 = NameAndType #5:#33 // "<init>":(Lnl/sander/beejava/api/BeeSource;)V
#33 = Utf8 (Lnl/sander/beejava/api/BeeSource;)V
#34 = Methodref #11.#23 // nl/sander/beejava/Compiler."<init>":(Lnl/sander/beejava/CompiledClass;)V
#35 = Methodref #11.#36 // nl/sander/beejava/Compiler.compile:()Lnl/sander/beejava/CompiledClass;
#36 = NameAndType #37:#38 // compile:()Lnl/sander/beejava/CompiledClass;
#37 = Utf8 compile
#38 = Utf8 ()Lnl/sander/beejava/CompiledClass;
#39 = Methodref #11.#40 // nl/sander/beejava/Compiler.createConstantPool:()Lnl/sander/beejava/constantpool/ConstantPool;
#40 = NameAndType #41:#42 // createConstantPool:()Lnl/sander/beejava/constantpool/ConstantPool;
#41 = Utf8 createConstantPool
#42 = Utf8 ()Lnl/sander/beejava/constantpool/ConstantPool;
#43 = Methodref #29.#44 // nl/sander/beejava/CompiledClass.setConstantPool:(Lnl/sander/beejava/constantpool/ConstantPool;)V
#44 = NameAndType #45:#46 // setConstantPool:(Lnl/sander/beejava/constantpool/ConstantPool;)V
#45 = Utf8 setConstantPool
#46 = Utf8 (Lnl/sander/beejava/constantpool/ConstantPool;)V
#47 = Methodref #20.#48 // nl/sander/beejava/ConstantPoolEntryCreator.addInterfaces:()V
#48 = NameAndType #49:#6 // addInterfaces:()V
#49 = Utf8 addInterfaces
#50 = Methodref #20.#51 // nl/sander/beejava/ConstantPoolEntryCreator.addFields:()V
#51 = NameAndType #52:#6 // addFields:()V
#52 = Utf8 addFields
#53 = Methodref #11.#54 // nl/sander/beejava/Compiler.addConstructors:()V
#54 = NameAndType #55:#6 // addConstructors:()V
#55 = Utf8 addConstructors
#56 = Methodref #11.#57 // nl/sander/beejava/Compiler.addMethods:()V
#57 = NameAndType #58:#6 // addMethods:()V
#58 = Utf8 addMethods
#59 = Methodref #29.#60 // nl/sander/beejava/CompiledClass.getSource:()Lnl/sander/beejava/api/BeeSource;
#60 = NameAndType #61:#62 // getSource:()Lnl/sander/beejava/api/BeeSource;
#61 = Utf8 getSource
#62 = Utf8 ()Lnl/sander/beejava/api/BeeSource;
#63 = Methodref #64.#65 // nl/sander/beejava/api/BeeSource.getConstructors:()Ljava/util/Set;
#64 = Class #66 // nl/sander/beejava/api/BeeSource
#65 = NameAndType #67:#68 // getConstructors:()Ljava/util/Set;
#66 = Utf8 nl/sander/beejava/api/BeeSource
#67 = Utf8 getConstructors
#68 = Utf8 ()Ljava/util/Set;
#69 = InvokeDynamic #0:#70 // #0:accept:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
#70 = NameAndType #71:#72 // accept:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
#71 = Utf8 accept
#72 = Utf8 (Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
#73 = InterfaceMethodref #74.#75 // java/util/Set.forEach:(Ljava/util/function/Consumer;)V
#74 = Class #76 // java/util/Set
#75 = NameAndType #77:#78 // forEach:(Ljava/util/function/Consumer;)V
#76 = Utf8 java/util/Set
#77 = Utf8 forEach
#78 = Utf8 (Ljava/util/function/Consumer;)V
#79 = Methodref #64.#80 // nl/sander/beejava/api/BeeSource.getMethods:()Ljava/util/Set;
#80 = NameAndType #81:#68 // getMethods:()Ljava/util/Set;
#81 = Utf8 getMethods
#82 = InvokeDynamic #1:#70 // #1:accept:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
#83 = Methodref #20.#84 // nl/sander/beejava/ConstantPoolEntryCreator.addThisClass:()Lnl/sander/beejava/constantpool/entry/ClassEntry;
#84 = NameAndType #85:#86 // addThisClass:()Lnl/sander/beejava/constantpool/entry/ClassEntry;
#85 = Utf8 addThisClass
#86 = Utf8 ()Lnl/sander/beejava/constantpool/entry/ClassEntry;
#87 = Methodref #29.#88 // nl/sander/beejava/CompiledClass.setThisClass:(Lnl/sander/beejava/constantpool/entry/ClassEntry;)V
#88 = NameAndType #89:#90 // setThisClass:(Lnl/sander/beejava/constantpool/entry/ClassEntry;)V
#89 = Utf8 setThisClass
#90 = Utf8 (Lnl/sander/beejava/constantpool/entry/ClassEntry;)V
#91 = String #92 // Code
#92 = Utf8 Code
#93 = Methodref #20.#94 // nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateUtf8:(Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
#94 = NameAndType #95:#96 // getOrCreateUtf8:(Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
#95 = Utf8 getOrCreateUtf8
#96 = Utf8 (Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
#97 = Fieldref #11.#98 // nl/sander/beejava/Compiler.codeAttributeNameEntry:Lnl/sander/beejava/constantpool/entry/Utf8Entry;
#98 = NameAndType #99:#100 // codeAttributeNameEntry:Lnl/sander/beejava/constantpool/entry/Utf8Entry;
#99 = Utf8 codeAttributeNameEntry
#100 = Utf8 Lnl/sander/beejava/constantpool/entry/Utf8Entry;
#101 = Methodref #29.#102 // nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
#102 = NameAndType #103:#104 // addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
#103 = Utf8 addConstantPoolEntry
#104 = Utf8 (Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
#105 = Methodref #29.#106 // nl/sander/beejava/CompiledClass.getConstantTree:()Ljava/util/Set;
#106 = NameAndType #107:#68 // getConstantTree:()Ljava/util/Set;
#107 = Utf8 getConstantTree
#108 = Methodref #7.#109 // nl/sander/beejava/ConstantPoolCreator.createConstantPool:(Ljava/util/Set;)Lnl/sander/beejava/constantpool/ConstantPool;
#109 = NameAndType #41:#110 // createConstantPool:(Ljava/util/Set;)Lnl/sander/beejava/constantpool/ConstantPool;
#110 = Utf8 (Ljava/util/Set;)Lnl/sander/beejava/constantpool/ConstantPool;
#111 = InterfaceMethodref #74.#112 // java/util/Set.stream:()Ljava/util/stream/Stream;
#112 = NameAndType #113:#114 // stream:()Ljava/util/stream/Stream;
#113 = Utf8 stream
#114 = Utf8 ()Ljava/util/stream/Stream;
#115 = InvokeDynamic #2:#116 // #2:apply:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Function;
#116 = NameAndType #117:#118 // apply:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Function;
#117 = Utf8 apply
#118 = Utf8 (Lnl/sander/beejava/Compiler;)Ljava/util/function/Function;
#119 = InterfaceMethodref #120.#121 // java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
#120 = Class #122 // java/util/stream/Stream
#121 = NameAndType #123:#124 // map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
#122 = Utf8 java/util/stream/Stream
#123 = Utf8 map
#124 = Utf8 (Ljava/util/function/Function;)Ljava/util/stream/Stream;
#125 = Methodref #126.#127 // java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
#126 = Class #128 // java/util/Objects
#127 = NameAndType #129:#130 // requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
#128 = Utf8 java/util/Objects
#129 = Utf8 requireNonNull
#130 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object;
#131 = InvokeDynamic #3:#132 // #3:accept:(Lnl/sander/beejava/CompiledClass;)Ljava/util/function/Consumer;
#132 = NameAndType #71:#133 // accept:(Lnl/sander/beejava/CompiledClass;)Ljava/util/function/Consumer;
#133 = Utf8 (Lnl/sander/beejava/CompiledClass;)Ljava/util/function/Consumer;
#134 = InterfaceMethodref #120.#75 // java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
#135 = InterfaceMethodref #74.#136 // java/util/Set.iterator:()Ljava/util/Iterator;
#136 = NameAndType #137:#138 // iterator:()Ljava/util/Iterator;
#137 = Utf8 iterator
#138 = Utf8 ()Ljava/util/Iterator;
#139 = InterfaceMethodref #140.#141 // java/util/Iterator.hasNext:()Z
#140 = Class #142 // java/util/Iterator
#141 = NameAndType #143:#144 // hasNext:()Z
#142 = Utf8 java/util/Iterator
#143 = Utf8 hasNext
#144 = Utf8 ()Z
#145 = InterfaceMethodref #140.#146 // java/util/Iterator.next:()Ljava/lang/Object;
#146 = NameAndType #147:#148 // next:()Ljava/lang/Object;
#147 = Utf8 next
#148 = Utf8 ()Ljava/lang/Object;
#149 = Class #150 // nl/sander/beejava/api/BeeMethod
#150 = Utf8 nl/sander/beejava/api/BeeMethod
#151 = Methodref #11.#152 // nl/sander/beejava/Compiler.createMethod:(Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/MethodInfo;
#152 = NameAndType #153:#154 // createMethod:(Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/MethodInfo;
#153 = Utf8 createMethod
#154 = Utf8 (Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/MethodInfo;
#155 = Methodref #29.#156 // nl/sander/beejava/CompiledClass.addMethod:(Lnl/sander/beejava/classinfo/MethodInfo;)V
#156 = NameAndType #157:#158 // addMethod:(Lnl/sander/beejava/classinfo/MethodInfo;)V
#157 = Utf8 addMethod
#158 = Utf8 (Lnl/sander/beejava/classinfo/MethodInfo;)V
#159 = Class #160 // nl/sander/beejava/classinfo/MethodInfo
#160 = Utf8 nl/sander/beejava/classinfo/MethodInfo
#161 = Methodref #162.#163 // nl/sander/beejava/CodeContainer.getName:()Ljava/lang/String;
#162 = Class #164 // nl/sander/beejava/CodeContainer
#163 = NameAndType #165:#166 // getName:()Ljava/lang/String;
#164 = Utf8 nl/sander/beejava/CodeContainer
#165 = Utf8 getName
#166 = Utf8 ()Ljava/lang/String;
#167 = Methodref #162.#168 // nl/sander/beejava/CodeContainer.getSignature:()Ljava/lang/String;
#168 = NameAndType #169:#166 // getSignature:()Ljava/lang/String;
#169 = Utf8 getSignature
#170 = Methodref #159.#171 // nl/sander/beejava/classinfo/MethodInfo."<init>":(Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/constantpool/entry/Utf8Entry;)V
#171 = NameAndType #5:#172 // "<init>":(Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/constantpool/entry/Utf8Entry;)V
#172 = Utf8 (Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/constantpool/entry/Utf8Entry;)V
#173 = Methodref #162.#174 // nl/sander/beejava/CodeContainer.getAccessFlags:()Ljava/util/Set;
#174 = NameAndType #175:#68 // getAccessFlags:()Ljava/util/Set;
#175 = Utf8 getAccessFlags
#176 = Methodref #159.#177 // nl/sander/beejava/classinfo/MethodInfo.addAccessFlags:(Ljava/util/Set;)Lnl/sander/beejava/classinfo/MethodInfo;
#177 = NameAndType #178:#179 // addAccessFlags:(Ljava/util/Set;)Lnl/sander/beejava/classinfo/MethodInfo;
#178 = Utf8 addAccessFlags
#179 = Utf8 (Ljava/util/Set;)Lnl/sander/beejava/classinfo/MethodInfo;
#180 = Methodref #181.#182 // nl/sander/beejava/MethodCodeCreator.createCodeAttribute:(Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/attributes/CodeAttribute;
#181 = Class #183 // nl/sander/beejava/MethodCodeCreator
#182 = NameAndType #184:#185 // createCodeAttribute:(Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/attributes/CodeAttribute;
#183 = Utf8 nl/sander/beejava/MethodCodeCreator
#184 = Utf8 createCodeAttribute
#185 = Utf8 (Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/attributes/CodeAttribute;
#186 = Methodref #159.#187 // nl/sander/beejava/classinfo/MethodInfo.addAttribute:(Lnl/sander/beejava/classinfo/attributes/Attribute;)Lnl/sander/beejava/classinfo/Info;
#187 = NameAndType #188:#189 // addAttribute:(Lnl/sander/beejava/classinfo/attributes/Attribute;)Lnl/sander/beejava/classinfo/Info;
#188 = Utf8 addAttribute
#189 = Utf8 (Lnl/sander/beejava/classinfo/attributes/Attribute;)Lnl/sander/beejava/classinfo/Info;
#190 = Methodref #162.#191 // nl/sander/beejava/CodeContainer.getCode:()Ljava/util/List;
#191 = NameAndType #192:#193 // getCode:()Ljava/util/List;
#192 = Utf8 getCode
#193 = Utf8 ()Ljava/util/List;
#194 = InvokeDynamic #4:#70 // #4:accept:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
#195 = InterfaceMethodref #196.#75 // java/util/List.forEach:(Ljava/util/function/Consumer;)V
#196 = Class #197 // java/util/List
#197 = Utf8 java/util/List
#198 = Methodref #199.#200 // nl/sander/beejava/api/CodeLine.hasMethodCall:()Z
#199 = Class #201 // nl/sander/beejava/api/CodeLine
#200 = NameAndType #202:#144 // hasMethodCall:()Z
#201 = Utf8 nl/sander/beejava/api/CodeLine
#202 = Utf8 hasMethodCall
#203 = Methodref #20.#204 // nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateMethodRefEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/MethodRefEntry;
#204 = NameAndType #205:#206 // getOrCreateMethodRefEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/MethodRefEntry;
#205 = Utf8 getOrCreateMethodRefEntry
#206 = Utf8 (Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/MethodRefEntry;
#207 = Methodref #199.#208 // nl/sander/beejava/api/CodeLine.setAssignedEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
#208 = NameAndType #209:#104 // setAssignedEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
#209 = Utf8 setAssignedEntry
#210 = Methodref #199.#211 // nl/sander/beejava/api/CodeLine.hasRefToOwnField:()Z
#211 = NameAndType #212:#144 // hasRefToOwnField:()Z
#212 = Utf8 hasRefToOwnField
#213 = Methodref #199.#214 // nl/sander/beejava/api/CodeLine.hasRefToExternalField:()Z
#214 = NameAndType #215:#144 // hasRefToExternalField:()Z
#215 = Utf8 hasRefToExternalField
#216 = Methodref #20.#217 // nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateFieldRefEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/FieldRefEntry;
#217 = NameAndType #218:#219 // getOrCreateFieldRefEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/FieldRefEntry;
#218 = Utf8 getOrCreateFieldRefEntry
#219 = Utf8 (Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/FieldRefEntry;
#220 = Methodref #199.#221 // nl/sander/beejava/api/CodeLine.hasConstValue:()Z
#221 = NameAndType #222:#144 // hasConstValue:()Z
#222 = Utf8 hasConstValue
#223 = Methodref #20.#224 // nl/sander/beejava/ConstantPoolEntryCreator.getOrCreatePrimitiveEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;
#224 = NameAndType #225:#226 // getOrCreatePrimitiveEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;
#225 = Utf8 getOrCreatePrimitiveEntry
#226 = Utf8 (Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;
#227 = Utf8 LineNumberTable
#228 = Utf8 LocalVariableTable
#229 = Utf8 this
#230 = Utf8 Lnl/sander/beejava/Compiler;
#231 = Utf8 (Lnl/sander/beejava/api/BeeSource;)Lnl/sander/beejava/CompiledClass;
#232 = Utf8 beSource
#233 = Utf8 Lnl/sander/beejava/api/BeeSource;
#234 = Utf8 method
#235 = Utf8 Lnl/sander/beejava/api/BeeMethod;
#236 = Utf8 StackMapTable
#237 = Utf8 Lnl/sander/beejava/CodeContainer;
#238 = Utf8 updateConstantPool
#239 = Utf8 (Lnl/sander/beejava/CodeContainer;)V
#240 = Utf8 codeContainer
#241 = Utf8 (Lnl/sander/beejava/api/CodeLine;)V
#242 = Utf8 methodRefEntry
#243 = Utf8 Lnl/sander/beejava/constantpool/entry/MethodRefEntry;
#244 = Utf8 fieldRefEntry
#245 = Utf8 Lnl/sander/beejava/constantpool/entry/FieldRefEntry;
#246 = Utf8 primitiveEntry
#247 = Utf8 Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;
#248 = Utf8 codeline
#249 = Utf8 Lnl/sander/beejava/api/CodeLine;
#250 = Utf8 SourceFile
#251 = Utf8 Compiler.java
#252 = Utf8 BootstrapMethods
#253 = MethodHandle 6:#254 // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#254 = Methodref #255.#256 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#255 = Class #257 // java/lang/invoke/LambdaMetafactory
#256 = NameAndType #258:#259 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#257 = Utf8 java/lang/invoke/LambdaMetafactory
#258 = Utf8 metafactory
#259 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#260 = MethodType #261 // (Ljava/lang/Object;)V
#261 = Utf8 (Ljava/lang/Object;)V
#262 = MethodHandle 5:#263 // REF_invokeVirtual nl/sander/beejava/Compiler.updateConstantPool:(Lnl/sander/beejava/CodeContainer;)V
#263 = Methodref #11.#264 // nl/sander/beejava/Compiler.updateConstantPool:(Lnl/sander/beejava/CodeContainer;)V
#264 = NameAndType #238:#239 // updateConstantPool:(Lnl/sander/beejava/CodeContainer;)V
#265 = MethodType #266 // (Lnl/sander/beejava/api/BeeConstructor;)V
#266 = Utf8 (Lnl/sander/beejava/api/BeeConstructor;)V
#267 = MethodType #268 // (Lnl/sander/beejava/api/BeeMethod;)V
#268 = Utf8 (Lnl/sander/beejava/api/BeeMethod;)V
#269 = MethodType #130 // (Ljava/lang/Object;)Ljava/lang/Object;
#270 = MethodHandle 5:#151 // REF_invokeVirtual nl/sander/beejava/Compiler.createMethod:(Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/MethodInfo;
#271 = MethodType #272 // (Lnl/sander/beejava/api/BeeConstructor;)Lnl/sander/beejava/classinfo/MethodInfo;
#272 = Utf8 (Lnl/sander/beejava/api/BeeConstructor;)Lnl/sander/beejava/classinfo/MethodInfo;
#273 = MethodHandle 5:#155 // REF_invokeVirtual nl/sander/beejava/CompiledClass.addMethod:(Lnl/sander/beejava/classinfo/MethodInfo;)V
#274 = MethodType #158 // (Lnl/sander/beejava/classinfo/MethodInfo;)V
#275 = MethodHandle 5:#276 // REF_invokeVirtual nl/sander/beejava/Compiler.updateConstantPool:(Lnl/sander/beejava/api/CodeLine;)V
#276 = Methodref #11.#277 // nl/sander/beejava/Compiler.updateConstantPool:(Lnl/sander/beejava/api/CodeLine;)V
#277 = NameAndType #238:#241 // updateConstantPool:(Lnl/sander/beejava/api/CodeLine;)V
#278 = MethodType #241 // (Lnl/sander/beejava/api/CodeLine;)V
#279 = Utf8 InnerClasses
#280 = Class #281 // java/lang/invoke/MethodHandles$Lookup
#281 = Utf8 java/lang/invoke/MethodHandles$Lookup
#282 = Class #283 // java/lang/invoke/MethodHandles
#283 = Utf8 java/lang/invoke/MethodHandles
#284 = Utf8 Lookup
{
private final nl.sander.beejava.CompiledClass compiledClass;
descriptor: Lnl/sander/beejava/CompiledClass;
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
private final nl.sander.beejava.ConstantPoolEntryCreator constantPoolEntryCreator;
descriptor: Lnl/sander/beejava/ConstantPoolEntryCreator;
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
private final nl.sander.beejava.ConstantPoolCreator constantPoolCreator;
descriptor: Lnl/sander/beejava/ConstantPoolCreator;
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
private nl.sander.beejava.constantpool.entry.Utf8Entry codeAttributeNameEntry;
descriptor: Lnl/sander/beejava/constantpool/entry/Utf8Entry;
flags: (0x0002) ACC_PRIVATE
public nl.sander.beejava.Compiler(nl.sander.beejava.CompiledClass);
descriptor: (Lnl/sander/beejava/CompiledClass;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #7 // class nl/sander/beejava/ConstantPoolCreator
8: dup
9: invokespecial #9 // Method nl/sander/beejava/ConstantPoolCreator."<init>":()V
12: putfield #10 // Field constantPoolCreator:Lnl/sander/beejava/ConstantPoolCreator;
15: aload_0
16: aload_1
17: putfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
20: aload_0
21: new #20 // class nl/sander/beejava/ConstantPoolEntryCreator
24: dup
25: aload_1
26: invokespecial #22 // Method nl/sander/beejava/ConstantPoolEntryCreator."<init>":(Lnl/sander/beejava/CompiledClass;)V
29: putfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
32: return
LineNumberTable:
line 34: 0
line 25: 4
line 35: 15
line 36: 20
line 37: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 this Lnl/sander/beejava/Compiler;
0 33 1 compiledClass Lnl/sander/beejava/CompiledClass;
public static nl.sander.beejava.CompiledClass compile(nl.sander.beejava.api.BeeSource);
descriptor: (Lnl/sander/beejava/api/BeeSource;)Lnl/sander/beejava/CompiledClass;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=1, args_size=1
0: new #11 // class nl/sander/beejava/Compiler
3: dup
4: new #29 // class nl/sander/beejava/CompiledClass
7: dup
8: aload_0
9: invokespecial #31 // Method nl/sander/beejava/CompiledClass."<init>":(Lnl/sander/beejava/api/BeeSource;)V
12: invokespecial #34 // Method "<init>":(Lnl/sander/beejava/CompiledClass;)V
15: invokevirtual #35 // Method compile:()Lnl/sander/beejava/CompiledClass;
18: areturn
LineNumberTable:
line 46: 0
LocalVariableTable:
Start Length Slot Name Signature
0 19 0 beSource Lnl/sander/beejava/api/BeeSource;
public nl.sander.beejava.CompiledClass compile();
descriptor: ()Lnl/sander/beejava/CompiledClass;
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
4: aload_0
5: invokevirtual #39 // Method createConstantPool:()Lnl/sander/beejava/constantpool/ConstantPool;
8: invokevirtual #43 // Method nl/sander/beejava/CompiledClass.setConstantPool:(Lnl/sander/beejava/constantpool/ConstantPool;)V
11: aload_0
12: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
15: invokevirtual #47 // Method nl/sander/beejava/ConstantPoolEntryCreator.addInterfaces:()V
18: aload_0
19: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
22: invokevirtual #50 // Method nl/sander/beejava/ConstantPoolEntryCreator.addFields:()V
25: aload_0
26: invokevirtual #53 // Method addConstructors:()V
29: aload_0
30: invokevirtual #56 // Method addMethods:()V
33: aload_0
34: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
37: areturn
LineNumberTable:
line 53: 0
line 55: 11
line 56: 18
line 57: 25
line 58: 29
line 60: 33
LocalVariableTable:
Start Length Slot Name Signature
0 38 0 this Lnl/sander/beejava/Compiler;
private nl.sander.beejava.constantpool.ConstantPool createConstantPool();
descriptor: ()Lnl/sander/beejava/constantpool/ConstantPool;
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
4: invokevirtual #59 // Method nl/sander/beejava/CompiledClass.getSource:()Lnl/sander/beejava/api/BeeSource;
7: invokevirtual #63 // Method nl/sander/beejava/api/BeeSource.getConstructors:()Ljava/util/Set;
10: aload_0
11: invokedynamic #69, 0 // InvokeDynamic #0:accept:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
16: invokeinterface #73, 2 // InterfaceMethod java/util/Set.forEach:(Ljava/util/function/Consumer;)V
21: aload_0
22: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
25: invokevirtual #59 // Method nl/sander/beejava/CompiledClass.getSource:()Lnl/sander/beejava/api/BeeSource;
28: invokevirtual #79 // Method nl/sander/beejava/api/BeeSource.getMethods:()Ljava/util/Set;
31: aload_0
32: invokedynamic #82, 0 // InvokeDynamic #1:accept:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
37: invokeinterface #73, 2 // InterfaceMethod java/util/Set.forEach:(Ljava/util/function/Consumer;)V
42: aload_0
43: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
46: aload_0
47: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
50: invokevirtual #83 // Method nl/sander/beejava/ConstantPoolEntryCreator.addThisClass:()Lnl/sander/beejava/constantpool/entry/ClassEntry;
53: invokevirtual #87 // Method nl/sander/beejava/CompiledClass.setThisClass:(Lnl/sander/beejava/constantpool/entry/ClassEntry;)V
56: aload_0
57: aload_0
58: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
61: ldc #91 // String Code
63: invokevirtual #93 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateUtf8:(Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
66: putfield #97 // Field codeAttributeNameEntry:Lnl/sander/beejava/constantpool/entry/Utf8Entry;
69: aload_0
70: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
73: aload_0
74: getfield #97 // Field codeAttributeNameEntry:Lnl/sander/beejava/constantpool/entry/Utf8Entry;
77: invokevirtual #101 // Method nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
80: aload_0
81: getfield #10 // Field constantPoolCreator:Lnl/sander/beejava/ConstantPoolCreator;
84: aload_0
85: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
88: invokevirtual #105 // Method nl/sander/beejava/CompiledClass.getConstantTree:()Ljava/util/Set;
91: invokevirtual #108 // Method nl/sander/beejava/ConstantPoolCreator.createConstantPool:(Ljava/util/Set;)Lnl/sander/beejava/constantpool/ConstantPool;
94: areturn
LineNumberTable:
line 64: 0
line 65: 21
line 67: 42
line 69: 56
line 70: 69
line 72: 80
LocalVariableTable:
Start Length Slot Name Signature
0 95 0 this Lnl/sander/beejava/Compiler;
public void addConstructors();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
4: invokevirtual #59 // Method nl/sander/beejava/CompiledClass.getSource:()Lnl/sander/beejava/api/BeeSource;
7: invokevirtual #63 // Method nl/sander/beejava/api/BeeSource.getConstructors:()Ljava/util/Set;
10: invokeinterface #111, 1 // InterfaceMethod java/util/Set.stream:()Ljava/util/stream/Stream;
15: aload_0
16: invokedynamic #115, 0 // InvokeDynamic #2:apply:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Function;
21: invokeinterface #119, 2 // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
26: aload_0
27: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
30: dup
31: invokestatic #125 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
34: pop
35: invokedynamic #131, 0 // InvokeDynamic #3:accept:(Lnl/sander/beejava/CompiledClass;)Ljava/util/function/Consumer;
40: invokeinterface #134, 2 // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
45: return
LineNumberTable:
line 76: 0
line 77: 21
line 78: 31
line 79: 45
LocalVariableTable:
Start Length Slot Name Signature
0 46 0 this Lnl/sander/beejava/Compiler;
public void addMethods();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: aload_0
1: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
4: aload_0
5: getfield #97 // Field codeAttributeNameEntry:Lnl/sander/beejava/constantpool/entry/Utf8Entry;
8: invokevirtual #101 // Method nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
11: aload_0
12: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
15: invokevirtual #59 // Method nl/sander/beejava/CompiledClass.getSource:()Lnl/sander/beejava/api/BeeSource;
18: invokevirtual #79 // Method nl/sander/beejava/api/BeeSource.getMethods:()Ljava/util/Set;
21: invokeinterface #135, 1 // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
26: astore_1
27: aload_1
28: invokeinterface #139, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
33: ifeq 61
36: aload_1
37: invokeinterface #145, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
42: checkcast #149 // class nl/sander/beejava/api/BeeMethod
45: astore_2
46: aload_0
47: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
50: aload_0
51: aload_2
52: invokevirtual #151 // Method createMethod:(Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/MethodInfo;
55: invokevirtual #155 // Method nl/sander/beejava/CompiledClass.addMethod:(Lnl/sander/beejava/classinfo/MethodInfo;)V
58: goto 27
61: return
LineNumberTable:
line 86: 0
line 87: 11
line 88: 46
line 89: 58
line 94: 61
LocalVariableTable:
Start Length Slot Name Signature
46 12 2 method Lnl/sander/beejava/api/BeeMethod;
0 62 0 this Lnl/sander/beejava/Compiler;
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 27
locals = [ class java/util/Iterator ]
frame_type = 250 /* chop */
offset_delta = 33
private nl.sander.beejava.classinfo.MethodInfo createMethod(nl.sander.beejava.api.CodeContainer);
descriptor: (Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/MethodInfo;
flags: (0x0002) ACC_PRIVATE
Code:
stack=5, locals=2, args_size=2
0: new #159 // class nl/sander/beejava/classinfo/MethodInfo
3: dup
4: aload_0
5: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
8: aload_1
9: invokevirtual #161 // Method nl/sander/beejava/CodeContainer.getName:()Ljava/lang/String;
12: invokevirtual #93 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateUtf8:(Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
15: aload_0
16: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
19: aload_1
20: invokevirtual #167 // Method nl/sander/beejava/CodeContainer.getSignature:()Ljava/lang/String;
23: invokevirtual #93 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateUtf8:(Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
26: invokespecial #170 // Method nl/sander/beejava/classinfo/MethodInfo."<init>":(Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/constantpool/entry/Utf8Entry;)V
29: aload_1
30: invokevirtual #173 // Method nl/sander/beejava/CodeContainer.getAccessFlags:()Ljava/util/Set;
33: invokevirtual #176 // Method nl/sander/beejava/classinfo/MethodInfo.addAccessFlags:(Ljava/util/Set;)Lnl/sander/beejava/classinfo/MethodInfo;
36: aload_0
37: getfield #97 // Field codeAttributeNameEntry:Lnl/sander/beejava/constantpool/entry/Utf8Entry;
40: aload_1
41: invokestatic #180 // Method nl/sander/beejava/MethodCodeCreator.createCodeAttribute:(Lnl/sander/beejava/constantpool/entry/Utf8Entry;Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/attributes/CodeAttribute;
44: invokevirtual #186 // Method nl/sander/beejava/classinfo/MethodInfo.addAttribute:(Lnl/sander/beejava/classinfo/attributes/Attribute;)Lnl/sander/beejava/classinfo/Info;
47: checkcast #159 // class nl/sander/beejava/classinfo/MethodInfo
50: areturn
LineNumberTable:
line 101: 0
line 102: 9
line 103: 20
line 104: 30
line 105: 41
line 101: 50
LocalVariableTable:
Start Length Slot Name Signature
0 51 0 this Lnl/sander/beejava/Compiler;
0 51 1 method Lnl/sander/beejava/CodeContainer;
private void updateConstantPool(nl.sander.beejava.api.CodeContainer);
descriptor: (Lnl/sander/beejava/CodeContainer;)V
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=2, args_size=2
0: aload_0
1: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
4: aload_0
5: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
8: aload_1
9: invokevirtual #161 // Method nl/sander/beejava/CodeContainer.getName:()Ljava/lang/String;
12: invokevirtual #93 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateUtf8:(Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
15: invokevirtual #101 // Method nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
18: aload_0
19: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
22: aload_0
23: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
26: aload_1
27: invokevirtual #167 // Method nl/sander/beejava/CodeContainer.getSignature:()Ljava/lang/String;
30: invokevirtual #93 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateUtf8:(Ljava/lang/String;)Lnl/sander/beejava/constantpool/entry/Utf8Entry;
33: invokevirtual #101 // Method nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
36: aload_1
37: invokevirtual #190 // Method nl/sander/beejava/CodeContainer.getCode:()Ljava/util/List;
40: aload_0
41: invokedynamic #194, 0 // InvokeDynamic #4:accept:(Lnl/sander/beejava/Compiler;)Ljava/util/function/Consumer;
46: invokeinterface #195, 2 // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
51: return
LineNumberTable:
line 112: 0
line 113: 18
line 114: 36
line 115: 51
LocalVariableTable:
Start Length Slot Name Signature
0 52 0 this Lnl/sander/beejava/Compiler;
0 52 1 codeContainer Lnl/sander/beejava/CodeContainer;
private void updateConstantPool(nl.sander.beejava.api.CodeLine);
descriptor: (Lnl/sander/beejava/api/CodeLine;)V
flags: (0x0002) ACC_PRIVATE
Code:
stack=2, locals=3, args_size=2
0: aload_1
1: invokevirtual #198 // Method nl/sander/beejava/api/CodeLine.hasMethodCall:()Z
4: ifeq 29
7: aload_0
8: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
11: aload_1
12: invokevirtual #203 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateMethodRefEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/MethodRefEntry;
15: astore_2
16: aload_1
17: aload_2
18: invokevirtual #207 // Method nl/sander/beejava/api/CodeLine.setAssignedEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
21: aload_0
22: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
25: aload_2
26: invokevirtual #101 // Method nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
29: aload_1
30: invokevirtual #210 // Method nl/sander/beejava/api/CodeLine.hasRefToOwnField:()Z
33: ifne 43
36: aload_1
37: invokevirtual #213 // Method nl/sander/beejava/api/CodeLine.hasRefToExternalField:()Z
40: ifeq 65
43: aload_0
44: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
47: aload_1
48: invokevirtual #216 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreateFieldRefEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/FieldRefEntry;
51: astore_2
52: aload_1
53: aload_2
54: invokevirtual #207 // Method nl/sander/beejava/api/CodeLine.setAssignedEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
57: aload_0
58: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
61: aload_2
62: invokevirtual #101 // Method nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
65: aload_1
66: invokevirtual #220 // Method nl/sander/beejava/api/CodeLine.hasConstValue:()Z
69: ifeq 94
72: aload_0
73: getfield #25 // Field constantPoolEntryCreator:Lnl/sander/beejava/ConstantPoolEntryCreator;
76: aload_1
77: invokevirtual #223 // Method nl/sander/beejava/ConstantPoolEntryCreator.getOrCreatePrimitiveEntry:(Lnl/sander/beejava/api/CodeLine;)Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;
80: astore_2
81: aload_1
82: aload_2
83: invokevirtual #207 // Method nl/sander/beejava/api/CodeLine.setAssignedEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
86: aload_0
87: getfield #16 // Field compiledClass:Lnl/sander/beejava/CompiledClass;
90: aload_2
91: invokevirtual #101 // Method nl/sander/beejava/CompiledClass.addConstantPoolEntry:(Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;)V
94: return
LineNumberTable:
line 127: 0
line 128: 7
line 129: 16
line 130: 21
line 133: 29
line 134: 43
line 135: 52
line 136: 57
line 139: 65
line 140: 72
line 141: 81
line 142: 86
line 144: 94
LocalVariableTable:
Start Length Slot Name Signature
16 13 2 methodRefEntry Lnl/sander/beejava/constantpool/entry/MethodRefEntry;
52 13 2 fieldRefEntry Lnl/sander/beejava/constantpool/entry/FieldRefEntry;
81 13 2 primitiveEntry Lnl/sander/beejava/constantpool/entry/ConstantPoolEntry;
0 95 0 this Lnl/sander/beejava/Compiler;
0 95 1 codeline Lnl/sander/beejava/api/CodeLine;
StackMapTable: number_of_entries = 4
frame_type = 29 /* same */
frame_type = 13 /* same */
frame_type = 21 /* same */
frame_type = 28 /* same */
}
SourceFile: "Compiler.java"
BootstrapMethods:
0: #253 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#260 (Ljava/lang/Object;)V
#262 REF_invokeVirtual nl/sander/beejava/Compiler.updateConstantPool:(Lnl/sander/beejava/CodeContainer;)V
#265 (Lnl/sander/beejava/api/BeeConstructor;)V
1: #253 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#260 (Ljava/lang/Object;)V
#262 REF_invokeVirtual nl/sander/beejava/Compiler.updateConstantPool:(Lnl/sander/beejava/CodeContainer;)V
#267 (Lnl/sander/beejava/api/BeeMethod;)V
2: #253 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#269 (Ljava/lang/Object;)Ljava/lang/Object;
#270 REF_invokeVirtual nl/sander/beejava/Compiler.createMethod:(Lnl/sander/beejava/CodeContainer;)Lnl/sander/beejava/classinfo/MethodInfo;
#271 (Lnl/sander/beejava/api/BeeConstructor;)Lnl/sander/beejava/classinfo/MethodInfo;
3: #253 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#260 (Ljava/lang/Object;)V
#273 REF_invokeVirtual nl/sander/beejava/CompiledClass.addMethod:(Lnl/sander/beejava/classinfo/MethodInfo;)V
#274 (Lnl/sander/beejava/classinfo/MethodInfo;)V
4: #253 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#260 (Ljava/lang/Object;)V
#275 REF_invokeVirtual nl/sander/beejava/Compiler.updateConstantPool:(Lnl/sander/beejava/api/CodeLine;)V
#278 (Lnl/sander/beejava/api/CodeLine;)V
InnerClasses:
public static final #284= #280 of #282; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

18
pom.xml
View file

@ -2,35 +2,25 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>beejava</name>
<name>bejava</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>14</source>
<target>14</target>
<release>15</release>
</configuration>
</plugin>
</plugins>
</build>
<groupId>nl.sander</groupId>
<artifactId>beejava</artifactId>
<artifactId>bejava</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View file

@ -1,59 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeParameter;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.flags.MethodAccessFlags;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* parent of a constructor or a method.
*/
public abstract class CodeContainer {
protected final List<CodeLine> code = new LinkedList<>();
protected final Set<BeeParameter> formalParameters = new HashSet<>();
protected final Set<MethodAccessFlags> accessFlags = new HashSet<>();
private BeeSource owner;
public List<CodeLine> getCode() {
return code;
}
public String getSignature() {
return getParametersSignature() + TypeMapper.map(getReturnType());
}
public abstract String getName();
public abstract Class<?> getReturnType();
private String getParametersSignature() {
return formalParameters.stream()
.map(BeeParameter::getType)
.map(TypeMapper::map)
.collect(Collectors.joining(",", "(", ")"));
}
public Set<MethodAccessFlags> getAccessFlags() {
return accessFlags;
}
public BeeSource getOwner() {
return owner;
}
public void setOwner(BeeSource beeSource) {
if (owner != null) {
throw new IllegalStateException("Owner set twice. Sue the developer!");
}
this.owner = beeSource;
}
public abstract boolean isConstructor();
}

View file

@ -1,140 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.classinfo.MethodInfo;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.constantpool.entry.FieldRefEntry;
import nl.sander.beejava.constantpool.entry.MethodRefEntry;
import nl.sander.beejava.constantpool.entry.Utf8Entry;
/**
* Builds a set of a tree of constant pool entries that refer to each other.
* <p>
* A client must supply a {@link BeeSource} containing a set of {@link CodeLine}s that is assumed to be correct.
* It doesn't check if a valid state is reached.
*/
/* So the name isn't entirely correct. Waiting for inspiration.
* also TODO make sure entries aren't duplicates
*/
public class Compiler {
private final CompiledClass compiledClass;
private final ConstantPoolEntryCreator constantPoolEntryCreator;
private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator();
private Utf8Entry codeAttributeNameEntry;
/**
* construct a compiler object.
*/
/*
* At this moment I'm not sure if this class will be able to reused.
*/
public Compiler(CompiledClass compiledClass) {
this.compiledClass = compiledClass;
this.constantPoolEntryCreator = new ConstantPoolEntryCreator(compiledClass);
}
/**
* compile a BeeSource object into a CompiledClass object.
*
* @param beeSource the Class object for which the constant pool needs to be created
* @return a CompiledClass object (that can be turned into bytecode)
*/
public static CompiledClass compile(BeeSource beeSource) {
return new Compiler(new CompiledClass(beeSource)).compile();
}
/**
* construct a CompiledClass object that contains all information for generating the bytecode
*/
public CompiledClass compile() {
compiledClass.setConstantPool(createConstantPool());
constantPoolEntryCreator.addInterfaces();
constantPoolEntryCreator.addFields();
addConstructors();
addMethods();
return compiledClass;
}
private ConstantPool createConstantPool() {
compiledClass.getSource().getConstructors().forEach(this::updateConstantPool);
compiledClass.getSource().getMethods().forEach(this::updateConstantPool); // compiledClass.getSource() ?
compiledClass.setThisClass(constantPoolEntryCreator.addThisClass());
this.codeAttributeNameEntry = constantPoolEntryCreator.getOrCreateUtf8("Code");
compiledClass.addConstantPoolEntry(codeAttributeNameEntry);
return constantPoolCreator.createConstantPool(compiledClass.getConstantTree());
}
public void addConstructors() {
compiledClass.getSource().getConstructors().stream()
.map(this::createMethod)
.forEach(compiledClass::addMethod);
}
/*
* maps methods from the source to a MethodInfo and adds that to the CompiledClass.
*/
public void addMethods() {
compiledClass.addConstantPoolEntry(codeAttributeNameEntry);
compiledClass.getSource().getMethods().stream()
.map(this::createMethod)
.forEach(compiledClass::addMethod);
}
/*
* create bytecode for method
* create methodInfo object for classfile
*/
private MethodInfo createMethod(CodeContainer method) {
return new MethodInfo(
constantPoolEntryCreator.getOrCreateUtf8(method.getName()),
constantPoolEntryCreator.getOrCreateUtf8(method.getSignature()))
.addAccessFlags(method.getAccessFlags())
.addAttribute(MethodCodeCreator.createCodeAttribute(codeAttributeNameEntry, method));
}
/*
* inspect a method or constructor, extract items that need to be added, and add them to the constant pool
*/
private void updateConstantPool(CodeContainer codeContainer) {
compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateUtf8(codeContainer.getName()));
compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateUtf8(codeContainer.getSignature()));
codeContainer.getCode().forEach(this::updateConstantPool);
}
/*
* Scan code line for items that need adding to the constant pool
*
* Constantpool uniqueness is maintained in two ways:
* 1. ConstantPoolEntryCreator.getOrCreateMethodRefEntry (and other methods) return a tree of entries.
* Sub-entries are kept in cache by the ConstantPoolEntryCreator and it returns a cached entry over a new one, if deemed equal.
* 2. the root node (also in aforementioned cache) is not symmetric because it is the only one that can be added to The compiledClass.
* So Compiled class also contains a set of unique root nodes.
*/
private void updateConstantPool(CodeLine codeline) {
if (codeline.hasMethodCall()) {
MethodRefEntry methodRefEntry = constantPoolEntryCreator.getOrCreateMethodRefEntry(codeline);
codeline.setAssignedEntry(methodRefEntry);
compiledClass.addConstantPoolEntry(methodRefEntry);
}
if (codeline.hasRefToOwnField() || codeline.hasRefToExternalField()) {
FieldRefEntry fieldRefEntry = constantPoolEntryCreator.getOrCreateFieldRefEntry(codeline);
codeline.setAssignedEntry(fieldRefEntry);
compiledClass.addConstantPoolEntry(fieldRefEntry);
}
if (codeline.hasConstValue()) {
ConstantPoolEntry primitiveEntry = constantPoolEntryCreator.getOrCreatePrimitiveEntry(codeline);
codeline.setAssignedEntry(primitiveEntry);
compiledClass.addConstantPoolEntry(primitiveEntry);
}
}
}

View file

@ -1,165 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.api.Ref;
import nl.sander.beejava.classinfo.FieldInfo;
import nl.sander.beejava.classinfo.attributes.CodeAttribute;
import nl.sander.beejava.constantpool.entry.*;
import java.util.HashMap;
import java.util.Map;
/**
* Responsible for creating unique constant pool entries
*/
class ConstantPoolEntryCreator {
private final Map<Integer, ConstantPoolEntry> cache = new HashMap<>();
private final CompiledClass compiledClass;
public ConstantPoolEntryCreator(CompiledClass compiledClass) {
this.compiledClass = compiledClass;
}
/*
* creates a FieldRefEntry when not found in cache, otherwise gets it from there
*/
FieldRefEntry getOrCreateFieldRefEntry(CodeLine codeLine) {
return cache(new FieldRefEntry(getOrCreateClassEntry(codeLine), getOrCreateFieldNameAndType(codeLine)));
}
/*
* creates a MethodRefEntry when not found in cache, otherwise gets it from there
*/
MethodRefEntry getOrCreateMethodRefEntry(CodeLine codeline) {
ClassEntry classEntry = getOrCreateClassEntry(codeline);
return cache(new MethodRefEntry(classEntry, getOrCreateMethodNameAndType(codeline)));
}
/*
* creates a NamAndTypeEntry for a field when not found in cache, otherwise gets it from there
*/
private NameAndTypeEntry getOrCreateFieldNameAndType(CodeLine codeline) {
if (codeline.hasRefToOwnField()) {
return cache(new NameAndTypeEntry(
cache(new Utf8Entry(codeline.getOwnfield().getName())),
cache(new Utf8Entry(TypeMapper.map(codeline.getOwnfield().getType()))))); // is actually a shortcut
} else {//TODO this method may need some work
return cache(new NameAndTypeEntry(
cache(new Utf8Entry(codeline.getExternalfield().getName())),
cache(new Utf8Entry(TypeMapper.map(codeline.getExternalfield().getType())))
));
}
}
/*
* creates a NamAndTypeEntry for a method when not found in cache, otherwise gets it from there
*/
private NameAndTypeEntry getOrCreateMethodNameAndType(CodeLine codeline) {
return new NameAndTypeEntry(
cache(new Utf8Entry(codeline.getMethodName())),
cache(new Utf8Entry(codeline.getMethodSignature())));
}
/*
* creates a ClassEntry when not found in cache, otherwise gets it from there
*/
private ClassEntry getOrCreateClassEntry(CodeLine codeline) {
if (codeline.hasRef()) {
if (codeline.getRef() == Ref.SUPER) { // this and super are rather special
ClassEntry superClass = getClassEntry(compiledClass.getSource().getSuperClass().getName());
compiledClass.setSuperClass(superClass);
return superClass;
} else if (codeline.getRef() == Ref.THIS) {
return addThisClass();
}
} else if (codeline.hasClassName()) {
return getClassEntry(codeline.getClassName());
}
throw new RuntimeException("shouldn't be here");
}
/*
* this method gives me a headache. It adds, but also creates a classEntry for the this class
*/
ClassEntry addThisClass() {
ClassEntry classEntry = getClassEntry(compiledClass.getSource().getName());
compiledClass.addConstantPoolEntry(classEntry);
return classEntry;
}
/*
* get or create a ClassEntry
*/
private ClassEntry getClassEntry(String externalClassName) {
return cache(new ClassEntry(cache(new Utf8Entry(internalName(externalClassName)))));
}
/*
* Adds interfaces to the constant pool as well as the class.
*
* interfaces[] in the class file is an array of cp entries
*/
public void addInterfaces() {
compiledClass.getSource().getInterfaces().forEach(interfase -> {
ClassEntry interfaceEntry = cache(new ClassEntry(cache(new Utf8Entry(internalName(interfase.getName())))));
compiledClass.addInterface(interfaceEntry);
compiledClass.addConstantPoolEntry(interfaceEntry);
});
}
/*
* If a constant is in the codeline, it needs to be added to the constant pool.
*/
public ConstantPoolEntry getOrCreatePrimitiveEntry(CodeLine codeline) {
Object v = codeline.getConstValue();
if (v instanceof String) {
return cache(new StringEntry(cache(new Utf8Entry((String) v))));
} else if (v instanceof Integer) {
return cache(new IntegerEntry((Integer) v));
} else if (v instanceof Long) {
return cache(new LongEntry((Long) v));
} else if (v instanceof Float) {
return cache(new FloatEntry((Float) v));
} else if (v instanceof Double) {
return cache(new DoubleEntry((Double) v));
}
throw new RuntimeException(); // TODO find out why are you here
}
/*
* maps a field from the source to a FieldInfo and adds that to the CompiledClass.
*/
public void addFields() {
compiledClass.getSource().getFields().stream()
.map(field -> new FieldInfo(
cache(new Utf8Entry(field.getName())),
cache(new Utf8Entry(internalName(field.getType().getName())))
).addAccessFlags(field.getAccessFlags())
)
.forEach(compiledClass::addField);
}
public Utf8Entry getOrCreateUtf8(String utf8) {
return cache(new Utf8Entry(utf8));
}
// why not put this everywhere, it's not like it's ever going to change
private String internalName(String name) {
return name.replaceAll("\\.", "/");
}
@SuppressWarnings("unchecked")
<T extends ConstantPoolEntry> T cache(T newEntry) {
// First create an object using the supplier, but if it's found in cache, return the cached entry and discard the first.
// Can't check for equality unless you create a potential new entry first
int hash = newEntry.hashCode();
return (T) cache.computeIfAbsent(hash, k -> newEntry);
// a hashmap with key hash of value is weird right?
// A HashSet is a HashMap with entry: key = value, which would work, but I cannot _get_ anything from a set.
}
}

View file

@ -1,54 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.api.Opcode;
import nl.sander.beejava.flags.ClassAccessFlags;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static nl.sander.beejava.JavaOpcode.*;
public class OpcodeMapper {
public static JavaOpcode mapOpcode(CodeLine codeLine) {
Opcode opcode = codeLine.getOpcode();
return switch (opcode) {
case GET -> isStatic(codeLine.getExternalfield()) ? GETSTATIC : GETFIELD;
case LD_VAR -> ALOAD_0;
case LD_CONST -> loadConst(codeLine);
case INVOKE -> invoke(codeLine);
case RETURN -> JavaOpcode.RETURN; //TODO not complete yet
default -> throw new IllegalStateException("something not implemented");
};
}
/* TODO cover more cases */
private static JavaOpcode invoke(CodeLine codeLine) {
BeeSource source = codeLine.getOwner().getOwner();
if (source.getAccessFlags().contains(ClassAccessFlags.SUPER) && codeLine.getOwner().isConstructor()) {
return INVOKESPECIAL;
} else {
return INVOKEVIRTUAL;
}
}
private static JavaOpcode loadConst(CodeLine codeLine) {
Object constValue = codeLine.getConstValue();
int index = codeLine.getAssignedEntry().getIndex();
if (constValue instanceof Double || constValue instanceof Long) {
return LDC2_W;
} else {
if (index > 0xff) {
return LDC_W;
} else {
return LDC;
}
}
}
private static boolean isStatic(Field field) {
return (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
}
}

View file

@ -1,5 +0,0 @@
package nl.sander.beejava;
public enum StackOp {
PUSH,POP
}

View file

@ -1,89 +0,0 @@
package nl.sander.beejava.api;
import nl.sander.beejava.CodeContainer;
import nl.sander.beejava.flags.MethodAccessFlags;
import java.util.*;
/**
* Models a constructor
*/
public final class BeeConstructor extends CodeContainer {
private BeeConstructor(Set<MethodAccessFlags> accessFlags,
List<BeeParameter> formalParameters,
List<CodeLine> code) {
this.formalParameters.addAll(formalParameters);
this.accessFlags.addAll(accessFlags);
super.code.addAll(code);
}
public static Builder builder() {
return new Builder();
}
public String getName() {
return "<init>";
}
@Override
public String toString() {
return "BeeConstructor{" +
"formalParameters=" + formalParameters +
'}';
}
public Class<?> getReturnType() {
return Void.class;
}
@Override
public boolean isConstructor() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeeConstructor that = (BeeConstructor) o;
return formalParameters.equals(that.formalParameters);
}
@Override
public int hashCode() {
return Objects.hash(formalParameters);
}
public static class Builder {
private final Set<MethodAccessFlags> accessFlags = new HashSet<>();
private final List<BeeParameter> formalParameters = new LinkedList<>();
private final List<CodeLine> code = new LinkedList<>();
private Builder() {
}
public Builder withFormalParameters(BeeParameter... formalParameters) {
this.formalParameters.addAll(Arrays.asList(formalParameters));
return this;
}
public Builder withAccessFlags(MethodAccessFlags... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public Builder withCode(CodeLine... lines) {
this.code.addAll(Arrays.asList(lines));
return this;
}
public BeeConstructor build() {
BeeConstructor beeConstructor = new BeeConstructor(accessFlags, formalParameters, code);
code.forEach(line -> line.setOwner(beeConstructor));
return beeConstructor;
}
}
}

View file

@ -1,82 +0,0 @@
package nl.sander.beejava.api;
import nl.sander.beejava.flags.FieldAccessFlags;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* Models a field in a BeeClass
*/
public final class BeeField {
private final Set<FieldAccessFlags> accessFlags = new HashSet<>();
private final Class<?> type;
private final String name;
private BeeField(Set<FieldAccessFlags> accessFlags, Class<?> type, String name) {
this.accessFlags.addAll(accessFlags);
this.type = type;
this.name = name;
}
public static BeeField.Builder builder(){
return new Builder();
}
public Set<FieldAccessFlags> getAccessFlags() {
return accessFlags;
}
public Class<?> getType() {
return type;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeeField beeField = (BeeField) o;
return name.equals(beeField.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
public static class Builder {
private final Set<FieldAccessFlags> accessFlags = new HashSet<>();
private Class<?> type;
private String name;
private Builder(){
}
public BeeField.Builder withAccessFlags(FieldAccessFlags... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public BeeField.Builder withType(Class<?> type) {
this.type=type;
return this;
}
public BeeField.Builder withName(String name) {
this.name=name;
return this;
}
public BeeField build() {
return new BeeField(accessFlags, type, name);
}
}
}

View file

@ -1,95 +0,0 @@
package nl.sander.beejava.api;
import nl.sander.beejava.CodeContainer;
import nl.sander.beejava.TypeMapper;
import nl.sander.beejava.flags.MethodAccessFlags;
import java.util.*;
import java.util.stream.Collectors;
/**
* Models a method in a BeeClass
*/
public final class BeeMethod extends CodeContainer {
private final String name;
private final Class<?> returnType;
private BeeMethod(String name, Set<MethodAccessFlags> accessFlags,
List<BeeParameter> formalParameters,
Class<?> returnType, List<CodeLine> code) {
this.name = name;
this.accessFlags.addAll(accessFlags);
this.formalParameters.addAll(formalParameters);
this.returnType = returnType;
super.code.addAll(code);
}
public static Builder builder() {
return new Builder();
}
public String getName() {
return name;
}
public Class<?> getReturnType() {
return returnType;
}
@Override
public boolean isConstructor() {
return false;
}
public void validate() {
//TODO
/*
* here we could add checks like:
* -If this method is in a class rather than an interface, and the name of the method is <init>, then the descriptor must denote a void method.
* -If the name of the method is <clinit>, then the descriptor must denote avoid method, and, in a class file whose version number is 51.0 or above,a method that takes no arguments
*/
}
public static class Builder {
private final Set<MethodAccessFlags> accessFlags = new HashSet<>();
private final List<BeeParameter> formalParameters = new LinkedList<>();
private final List<CodeLine> code = new LinkedList<>();
private String name;
private Class<?> returnType = Void.class;
private Builder() {
}
public Builder withName(String name) {
this.name = name;
return this;
}
public Builder withAccessFlags(MethodAccessFlags... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public Builder withFormalParameters(BeeParameter... formalParameters) {
this.formalParameters.addAll(Arrays.asList(formalParameters));
return this;
}
public Builder withReturnType(Class<?> returnType) {
this.returnType = returnType;
return this;
}
public Builder withCode(CodeLine... lines) {
this.code.addAll(Arrays.asList(lines));
return this;
}
public BeeMethod build() {
BeeMethod beeMethod = new BeeMethod(name, accessFlags, formalParameters, returnType, code);
code.forEach(line -> line.setOwner(beeMethod));
return beeMethod;
}
}
}

View file

@ -1,17 +0,0 @@
package nl.sander.beejava.api;
/**
* Contains the name of the package for a class
*/
public final class BeePackage {
private final String name;
BeePackage(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View file

@ -1,190 +0,0 @@
package nl.sander.beejava.api;
import nl.sander.beejava.flags.ClassAccessFlags;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Contains all information needed for compilation
*
* End users need to create an instance of this class (using the Builder) and add all fields, methods etc.
* Once created the BeeSource object is immutable.
*/
public final class BeeSource {
private final Version classFileVersion;
private final BeePackage beePackage;
private final Set<ClassAccessFlags> accessFlags = new HashSet<>();
private final String simpleName;
private final Class<?> superClass;
private final Set<Class<?>> interfaces = new HashSet<>();
private final Set<BeeField> fields = new HashSet<>();
private final Set<BeeConstructor> constructors = new HashSet<>();
private final Set<BeeMethod> methods = new HashSet<>();
private BeeSource(Version classFileVersion,
BeePackage beePackage, Set<ClassAccessFlags> accessFlags, String simpleName, Class<?> superClass,
Set<Class<?>> interfaces, Set<BeeField> fields, Set<BeeConstructor> constructors, Set<BeeMethod> methods) {
this.classFileVersion = classFileVersion;
this.beePackage = beePackage;
this.accessFlags.addAll(accessFlags);
this.simpleName = simpleName;
this.superClass = superClass;
this.interfaces.addAll(interfaces);
this.fields.addAll(fields);
this.constructors.addAll(constructors);
this.methods.addAll(methods);
}
/**
* Create a new BeeSource Builder class.
*
* @return a new instance of a Builder
*/
public static BeeSource.Builder builder() {
return new Builder();
}
/**
* @return The classfile version
*/
public Version getClassFileVersion() {
return classFileVersion;
}
/**
* @return The package in which the compiled class will reside.
*/
public BeePackage getPackage() {
return beePackage;
}
/**
* returns the unqualified name, like java.lang.Class
*/
public String getSimpleName() {
return simpleName;
}
/**
* @return all constructors that are provided with the class
*/
public Set<BeeConstructor> getConstructors() {
return Collections.unmodifiableSet(constructors);
}
/**
* @return all methods that are provided with the class
*/
public Set<BeeMethod> getMethods() {
return methods;
}
/**
* @return The access flags for the class
*/
public Set<ClassAccessFlags> getAccessFlags() {
return Collections.unmodifiableSet(accessFlags);
}
/**
* @return The full name, like java.lang.Class
*/
public String getName() {
return beePackage.getName() + "." + simpleName;
}
/**
* @return The superclass
*/
public Class<?> getSuperClass() {
return superClass;
}
/**
* @return a list of unique interfaces that the class will implements
*/
public Set<Class<?>> getInterfaces() {
return Collections.unmodifiableSet(interfaces);
}
/**
* @return a list of unique fields that the class will contain
*/
public Set<BeeField> getFields() {
return Collections.unmodifiableSet(fields);
}
/**
* Helper class for creating BeeSource classes
*/
public static class Builder {
private final Set<ClassAccessFlags> accessFlags = new HashSet<>();
private final Set<Class<?>> interfaces = new HashSet<>();
private final Set<BeeField> fields = new HashSet<>();
private Version version;
private BeePackage beePackage;
private Class<?> superClass = Object.class;
private String simpleName;
private final Set<BeeConstructor> constructors = new HashSet<>();
private final Set<BeeMethod> methods = new HashSet<>();
private Builder() {
}
public Builder withClassFileVersion(Version version) {
this.version = version;
return this;
}
public BeeSource.Builder withPackage(String beePackage) {
this.beePackage = new BeePackage(beePackage);
return this;
}
public BeeSource.Builder withAccessFlags(ClassAccessFlags... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public BeeSource.Builder withSimpleName(String simpleName) {
this.simpleName = simpleName;
return this;
}
public Builder withSuperClass(Class<?> superClass) {
this.superClass = superClass;
return this;
}
public Builder withInterfaces(Class<?>... interfaces) {
this.interfaces.addAll(Arrays.asList(interfaces));
return this;
}
public Builder withFields(BeeField... fields) {
this.fields.addAll(Arrays.asList(fields));
return this;
}
public Builder withConstructors(BeeConstructor... constructors) {
this.constructors.addAll(Arrays.asList(constructors));
return this;
}
public Builder withMethods(BeeMethod... methods) {
this.methods.addAll(Arrays.asList(methods));
return this;
}
public BeeSource build() {
BeeSource beeSource = new BeeSource(version, beePackage, accessFlags, simpleName, superClass, interfaces, fields, constructors, methods);
constructors.forEach(c -> c.setOwner(beeSource));
methods.forEach(m -> m.setOwner(beeSource));
return beeSource;
}
}
}

View file

@ -1,252 +0,0 @@
package nl.sander.beejava.api;
import nl.sander.beejava.CodeContainer;
import nl.sander.beejava.JavaOpcode;
import nl.sander.beejava.TypeMapper;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public final class CodeLine {
private final int linenumber;
private final Opcode opcode;
private Ref ref;
private BeeParameter parameter;
private Class<?> type;
private String methodName;
private List<Class<?>> inputSignature;
private String outputSignature;
private BeeField ownfield; // when you create a class with a field, you can refer to it
private Field externalfield; // when you refer to a field from another class
private Object constValue;
private ConstantPoolEntry assignedEntry;
private CodeContainer owner;
private JavaOpcode javaOpcode;
CodeLine(int linenumber, Opcode opcode) {
this.linenumber = linenumber;
this.opcode = opcode;
}
public static CodeLine line(int nr, Opcode opcode, Ref ref) {
return new CodeLine(nr, opcode).withRef(ref);
}
public static CodeLine line(int nr, Opcode opcode, String fieldClass, String fieldName) {
return new CodeLine(nr, opcode).withExternalFieldRef(fieldClass, fieldName);
}
public static CodeLine line(int nr, Opcode opcode, Object constValue) {
return new CodeLine(nr, opcode).withConstValue(constValue);
}
public static CodeLine line(int nr, Opcode opcode, String className, String methodName, String inputSignature) throws ClassNotFoundException {
return new CodeLine(nr, opcode).withClassName(className).withMethodName(methodName).withInput(parse(inputSignature)).withVoidOutput();
}
private static List<Class<?>> parse(String inputSignature) throws ClassNotFoundException {
if ("()".equals(inputSignature)) {
return Collections.emptyList();
} else {
String[] params = inputSignature.split(",");
List<Class<?>> paramClasses = new ArrayList<>();
for (String param : params) {
paramClasses.add(Class.forName(param));
}
return paramClasses;
}
}
public static CodeLine line(int nr, Opcode opcode,
Ref ref, String methodNameRef, String inputSignature) throws ClassNotFoundException {
return new CodeLine(nr, opcode).withRef(ref).withMethodName(methodNameRef).withInput(parse(inputSignature)).withVoidOutput();
}
public static CodeLine line(int nr, Opcode opcode,
Ref ref, String methodNameRef, String inputSignature, String outputSignature) throws ClassNotFoundException {
return new CodeLine(nr, opcode).withRef(ref).withMethodName(methodNameRef).withInput(parse(inputSignature)).withOutput(outputSignature);
}
public static CodeLine line(int nr, Opcode opcode) {
return new CodeLine(nr, opcode);
}
public static CodeLine line(int nr, Opcode opcode, BeeParameter parameter) {
return new CodeLine(nr, opcode).withParameter(parameter);
}
public static CodeLine line(int nr, Opcode opcode, BeeField intField) {
return new CodeLine(nr, opcode).withRef(Ref.THIS).withOwnField(intField);
}
private CodeLine withRef(Ref ref) {
this.ref = ref;
return this;
}
private CodeLine withClassName(String className) {
this.type = loadClass(className);
return this;
}
private CodeLine withMethodName(String methodName) {
this.methodName = methodName;
return this;
}
private CodeLine withInput(List<Class<?>> inputSignature) {
this.inputSignature = inputSignature;
return this;
}
private CodeLine withVoidOutput() {
return withOutput("V");
}
private CodeLine withOutput(String outputSignature) {
this.outputSignature = outputSignature;
return this;
}
private CodeLine withParameter(BeeParameter parameter) {
this.parameter = parameter;
return this;
}
private CodeLine withConstValue(Object constValue) {
this.constValue = constValue;
return this; //TODO
}
private CodeLine withOwnField(BeeField beeField) {
this.ownfield = beeField;
return this;
}
private CodeLine withExternalFieldRef(String className, String field) {
this.type = loadClass(className);
this.externalfield = loadField(field);
return this;
}
// TODO decide whether to work with Strings or class objects...
/*
* Look up the type of a field in a class
*
* Assumes field is accessible
*
* @param className the class containing a field
* @param field name of the field
* @return the field type
*/
private Field loadField(String field) {
try {
return type.getField(field);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
public boolean hasClassName() {
return type != null;
}
public String getClassName() {
return type.getName();
}
public String getMethodName() {
return methodName;
}
public boolean hasMethodCall() {
return methodName != null;
}
public String getMethodSignature() {
return inputSignature.stream()
.map(TypeMapper::map)
.collect(Collectors.joining(",", "(", ")")) + outputSignature;
}
public Ref getRef() {
return ref;
}
public boolean hasRef() {
return ref != null;
}
public boolean hasRefToOwnField() {
return ownfield != null;
}
public Object getConstValue() {
return constValue;
}
public boolean hasConstValue() {
return constValue != null;
}
public BeeField getOwnfield() {
return ownfield;
}
public BeeParameter getParameter() {
return parameter;
}
public boolean hasRefToExternalField() {
return externalfield != null;
}
public Field getExternalfield() {
return externalfield;
}
public ConstantPoolEntry getAssignedEntry() {
return assignedEntry;
}
public void setAssignedEntry(ConstantPoolEntry assignedEntry) {
this.assignedEntry = assignedEntry;
}
public Opcode getOpcode() {
return opcode;
}
private Class<?> loadClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e); //TODO specific exception
}
}
public CodeContainer getOwner() {
return owner;
}
public void setOwner(CodeContainer codeContainer) {
this.owner = codeContainer;
}
public JavaOpcode getJavaOpcode() {
return javaOpcode;
}
public void setJavaOpcode(JavaOpcode javaOpcode) {
this.javaOpcode = javaOpcode;
}
}

View file

@ -1,71 +0,0 @@
package nl.sander.beejava.api;
public enum Opcode {
LD_VAR("ld_var"),
LD_CONST("ld_const"),
LD_FIELD("ld_field"),
STORE("store"),
CONST("const"),
NEWARRAY("new"),
RETURN("return"),
ARRAYLENGTH("length"),
THROW("throw"),
PUSH("push"),
CHECKCAST("checkcast"),
ADD("add"),
COMPARE("cmp"),
DIVIDE("div"),
MULTIPLY("mul"),
NEGATE("neg"),
REMAINDER("rem"),
SUBTRACT("sub"),
DUPLICATE("dup"),
DUPLICATE_DOWN("dup_x1"),
DUPLICATE2("dup2"),
DUPLICATE2_DOWN("dup2_x1"),
GET("get"),
GOTO("goto"),
GOTO_W("goto_w"),
TO_DOUBLE("2d"),
TO_INT("2i"),
TO_LONG("2l"),
TO_BYTE("2b"),
TO_CHAR("2c"),
TO_FLOAT("2f"),
TO_SHORT("2s"),
AND("and"),
IF_EQUAL("ifeq"),
IF_NOT_EQUAL("ifneq"),
IF_LESS_THAN("iflt"),
IF_GREATER_OR_EQUAL("ifge"),
IF_GREATER_THAN("ifgt"),
IF_LESS_OR_EQUAL("ifle"),
IF_NOT_NULL("ifnotnull"),
IF_NULL("ifnull"),
INCREMENT("inc"),
INSTANCEOF("instanceof"),
INVOKE("invoke"),
OR("or"),
SHIFT_LEFT("shr"),
SHIFT_RIGHT("shl"),
LOGICAL_SHIFT_RIGHT("ushr"),
XOR("xor"),
LOOKUPSWITCH("lookupswitch"),
MONITORENTER("monitorenter"),
MONITOREXIT("monitorexit"),
MULTIANEWARRAY("multinewarray"),
NEW("new"),
NOP("nop"),
POP("pop"),
POP2("pop2"),
PUT("put"),
SWAP("swap"),
TABLESWITCH("tableswitch"),
WIDE("wide");
private final String name;
Opcode(String name) {
this.name=name;
}
}

View file

@ -1,9 +0,0 @@
package nl.sander.beejava.api;
/**
* used to indicate this, super, or none of those, in which case it's class
*/
public enum Ref {
THIS,
SUPER
}

View file

@ -1,8 +1,7 @@
package nl.sander.beejava;
package nl.sander.bejava;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.flags.AccessFlags;
import nl.sander.beejava.util.ByteBuf;
import nl.sander.bejava.flags.AccessFlags;
import nl.sander.bejava.util.ByteBuf;
public class BytecodeGenerator {
@ -13,12 +12,12 @@ public class BytecodeGenerator {
}
public static byte[] generate(CompiledClass compiledClass) {
BytecodeGenerator bytecodeGenerator = new BytecodeGenerator(compiledClass);
var bytecodeGenerator = new BytecodeGenerator(compiledClass);
return bytecodeGenerator.generate();
}
private byte[] generate() {
ByteBuf buf = new ByteBuf();
var buf = new ByteBuf();
buf.addU8(0xCA, 0xFE, 0xBA, 0xBE);
buf.addU16(compiledClass.getSource().getClassFileVersion().getMinor());
buf.addU16(compiledClass.getSource().getClassFileVersion().getMajor());

View file

@ -1,29 +1,32 @@
package nl.sander.beejava;
package nl.sander.bejava;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.classinfo.FieldInfo;
import nl.sander.beejava.classinfo.MethodInfo;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ClassEntry;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.bejava.api.BeSource;
import nl.sander.bejava.classinfo.FieldInfo;
import nl.sander.bejava.classinfo.MethodInfo;
import nl.sander.bejava.constantpool.ConstantPool;
import nl.sander.bejava.constantpool.entry.ClassEntry;
import nl.sander.bejava.constantpool.entry.ConstantPoolEntry;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Eventually contains all information for generation of the actual bytecode.
* Output of the {@link Compiler} class.
*/
public class CompiledClass {
private final Set<ConstantPoolEntry> constantTree = new LinkedHashSet<>();
private final Set<ClassEntry> interfaces = new HashSet<>();
private final Set<FieldInfo> fields = new HashSet<>();
private final Set<MethodInfo> methods = new HashSet<>();
private final BeeSource beeSource;
private final BeSource beSource;
private ClassEntry thisClass;
private ClassEntry superClass;
private ConstantPool constantPool;
CompiledClass(BeeSource beeSource) {
this.beeSource = beeSource;
CompiledClass(BeSource beSource) {
this.beSource = beSource;
}
int getSuperIndex() {
@ -56,8 +59,8 @@ public class CompiledClass {
interfaces.add(interfaceEntry);
}
BeeSource getSource() {
return beeSource;
BeSource getSource() {
return beSource;
}
public void setThisClass(ClassEntry newThisClass) {

View file

@ -0,0 +1,158 @@
package nl.sander.bejava;
import nl.sander.bejava.api.*;
import nl.sander.bejava.classinfo.FieldInfo;
import nl.sander.bejava.classinfo.MethodInfo;
import nl.sander.bejava.constantpool.entry.Utf8Entry;
/**
* Transforms BeeSource to CompiledClass
* A client must supply a {@link BeSource} containing a set of {@link CodeLine}s that is assumed to be correct.
* It doesn't check if a valid state is reached.
*/
public class Compiler {
private final CompiledClass compiledClass;
private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator();
private Utf8Entry codeAttributeNameEntry;
/**
* construct a compiler object.
*/
/*
* At this moment I'm not sure if this class will be able to reused.
*/
public Compiler(CompiledClass compiledClass) {
this.compiledClass = compiledClass;
}
/**
* compile a BeeSource object into a CompiledClass object.
*
* @param beSource the Class object for which the constant pool needs to be created
* @return a CompiledClass object (that can be turned into bytecode)
*/
public static CompiledClass compile(BeSource beSource) {
return new Compiler(new CompiledClass(beSource)).compile();
}
/**
* construct a CompiledClass object that contains all information for generating the bytecode
*/
public CompiledClass compile() {
compiledClass.getSource().getFields().forEach(this::addToConstantPool);
compiledClass.getSource().getConstructors().forEach(this::addToConstantPool);
compiledClass.getSource().getMethods().forEach(this::addToConstantPool); // compiledClass.getSource() ?
addClassAndSuperClassEntriesToCompiledClass();
var constantPool = constantPoolCreator.createConstantPool(compiledClass.getConstantTree());
compiledClass.setConstantPool(constantPool);
// add refs to super interfaces to class descriptor and constantpool
compiledClass.getSource().getInterfaces().forEach(interfase -> {
var interfaceEntry = ConstantPoolEntryCreator.getOrCreateClassEntry(interfase);
compiledClass.addInterface(interfaceEntry);
compiledClass.addConstantPoolEntry(interfaceEntry);
});
// add refs to fields to class descriptor
compiledClass.getSource().getFields().stream()
.map(field ->
new FieldInfo(ConstantPoolEntryCreator.getOrCreateUtf8(field.getName()),
ConstantPoolEntryCreator.getOrCreateUtf8(internalName(field.getType().getName())))
.addAccessFlags(field.getAccessFlags())
)
.forEach(compiledClass::addField);
addConstructors();
addMethods();
return compiledClass;
}
// why not put this everywhere, it's not like it's ever going to change
private String internalName(String name) {
return name.replaceAll("\\.", "/");
}
private void addClassAndSuperClassEntriesToCompiledClass() {
var classEntry = ConstantPoolEntryCreator.getOrCreateClassEntry(compiledClass.getSource().getName());
compiledClass.setThisClass(classEntry);
compiledClass.addConstantPoolEntry(classEntry);
var superClass = ConstantPoolEntryCreator.getOrCreateClassEntry(compiledClass.getSource().getSuperClass());
compiledClass.addConstantPoolEntry(superClass);
compiledClass.setSuperClass(superClass);
this.codeAttributeNameEntry = ConstantPoolEntryCreator.getOrCreateUtf8("Code");
compiledClass.addConstantPoolEntry(codeAttributeNameEntry);
}
public void addConstructors() {
compiledClass.getSource().getConstructors().stream()
.map(this::createMethod)
.forEach(compiledClass::addMethod);
}
/**
* maps methods from the source to a MethodInfo and adds that to the CompiledClass.
*/
public void addMethods() {
compiledClass.addConstantPoolEntry(codeAttributeNameEntry);
for (var method : compiledClass.getSource().getMethods()) {
compiledClass.addMethod(createMethod(method));
}
}
/**
* create methodInfo object for classfile
*/
private MethodInfo createMethod(CodeContainer method) {
return new MethodInfo(
ConstantPoolEntryCreator.getOrCreateUtf8(method.getName()),
ConstantPoolEntryCreator.getOrCreateUtf8(method.getSignature()))
.addAccessFlags(method.getAccessFlags())
.addAttribute(MethodCodeAttributeCreator.createCodeAttribute(codeAttributeNameEntry, method));
}
private void addToConstantPool(BeField field) {
var fieldRefEntry = ConstantPoolEntryCreator.getOrCreateFieldRefEntry(compiledClass.getSource().getName(), field.getName(), field.getType());
compiledClass.addConstantPoolEntry(fieldRefEntry);
}
/**
* inspect a method or constructor, extract items that need to be added, and add them to the constant pool
*/
private void addToConstantPool(CodeContainer codeContainer) {
compiledClass.addConstantPoolEntry(ConstantPoolEntryCreator.getOrCreateUtf8(codeContainer.getName()));
compiledClass.addConstantPoolEntry(ConstantPoolEntryCreator.getOrCreateUtf8(codeContainer.getSignature()));
codeContainer.getExpandedCode().forEach(this::updateConstantPool);
}
/**
* Scan code line for items that need adding to the constant pool
*
* Constantpool uniqueness is maintained in two ways:
* 1. ConstantPoolEntryCreator.getOrCreateMethodRefEntry (and other methods) return a tree of entries.
* Sub-entries are kept in cache by the ConstantPoolEntryCreator and it returns a cached entry over a new one, if deemed equal.
* 2. the root node (also in aforementioned cache) is not symmetric because it is the only one that can be added to The compiledClass.
* So Compiled class also contains a set of unique root nodes.
*/
private void updateConstantPool(JavaInstruction instruction) {
if (instruction.hasMethodRef()) {
compiledClass.addConstantPoolEntry(instruction.getMethodRef());
}
if (instruction.hasFieldRef()) {
compiledClass.addConstantPoolEntry(instruction.getFieldRef());
}
if (instruction.hasConstantRef()) {
compiledClass.addConstantPoolEntry(instruction.getConstantEntry());
}
}
}

View file

@ -1,7 +1,7 @@
package nl.sander.beejava;
package nl.sander.bejava;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.bejava.constantpool.ConstantPool;
import nl.sander.bejava.constantpool.entry.ConstantPoolEntry;
import java.util.Set;
@ -44,7 +44,7 @@ public class ConstantPoolCreator {
* - then add grandchildren (I don't think there's more levels)
*/
private void updateToplevelElements(Set<ConstantPoolEntry> children) {
for (ConstantPoolEntry topElement : children) {
for (var topElement : children) {
addToPool(topElement); // grandparents
updateChildElements(topElement.getChildren());
}
@ -52,11 +52,11 @@ public class ConstantPoolCreator {
private void updateChildElements(Set<ConstantPoolEntry> children) {
// parents
for (ConstantPoolEntry child : children) {
for (var child : children) {
addToPool(child);
}
// then grandchildren
for (ConstantPoolEntry child : children) {
for (var child : children) {
updateChildElements(child.getChildren()); // no problem if there are great grand children
}
}

View file

@ -0,0 +1,99 @@
package nl.sander.bejava;
import nl.sander.bejava.constantpool.entry.*;
import nl.sander.bejava.operands.ConstantOperand;
import java.util.HashMap;
import java.util.Map;
/**
* Responsible for creating unique constant pool entries
*/
class ConstantPoolEntryCreator {
private static final Map<Integer, ConstantPoolEntry> cache = new HashMap<>();
private ConstantPoolEntryCreator() {
}
/*
* creates a FieldRefEntry when not found in cache, otherwise gets it from there
*/
static FieldRefEntry getOrCreateFieldRefEntry(String declaringClassName, String fieldName, Class<?> fieldType) {
return cache(new FieldRefEntry(getOrCreateClassEntry(declaringClassName), getOrCreateFieldNameAndType(fieldName, fieldType)));
}
/*
* creates a MethodRefEntry when not found in cache, otherwise gets it from there
*/
static MethodRefEntry getOrCreateMethodRefEntry(String className, String methodName, String signature) {
ClassEntry classEntry = getOrCreateClassEntry(className);
return cache(new MethodRefEntry(classEntry, getOrCreateMethodNameAndType(methodName, signature)));
}
/*
* creates a NamAndTypeEntry for a field when not found in cache, otherwise gets it from there
*/
private static NameAndTypeEntry getOrCreateFieldNameAndType(String name, Class<?> type) {
return cache(new NameAndTypeEntry(
cache(new Utf8Entry(name)),
cache(new Utf8Entry(TypeMapper.map(type)))));
}
/*
* creates a NamAndTypeEntry for a method when not found in cache, otherwise gets it from there
*/
private static NameAndTypeEntry getOrCreateMethodNameAndType(String methodName, String methodSignature) {
return new NameAndTypeEntry(
cache(new Utf8Entry(methodName)),
cache(new Utf8Entry(methodSignature)));
}
/*
* get or create a ClassEntry
*/
public static ClassEntry getOrCreateClassEntry(String externalClassName) {
return cache(new ClassEntry(cache(new Utf8Entry(internalName(externalClassName)))));
}
public static ClassEntry getOrCreateClassEntry(Class<?> type) {
return getOrCreateClassEntry(type.getName());
}
/*
* If a constant is in the codeline, it needs to be added to the constant pool.
*/
public static ConstantPoolEntry getOrCreatePrimitiveConstantEntry(ConstantOperand operand) {
return cache(switch (operand.getType()) {
case STRING -> new StringEntry(cache(new Utf8Entry(operand.getValue())));
case INT, BYTE, SHORT -> new IntegerEntry(operand.getValue());
case LONG -> new LongEntry(Long.parseLong(operand.getValue()));
case FLOAT -> new FloatEntry(operand.getValue());
case DOUBLE -> new DoubleEntry(Double.parseDouble(operand.getValue()));
case CHAR -> new IntegerEntry(Character.codePointAt(operand.getValue(), 0));
case BOOLEAN -> new IntegerEntry(Boolean.parseBoolean(operand.getValue()) ? 1 : 0);
});
}
public static Utf8Entry getOrCreateUtf8(String utf8) {
return cache(new Utf8Entry(utf8));
}
// why not put this everywhere, it's not like it's ever going to change
private static String internalName(String name) {
return name.replaceAll("\\.", "/");
}
@SuppressWarnings("unchecked")
static <T extends ConstantPoolEntry> T cache(T newEntry) {
// if the argument newEntry is found in cache, return the cached entry and discard the argument
// Otherwise store it.
// Can't check for equality unless you create a potential new entry first
int hash = newEntry.hashCode();
return (T) cache.computeIfAbsent(hash, k -> newEntry);
// a hashmap with key hash of value is weird right?
// A HashSet is a HashMap with entry: key = value, which would work, but I cannot _get_ anything from a set.
}
}

View file

@ -0,0 +1,43 @@
package nl.sander.bejava;
import nl.sander.bejava.api.BeField;
import nl.sander.bejava.flags.AccessFlags;
import java.lang.reflect.Field;
/**
* Union type for an existing standard java field (already loaded) or a field for the class that is being compiled
*/
public class FieldWrapper {
private final Field reflectField;
private final BeField beefield;
public FieldWrapper(Field reflectField, BeField beefield) {
this.reflectField = reflectField;
this.beefield = beefield;
}
public Class<?> getType() {
if (reflectField != null) {
return reflectField.getType();
} else {
return beefield.getType();
}
}
public String getName() {
if (reflectField != null) {
return reflectField.getName();
} else {
return beefield.getName();
}
}
public int getModifiers() {
if (reflectField != null) {
return reflectField.getModifiers();
} else {
return AccessFlags.combine(beefield.getAccessFlags());
}
}
}

View file

@ -0,0 +1,84 @@
package nl.sander.bejava;
import nl.sander.bejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.bejava.constantpool.entry.FieldRefEntry;
import nl.sander.bejava.constantpool.entry.MethodRefEntry;
/**
* bejava opcodes translate to real java opcodes. A JavaInstruct
*/
public class JavaInstruction {
private final JavaOpcode opcode;
private final MethodRefEntry methodRefEntry;
private final FieldRefEntry fieldRefEntry;
private final ConstantPoolEntry constantEntry;
private Integer localVariableIndex; // TODO not used yet
public JavaInstruction(JavaOpcode opcode) {
this(opcode, null, null, null, null);
}
public JavaInstruction(JavaOpcode opcode, int localVariableIndex) {
this(opcode, localVariableIndex, null, null, null);
}
public JavaInstruction(JavaOpcode opcode, ConstantPoolEntry constantEntry) {
this(opcode, null, null, null, constantEntry);
}
public JavaInstruction(JavaOpcode opcode, MethodRefEntry methodRefEntry) {
this(opcode, null, methodRefEntry, null, null);
}
public JavaInstruction(JavaOpcode opcode, FieldRefEntry fieldRefEntry) {
this(opcode, null, null, fieldRefEntry, null);
}
private JavaInstruction(JavaOpcode opcode, Integer localVariableIndex, MethodRefEntry methodRefEntry, FieldRefEntry fieldRefEntry, ConstantPoolEntry constantEntry) {
this.opcode = opcode;
this.localVariableIndex = localVariableIndex;
this.methodRefEntry = methodRefEntry;
this.fieldRefEntry = fieldRefEntry;
this.constantEntry = constantEntry;
}
public JavaOpcode getOpcode() {
return opcode;
}
public MethodRefEntry getMethodRef() {
return methodRefEntry;
}
public FieldRefEntry getFieldRef() {
return fieldRefEntry;
}
public boolean hasMethodRef() {
return methodRefEntry != null;
}
public boolean hasFieldRef() {
return fieldRefEntry != null;
}
public ConstantPoolEntry getEntry() {
if (methodRefEntry != null) {
return methodRefEntry;
} else if (fieldRefEntry != null) {
return fieldRefEntry;
} else if (constantEntry!=null){
return constantEntry;
} else {
return null; // Not sure yet what to do here.
}
}
public boolean hasConstantRef() {
return constantEntry !=null;
}
public ConstantPoolEntry getConstantEntry() {
return constantEntry;
}
}

View file

@ -1,12 +1,24 @@
package nl.sander.beejava;
package nl.sander.bejava;
public enum JavaOpcode {
LDC(0x12, false, +1),
LDC_W(0x13,true, +1),
LDC2_W ( 0x14, true, +2),
ALOAD ( 0x19, false, +1),
ALOAD_0 ( 0x2a, false, +1),
ALOAD_1 ( 0x2b, false, +1),
ALOAD_2 ( 0x2c, false, +1),
ALOAD_3 ( 0x2d, false, +1),
IRETURN ( 0xac,false, -1),
LRETURN(0xad, false, -2),
FRETURN(0xae,false,-1),
DRETURN(0xaf,false,-2),
ARETURN ( 0xb0,false, -1),
RETURN ( 0xb1,false, 0),
GETSTATIC ( 0xb2,true, +1),
GETFIELD ( 0xb4,true, +1),
@ -14,7 +26,10 @@ public enum JavaOpcode {
INVOKEVIRTUAL ( 0xb6, true, -1),
INVOKESPECIAL ( 0xb7, true, -1),
INVOKESTATIC ( 0xb8, true, -1),
INVOKEDYNAMIC ( 0xba,true,-1);
INVOKEDYNAMIC ( 0xba,true,-1),
PUTFIELD(0xb5, true, -2);
private final int opcode;
private final boolean wide;

View file

@ -1,24 +1,24 @@
package nl.sander.beejava;
package nl.sander.bejava;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.classinfo.attributes.CodeAttribute;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.constantpool.entry.MethodRefEntry;
import nl.sander.beejava.constantpool.entry.Utf8Entry;
import nl.sander.beejava.util.ByteBuf;
import nl.sander.bejava.api.CodeContainer;
import nl.sander.bejava.classinfo.attributes.CodeAttribute;
import nl.sander.bejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.bejava.constantpool.entry.MethodRefEntry;
import nl.sander.bejava.constantpool.entry.Utf8Entry;
import nl.sander.bejava.util.ByteBuf;
public class MethodCodeCreator {
public class MethodCodeAttributeCreator {
public static CodeAttribute createCodeAttribute(Utf8Entry codeAttributeNameEntry, CodeContainer codeContainer) {
CodeAttribute codeAttribute = new CodeAttribute(codeAttributeNameEntry);
var codeAttribute = new CodeAttribute(codeAttributeNameEntry);
codeAttribute.setMaxStack(calculateMaxStack(codeContainer));
codeAttribute.setMaxLocals(codeContainer.formalParameters.size() + 1);
ByteBuf byteBuf = new ByteBuf();
codeAttribute.setMaxLocals(codeContainer.getFormalParameters().size() + 1);
var byteBuf = new ByteBuf();
codeContainer.getCode().forEach(codeLine -> {
JavaOpcode javaOpcode = codeLine.getJavaOpcode(); // the opcode we determined in calculateMaxStack
codeContainer.getExpandedCode().forEach(instruction -> {
var javaOpcode = instruction.getOpcode();
byteBuf.addU8(javaOpcode.getByteCode());
ConstantPoolEntry constantPoolEntry = codeLine.getAssignedEntry();
var constantPoolEntry = instruction.getEntry();
if (constantPoolEntry != null) {
if (javaOpcode.isWide()) {
byteBuf.addU16(constantPoolEntry.getIndex());
@ -36,14 +36,12 @@ public class MethodCodeCreator {
private static int calculateMaxStack(CodeContainer codeContainer) {
int stackSize = 0;
int maxStackSize = 0;
for (CodeLine codeLine : codeContainer.getCode()) {
JavaOpcode javaOpcode = OpcodeMapper.mapOpcode(codeLine);
codeLine.setJavaOpcode(javaOpcode); //not really nice that we mutate codeLine, but this way we don't have to calculate the JavaOpcode twice
stackSize += javaOpcode.getStackDif();
ConstantPoolEntry assignedEntry = codeLine.getAssignedEntry();
for (var instruction : codeContainer.getExpandedCode()) {
stackSize += instruction.getOpcode().getStackDif();
var assignedEntry = instruction.getEntry();
if (assignedEntry instanceof MethodRefEntry) {
MethodRefEntry methodRefEntry = (MethodRefEntry) assignedEntry;
String argumentTypes = methodRefEntry.getNameAndType().getType();
var methodRefEntry = (MethodRefEntry) assignedEntry;
var argumentTypes = methodRefEntry.getNameAndType().getType();
stackSize -= getNrArguments(argumentTypes);
}
if (stackSize > maxStackSize) {

View file

@ -0,0 +1,209 @@
package nl.sander.bejava;
import nl.sander.bejava.api.*;
import nl.sander.bejava.operands.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Translates from BeeJava opcodes to actual java opcodes.
*/
public class OpcodeTranslator {
private final static OpcodeTranslator instance = new OpcodeTranslator();
private final Parser parser = new Parser();
private BeSource beSource;
/*
* trying to make SourceExpander reusable.
* @not_multithreaded.
* //TODO should not mutate beesource
*/
public static void translate(BeSource beSource) {
instance.init(beSource);
instance.doExpand();
}
private void init(BeSource beSource) {
this.beSource = beSource;
}
private void doExpand() {
beSource.getConstructors().forEach(this::translate);
beSource.getMethods().forEach(this::translate);
}
private void translate(CodeContainer codeContainer) {
var methodInstructions = codeContainer.getCode().stream()
.flatMap(o -> translate(codeContainer, o).stream())
.collect(Collectors.toList());
codeContainer.setExpandedCode(methodInstructions);
}
private List<JavaInstruction> translate(CodeContainer codeContainer, CodeLine codeLine) {
var operand = parser.parse(codeLine);
return translate(codeContainer, codeLine.getOpcode(), operand);
}
private List<JavaInstruction> translate(CodeContainer codeContainer, Opcode opcode, Operand operand) {
List<JavaInstruction> instructions = new ArrayList<>();
if (operand instanceof MethodOperand) {
var mo = (MethodOperand) operand;
if ("this".equals(mo.className)) {
instructions.add(new JavaInstruction(JavaOpcode.ALOAD_0));
}
}
if (opcode == Opcode.LOAD) {
if (operand instanceof ConstantOperand) {
var constantEntry = ConstantPoolEntryCreator.getOrCreatePrimitiveConstantEntry((ConstantOperand) operand);
instructions.add(new JavaInstruction(JavaOpcode.LDC, constantEntry));
}
} else if (opcode == Opcode.RETURN && operand instanceof VoidOperand) {
instructions.add(new JavaInstruction(JavaOpcode.RETURN));
} else if (opcode == Opcode.GET) {
assert operand instanceof FieldOperand;
var fieldOperand = (FieldOperand) operand;
var field = getField(codeContainer, fieldOperand);
instructions.add(new JavaInstruction(getJavaOpcodeForGet(field),
ConstantPoolEntryCreator.getOrCreateFieldRefEntry(getType(codeContainer, fieldOperand), field.getName(), field.getType())));
} else if (opcode == Opcode.INVOKE) {
assert operand instanceof MethodOperand;
var mo = (MethodOperand) operand;
if ("super".equals(mo.methodName)) {
instructions.add(new JavaInstruction(JavaOpcode.INVOKESPECIAL,
ConstantPoolEntryCreator.getOrCreateMethodRefEntry(beSource.getSuperClass().getName(), "<init>", "()V")));
} else {
instructions.add(new JavaInstruction(JavaOpcode.INVOKEVIRTUAL, ConstantPoolEntryCreator.getOrCreateMethodRefEntry(mo.className, mo.methodName, mo.signature)));
}
} else if (operand instanceof FieldOperand) {
var fieldOperand = (FieldOperand) operand;
if (fieldOperand.className.equals("this")) {
instructions.add(new JavaInstruction(JavaOpcode.ALOAD_0));
}
if (opcode == Opcode.RETURN) {
instructions.add(getReturnInstruction(codeContainer, fieldOperand));
}
} else if (operand instanceof LocalVariableOperand) {
var localVariable = (LocalVariableOperand) operand;
instructions.add(codeContainer.getParameter(localVariable.name)
.map(this::getJavaLoadInstruction)
.orElse(null)); //else case
}
// TODO continue here. finish simpleBean
return instructions;
}
private JavaOpcode getJavaOpcodeForGet(FieldWrapper field) {
JavaOpcode get;
if (Modifier.isStatic(field.getModifiers())) {
get = JavaOpcode.GETSTATIC;
} else {
get = JavaOpcode.GETFIELD;
}
return get;
}
private JavaInstruction getReturnInstruction(CodeContainer codeContainer, FieldOperand fo) {
var field = getField(codeContainer, fo);
var javaOpcode = switch (field.getName()) {
case "int", "byte", "boolean", "short" -> JavaOpcode.IRETURN;
case "long" -> JavaOpcode.LRETURN;
case "float" -> JavaOpcode.FRETURN;
case "double" -> JavaOpcode.DRETURN;
default -> JavaOpcode.ARETURN;
};
return new JavaInstruction(javaOpcode);
}
private String getType(CodeContainer codeContainer, FieldOperand fo) {
if (fo.className.equals("this")) {
return codeContainer.getName();
} else {
return fo.className;
}
}
private FieldWrapper getField(CodeContainer codeContainer, FieldOperand fo) {
try {
Field javaField = null;
BeField beField = null;
if (fo.className.equals("this")) {
beField = codeContainer.getOwner().getField(fo.fieldName);
} else {
var type = Class.forName(fo.className);
javaField = type.getDeclaredField(fo.fieldName);
}
return new FieldWrapper(javaField, beField);
} catch (ClassNotFoundException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
private JavaInstruction getJavaLoadInstruction(BeParameter parameter) {
return switch (parameter.getIndex()) {
case 0 -> new JavaInstruction(JavaOpcode.ALOAD_0);
case 1 -> new JavaInstruction(JavaOpcode.ALOAD_1);
case 2 -> new JavaInstruction(JavaOpcode.ALOAD_2);
case 3 -> new JavaInstruction(JavaOpcode.ALOAD_3);
default -> new JavaInstruction(JavaOpcode.ALOAD, parameter.getIndex());
};
}
}
class Parser {
Operand parse(CodeLine codeLine) {
var operand = codeLine.getOperand();
if (operand == null) {
return VoidOperand.INSTANCE;
} else {
if (operand.contains("(")) {
var parenIndex = operand.indexOf('(');
var signature = operand.substring(parenIndex);
var classAndMethodeName = operand.substring(0, parenIndex);
int lastDotIndex = classAndMethodeName.lastIndexOf('.');
var className = classAndMethodeName.substring(0, lastDotIndex);
var methodName = classAndMethodeName.substring(lastDotIndex + 1);
return new MethodOperand(className, methodName, signature);
} else {
int index = operand.lastIndexOf('.');
if (index < 0) {
var constantOperandOrNull = asConstantOperand(operand);
return Objects.requireNonNullElseGet(constantOperandOrNull, () -> new LocalVariableOperand(operand));
} else if (index < operand.length() - 1) {
var className = operand.substring(0, index);
var fieldName = operand.substring(index + 1);
return new FieldOperand(className, fieldName);
}
}
}
throw new IllegalArgumentException("compile error");
}
ConstantOperand asConstantOperand(String value) {
//TODO add boxed types
var tokens = value.split(" ");
var type = Primitive.from(tokens[0].toUpperCase(Locale.ROOT));
if (tokens.length == 2 && type != null) {
return new ConstantOperand(type, tokens[1]);
} else if (value.startsWith("\"")) {
if (value.endsWith("\"")) {
return new ConstantOperand(Primitive.STRING, value.substring(1, value.length() - 1));
} else {
throw new IllegalArgumentException("Unterminated String value");
}
}
return null;
}
}

View file

@ -0,0 +1,20 @@
package nl.sander.bejava;
import nl.sander.bejava.api.BeSource;
/**
* Contains all four passes necessary to go from source as text to bytecode.
*/
public final class OverallCompiler {
private OverallCompiler() {
//
}
public static byte[] compile(String sourcecode) {
BeSource beSource = SourceCompiler.compile(sourcecode);
OpcodeTranslator.translate(beSource);
CompiledClass compiledClass = Compiler.compile(beSource);
return BytecodeGenerator.generate(compiledClass);
}
}

View file

@ -0,0 +1,14 @@
package nl.sander.bejava;
import java.util.Arrays;
public enum Primitive {
INT, SHORT, BYTE, LONG, FLOAT, DOUBLE, CHAR, BOOLEAN, STRING;
public static Primitive from(String value) {
return Arrays.stream(values())
.filter(e -> e.toString().equals(value))
.findFirst()
.orElse(null);
}
}

View file

@ -0,0 +1,282 @@
package nl.sander.bejava;
import nl.sander.bejava.api.*;
import nl.sander.bejava.flags.ClassAccessFlags;
import nl.sander.bejava.flags.FieldAccessFlag;
import nl.sander.bejava.flags.MethodAccessFlag;
import java.util.*;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Parses code as text to a BeeSource object
*/
public class SourceCompiler {
private final static Pattern firstBlanksplitter = Pattern.compile("(.+?) (.+)");
private final static Pattern parensplitter = Pattern.compile("(.+?)\\((.*?)\\)");
private final static Pattern returntypesplitter = Pattern.compile("->");
private final String sourcecode;
private final List<Instruction> instructions = new ArrayList<>();
private int currentLine = 0;
public SourceCompiler(String sourcecode) {
this.sourcecode = sourcecode;
}
public static BeSource compile(String sourcecode) {
return new SourceCompiler(sourcecode).doCompile();
}
public BeSource doCompile() {
Arrays.stream(sourcecode.split("\n"))
.map(this::compileLine)
.forEach(instructions::add);
BeSource beSource = new BeSource();
for (currentLine = 0; currentLine < instructions.size(); ) {
Instruction ins = instructions.get(currentLine);
if (currentLine == 0) {
parseClassDeclaration(ins, beSource);
} else {
parseInstruction(ins, beSource);
}
}
return beSource;
}
private void parseInstruction(Instruction instruction, BeSource beSource) {
if (instruction instanceof ClassInstruction) {
ClassInstruction classInstruction = (ClassInstruction) instruction;
String operand = classInstruction.getOperand();
switch (classInstruction.getOperation()) {
case FIELD -> beSource.addFields(parseField(beSource, operand));
case CONSTRUCTOR -> beSource.addConstructors(parseConstructor(beSource, operand));
case METHOD -> beSource.addMethods(parseMethod(beSource, operand));
default -> throw new IllegalArgumentException("Not allowed here");
}
}
}
private BeMethod parseMethod(BeSource beSource, String text) {
String[] tokens = returntypesplitter.split(text);
final String first;
final Class<?> returnType;
if (tokens.length > 1) {
returnType = getType(tokens[1].trim());
first = tokens[0].trim();
} else {
first = text;
returnType = Void.TYPE;
}
String[] flagsNameParameters = split(first, firstBlanksplitter);
Set<MethodAccessFlag> flags = new HashSet<>();
int i = 0;
Optional<MethodAccessFlag> maybeFlag = MethodAccessFlag.get(flagsNameParameters[i].toUpperCase());
while (maybeFlag.isPresent()) {
flags.add(maybeFlag.get());
i += 1;
maybeFlag = MethodAccessFlag.get(flagsNameParameters[i]);
}
String[] nameParams = split(flagsNameParameters[i], parensplitter);
Set<BeParameter> parameters = new HashSet<>();
String methodName = null;
if (nameParams.length > 0) {
methodName = nameParams[0];
if (nameParams[1].length() > 0) {
int index = 0;
String params = nameParams[1];
String[] paramTokens = params.split(",");
for (String paramToken : paramTokens) {
String[] declaration = paramToken.trim().split(" ");
String type = declaration[0];
String name = declaration[1];
parameters.add(new BeParameter(getType(type), name, index++));
}
}
}
if (methodName == null) {
throw new IllegalArgumentException("method name not found");
}
currentLine += 1;
List<nl.sander.bejava.api.CodeLine> lines = new ArrayList<>();
Instruction nextInstruction = instructions.get(currentLine);
while (currentLine < instructions.size() && nextInstruction instanceof CodeLine) {
nextInstruction = instructions.get(currentLine);
if (nextInstruction instanceof CodeLine) {
lines.add((CodeLine) nextInstruction);
currentLine += 1;
}
}
return new BeMethod(beSource, methodName, flags, parameters, returnType, lines);
}
private BeConstructor parseConstructor(BeSource beSource, String text) {
String[] tokens = split(text, parensplitter);
String flag = tokens[0];
MethodAccessFlag methodAccessFlag = MethodAccessFlag.get(flag.toUpperCase()).orElseThrow(illegalArgument("Not a valid flag " + flag));
String params = tokens[1];
Set<BeParameter> parameters = new HashSet<>();
if (params.length() > 0) {
String[] paramTokens = params.split(",");
int index = 0;
for (String paramToken : paramTokens) {
String[] declaration = paramToken.trim().split(" ");
String type = declaration[0];
String name = declaration[1];
parameters.add(new BeParameter(getType(type), name, index++));
}
}
currentLine += 1;
List<CodeLine> lines = new ArrayList<>();
Instruction nextInstruction = instructions.get(currentLine);
while (currentLine < instructions.size() && nextInstruction instanceof CodeLine) {
nextInstruction = instructions.get(currentLine);
if (nextInstruction instanceof CodeLine) {
lines.add((CodeLine) nextInstruction);
currentLine += 1;
}
}
return new BeConstructor(beSource, Set.of(methodAccessFlag), parameters, lines);
}
private BeField parseField(BeSource beSource, String operand) {
String[] tokens = operand.split(" ");
Set<FieldAccessFlag> flags = new HashSet<>();
String type = null;
String name = null;
for (String token : tokens) {
Optional<FieldAccessFlag> maybeFlag = FieldAccessFlag.get(token.toUpperCase());
if (maybeFlag.isPresent()) {
flags.add(maybeFlag.get());
} else {
if (type == null) {
type = token;
} else {
name = token;
}
}
}
currentLine += 1;
return new BeField(beSource.getName(), flags, getType(type), name);
}
private void parseClassDeclaration(Instruction instruction, BeSource beSource) {
if (instruction instanceof ClassInstruction) {
ClassInstruction classDeclaration = (ClassInstruction) instruction;
ClassOperation operation = classDeclaration.getOperation();
switch (operation) {
case CLASS -> {
beSource.addAccessFlags(ClassAccessFlags.SUPER);
beSource.setClassFileVersion(getVersion(classDeclaration));
String[] tokens = split(classDeclaration.getOperand(), parensplitter);
String rightHand = classDeclaration.getOperand();
if (tokens.length > 0) { // has Version tag
rightHand = tokens[0];
}
tokens = rightHand.split(" ");
if (tokens.length == 2) { // has access flag
beSource.addAccessFlags(ClassAccessFlags.valueOf(tokens[0].toUpperCase()));
}
beSource.setName(getClassName(tokens));
}
case INTERFACE -> {
}//TODO
case ENUM -> {
}//TODO
default -> throw new IllegalArgumentException("Not allowed here");
}
} else {
throw new IllegalArgumentException("class must start with 'class name(target.jdk.version)'");
}
currentLine += 1;
}
private Version getVersion(ClassInstruction classDeclaration) {
String[] tokens2 = split(classDeclaration.getOperand(), parensplitter);
if (tokens2.length == 0) {
return Version.V8;
} else {
return Version.get(tokens2[1]).orElseThrow(illegalArgument(tokens2[1]));
}
}
private String getClassName(String[] tokens) {
if (tokens.length == 2) {
return tokens[1];
} else {
return tokens[0];
}
}
private Instruction compileLine(String line) {
if (!line.startsWith(" ")) {
String[] tokens = split(line, firstBlanksplitter);
String operation = tokens[0];
String operand = tokens[1];
ClassOperation classOperation = ClassOperation.get(operation).orElseThrow(illegalArgument(operation));
return new ClassInstruction(classOperation, operand);
} else {
line = line.substring(2);
if (line.startsWith(" ")) {
throw new IllegalArgumentException("Illegal indent -> must be 2 spaces");
}
String operation;
String operand;
if (line.indexOf(' ') > -1) {
String[] tokens = split(line, firstBlanksplitter);
operation = tokens[0];
operand = tokens[1];
} else {
operation = line;
operand = null;
}
Opcode opcode = Opcode.get(operation).orElseThrow(illegalArgument("Illegal opcode: " + operation));
return new CodeLine(opcode, operand);
}
}
private Supplier<IllegalArgumentException> illegalArgument(String text) {
return () -> new IllegalArgumentException(text);
}
private String[] split(String text, Pattern splitter) {
Matcher matcher = splitter.matcher(text);
if (matcher.find()) {
return new String[]{matcher.group(1), matcher.group(2)};
} else {
return new String[]{};
}
}
private Class<?> getType(String type) {
try {
return switch (type) {
case "int" -> int.class;
case "short" -> short.class;
case "byte" -> byte.class;
case "long" -> long.class;
case "float" -> float.class;
case "double" -> double.class;
case "boolean" -> boolean.class;
case "char" -> char.class;
default -> Class.forName(type);
};
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Not a valid type: " + type);
}
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava;
package nl.sander.bejava;
import java.util.Map;
import java.util.Optional;
@ -16,8 +16,7 @@ public class TypeMapper {
MAP.put(long.class, "J");
MAP.put(short.class, "S");
MAP.put(boolean.class, "Z");
MAP.put(Void.class, "V");
MAP.put(void.class, "V");
}
//TODO something with arrays

View file

@ -0,0 +1,49 @@
package nl.sander.bejava.api;
import nl.sander.bejava.flags.MethodAccessFlag;
import java.util.*;
/**
* Models a constructor
*/
public final class BeConstructor extends CodeContainer {
public BeConstructor(BeSource beSource, Set<MethodAccessFlag> accessFlags,
Set<BeParameter> formalParameters,
List<CodeLine> code) {
this.formalParameters.addAll(formalParameters);
this.accessFlags.addAll(accessFlags);
super.code.addAll(code);
setOwner(beSource);
}
public String getName() {
return "<init>";
}
@Override
public String toString() {
return "BeeConstructor{" +
"formalParameters=" + formalParameters +
'}';
}
public Class<?> getReturnType() {
return void.class;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
var that = (BeConstructor) o;
return formalParameters.equals(that.formalParameters);
}
@Override
public int hashCode() {
return Objects.hash(formalParameters);
}
}

View file

@ -0,0 +1,63 @@
package nl.sander.bejava.api;
import nl.sander.bejava.flags.FieldAccessFlag;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* Models a field in a BeeClass
*/
public final class BeField {
private final String declaringClass;
private final Set<FieldAccessFlag> accessFlags = new HashSet<>();
private final Class<?> type;
private final String name;
/**
*
* @param declaringClass class that declares the field. Can be existing class, or the one that is under construction
* @param accessFlags
* @param type field type. Must be existing type.
* @param name
*/
public BeField(String declaringClass, Set<FieldAccessFlag> accessFlags, Class<?> type, String name) {
this.declaringClass = declaringClass;
this.accessFlags.addAll(accessFlags);
this.type = type;
this.name = name;
}
public Set<FieldAccessFlag> getAccessFlags() {
return accessFlags;
}
public Class<?> getType() {
return type;
}
public String getName() {
return name;
}
public String getDeclaringClass() {
return declaringClass;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
var beeField = (BeField) o;
return name.equals(beeField.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}

View file

@ -0,0 +1,57 @@
package nl.sander.bejava.api;
import nl.sander.bejava.flags.MethodAccessFlag;
import java.util.*;
/**
* Models a method in a BeeClass
*/
public final class BeMethod extends CodeContainer {
private final String name;
private final Class<?> returnType;
public BeMethod(BeSource beSource, String name, Set<MethodAccessFlag> accessFlags,
Set<BeParameter> formalParameters,
Class<?> returnType, List<CodeLine> code) {
this.name = name;
this.accessFlags.addAll(accessFlags);
this.formalParameters.addAll(formalParameters);
this.returnType = returnType;
super.code.addAll(code);
setOwner(beSource);
}
public String getName() {
return name;
}
public Class<?> getReturnType() {
return returnType;
}
// public void validate() {
// //TODO
// /*
// * here we could add checks like:
// * -If this method is in a class rather than an interface, and the name of the method is <init>, then the descriptor must denote a void method.
// * -If the name of the method is <clinit>, then the descriptor must denote avoid method, and, in a class file whose version number is 51.0 or above,a method that takes no arguments
// */
// }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
BeMethod beMethod = (BeMethod) o;
return name.equals(beMethod.name) &&
returnType.equals(beMethod.returnType);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name, returnType);
}
}

View file

@ -1,21 +1,25 @@
package nl.sander.beejava.api;
package nl.sander.bejava.api;
import java.util.Objects;
/**
* Models a formal parameter in a method declaration.
*/
public final class BeeParameter {
public final class BeParameter {
private final Class<?> type;
private final String name;
private final int index;
private BeeParameter(Class<?> type, String name) {
public BeParameter(Class<?> type, String name) {
this.type = type;
this.name = name;
this.index = -1;
}
public static BeeParameter create(Class<?> type, String name) {
return new BeeParameter(type, Objects.requireNonNull(name));
public BeParameter(Class<?> type, String name, int index) {
this.type = type;
this.name = name;
this.index = index;
}
public Class<?> getType() {
@ -30,10 +34,14 @@ public final class BeeParameter {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeeParameter that = (BeeParameter) o;
var that = (BeParameter) o;
return name.equals(that.name);
}
public int getIndex() {
return index;
}
@Override
public int hashCode() {
return Objects.hash(name);

View file

@ -0,0 +1,129 @@
package nl.sander.bejava.api;
import nl.sander.bejava.flags.ClassAccessFlags;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Contains parsed class elements like constructors and methods, but the opcode is not compiled to bytecode yet.
*/
public final class BeSource {
private final Set<ClassAccessFlags> accessFlags = new HashSet<>();
private final Set<Class<?>> interfaces = new HashSet<>();
private final Set<BeField> fields = new HashSet<>();
private final Set<BeConstructor> constructors = new HashSet<>();
private final Set<BeMethod> methods = new HashSet<>();
private Version classFileVersion;
private String name;
private Class<?> superClass = Object.class;
/**
* @return The classfile version
*/
public Version getClassFileVersion() {
return classFileVersion;
}
public void setClassFileVersion(Version classFileVersion) {
this.classFileVersion = classFileVersion;
}
/**
* @return all constructors that are provided with the class
*/
public Set<BeConstructor> getConstructors() {
return Collections.unmodifiableSet(constructors);
}
public void addConstructors(BeConstructor... constructors) {
this.constructors.addAll(Set.of(constructors));
}
/**
* @return all methods that are provided with the class
*/
public Set<BeMethod> getMethods() {
return methods;
}
public void addMethods(BeMethod... methods) {
this.methods.addAll(Set.of(methods));
}
/**
* @return The access flags for the class
*/
public Set<ClassAccessFlags> getAccessFlags() {
return Collections.unmodifiableSet(accessFlags);
}
/**
* Add access flag eg public static etc
*
* @param classAccessFlags varargs of access flag
*/
public void addAccessFlags(ClassAccessFlags... classAccessFlags) {
this.accessFlags.addAll(Set.of(classAccessFlags));
}
/**
* @return The full name, like java.lang.Class
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return The superclass
*/
public Class<?> getSuperClass() {
return superClass;
}
/**
* Sets the super class. Default superclass is Object
* @param superClass The class to extend from.
*/
public void setSuperClass(Class<?> superClass) {
this.superClass = superClass;
}
/**
* @return a list of unique interfaces that the class will implements
*/
public Set<Class<?>> getInterfaces() {
return Collections.unmodifiableSet(interfaces);
}
/**
* Adds an interface to implement
* @param interfaces varargs array of interface class
*/
public void addInterfaces(Class<?>... interfaces) {
this.interfaces.addAll(Set.of(interfaces));
}
/**
* @return a list of unique fields that the class will contain
*/
public Set<BeField> getFields() {
return Collections.unmodifiableSet(fields);
}
public void addFields(BeField... fields) {
this.fields.addAll(Set.of(fields));
}
public BeField getField(String fieldName) {
return fields.stream()
.filter(f -> f.getName().equals(fieldName))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("field " + fieldName + " not found in " + getName()));
}
}

View file

@ -0,0 +1,18 @@
package nl.sander.bejava.api;
public class ClassInstruction extends Instruction {
private final ClassOperation classOperation;
public ClassInstruction(ClassOperation classOperation, String operand) {
super(operand);
this.classOperation = classOperation;
}
public String getOperand() {
return operand;
}
public ClassOperation getOperation() {
return classOperation;
}
}

View file

@ -0,0 +1,22 @@
package nl.sander.bejava.api;
import java.util.Optional;
public enum ClassOperation {
CLASS,
INTERFACE,
ENUM,
FIELD,
CONSTRUCTOR,
METHOD;
public static Optional<ClassOperation> get(String text){
String upper = text.toUpperCase();
for (ClassOperation val: ClassOperation.values()){
if (val.toString().equals(upper)){
return Optional.of(val);
}
}
return Optional.empty();
}
}

View file

@ -0,0 +1,84 @@
package nl.sander.bejava.api;
import nl.sander.bejava.JavaInstruction;
import nl.sander.bejava.TypeMapper;
import nl.sander.bejava.flags.MethodAccessFlag;
import java.util.*;
import java.util.stream.Collectors;
/**
* parent of a constructor or a method.
*/
public abstract class CodeContainer {
protected final List<CodeLine> code = new LinkedList<>();
protected final Set<BeParameter> formalParameters = new HashSet<>();
protected final Set<MethodAccessFlag> accessFlags = new HashSet<>();
private final List<JavaInstruction> expandedCode = new ArrayList<>();
private BeSource owner;
public List<CodeLine> getCode() {
return code;
}
public String getSignature() {
return getParametersSignature() + TypeMapper.map(getReturnType());
}
public abstract String getName();
public abstract Class<?> getReturnType();
private String getParametersSignature() {
return formalParameters.stream()
.map(BeParameter::getType)
.map(TypeMapper::map)
.collect(Collectors.joining(",", "(", ")"));
}
public Set<MethodAccessFlag> getAccessFlags() {
return accessFlags;
}
public BeSource getOwner() {
return owner;
}
public void setOwner(BeSource beSource) {
if (owner != null) {
throw new IllegalStateException("Owner set twice. Sue the developer!");
}
this.owner = beSource;
}
public List<JavaInstruction> getExpandedCode() {
return Collections.unmodifiableList(expandedCode);
}
public void setExpandedCode(List<JavaInstruction> instructions) {
expandedCode.clear();
expandedCode.addAll(instructions);
}
public Set<BeParameter> getFormalParameters() {
return formalParameters;
}
public Optional<BeParameter> getParameter(String parameterName) {
return formalParameters.stream().filter(p -> parameterName.equals(p.getName())).findAny();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CodeContainer that = (CodeContainer) o;
return formalParameters.equals(that.formalParameters);
}
@Override
public int hashCode() {
return Objects.hash(formalParameters);
}
}

View file

@ -0,0 +1,20 @@
package nl.sander.bejava.api;
public class CodeLine extends Instruction {
private final Opcode opcode;
private CodeContainer owner;
public CodeLine(Opcode opcode, String operand) {
super(operand);
this.opcode = opcode;
}
public String getOperand() {
return operand;
}
public Opcode getOpcode() {
return opcode;
}
}

View file

@ -0,0 +1,9 @@
package nl.sander.bejava.api;
public class Instruction {
protected final String operand;
public Instruction(String operand) {
this.operand = operand;
}
}

View file

@ -0,0 +1,65 @@
package nl.sander.bejava.api;
import java.util.Optional;
public enum Opcode {
LOAD,
STORE,
CONST,
RETURN,
ARRAYLENGTH,
THROW,
CAST,
ADD,
COMPARE,
DIVIDE,
MULTIPLY,
NEGATE,
REMAINDER,
SUBTRACT,
GET,
GOTO,
TO_DOUBLE,
TO_INT,
TO_LONG,
TO_BYTE,
TO_CHAR,
TO_FLOAT,
TO_SHORT,
AND,
IF_EQUAL,
IF_NOT_EQUAL,
IF_LESS_THAN,
IF_GREATER_OR_EQUAL,
IF_GREATER_THAN,
IF_LESS_OR_EQUAL,
IF_NOT_NULL,
IF_NULL,
INCREMENT,
INSTANCEOF,
INVOKE,
OR,
SHIFT_LEFT,
SHIFT_RIGHT,
LOGICAL_SHIFT_RIGHT,
XOR,
LOOKUPSWITCH,
MONITORENTER,
MONITOREXIT,
NEW,
NEWARRAY,
MULTIANEWARRAY,
PUT,
SWAP,
TABLESWITCH,
WIDE;
public static Optional<Opcode> get(String text) {
for (Opcode opcode : Opcode.values()) {
if (opcode.toString().equals(text)) {
return Optional.of(opcode);
}
}
return Optional.empty();
}
}

View file

@ -1,4 +1,6 @@
package nl.sander.beejava.api;
package nl.sander.bejava.api;
import java.util.Optional;
public enum Version {
V1_0_2(45),
@ -31,4 +33,13 @@ public enum Version {
public int getMinor(){
return 0;
}
public static Optional<Version> get(String text){
for (Version version : Version.values()) {
if (version.toString().equals(text)) {
return Optional.of(version);
}
}
return Optional.empty();
}
}

View file

@ -1,29 +1,29 @@
package nl.sander.beejava.classinfo;
package nl.sander.bejava.classinfo;
import nl.sander.beejava.constantpool.entry.Utf8Entry;
import nl.sander.beejava.flags.AccessFlags;
import nl.sander.beejava.flags.FieldAccessFlags;
import nl.sander.beejava.util.ByteBuf;
import nl.sander.bejava.constantpool.entry.Utf8Entry;
import nl.sander.bejava.flags.AccessFlags;
import nl.sander.bejava.flags.FieldAccessFlag;
import nl.sander.bejava.util.ByteBuf;
import java.util.HashSet;
import java.util.Set;
/**
* Conforms to field_info struct in JVM specification
* See 4.1 The ClassFile Structure
*/
public class FieldInfo extends Info<FieldInfo> {
private final Set<FieldAccessFlags> accessFlags = new HashSet<>();
private final Set<FieldAccessFlag> accessFlags = new HashSet<>();
public FieldInfo(Utf8Entry nameEntry, Utf8Entry descriptorEntry) {
super(nameEntry, descriptorEntry);
}
public FieldInfo addAccessFlags(Set<FieldAccessFlags> accessFlags) {
public FieldInfo addAccessFlags(Set<FieldAccessFlag> accessFlags) {
this.accessFlags.addAll(accessFlags);
return this;
}
public Set<FieldAccessFlags> getAccessFlags() {
return accessFlags;
}
public void addBytes(ByteBuf buf) {
buf.addU16(AccessFlags.combine(accessFlags));
buf.addU16(nameEntry.getIndex());

View file

@ -1,12 +1,12 @@
package nl.sander.beejava.classinfo;
package nl.sander.bejava.classinfo;
import nl.sander.beejava.classinfo.attributes.Attribute;
import nl.sander.beejava.constantpool.entry.Utf8Entry;
import nl.sander.bejava.classinfo.attributes.Attribute;
import nl.sander.bejava.constantpool.entry.Utf8Entry;
import java.util.HashSet;
import java.util.Set;
public abstract class Info<T extends Info> {
public abstract class Info<T extends Info<T>> {
protected final Utf8Entry nameEntry;
protected final Utf8Entry descriptorEntry;
@ -17,14 +17,6 @@ public abstract class Info<T extends Info> {
this.descriptorEntry = descriptorEntry;
}
public Utf8Entry getNameEntry() {
return nameEntry;
}
public Utf8Entry getDescriptorEntry() {
return descriptorEntry;
}
public Set<Attribute> getAttributes() {
return attributes;
}

View file

@ -1,20 +1,24 @@
package nl.sander.beejava.classinfo;
package nl.sander.bejava.classinfo;
import nl.sander.beejava.constantpool.entry.Utf8Entry;
import nl.sander.beejava.flags.AccessFlags;
import nl.sander.beejava.flags.MethodAccessFlags;
import nl.sander.beejava.util.ByteBuf;
import nl.sander.bejava.constantpool.entry.Utf8Entry;
import nl.sander.bejava.flags.AccessFlags;
import nl.sander.bejava.flags.MethodAccessFlag;
import nl.sander.bejava.util.ByteBuf;
import java.util.HashSet;
import java.util.Set;
/**
* Conforms to method_info struct in JVM specification
* See 4.1 The ClassFile Structure
*/
public class MethodInfo extends Info<MethodInfo> {
private final Set<MethodAccessFlags> accessFlags = new HashSet<>();
private final Set<MethodAccessFlag> accessFlags = new HashSet<>();
public MethodInfo(Utf8Entry nameEntry, Utf8Entry descriptorEntry) {
super(nameEntry, descriptorEntry);
}
public MethodInfo addAccessFlags(Set<MethodAccessFlags> accessFlags) {
public MethodInfo addAccessFlags(Set<MethodAccessFlag> accessFlags) {
this.accessFlags.addAll(accessFlags);
return this;
}

View file

@ -1,8 +1,12 @@
package nl.sander.beejava.classinfo.attributes;
package nl.sander.bejava.classinfo.attributes;
import nl.sander.beejava.constantpool.entry.Utf8Entry;
import nl.sander.beejava.util.ByteBuf;
import nl.sander.bejava.constantpool.entry.Utf8Entry;
import nl.sander.bejava.util.ByteBuf;
/**
* attribute_info struct as in the JVM spec.
* See 4.1 The ClassFile Structure.
*/
public abstract class Attribute {
protected final Utf8Entry nameEntry;
protected int length;

View file

@ -1,7 +1,7 @@
package nl.sander.beejava.classinfo.attributes;
package nl.sander.bejava.classinfo.attributes;
import nl.sander.beejava.constantpool.entry.Utf8Entry;
import nl.sander.beejava.util.ByteBuf;
import nl.sander.bejava.constantpool.entry.Utf8Entry;
import nl.sander.bejava.util.ByteBuf;
import java.util.HashSet;
import java.util.Set;
@ -19,7 +19,7 @@ public class CodeAttribute extends Attribute {
private static final int EXCEPTION_HANDLER_SIZE = 8; // nr of bytes per exception_hander
private final Set<Attribute> attributes = new HashSet<>();
private final Set<ExceptionHandler> exceptionHandlers = new HashSet<>();
private final Set<ExceptionHandler> exceptionHandlers = new HashSet<>(); //TODO exception handlers are yet to be implemented
private int maxStack; // u2
private int maxLocals; // u2
private byte[] code;

View file

@ -1,7 +1,8 @@
package nl.sander.beejava.classinfo.attributes;
package nl.sander.bejava.classinfo.attributes;
/**
* see §4.7.3 The code attribute
* TODO implement exception handlers
*/
public class ExceptionHandler {
private int startPc; //u2

View file

@ -1,7 +1,7 @@
package nl.sander.beejava.constantpool;
package nl.sander.bejava.constantpool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.util.ByteBuf;
import nl.sander.bejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.bejava.util.ByteBuf;
import java.util.ArrayList;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -29,7 +29,7 @@ public class ClassEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassEntry that = (ClassEntry) o;
var that = (ClassEntry) o;
return name.equals(that.name);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Arrays;
import java.util.LinkedHashSet;
@ -29,10 +29,19 @@ public abstract class ConstantPoolEntry {
return getBytes()[0];
}
/**
* The output of this ends up in the class file as the constantpool index of the entry.
*
* @return the cp index of the entry.
*/
public int getIndex() {
return index;
}
/**
* With this the ConstantPoolCreator sets the calculated index.
* @param index the cp index to assign to the entry
*/
public void setIndex(int index) {
this.index = index;
}
@ -61,8 +70,20 @@ public abstract class ConstantPoolEntry {
return (byte) (u16 >>> 8);
}
protected byte getByte(long bits, int positions) {
return (byte) ((bits >>> (positions * 8)) & 0xFF);
/**
* get the Nth byte in an "array" of bits encoded in a long (i64). Used to create the stream representation of 64bit numbers
*
* @param bits a long in which a long or a double is encoded.
* @param position the index of the byte (0..7) in the array of bits
* @return the Nth byte in the long
*/
protected byte getByte(long bits, int position) {
if (position > 0 && position < 8) {
return (byte) ((bits >>> (position * 8)) & 0xFF);
} else {
throw new IllegalArgumentException("position must be 0..7");
}
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -22,7 +22,7 @@ public class DoubleEntry extends LeafEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DoubleEntry that = (DoubleEntry) o;
var that = (DoubleEntry) o;
return Double.compare(that.doubleVal, doubleVal) == 0;
}

View file

@ -1,6 +1,4 @@
package nl.sander.beejava.constantpool.entry;
import java.util.Objects;
package nl.sander.bejava.constantpool.entry;
public class DynamicEntry extends ConstantPoolEntry {
private static final byte TAG = 17;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -38,7 +38,7 @@ public class FieldRefEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FieldRefEntry that = (FieldRefEntry) o;
var that = (FieldRefEntry) o;
return classEntry.equals(that.classEntry) &&
nameAndTypeEntry.equals(that.nameAndTypeEntry);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -7,6 +7,10 @@ public class FloatEntry extends LeafEntry {
private final float floatVal;
public FloatEntry(String floatVal) {
this.floatVal = Float.parseFloat(floatVal);
}
public FloatEntry(float floatVal) {
this.floatVal = floatVal;
}
@ -20,7 +24,7 @@ public class FloatEntry extends LeafEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FloatEntry that = (FloatEntry) o;
var that = (FloatEntry) o;
return Float.compare(that.floatVal, floatVal) == 0;
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -7,8 +7,12 @@ public class IntegerEntry extends LeafEntry {
private final int intVal;
public IntegerEntry(int integer) {
this.intVal = integer;
public IntegerEntry(String integerVal) {
this.intVal = Integer.parseInt(integerVal);
}
public IntegerEntry(int integerVal) {
this.intVal = integerVal;
}
@Override
@ -25,7 +29,7 @@ public class IntegerEntry extends LeafEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IntegerEntry that = (IntegerEntry) o;
var that = (IntegerEntry) o;
return intVal == that.intVal;
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -32,7 +32,7 @@ public class InterfaceMethodRefEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InterfaceMethodRefEntry that = (InterfaceMethodRefEntry) o;
var that = (InterfaceMethodRefEntry) o;
return classEntry.equals(that.classEntry) &&
nameAndTypeEntry.equals(that.nameAndTypeEntry);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
public class InvokeDynamicEntry extends ConstantPoolEntry {
private static final byte TAG = 18;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Collections;
import java.util.Set;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -27,7 +27,7 @@ public class LongEntry extends LeafEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LongEntry longEntry = (LongEntry) o;
var longEntry = (LongEntry) o;
return longVal == longEntry.longVal;
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
//TODO implement later
public class MethodHandleEntry extends ConstantPoolEntry {

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -35,7 +35,7 @@ public class MethodRefEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodRefEntry that = (MethodRefEntry) o;
var that = (MethodRefEntry) o;
return classRef.equals(that.classRef) &&
nameAndType.equals(that.nameAndType);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -24,7 +24,7 @@ public class MethodTypeEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodTypeEntry that = (MethodTypeEntry) o;
var that = (MethodTypeEntry) o;
return methodDescriptor.equals(that.methodDescriptor);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -25,7 +25,7 @@ public class ModuleEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ModuleEntry that = (ModuleEntry) o;
var that = (ModuleEntry) o;
return nameEntry.equals(that.nameEntry);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -37,7 +37,7 @@ public final class NameAndTypeEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NameAndTypeEntry that = (NameAndTypeEntry) o;
var that = (NameAndTypeEntry) o;
return name.equals(that.name) &&
descriptor.equals(that.descriptor);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -23,7 +23,7 @@ public final class PackageEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackageEntry that = (PackageEntry) o;
var that = (PackageEntry) o;
return nameEntry.equals(that.nameEntry);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.util.Objects;
@ -23,7 +23,7 @@ public final class StringEntry extends ConstantPoolEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StringEntry that = (StringEntry) o;
var that = (StringEntry) o;
return utf8Entry.equals(that.utf8Entry);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@ -16,8 +16,8 @@ public final class Utf8Entry extends LeafEntry {
}
public byte[] getBytes() {
byte[] utf8Bytes = value.getBytes(StandardCharsets.UTF_8);
byte[] bytes = new byte[utf8Bytes.length + 3];
var utf8Bytes = value.getBytes(StandardCharsets.UTF_8);
var bytes = new byte[utf8Bytes.length + 3];
bytes[0] = TAG;
bytes[1] = upperByte(utf8Bytes.length);
bytes[2] = lowerByte(utf8Bytes.length);
@ -29,7 +29,7 @@ public final class Utf8Entry extends LeafEntry {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Utf8Entry utf8Entry = (Utf8Entry) o;
var utf8Entry = (Utf8Entry) o;
return value.equals(utf8Entry.value);
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.flags;
package nl.sander.bejava.flags;
import java.util.Collection;
@ -9,4 +9,5 @@ public interface AccessFlags {
int getBytecode();
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.flags;
package nl.sander.bejava.flags;
public enum ClassAccessFlags implements AccessFlags {
PUBLIC(0x0001), // Declared public; may be accessed from outside its package.
@ -11,17 +11,15 @@ public enum ClassAccessFlags implements AccessFlags {
ENUM(0x4000), // Declared as an enum type.
MODULE(0x8000); // Is a module, not a class or interface.
private final int bytecode;
ClassAccessFlags(int bytecode) {
this.bytecode = bytecode;
}
@Override
public int getBytecode() {
return bytecode;
}
}

View file

@ -1,6 +1,8 @@
package nl.sander.beejava.flags;
package nl.sander.bejava.flags;
public enum FieldAccessFlags implements AccessFlags {
import java.util.Optional;
public enum FieldAccessFlag implements AccessFlags {
PUBLIC(0x0001), // Declared public; may be accessed from outside itspackage.
PRIVATE(0x0002), // Declared private; accessible only within the defining class and other classes belonging to the samenest (§5.4.4).
PROTECTED(0x0004), // Declared protected; may be accessed within subclasses.
@ -13,11 +15,20 @@ public enum FieldAccessFlags implements AccessFlags {
private final int bytecode;
FieldAccessFlags(int bytecode) {
FieldAccessFlag(int bytecode) {
this.bytecode = bytecode;
}
public int getBytecode() {
return bytecode;
}
public static Optional<FieldAccessFlag> get(String text) {
for (FieldAccessFlag flag : FieldAccessFlag.values()) {
if (flag.toString().equals(text)) {
return Optional.of(flag);
}
}
return Optional.empty();
}
}

View file

@ -1,6 +1,8 @@
package nl.sander.beejava.flags;
package nl.sander.bejava.flags;
public enum MethodAccessFlags implements AccessFlags {
import java.util.Optional;
public enum MethodAccessFlag implements AccessFlags {
PUBLIC(0x0001), // Declared public; may be accessed from outside its package.
PRIVATE(0x0002), // Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4).
PROTECTED(0x0004), // Declared protected; may be accessed within subclasses.
@ -16,11 +18,20 @@ public enum MethodAccessFlags implements AccessFlags {
private final int bytecode;
MethodAccessFlags(int bytecode) {
MethodAccessFlag(int bytecode) {
this.bytecode = bytecode;
}
public int getBytecode() {
return bytecode;
}
public static Optional<MethodAccessFlag> get(String text) {
for (MethodAccessFlag flag : MethodAccessFlag.values()) {
if (flag.toString().equals(text)) {
return Optional.of(flag);
}
}
return Optional.empty();
}
}

View file

@ -0,0 +1,25 @@
package nl.sander.bejava.operands;
import nl.sander.bejava.Primitive;
/**
* The operand is a constant
*/
public class ConstantOperand extends Operand {
private final Primitive type;
private final String value;
public ConstantOperand(Primitive type, String value) {
this.type = type;
this.value = value;
}
public Primitive getType() {
return type;
}
public String getValue() {
return value;
}
}

View file

@ -0,0 +1,14 @@
package nl.sander.bejava.operands;
/**
* The operand is a field member of the class
*/
public class FieldOperand extends Operand{
public final String className;
public final String fieldName;
public FieldOperand(String className, String fieldName) {
this.className = className;
this.fieldName = fieldName;
}
}

View file

@ -0,0 +1,12 @@
package nl.sander.bejava.operands;
/**
* The operand is a local variable
*/
public class LocalVariableOperand extends Operand{
public final String name;
public LocalVariableOperand(String name) {
this.name = name;
}
}

View file

@ -0,0 +1,25 @@
package nl.sander.bejava.operands;
/**
* The operand is a method call.
*/
public class MethodOperand extends Operand{
public final String className;
public final String methodName;
public final String signature;
public MethodOperand(String className, String methodName, String signature) {
this.className = className;
this.methodName = methodName;
this.signature = signature;
}
@Override
public String toString() {
return "MethodOperand{" +
"className='" + className + '\'' +
", methodName='" + methodName + '\'' +
", signature='" + signature + '\'' +
'}';
}
}

View file

@ -0,0 +1,4 @@
package nl.sander.bejava.operands;
public abstract class Operand {
}

View file

@ -0,0 +1,13 @@
package nl.sander.bejava.operands;
/**
* The operand is void. An operand is compulsory in a {@link nl.sander.bejava.JavaInstruction}.
* Used when the opcode is RETURN and the return type is void.
*/
public class VoidOperand extends Operand{
public final static VoidOperand INSTANCE = new VoidOperand();
private VoidOperand() {
//
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.util;
package nl.sander.bejava.util;
import java.nio.ByteBuffer;
import java.nio.charset.*;
@ -29,7 +29,7 @@ public class ByteBuf {
if (data.remaining() < ints.length) {
enlarge(ints.length);
}
byte[] bytes = new byte[ints.length];
var bytes = new byte[ints.length];
for (int i = 0; i < ints.length; i++) {
bytes[i] = (byte) (ints[i] & 0xFF);
}
@ -45,9 +45,9 @@ public class ByteBuf {
}
private void enlarge(final int size) {
final int length1 = 2 * data.limit();
final int length2 = data.limit() + size;
ByteBuffer newData = ByteBuffer.allocate(Math.max(length1, length2));
final var length1 = 2 * data.limit();
final var length2 = data.limit() + size;
var newData = ByteBuffer.allocate(Math.max(length1, length2));
data.flip();
newData.put(data);
data = newData;
@ -61,25 +61,25 @@ public class ByteBuf {
return arr;
}
public String toString() {
return toString(StandardCharsets.UTF_8);
}
public String toString(Charset charset) {
data.flip();
CharsetDecoder decoder = charset.newDecoder(); // decode is not threadsafe, might put it in threadlocal
// but I don't think this (newDecoder()+config) is expensive
decoder.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
try {
return decoder.decode(data).toString();
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
}
// public String toString() {
// return toString(StandardCharsets.UTF_8);
// }
//
// public String toString(Charset charset) {
// data.flip();
//
// CharsetDecoder decoder = charset.newDecoder(); // decode is not threadsafe, might put it in threadlocal
// // but I don't think this (newDecoder()+config) is expensive
//
// decoder.onMalformedInput(CodingErrorAction.REPLACE)
// .onUnmappableCharacter(CodingErrorAction.REPLACE);
//
// try {
// return decoder.decode(data).toString();
// } catch (CharacterCodingException e) {
// throw new RuntimeException(e);
// }
// }
//
}

View file

@ -1,29 +0,0 @@
package nl.sander.beejava;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class BytecodeGeneratorTests {
@Test
public void testEmpty() throws IOException, ClassNotFoundException {
byte[] bytecode = BytecodeGenerator.generate(Compiler.compile(TestData.createEmptyClass()));
File dir = new File("target/nl/sander/beejava/test");
dir.mkdirs();
try (FileOutputStream outputStream = new FileOutputStream(new File(dir, "EmptyBean.class"))) {
outputStream.write(bytecode);
}
}
@Test
public void testInterface() throws ClassNotFoundException {
BytecodeGenerator.generate(Compiler.compile(TestData.emptyClassWithInterface()));
}
@Test
public void testFields() throws ClassNotFoundException {
BytecodeGenerator.generate(Compiler.compile(TestData.createClassWithField(int.class)));
}
}

View file

@ -1,66 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.*;
import nl.sander.beejava.constantpool.entry.*;
import org.junit.jupiter.api.Test;
import java.util.Iterator;
import java.util.Set;
import static nl.sander.beejava.api.CodeLine.line;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CompilerTests {
// creates simplest class possible and checks the tree, that the ConstantTreeCreator emits
@Test // This is not a maintainable test
public void testMethodRefEntryForSuperConstructor() throws ClassNotFoundException {
// Arrange
BeeSource classWithIntField = TestData.createEmptyClass();
// Act
CompiledClass compiledClass = Compiler.compile(classWithIntField);
// Assert
Set<ConstantPoolEntry> constantTree = compiledClass.getConstantTree();
assertEquals(3, constantTree.size());
ConstantPoolEntry superConstructor = constantTree.iterator().next();
assertEquals(MethodRefEntry.class, superConstructor.getClass());
MethodRefEntry methodRefEntry = (MethodRefEntry) superConstructor;
Set<ConstantPoolEntry> methodRefEntryChildren = methodRefEntry.getChildren();
assertEquals(2, methodRefEntryChildren.size());
Iterator<ConstantPoolEntry> firstChildren = methodRefEntryChildren.iterator();
ConstantPoolEntry child1 = firstChildren.next();
assertEquals(ClassEntry.class, child1.getClass());
ClassEntry classEntry = (ClassEntry) child1;
Set<ConstantPoolEntry> classEntryChildren = classEntry.getChildren();
assertEquals(1, classEntryChildren.size());
ConstantPoolEntry child2 = classEntryChildren.iterator().next();
assertEquals(Utf8Entry.class, child2.getClass());
Utf8Entry className = (Utf8Entry) child2;
assertEquals("java/lang/Object", className.getUtf8());
ConstantPoolEntry child3 = firstChildren.next();
assertEquals(NameAndTypeEntry.class, child3.getClass());
NameAndTypeEntry nameAndTypeEntry = (NameAndTypeEntry) child3;
Set<ConstantPoolEntry> nameAndTypeEntryChildren = nameAndTypeEntry.getChildren();
assertEquals(2, nameAndTypeEntryChildren.size());
Iterator<ConstantPoolEntry> nameAndTypeChildrenIterator = nameAndTypeEntryChildren.iterator();
ConstantPoolEntry child4 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child4.getClass());
Utf8Entry name = (Utf8Entry) child4;
assertEquals("<init>", name.getUtf8());
ConstantPoolEntry child5 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child5.getClass());
Utf8Entry type = (Utf8Entry) child5;
assertEquals("()V", type.getUtf8());
}
}

View file

@ -1,77 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ClassEntry;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ConstantPoolUniquenessTests {
@Test
public void test() throws Exception {
// Arrange
BeeSource someClass = TestData.createEmptyClass();
// Act
CompiledClass compiledClass = Compiler.compile(someClass);
ConstantPool constantPool = new ConstantPoolCreator().createConstantPool(compiledClass.getConstantTree());
// Assert
List<ClassEntry> refsToSystem = constantPool.stream()
.filter(cpe -> cpe instanceof ClassEntry)
.map(cpe -> (ClassEntry) cpe)
.filter(ce -> ce.getName().equals("java/lang/System"))
.collect(Collectors.toList());
assertEquals(0, refsToSystem.size());
byte[] bytecode = BytecodeGenerator.generate(compiledClass);
int x = 1;
for (ConstantPoolEntry e : constantPool) {
System.out.println((x++) + ":" + e);
}
x = 1;
for (ConstantPoolEntry e : constantPool) {
System.out.print((x++) + ":");
printBytes(e.getBytes());
}
File dir = new File("target/nl/sander/beejava/test");
dir.mkdirs();
try (FileOutputStream outputStream = new FileOutputStream(new File(dir, "EmptyBean.class"))) {
outputStream.write(bytecode);
}
printBytes2(bytecode);
}
//TODO remove
private void printBytes(byte[] bytes) {
for (byte b : bytes) {
System.out.print(String.format("%2s", Integer.toHexString(b & 0xFF)).replace(' ', '0') + " ");
}
System.out.println();
}
private void printBytes2(byte[] bytes) {
int count = 0;
for (byte b : bytes) {
System.out.print(String.format("%2s", Integer.toHexString(b & 0xFF)).replace(' ', '0') + (count % 2 == 0 ? "" : " "));
count += 1;
if (count > 15) {
count = 0;
System.out.println();
}
}
}
}

View file

@ -1,113 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.*;
import nl.sander.beejava.flags.FieldAccessFlags;
import nl.sander.beejava.flags.MethodAccessFlags;
import java.io.Serializable;
import static nl.sander.beejava.api.CodeLine.line;
import static nl.sander.beejava.api.Opcode.*;
import static nl.sander.beejava.flags.ClassAccessFlags.PUBLIC;
import static nl.sander.beejava.flags.ClassAccessFlags.SUPER;
public class TestData {
public static BeeSource createEmptyClass() throws ClassNotFoundException {
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC, SUPER)
.withSimpleName("EmptyBean")
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
}
public static BeeSource emptyClassWithInterface() throws ClassNotFoundException {
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withSimpleName("EmptyBean")
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode
.withInterfaces(Serializable.class)
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
}
public static BeeSource createClassWithTwoReferencesToSomeClass() throws ClassNotFoundException {
BeeMethod print1 = BeeMethod.builder()
.withName("print1")
.withAccessFlags(MethodAccessFlags.PUBLIC)
.withCode(
line(0, GET, "java.lang.System","out"),
line(1, LD_CONST, "1"),
line(2, INVOKE, "java.io.PrintStream", "println", "java.lang.String"),
line(3, RETURN))
.build();
// INVOKE System.out.println("1")
BeeMethod print2 = BeeMethod.builder()
.withName("print2")
.withAccessFlags(MethodAccessFlags.PUBLIC)
.withCode(
line(0, GET, "java.lang.System","out"),
line(1, LD_CONST, "2"),
line(2, INVOKE, "java.io.PrintStream", "println", "java.lang.String"),
line(3, RETURN))
.build();
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC, SUPER)
.withSimpleName("ClassWithReferences")
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.withMethods(print1, print2)
.build();
}
public static BeeSource createClassWithField(Class<?> fieldType) throws ClassNotFoundException {
BeeField field = BeeField.builder()
.withAccessFlags(FieldAccessFlags.PRIVATE)
.withType(fieldType)
.withName("field")
.build();
BeeParameter parameter = BeeParameter.create(fieldType, "value");
BeeConstructor constructor = BeeConstructor.builder()
.withAccessFlags(MethodAccessFlags.PUBLIC)
.withFormalParameters(parameter)
.withCode(
line(0, LD_VAR, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(2, LD_VAR, Ref.THIS),
line(3, LD_VAR, parameter),
line(4, PUT, field),
line(5, RETURN))
.build();
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withSimpleName("Bean")
.withSuperClass(Object.class)
.withFields(field)
.withConstructors(constructor)
.build();
}
public static BeeConstructor createDefaultConstructor() throws ClassNotFoundException {
return BeeConstructor.builder()
.withAccessFlags(MethodAccessFlags.PUBLIC)
.withCode(
line(0, LD_VAR, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(5, RETURN))
.build();
}
}

View file

@ -1,48 +0,0 @@
package nl.sander.beejava.e2e;
import nl.sander.beejava.BytecodeGenerator;
import nl.sander.beejava.CompiledClass;
import nl.sander.beejava.Compiler;
import nl.sander.beejava.TestData;
import nl.sander.beejava.api.BeeSource;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static org.junit.jupiter.api.Assertions.*;
/**
* A rather simple case, still meaningful nonetheless.
* Green means class can be loaded, so the class file structure is valid.
* The EmptyBean class just contains a default constructor.
*/
public class BeanWithMethodsTest {
@Test
public void testEmptyBean() throws Exception {
// Arrange
BeeSource emptyClass = TestData.createClassWithTwoReferencesToSomeClass();
// Act
CompiledClass compiledClass = Compiler.compile(emptyClass);
byte[] bytecode = BytecodeGenerator.generate(compiledClass);
ByteClassLoader classLoader = new ByteClassLoader();
classLoader.setByteCode("nl.sander.beejava.test.ClassWithReferences", bytecode);
// Assert
Class<?> classWithReferences = classLoader.loadClass("nl.sander.beejava.test.ClassWithReferences");
assertNotNull(classWithReferences);
Method[] methods = classWithReferences.getDeclaredMethods();
assertEquals(2, methods.length);
assertEquals("print1", methods[0].getName());
assertTrue(Modifier.isPublic(methods[0].getModifiers()));
assertEquals(0,methods[0].getParameterCount());
assertEquals("print2", methods[1].getName()); // ordering may cause failures
assertTrue(Modifier.isPublic(methods[1].getModifiers()));
assertEquals(0,methods[1].getParameterCount());
}
}

View file

@ -1,69 +0,0 @@
package nl.sander.beejava.e2e;
import nl.sander.beejava.BytecodeGenerator;
import nl.sander.beejava.CompiledClass;
import nl.sander.beejava.Compiler;
import nl.sander.beejava.TestData;
import nl.sander.beejava.api.BeeConstructor;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.api.Ref;
import nl.sander.beejava.api.Version;
import nl.sander.beejava.flags.MethodAccessFlags;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import static nl.sander.beejava.api.CodeLine.line;
import static nl.sander.beejava.api.Opcode.*;
import static nl.sander.beejava.flags.ClassAccessFlags.PUBLIC;
import static nl.sander.beejava.flags.ClassAccessFlags.SUPER;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* A rather simple case, still meaningful nonetheless.
* Green means class can be loaded, so the class file structure is valid.
* The EmptyBean class just contains a default constructor.
*/
public class EmptyBeanTest {
@Test
public void testEmptyBean() throws Exception {
// Arrange
BeeSource emptyClass = createEmptyClass();
// Act
CompiledClass compiledClass = Compiler.compile(emptyClass);
byte[] bytecode = BytecodeGenerator.generate(compiledClass);
ByteClassLoader classLoader = new ByteClassLoader();
classLoader.setByteCode("nl.sander.beejava.test.EmptyBean", bytecode);
// Assert
Constructor<?> constructor = classLoader.loadClass("nl.sander.beejava.test.EmptyBean").getConstructor();
assertNotNull(constructor);
Object instance = constructor.newInstance();
assertNotNull(instance);
}
private BeeSource createEmptyClass() throws ClassNotFoundException {
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC, SUPER)
.withSimpleName("EmptyBean")
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
}
private BeeConstructor createDefaultConstructor() throws ClassNotFoundException {
return BeeConstructor.builder()
.withAccessFlags(MethodAccessFlags.PUBLIC)
.withCode(
line(0, LD_VAR, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(5, RETURN))
.build();
}
}

View file

@ -1,5 +0,0 @@
package nl.sander.beejava.testclasses;
public class EmptyBean {
}

View file

@ -0,0 +1,80 @@
package nl.sander.bejava;
import nl.sander.bejava.testutils.ByteWriter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* meant as regression tests
*/
public class BytecodeGeneratorTests {
@Test
public void testSuperConstructorCall() {
byte[] bytecode = BytecodeGenerator.generate(Compiler.compile(SourceCompiler.compile("""
class public nl.sander.beejava.test.EmptyBean
constructor public()
INVOKE this.super()
RETURN
""")));
assertEquals("""
cafe babe 0000 0034 0008 0100 063c 696e\s
6974 3e01 0003 2829 5607 0004 0100 206e\s
6c2f 7361 6e64 6572 2f62 6565 6a61 7661\s
2f74 6573 742f 456d 7074 7942 6561 6e07\s
0006 0100 106a 6176 612f 6c61 6e67 2f4f\s
626a 6563 7401 0004 436f 6465 0021 0003\s
0005 0000 0000 0001 0001 0001 0002 0001\s
0007 0000 000c 0000 0001 0000 0000 0000\s
0000 0000\s""",
ByteWriter.printBytes(bytecode));
}
@Test
public void testInterfaceImplementor() {
byte[] bytecode = BytecodeGenerator.generate(Compiler.compile(SourceCompiler.compile("""
class public nl.sander.beejava.test.EmptyBean implements java.io.Serializable
constructor public()
INVOKE this.super()V
RETURN
""")));
assertEquals("""
cafe babe 0000 0034 0008 0100 063c 696e\s
6974 3e01 0003 2829 5607 0004 0100 0670\s
7562 6c69 6307 0006 0100 106a 6176 612f\s
6c61 6e67 2f4f 626a 6563 7401 0004 436f\s
6465 0020 0003 0005 0000 0000 0001 0001\s
0001 0002 0001 0007 0000 000c 0000 0001\s
0000 0000 0000 0000 0000\s""",
ByteWriter.printBytes(bytecode));
}
@Test
public void testPutArgumentInField() {
byte[] bytecode = BytecodeGenerator.generate(Compiler.compile(SourceCompiler.compile("""
class com.acme.SimpleBean(V15)
field private int field
constructor public(int arg)
INVOKE this.super()V
LOAD arg
PUT this.field
RETURN
""")));
assertEquals("""
cafe babe 0000 003b 000e 0900 0900 0307\s
000a 0c00 0500 0601 0013 636f 6d2f 6163\s
6d65 2f53 696d 706c 6542 6561 6e01 0005\s
6669 656c 6401 0001 4901 0006 3c69 6e69\s
743e 0100 0428 4929 5607 000a 0100 1363\s
6f6d 2f61 636d 652f 5369 6d70 6c65 4265\s
616e 0700 0c01 0010 6a61 7661 2f6c 616e\s
672f 4f62 6a65 6374 0100 0443 6f64 6500\s
2000 0900 0b00 0000 0100 0200 0500 0000\s
0000 0100 0100 0700 0800 0100 0d00 0000\s
0c00 0000 0200 0000 0000 0000 0000 00""",
ByteWriter.printBytes(bytecode));
}
}

View file

@ -0,0 +1,37 @@
package nl.sander.bejava;
import nl.sander.bejava.api.BeSource;
import nl.sander.bejava.constantpool.ConstantPool;
import nl.sander.bejava.constantpool.entry.ClassEntry;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ConstantPoolUniquenessTests {
@Test
public void test() throws Exception {
// Arrange
BeSource someClass = SourceCompiler.compile("""
class public nl.sander.beejava.test.EmptyBean
constructor public()
INVOKE this.super()
RETURN
""");
// Act
CompiledClass compiledClass = Compiler.compile(someClass);
ConstantPool constantPool = new ConstantPoolCreator().createConstantPool(compiledClass.getConstantTree());
// Assert
List<ClassEntry> refsToSystem = constantPool.stream()
.filter(cpe -> cpe instanceof ClassEntry)
.map(cpe -> (ClassEntry) cpe)
.filter(ce -> ce.getName().equals("java/lang/System"))
.collect(Collectors.toList());
assertEquals(0, refsToSystem.size());
}
}

View file

@ -0,0 +1,50 @@
package nl.sander.bejava;
import nl.sander.bejava.api.BeField;
import nl.sander.bejava.api.BeMethod;
import nl.sander.bejava.api.BeParameter;
import nl.sander.bejava.api.BeSource;
import nl.sander.bejava.flags.AccessFlags;
import nl.sander.bejava.flags.ClassAccessFlags;
import nl.sander.bejava.flags.FieldAccessFlag;
import nl.sander.bejava.flags.MethodAccessFlag;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Compilation from sourcefile to BeeSource is the first pass in the total compilation.
* A BeeSource object can also be created programmatically.
*/
public class SourceCompilerTest {
@Test
public void testCompileSourceFileToSourceObject() {
BeSource beSource = new SourceCompiler("""
class public com.acme.SimpleBean(V15)
field private int value
constructor public()
INVOKE this.super()
RETURN
method public getValue() -> int
RETURN this.value
method public setValue(int newValue)
LOAD newValue
PUT this.value
RETURN
""").doCompile();
assertEquals("com.acme.SimpleBean", beSource.getName());
assertEquals(ClassAccessFlags.SUPER.getBytecode() | ClassAccessFlags.PUBLIC.getBytecode(), AccessFlags.combine(beSource.getAccessFlags()));
assertTrue(beSource.getFields().contains(new BeField(beSource.getName(), Set.of(FieldAccessFlag.PRIVATE), int.class, "value")));
assertEquals(1, beSource.getConstructors().size());
Set<BeMethod> methods = beSource.getMethods();
assertEquals(2, methods.size());
assertTrue(methods.contains(new BeMethod(beSource, "getValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(), int.class, List.of())));
assertTrue(methods.contains(new BeMethod(beSource, "setValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(new BeParameter(int.class, "newValue")), Void.TYPE, List.of())));
}
}

View file

@ -1,8 +1,8 @@
package nl.sander.beejava;
package nl.sander.bejava;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.NameAndTypeEntry;
import nl.sander.bejava.api.BeSource;
import nl.sander.bejava.constantpool.ConstantPool;
import nl.sander.bejava.constantpool.entry.NameAndTypeEntry;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -11,12 +11,12 @@ import static org.junit.jupiter.api.Assertions.fail;
public class TypeMapperTest {
@Test
public void test_int() throws ClassNotFoundException {
public void test_int() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(int.class);
BeSource beSource = createClassWithField(int.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -24,12 +24,12 @@ public class TypeMapperTest {
}
@Test
public void test_double() throws ClassNotFoundException {
public void test_double() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(double.class);
BeSource beSource = createClassWithField(double.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -37,12 +37,12 @@ public class TypeMapperTest {
}
@Test
public void test_float() throws ClassNotFoundException {
public void test_float() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(float.class);
BeSource beSource = createClassWithField(float.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -50,12 +50,12 @@ public class TypeMapperTest {
}
@Test
public void test_byte() throws ClassNotFoundException {
public void test_byte() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(byte.class);
BeSource beSource = createClassWithField(byte.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -63,12 +63,12 @@ public class TypeMapperTest {
}
@Test
public void test_short() throws ClassNotFoundException {
public void test_short() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(short.class);
BeSource beSource = createClassWithField(short.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -76,12 +76,12 @@ public class TypeMapperTest {
}
@Test
public void test_long() throws ClassNotFoundException {
public void test_long() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(long.class);
BeSource beSource = createClassWithField(long.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -89,12 +89,12 @@ public class TypeMapperTest {
}
@Test
public void test_char() throws ClassNotFoundException {
public void test_char() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(char.class);
BeSource beSource = createClassWithField(char.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -102,12 +102,12 @@ public class TypeMapperTest {
}
@Test
public void test_boolean() throws ClassNotFoundException {
public void test_boolean() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(boolean.class);
BeSource beSource = createClassWithField(boolean.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -115,12 +115,12 @@ public class TypeMapperTest {
}
@Test
public void test_Object() throws ClassNotFoundException {
public void test_Object() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(Object.class);
BeSource beSource = createClassWithField(Object.class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -128,12 +128,12 @@ public class TypeMapperTest {
}
@Test
public void test_int_array() throws ClassNotFoundException {
public void test_int_array() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(int[].class);
BeSource beSource = createClassWithField(int[].class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
@ -141,20 +141,20 @@ public class TypeMapperTest {
}
@Test
public void test_Object_array() throws ClassNotFoundException {
public void test_Object_array() {
// Arrange
BeeSource beeSource = TestData.createClassWithField(String[].class);
BeSource beSource = createClassWithField(String[].class);
// Act
ConstantPool constantPool = createConstantPool(beeSource);
ConstantPool constantPool = createConstantPool(beSource);
// Assert
NameAndTypeEntry fieldEntry = getFieldNameAndType(constantPool);
assertEquals("[Ljava/lang/String;", fieldEntry.getType());
}
private ConstantPool createConstantPool(BeeSource beeSource) {
CompiledClass compiledClass = Compiler.compile(beeSource);
private ConstantPool createConstantPool(BeSource beSource) {
CompiledClass compiledClass = Compiler.compile(beSource);
return ConstantPoolCreator.create(compiledClass.getConstantTree());
}
@ -165,4 +165,18 @@ public class TypeMapperTest {
.filter(nate -> nate.getName().equals("field"))
.findAny().orElseGet(() -> fail("'field' not found"));
}
private BeSource createClassWithField(Class<?> fieldType) {
String template = """
class com.acme.SimpleBean(V15)
field private %s field
constructor public(%s arg)
INVOKE this.super()V
LOAD arg
PUT this.field
RETURN
""";
return SourceCompiler.compile(String.format(template, fieldType.getName(), fieldType.getName()));
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import org.junit.jupiter.api.Test;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import org.junit.jupiter.api.Test;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import org.junit.jupiter.api.Test;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import org.junit.jupiter.api.Test;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import org.junit.jupiter.api.Test;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import org.junit.jupiter.api.Test;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.constantpool.entry;
package nl.sander.bejava.constantpool.entry;
import org.junit.jupiter.api.Test;
@ -12,7 +12,7 @@ public class TagCorrectnessTest {
public void testSpec() {
assertEquals(1, utf8().getTag());
assertEquals(3, new IntegerEntry(0).getTag());
assertEquals(4, new FloatEntry(0).getTag());
assertEquals(4, new FloatEntry(0F).getTag());
assertEquals(5, new LongEntry(0).getTag());
assertEquals(6, new DoubleEntry(0).getTag());
assertEquals(7, classEntry().getTag());

Some files were not shown because too many files have changed in this diff Show more