Skip to content

Commit f67f759

Browse files
committed
CodeAPI-BytecodeWriter 3.2 released
- CodeAPI Updated to 3.2 - Welcome new bytecode translation algorithm. - Better than old, support IfGroup... No weird bugs? See 'TestLoopBytecode.class.disassembled' file, how old algorithm generated that? HOW? (I will not try to understand how the algorithm generated 'if_cmplt' instead of 'if_icmpge', there is no reason to do that).
1 parent fd56263 commit f67f759

File tree

8 files changed

+368
-8
lines changed

8 files changed

+368
-8
lines changed

CodeAPI

build.gradle

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ buildscript {
1818
}
1919

2020
group 'com.github.jonathanxd'
21-
version '3.1.6-3.1.5'
21+
version '3.2.0-3.2.0'
2222

2323
apply from: project(":CodeAPI").file("gradle/common.gradle")
2424

@@ -31,4 +31,13 @@ dependencies {
3131

3232
dokka {
3333
enabled = false
34-
}
34+
}
35+
36+
shadowJar {
37+
dependencies {
38+
include(project(":CodeAPI"))
39+
include(dependency("org.ow2.asm:asm-all"))
40+
include(dependency("com.github.JonathanxD:BytecodeDisassembler"))
41+
include(dependency("com.github.JonathanxD:JwIUtils"))
42+
}
43+
}

src/main/kotlin/com/github/jonathanxd/codeapi/bytecode/gen/visitor/BytecodeIfStatementVisitor.kt

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,163 @@ import com.github.jonathanxd.codeapi.bytecode.util.CodePartUtil
4040
import com.github.jonathanxd.codeapi.bytecode.util.IfUtil
4141
import com.github.jonathanxd.codeapi.bytecode.util.OperatorUtil
4242
import com.github.jonathanxd.codeapi.common.Data
43+
import com.github.jonathanxd.codeapi.common.IfGroup
4344
import com.github.jonathanxd.codeapi.gen.visit.VisitorGenerator
4445
import com.github.jonathanxd.codeapi.literal.Literals
46+
import com.github.jonathanxd.codeapi.operator.Operator
4547
import com.github.jonathanxd.codeapi.operator.Operators
4648
import org.objectweb.asm.Label
4749
import org.objectweb.asm.Opcodes
4850

