Skip to content

Commit 9e61d67

Browse files
committed
Fixed double and long variable store.
1 parent 5087c7d commit 9e61d67

File tree

5 files changed

+229
-45
lines changed

5 files changed

+229
-45
lines changed

src/main/kotlin/com/github/jonathanxd/codeapi/bytecode/common/Frame.kt

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
*/
2828
package com.github.jonathanxd.codeapi.bytecode.common
2929

30+
import com.github.jonathanxd.codeapi.Types
3031
import com.github.jonathanxd.codeapi.type.CodeType
3132
import org.objectweb.asm.Label
3233
import java.util.*
@@ -49,6 +50,9 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
4950
if (i < 0 || i >= this.variables.size)
5051
return null
5152

53+
if(!this.variables[i].isVisible)
54+
throw IllegalArgumentException("Cannot access a invisible variable!!!")
55+
5256
return this.variables[i]
5357
}
5458

@@ -59,7 +63,7 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
5963
* @return Variable or `null` if not present.
6064
*/
6165
fun getVarByName(name: String): Variable? {
62-
return this.variables.find { `var` -> `var`.name == name }
66+
return this.variables.find { `var` -> `var`.isVisible && `var`.name == name }
6367
}
6468

6569
/**
@@ -74,7 +78,7 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
7478
return this.getVarByName(name)
7579
}
7680

77-
return this.variables.find { `var` -> `var`.name == name && `var`.type.compareTo(type) == 0 }
81+
return this.variables.find { `var` -> `var`.isVisible && `var`.name == name && `var`.type.compareTo(type) == 0 }
7882
}
7983

8084
/**
@@ -85,7 +89,7 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
8589
*/
8690
fun getVarPos(variable: Variable): OptionalInt {
8791
for (i in this.variables.indices.reversed()) {
88-
if (this.variables[i] == variable)
92+
if (variable.isVisible && this.variables[i] == variable)
8993
return OptionalInt.of(i)
9094
}
9195

@@ -97,6 +101,36 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
97101
*/
98102
fun add(variable: Variable) {
99103
this.variables.add(variable)
104+
this.handle(variable)
105+
}
106+
107+
/**
108+
* Add variable
109+
*/
110+
fun add(pos: Int, variable: Variable) {
111+
this.variables.add(pos, variable)
112+
this.handle(variable)
113+
}
114+
115+
/**
116+
* Set variable
117+
*/
118+
fun set(pos: Int, variable: Variable) {
119+
120+
if(variable.type.`is`(Types.DOUBLE) || variable.type.`is`(Types.LONG))
121+
throw IllegalArgumentException("Cannot set variable at pos '$pos' because it is of type Double or Long and it requires a right-move of all other variables.")
122+
123+
this.variables[pos] = variable
124+
}
125+
126+
/**
127+
* Handle variable addition.
128+
*
129+
* Workaround to properly store double and longs in the Local Variable Table.
130+
*/
131+
private fun handle(variable: Variable) {
132+
if(variable.type.`is`(Types.DOUBLE) || variable.type.`is`(Types.LONG))
133+
this.variables.add(variable.copy(name = "#${variable.name}ext_", isTemp = true, isVisible = false))
100134
}
101135

102136
/**
@@ -115,7 +149,7 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
115149
for (i in this.variables.indices.reversed()) {
116150
val variable1 = this.variables[i]
117151

118-
if (variable1 == variable) {
152+
if (variable1.isVisible && variable1 == variable) {
119153
if (variable1.isTemp) {
120154
throw RuntimeException("Cannot store variable named '$name'. Variable already stored!")
121155
}
@@ -124,7 +158,7 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
124158
}
125159
}
126160

127-
this.variables.add(variable)
161+
this.add(variable)
128162
// ? Last index with synchronized method is good!!!
129163
return this.getVarPos(variable)
130164
}
@@ -149,39 +183,18 @@ internal class Frame(val parent: Frame? = null, variables: List<Variable> = empt
149183
val variable = Variable(name, type, startLabel, endLabel ?: this.endLabel, true)
150184

151185
for (i in variables.indices.reversed()) {
152-
if (this.variables[i] == variable) {
186+
val variable_ = this.variables[i]
187+
188+
if (variable_.isVisible && variable_ == variable) {
153189
return OptionalInt.of(i)
154190
}
155191
}
156192

157-
this.variables.add(variable)
193+
this.add(variable)
158194
// ? Last index with synchronized method is good!!!
159195
return this.getVarPos(variable)
160196
}
161197

162-
/**
163-
* Redefine a variable in a `position`.
164-
*
165-
* @param pos Position of variable in stack map.
166-
* @param name Name of variable
167-
* @param type Type of variable
168-
* @param startLabel Start label (first occurrence of variable).
169-
* @param endLabel End label (last usage of variable).
170-
*/
171-
fun redefineVar(pos: Int, name: String, type: CodeType, startLabel: Label, endLabel: Label?) {
172-
val variable = Variable(name, type, startLabel, endLabel)
173-
174-
if (pos >= this.variables.size) {
175-
this.variables.add(pos, variable)
176-
} else {
177-
if (this.variables[pos].isTemp) {
178-
throw RuntimeException("Cannot store variable named '$name'. Variable already stored!")
179-
}
180-
181-
this.variables[pos] = variable
182-
}
183-
}
184-
185198
/**
186199
* Return last position in stack map.
187200
*

src/main/kotlin/com/github/jonathanxd/codeapi/bytecode/common/MVData.kt

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,6 @@ class MVData constructor(
171171
fun storeInternalVar(name: String, type: CodeType, startLabel: Label, endLabel: Label?): OptionalInt =
172172
this.frame.storeInternalVar(name, type, startLabel, endLabel)
173173

174-
/**
175-
* Redefine a variable in a `position`.
176-
*
177-
* @param pos Position of variable in stack map.
178-
* @param name Name of variable
179-
* @param type Type of variable
180-
* @param startLabel Start label (first occurrence of variable).
181-
* @param endLabel End label (last usage of variable).
182-
*/
183-
fun redefineVar(pos: Int, name: String, type: CodeType, startLabel: Label, endLabel: Label?) =
184-
this.frame.redefineVar(pos, name, type, startLabel, endLabel)
185-
186174
/**
187175
* Return last position in stack map.
188176
*
@@ -227,8 +215,8 @@ class MVData constructor(
227215

228216
for(variable in varis) {
229217

230-
if (variable.isTemp || variable.name.contains("#"))
231-
// Internal variables
218+
if (!variable.isVisible || variable.isTemp || variable.name.contains("#"))
219+
// Internal variables
232220
continue
233221

234222
val varStart = variable.startLabel

src/main/kotlin/com/github/jonathanxd/codeapi/bytecode/common/Variable.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ import java.util.*
3434

3535
/**
3636
* Internal class undocumented.
37+
*
38+
* isVisible -> If false, this variable will be ignored by get operations.
3739
*/
38-
class Variable @JvmOverloads constructor(val name: String, val type: CodeType, val startLabel: Label, val endLabel: Label?, val isTemp: Boolean = false) {
40+
data class Variable @JvmOverloads constructor(val name: String, val type: CodeType, val startLabel: Label, val endLabel: Label?, val isTemp: Boolean = false, val isVisible: Boolean = true) {
3941

4042
override fun hashCode(): Int {
4143
return Objects.hash(this.name, this.type)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.CodeAPI;
31+
import com.github.jonathanxd.codeapi.CodeSource;
32+
import com.github.jonathanxd.codeapi.Types;
33+
import com.github.jonathanxd.codeapi.base.TypeDeclaration;
34+
import com.github.jonathanxd.codeapi.bytecode.BytecodeClass;
35+
import com.github.jonathanxd.codeapi.bytecode.classloader.CodeClassLoader;
36+
import com.github.jonathanxd.codeapi.bytecode.gen.BytecodeGenerator;
37+
import com.github.jonathanxd.codeapi.common.CodeParameter;
38+
import com.github.jonathanxd.codeapi.helper.Predefined;
39+
40+
import org.junit.Test;
41+
42+
import java.util.EnumSet;
43+
44+
import static com.github.jonathanxd.codeapi.CodeAPI.parameter;
45+
import static com.github.jonathanxd.codeapi.CodeAPI.sourceOfParts;
46+
import static com.github.jonathanxd.codeapi.Types.VOID;
47+
import static com.github.jonathanxd.codeapi.common.CodeModifier.PUBLIC;
48+
import static com.github.jonathanxd.codeapi.factory.ClassFactory.aClass;
49+
import static com.github.jonathanxd.codeapi.factory.MethodFactory.method;
50+
import static com.github.jonathanxd.codeapi.factory.VariableFactory.variable;
51+
import static com.github.jonathanxd.codeapi.literal.Literals.STRING;
52+
53+
public class DoubleTest {
54+
55+
56+
@Test
57+
public void wiki() throws Throwable {
58+
CodeSource source = sourceOfParts(
59+
method(EnumSet.of(PUBLIC), "test", VOID, new CodeParameter[]{
60+
parameter(Float.TYPE, "f"),
61+
parameter(Boolean.TYPE, "b"),
62+
parameter(Short.TYPE, "s"),
63+
parameter(Double.TYPE, "d"),
64+
parameter(Long.TYPE, "l"),
65+
},
66+
sourceOfParts(
67+
variable(Types.STRING, "variable"),
68+
Predefined.invokePrintlnStr(STRING("Hello world")),
69+
Predefined.invokePrintln(CodeAPI.accessLocalVariable(Float.TYPE, "f")),
70+
Predefined.invokePrintln(CodeAPI.accessLocalVariable(Boolean.TYPE, "b")),
71+
Predefined.invokePrintln(CodeAPI.accessLocalVariable(Short.TYPE, "s")),
72+
Predefined.invokePrintln(CodeAPI.accessLocalVariable(Double.TYPE, "d")),
73+
Predefined.invokePrintln(CodeAPI.accessLocalVariable(Long.TYPE, "l"))
74+
)
75+
)
76+
);
77+
78+
TypeDeclaration decl = aClass(EnumSet.of(PUBLIC), "com.MyClass", source);
79+
80+
BytecodeGenerator bytecodeGenerator = new BytecodeGenerator();
81+
82+
BytecodeClass[] gen = bytecodeGenerator.gen(decl);
83+
84+
ResultSaver.save(this.getClass(), gen);
85+
86+
CodeClassLoader codeClassLoader = new CodeClassLoader();
87+
88+
Class<?> define = codeClassLoader.define(gen);
89+
90+
define.getDeclaredMethod("test", Float.TYPE, Boolean.TYPE, Short.TYPE, Double.TYPE, Long.TYPE).invoke(define.newInstance(), 1.0F, false, (short) 5, 56.8959D, 15656L);
91+
92+
93+
}
94+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
md5: 48f4b826284d716bfe215e93faa15d1d
2+
3+
version: Java 8 (52)
4+
access: ACC_PUBLIC (33)
5+
6+
source: MyClass.cai
7+
8+
public class com.MyClass extends java.lang.Object {
9+
10+
!access: ACC_PUBLIC (1)
11+
public void test(float, boolean, short, double, long) {
12+
desc: (FZSDJ)V
13+
maxStack: 5, maxLocals: 9
14+
Label_0:
15+
getstatic java.lang.System.out (type: java.io.PrintStream)
16+
ldc "Hello world" // type: java.lang.String
17+
invokevirtual java.io.PrintStream.println(java.lang.String)void (ownerIsInterface: false)
18+
getstatic java.lang.System.out (type: java.io.PrintStream)
19+
new java.lang.Float
20+
dup
21+
fload 1
22+
invokespecial java.lang.Float.<init>(float)void (ownerIsInterface: false)
23+
checkcast java.lang.Object
24+
invokevirtual java.io.PrintStream.println(java.lang.Object)void (ownerIsInterface: false)
25+
getstatic java.lang.System.out (type: java.io.PrintStream)
26+
new java.lang.Boolean
27+
dup
28+
iload 2
29+
invokespecial java.lang.Boolean.<init>(boolean)void (ownerIsInterface: false)
30+
checkcast java.lang.Object
31+
invokevirtual java.io.PrintStream.println(java.lang.Object)void (ownerIsInterface: false)
32+
getstatic java.lang.System.out (type: java.io.PrintStream)
33+
new java.lang.Short
34+
dup
35+
iload 3
36+
invokespecial java.lang.Short.<init>(short)void (ownerIsInterface: false)
37+
checkcast java.lang.Object
38+
invokevirtual java.io.PrintStream.println(java.lang.Object)void (ownerIsInterface: false)
39+
getstatic java.lang.System.out (type: java.io.PrintStream)
40+
new java.lang.Double
41+
dup
42+
dload 4
43+
invokespecial java.lang.Double.<init>(double)void (ownerIsInterface: false)
44+
checkcast java.lang.Object
45+
invokevirtual java.io.PrintStream.println(java.lang.Object)void (ownerIsInterface: false)
46+
getstatic java.lang.System.out (type: java.io.PrintStream)
47+
new java.lang.Long
48+
dup
49+
lload 6
50+
invokespecial java.lang.Long.<init>(long)void (ownerIsInterface: false)
51+
checkcast java.lang.Object
52+
invokevirtual java.io.PrintStream.println(java.lang.Object)void (ownerIsInterface: false)
53+
return
54+
Label_1:
55+
LocalVariables {
56+
index: 8, name: variable, start: Label_0, end: Label_1, type: java.lang.String, signature: null
57+
index: 6, name: l, start: Label_0, end: Label_1, type: long, signature: null
58+
index: 4, name: d, start: Label_0, end: Label_1, type: double, signature: null
59+
index: 3, name: s, start: Label_0, end: Label_1, type: short, signature: null
60+
index: 2, name: b, start: Label_0, end: Label_1, type: boolean, signature: null
61+
index: 1, name: f, start: Label_0, end: Label_1, type: float, signature: null
62+
index: 0, name: this, start: Label_0, end: Label_1, type: com.MyClass, signature: null
63+
}
64+
}
65+
66+
!access: ACC_PUBLIC (1)
67+
public void <init>() {
68+
desc: ()V
69+
maxStack: 1, maxLocals: 1
70+
Label_0:
71+
aload 0
72+
invokespecial java.lang.Object.<init>()void (ownerIsInterface: false)
73+
return
74+
Label_1:
75+
LocalVariables {
76+
index: 0, name: this, start: Label_0, end: Label_1, type: com.MyClass, signature: null
77+
}
78+
}
79+
80+
!access: PACKAGE_PRIVATE, ACC_STATIC (8)
81+
static void <clinit>() {
82+
desc: ()V
83+
maxStack: 0, maxLocals: 0
84+
return
85+
}
86+
87+
}

0 commit comments

Comments
 (0)