51+
fun visit(expressions: List<CodePart>,
52+
ifStart: Label,
53+
ifBody: Label,
54+
outOfIf: Label,
55+
isWhile: Boolean,
56+
extraData: Data,
57+
visitorGenerator: VisitorGenerator<BytecodeClass>,
58+
mvData: MVData,
59+
nextIsOr: Boolean = false) {
60+
61+
val visitor = mvData.methodVisitor
62+
63+
var index = 0
64+
65+
fun hasOr() =
66+
index + 1 < expressions.size
67+
&& expressions.slice((index + 1)..(expressions.size - 1)).takeWhile { it !is IfGroup }.any { it is Operator && it.name == Operators.OR.name }
68+
69+
fun nextIsOr() =
70+
if (index + 1 < expressions.size)
71+
(expressions[index + 1]).let { it is Operator && it.name == Operators.OR.name }
72+
else nextIsOr // fix for ifGroup
73+
74+
var orLabel: Label? = null
75+
76+
while (index < expressions.size) {
77+
val expr = expressions[index]
78+
79+
val inverse = !nextIsOr() || isWhile
80+
81+
val jumpLabel = if (hasOr() && !nextIsOr()) {
82+
if (orLabel == null) {
83+
orLabel = Label()
84+
}
85+
orLabel
86+
} else if (isWhile) ifStart else if(inverse) outOfIf else ifBody
87+
88+
if (expr is IfExpr) {
89+
if(index - 1 > 0 && expressions[index - 1].let { it is Operator && it.name == Operators.OR.name } )
90+
orLabel?.let { visitor.visitLabel(it) }
91+
92+
val expr1 = expr.expr1
93+
val operation = expr.operation
94+
val expr2 = expr.expr2
95+
96+
genBranch(expr1, expr2, operation, jumpLabel, inverse, extraData, visitorGenerator, mvData)
97+
}
98+
99+
if (expr is IfGroup) {
100+
visit(expr.expressions, ifStart, ifBody, outOfIf, isWhile, extraData, visitorGenerator, mvData, nextIsOr())
101+
}
102+
103+
++index
104+
}
105+
106+
}
107+
108+
fun genBranch(expr1_: CodePart, expr2_: CodePart, operation: Operator.Conditional, target: Label, inverse: Boolean, extraData: Data, visitorGenerator: VisitorGenerator<BytecodeClass>, mvData: MVData) {
109+
110+
var expr1 = expr1_
111+
var expr2 = expr2_
112+
113+
val expr1Type = CodePartUtil.getType(expr1)
114+
val expr2Type = CodePartUtil.getType(expr2)
115+
116+
val expr1Primitive = CodePartUtil.isPrimitive(expr1)
117+
val expr2Primitive = CodePartUtil.isPrimitive(expr2)
118+
119+
val firstIsBoolean = expr1Primitive && CodePartUtil.isBoolean(expr1)
120+
val secondIsBoolean = expr2Primitive && CodePartUtil.isBoolean(expr2)
121+
122+
if (firstIsBoolean || secondIsBoolean) {
123+
val operatorIsEq = operation === Operators.EQUAL_TO
124+
val value = if (firstIsBoolean) CodePartUtil.getBooleanValue(expr1) else CodePartUtil.getBooleanValue(expr2)
125+
var opcode = IfUtil.getIfNeEqOpcode(value)
126+
127+
if (!operatorIsEq)
128+
opcode = IfUtil.invertIfNeEqOpcode(opcode)
129+
130+
if (inverse)
131+
opcode = IfUtil.invertIfNeEqOpcode(opcode)
132+
133+
if (firstIsBoolean) {
134+
visitorGenerator.generateTo(expr2::class.java, expr2, extraData, null, mvData)
135+
mvData.methodVisitor.visitJumpInsn(opcode, target)
136+
} else {
137+
visitorGenerator.generateTo(expr1::class.java, expr1, extraData, null, mvData)
138+
mvData.methodVisitor.visitJumpInsn(opcode, target)
139+
}
140+
141+
} else {
142+
// Old Code ->
143+
// TODO: Rewrite
144+
145+
if (expr1Primitive != expr2Primitive) {
146+
147+
if (expr2Primitive) {
148+
expr1 = CodeAPI.cast(expr1Type, expr2Type, expr1)
149+
} else {
150+
expr2 = CodeAPI.cast(expr2Type, expr1Type, expr2)
151+
}
152+
}
153+
154+
visitorGenerator.generateTo(expr1::class.java, expr1, extraData, null, mvData)
155+
156+
if (expr2 === Literals.NULL) {
157+
mvData.methodVisitor.visitJumpInsn(OperatorUtil.nullCheckToAsm(operation, inverse), target)
158+
} else if (CodePartUtil.isPrimitive(expr1) && CodePartUtil.isPrimitive(expr2)) {
159+
visitorGenerator.generateTo(expr2::class.java, expr2, extraData, null, mvData)
160+
161+
val firstType = CodePartUtil.getType(expr1)
162+
val secondType = CodePartUtil.getType(expr2)
163+
164+
if (!firstType.`is`(secondType))
165+
throw IllegalArgumentException("'$expr1' and '$expr2' have different types, cast it to correct type.")
166+
167+
var generateCMPCheck = false
168+
169+
if (expr1Type.`is`(Types.LONG)) {
170+
mvData.methodVisitor.visitInsn(Opcodes.LCMP)
171+
generateCMPCheck = true
172+
} else if (expr1Type.`is`(Types.DOUBLE)) {
173+
mvData.methodVisitor.visitInsn(Opcodes.DCMPG)
174+
generateCMPCheck = true
175+
} else if (expr1Type.`is`(Types.FLOAT)) {
176+
mvData.methodVisitor.visitInsn(Opcodes.FCMPG)
177+
generateCMPCheck = true
178+
}
179+
180+
var check = OperatorUtil.primitiveToAsm(operation, inverse)
181+
182+
if (generateCMPCheck) {
183+
check = OperatorUtil.convertToSimpleIf(check)
184+
}
185+
186+
mvData.methodVisitor.visitJumpInsn(check, target)
187+
} else {
188+
visitorGenerator.generateTo(expr2::class.java, expr2, extraData, null, mvData)
189+
190+
mvData.methodVisitor.visitJumpInsn(OperatorUtil.referenceToAsm(operation, inverse), target)
191+
}
192+
}
193+
}
194+
195+
@Deprecated(
196+
message = "Too much bugs, don't supports IfGroup, generate incorrect if bytecode, the source of the devil... Deprecated since: 3.2. Will be removed in 4.0",
197+
level = DeprecationLevel.ERROR,
198+
replaceWith = ReplaceWith("visit(ifStatement.expressions, ifStartLabel, ifBody, outOfIfLabel, jumpToStart, extraData, visitorGenerator, mvData)")
199+
)
49200
fun visit(ifStatement: IfStatement,
50201
ifStartLabel: Label,
51202
outOfIfLabel: Label,
@@ -79,7 +230,8 @@ fun visit(ifStatement: IfStatement,
79230
if (current is IfExpr) {
80231
val isInverse = !revert == (next == null || next !== Operators.OR)
81232

82-
val lbl = if (ifStatement is SwitchVisitor.SwitchIfStatement) { // Workaround ?
233+
val lbl = if (ifStatement is SwitchVisitor.SwitchIfStatement) {
234+
// Workaround ?
83235
extraData.getRequired<Flow>(ConstantDatas.FLOW_TYPE_INFO).insideEnd
84236
} else {
85237
if (jumpToStart) ifStartLabel else if (!isInverse) inIfLabel else elseLabel ?: outOfIfLabel // Jump to else if exists

src/main/kotlin/com/github/jonathanxd/codeapi/bytecode/gen/visitor/IfStatementVisitor.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,60 @@
2727
*/
2828
package com.github.jonathanxd.codeapi.bytecode.gen.visitor
2929

30+
import com.github.jonathanxd.codeapi.CodeSource
3031
import com.github.jonathanxd.codeapi.base.IfStatement
3132
import com.github.jonathanxd.codeapi.bytecode.BytecodeClass
33+
import com.github.jonathanxd.codeapi.bytecode.common.Flow
3234
import com.github.jonathanxd.codeapi.bytecode.common.MVData
3335
import com.github.jonathanxd.codeapi.common.Data
3436
import com.github.jonathanxd.codeapi.gen.visit.VisitorGenerator
3537
import com.github.jonathanxd.codeapi.gen.visit.VoidVisitor
3638
import org.objectweb.asm.Label
39+
import org.objectweb.asm.Opcodes
3740

3841
object IfStatementVisitor : VoidVisitor<IfStatement, BytecodeClass, MVData> {
3942

4043
override fun voidVisit(t: IfStatement, extraData: Data, visitorGenerator: VisitorGenerator<BytecodeClass>, additional: MVData) {
4144
val startIfLabel = Label()
45+
val ifBody = Label()
4246
val endIfLabel = Label()
4347

48+
val elseLabel = Label()
49+
50+
val elseStatement = t.elseStatement
51+
52+
val jumpLabel = if(t is SwitchVisitor.SwitchIfStatement) extraData.getRequired<Flow>(ConstantDatas.FLOW_TYPE_INFO).insideEnd else if(elseStatement.isNotEmpty) elseLabel else endIfLabel
53+
4454
val methodVisitor = additional.methodVisitor
4555

4656
methodVisitor.visitLabel(startIfLabel)
4757

48-
visit(t, startIfLabel, endIfLabel, false, false, extraData, visitorGenerator, additional)
58+
visit(t.expressions, startIfLabel, ifBody, jumpLabel, false, extraData, visitorGenerator, additional)
59+
60+
val body = t.body
61+
62+
methodVisitor.visitLabel(ifBody)
63+
64+
additional.enterNewFrame()
65+
66+
visitorGenerator.generateTo(CodeSource::class.java, body, extraData, null, additional)
67+
68+
additional.exitFrame()
69+
70+
if (elseStatement.isNotEmpty) {
71+
methodVisitor.visitJumpInsn(Opcodes.GOTO, endIfLabel)
72+
}
73+
74+
75+
if (elseStatement.isNotEmpty) {
76+
methodVisitor.visitLabel(elseLabel)
77+
78+
additional.enterNewFrame()
79+
80+
visitorGenerator.generateTo(CodeSource::class.java, elseStatement, extraData, additional)
81+
82+
additional.exitFrame()
83+
}
4984

5085
methodVisitor.visitLabel(endIfLabel)
5186
}

src/main/kotlin/com/github/jonathanxd/codeapi/bytecode/gen/visitor/WhileStatementVisitor.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,27 @@ object WhileStatementVisitor : VoidVisitor<WhileStatement, BytecodeClass, MVData
7171

7272
mv.visitLabel(insideEnd)
7373

74-
visit(ifStatement, whileStart, outOfIf, true, true, extraData, visitorGenerator, additional)
74+
val startIfLabel = Label()
75+
val ifBody = Label()
76+
77+
val methodVisitor = additional.methodVisitor
78+
79+
methodVisitor.visitLabel(startIfLabel)
80+
81+
visit(ifStatement.expressions, whileStart, insideStart, outOfIf, true, extraData, visitorGenerator, additional)
82+
83+
val body = t.body
84+
85+
methodVisitor.visitLabel(ifBody)
86+
87+
additional.enterNewFrame()
88+
89+
visitorGenerator.generateTo(CodeSource::class.java, body, extraData, null, additional)
90+
91+
additional.exitFrame()
92+
93+
//visit(t, startIfLabel, endIfLabel, false, false, extraData, visitorGenerator, additional)
94+
methodVisitor.visitLabel(outOfIf)
7595

7696
mv.visitLabel(outsideEnd)
7797
} else if (t.type == WhileStatement.Type.WHILE) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* CodeAPI-BytecodeWriter - Framework to generate Java code and Bytecode code. <https://github.com/JonathanxD/CodeAPI-BytecodeWriter>
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2017 TheRealBuggy/JonathanxD (https://github.com/JonathanxD/ & https://github.com/TheRealBuggy/) <[email protected]>
7+
* Copyright (c) contributors
8+
*
9+
*
10+
* Permission is hereby granted, free of charge, to any person obtaining a copy
11+
* of this software and associated documentation files (the "Software"), to deal
12+
* in the Software without restriction, including without limitation the rights
13+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
* copies of the Software, and to permit persons to whom the Software is
15+
* furnished to do so, subject to the following conditions:
16+
*
17+
* The above copyright notice and this permission notice shall be included in
18+
* all copies or substantial portions of the Software.
19+
*
20+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26+
* THE SOFTWARE.
27+
*/
28+
package com.github.jonathanxd.codeapi.test.asm;
29+
30+
import com.github.jonathanxd.codeapi.CodeSource;
31+
import com.github.jonathanxd.codeapi.base.TypeDeclaration;
32+
import com.github.jonathanxd.codeapi.test.ComplexIfTest_;
33+
import com.github.jonathanxd.iutils.annotation.Named;
34+
import com.github.jonathanxd.iutils.object.Pair;
35+
36+
import org.junit.Test;
37+
38+
import java.lang.reflect.InvocationTargetException;
39+
40+
public class ComplexIfTest {
41+
42+
@Test
43+
public void testComplexIf() throws Exception {
44+
Pair<@Named("Main class") TypeDeclaration, @Named("Source") CodeSource> $ = ComplexIfTest_.$();
45+
46+
@Named("Instance") Object test = CommonBytecodeTest.test(this.getClass(), $._1(), $._2(), aClass -> {
47+
try {
48+
return aClass.getConstructor(Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
49+
.newInstance(true, true, false, true);
50+
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
51+
throw new RuntimeException(e);
52+
}
53+
});
54+
55+
}
56+
}

0 commit comments

Comments
 (0)