Skip to content

Commit d534450

Browse files
authored
Expand translator integration coverage (#4232)
1 parent 82361e3 commit d534450

File tree

2 files changed

+330
-3
lines changed

2 files changed

+330
-3
lines changed

vm/tests/src/test/java/com/codename1/tools/translator/BytecodeInstructionIntegrationTest.java

Lines changed: 278 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
package com.codename1.tools.translator;
2+
import com.codename1.tools.translator.bytecodes.ArrayLengthExpression;
3+
import com.codename1.tools.translator.bytecodes.ArrayLoadExpression;
4+
import com.codename1.tools.translator.bytecodes.AssignableExpression;
5+
import com.codename1.tools.translator.bytecodes.Instruction;
6+
import com.codename1.tools.translator.bytecodes.MultiArray;
27
import org.junit.jupiter.api.Test;
8+
import org.objectweb.asm.AnnotationVisitor;
9+
import org.objectweb.asm.Opcodes;
310

411
import javax.tools.JavaCompiler;
512
import javax.tools.ToolProvider;
13+
import java.io.ByteArrayOutputStream;
14+
import java.io.File;
15+
import java.io.FilenameFilter;
16+
import java.lang.reflect.Constructor;
17+
import java.lang.reflect.Field;
618
import java.nio.charset.StandardCharsets;
719
import java.nio.file.Files;
820
import java.nio.file.Path;
21+
import java.util.ArrayList;
922
import java.util.Arrays;
23+
import java.util.HashSet;
24+
import java.util.List;
25+
import java.util.Set;
26+
import java.util.TreeSet;
27+
import java.util.concurrent.atomic.AtomicBoolean;
1028
import java.util.stream.Stream;
1129

1230
import static org.junit.jupiter.api.Assertions.*;
@@ -90,7 +108,197 @@ void translatesOptimizedBytecodeToLLVMExecutable() throws Exception {
90108

91109
Path executable = buildDir.resolve("CustomBytecodeApp");
92110
String output = CleanTargetIntegrationTest.runCommand(Arrays.asList(executable.toString()), buildDir);
93-
assertTrue(output.contains("RESULT=54"), "Compiled program should print the expected arithmetic result");
111+
assertTrue(output.contains("RESULT=293"), "Compiled program should print the expected arithmetic result");
112+
}
113+
114+
private Set<String> snapshotArrayTypes() throws Exception {
115+
Field arrayTypesField = ByteCodeClass.class.getDeclaredField("arrayTypes");
116+
arrayTypesField.setAccessible(true);
117+
return new TreeSet<>((Set<String>) arrayTypesField.get(null));
118+
}
119+
120+
private void restoreArrayTypes(Set<String> snapshot) throws Exception {
121+
Field arrayTypesField = ByteCodeClass.class.getDeclaredField("arrayTypes");
122+
arrayTypesField.setAccessible(true);
123+
arrayTypesField.set(null, new TreeSet<>(snapshot));
124+
}
125+
126+
private static class StubAssignableExpression extends Instruction implements AssignableExpression {
127+
private final String expression;
128+
int dependencyCalls;
129+
130+
private StubAssignableExpression(int opcode, String expression) {
131+
super(opcode);
132+
this.expression = expression;
133+
}
134+
135+
@Override
136+
public void addDependencies(List<String> dependencyList) {
137+
dependencyCalls++;
138+
dependencyList.add(expression);
139+
}
140+
141+
@Override
142+
public void appendInstruction(StringBuilder b) {
143+
b.append(expression);
144+
}
145+
146+
@Override
147+
public boolean assignTo(String varName, StringBuilder sb) {
148+
if (varName != null) {
149+
sb.append(varName).append(" = ").append(expression).append(";\n");
150+
} else {
151+
sb.append(expression);
152+
}
153+
return true;
154+
}
155+
}
156+
157+
@Test
158+
void annotationVisitorWrapperDelegatesAndHandlesNullVisitor() {
159+
Parser parser = new Parser();
160+
161+
Parser.AnnotationVisitorWrapper wrapperWithNull = parser.new AnnotationVisitorWrapper(null);
162+
assertNull(wrapperWithNull.visitArray("values"));
163+
assertNull(wrapperWithNull.visitAnnotation("name", "LExample;"));
164+
assertDoesNotThrow(() -> wrapperWithNull.visit("flag", true));
165+
assertDoesNotThrow(() -> wrapperWithNull.visitEnum("choice", "LExample;", "VALUE"));
166+
167+
AtomicBoolean delegated = new AtomicBoolean(false);
168+
AnnotationVisitor delegate = new AnnotationVisitor(Opcodes.ASM5) {
169+
@Override
170+
public AnnotationVisitor visitArray(String name) {
171+
delegated.set(true);
172+
return this;
173+
}
174+
};
175+
176+
Parser.AnnotationVisitorWrapper wrapperWithDelegate = parser.new AnnotationVisitorWrapper(delegate);
177+
AnnotationVisitor result = wrapperWithDelegate.visitArray("values");
178+
assertSame(delegate, result);
179+
assertTrue(delegated.get(), "AnnotationVisitorWrapper should forward to the underlying visitor");
180+
}
181+
182+
@Test
183+
void byteCodeTranslatorFilenameFilterMatchesExpectedFiles() throws Exception {
184+
Class<?> filterClass = Class.forName("com.codename1.tools.translator.ByteCodeTranslator$3");
185+
Constructor<?> ctor = filterClass.getDeclaredConstructor();
186+
ctor.setAccessible(true);
187+
188+
FilenameFilter filter = (FilenameFilter) ctor.newInstance();
189+
File directory = Files.createTempDirectory("bytecode-filter").toFile();
190+
191+
assertTrue(filter.accept(directory, "assets.bundle"));
192+
assertTrue(filter.accept(directory, "model.xcdatamodeld"));
193+
assertTrue(filter.accept(directory, "VisibleSource.m"));
194+
assertFalse(filter.accept(directory, ".hidden"));
195+
assertFalse(filter.accept(directory, "Images.xcassets"));
196+
}
197+
198+
@Test
199+
void concatenatingFileOutputStreamWritesShardedOutputs() throws Exception {
200+
Path outputDir = Files.createTempDirectory("concatenating-output");
201+
ByteCodeTranslator.OutputType original = ByteCodeTranslator.output;
202+
ByteCodeTranslator.output = ByteCodeTranslator.OutputType.OUTPUT_TYPE_CLEAN;
203+
try {
204+
ConcatenatingFileOutputStream stream = new ConcatenatingFileOutputStream(outputDir.toFile());
205+
stream.beginNextFile("first");
206+
stream.write("abc".getBytes(StandardCharsets.UTF_8));
207+
stream.close();
208+
209+
stream.beginNextFile("second");
210+
stream.write("123".getBytes(StandardCharsets.UTF_8));
211+
stream.close();
212+
213+
Field destField = ConcatenatingFileOutputStream.class.getDeclaredField("dest");
214+
destField.setAccessible(true);
215+
ByteArrayOutputStream[] buffers = (ByteArrayOutputStream[]) destField.get(stream);
216+
for (int i = 0; i < buffers.length; i++) {
217+
if (buffers[i] == null) {
218+
buffers[i] = new ByteArrayOutputStream();
219+
}
220+
}
221+
destField.set(stream, buffers);
222+
223+
stream.realClose();
224+
225+
Path first = outputDir.resolve("concatenated_" + Math.abs("first".hashCode() % ConcatenatingFileOutputStream.MODULO) + ".c");
226+
Path second = outputDir.resolve("concatenated_" + Math.abs("second".hashCode() % ConcatenatingFileOutputStream.MODULO) + ".c");
227+
228+
assertTrue(Files.exists(first));
229+
assertEquals("abc\n", new String(Files.readAllBytes(first), StandardCharsets.UTF_8));
230+
231+
assertTrue(Files.exists(second));
232+
assertEquals("123\n", new String(Files.readAllBytes(second), StandardCharsets.UTF_8));
233+
} finally {
234+
ByteCodeTranslator.output = original;
235+
}
236+
}
237+
238+
@Test
239+
void multiArrayAddsDependenciesAndRegistersArrayTypes() throws Exception {
240+
List<String> dependencies = new ArrayList<>();
241+
MultiArray multiArray = new MultiArray("[[Ljava/lang/String;", 2);
242+
243+
Set<String> snapshot = snapshotArrayTypes();
244+
try {
245+
multiArray.addDependencies(dependencies);
246+
247+
assertTrue(dependencies.contains("java_lang_String"));
248+
assertTrue(snapshotArrayTypes().contains("2_java_lang_String"));
249+
} finally {
250+
restoreArrayTypes(snapshot);
251+
}
252+
}
253+
254+
@Test
255+
void arrayLengthExpressionReducesAndAssigns() throws Exception {
256+
List<Instruction> instructions = new ArrayList<>();
257+
StubAssignableExpression arrayRef = new StubAssignableExpression(Opcodes.ALOAD, "myArray");
258+
Instruction arrayLength = new Instruction(Opcodes.ARRAYLENGTH) { };
259+
instructions.add(arrayRef);
260+
instructions.add(arrayLength);
261+
262+
int reducedIndex = ArrayLengthExpression.tryReduce(instructions, 1);
263+
assertEquals(0, reducedIndex);
264+
assertEquals(1, instructions.size());
265+
ArrayLengthExpression reduced = (ArrayLengthExpression) instructions.get(0);
266+
267+
StringBuilder assignment = new StringBuilder();
268+
assertTrue(reduced.assignTo("len", assignment));
269+
assertEquals("len = CN1_ARRAY_LENGTH(myArray);\n", assignment.toString());
270+
271+
List<String> deps = new ArrayList<>();
272+
reduced.addDependencies(deps);
273+
assertEquals(1, arrayRef.dependencyCalls);
274+
assertEquals("myArray", deps.get(0));
275+
}
276+
277+
@Test
278+
void arrayLoadExpressionReducesAndAssigns() {
279+
List<Instruction> instructions = new ArrayList<>();
280+
StubAssignableExpression arrayRef = new StubAssignableExpression(Opcodes.ALOAD, "items");
281+
StubAssignableExpression index = new StubAssignableExpression(Opcodes.ILOAD, "index");
282+
Instruction loadInstr = new Instruction(Opcodes.IALOAD) { };
283+
instructions.add(arrayRef);
284+
instructions.add(index);
285+
instructions.add(loadInstr);
286+
287+
int reducedIndex = ArrayLoadExpression.tryReduce(instructions, 2);
288+
assertEquals(0, reducedIndex);
289+
assertEquals(1, instructions.size());
290+
ArrayLoadExpression reduced = (ArrayLoadExpression) instructions.get(0);
291+
292+
StringBuilder assignment = new StringBuilder();
293+
assertTrue(reduced.assignTo("value", assignment));
294+
assertEquals("value=CN1_ARRAY_ELEMENT_INT(items, index);\n", assignment.toString());
295+
296+
List<String> deps = new ArrayList<>();
297+
reduced.addDependencies(deps);
298+
assertEquals(1, arrayRef.dependencyCalls);
299+
assertEquals(1, index.dependencyCalls);
300+
assertTrue(deps.contains("items"));
301+
assertTrue(deps.contains("index"));
94302
}
95303

96304
private Path findGeneratedSource(Path srcRoot) throws Exception {
@@ -104,7 +312,14 @@ private Path findGeneratedSource(Path srcRoot) throws Exception {
104312

105313
private String appSource() {
106314
return "public class BytecodeInstructionApp {\n" +
315+
" private static final int STATIC_INCREMENT = 3;\n" +
107316
" private static native void report(int value);\n" +
317+
" private int instanceCounter = 7;\n" +
318+
" private int baseField = 11;\n" +
319+
" public BytecodeInstructionApp(int seed) {\n" +
320+
" instanceCounter = seed;\n" +
321+
" baseField = seed + 6;\n" +
322+
" }\n" +
108323
" private static int optimizedComputation(int a, int b) {\n" +
109324
" int counter = a;\n" +
110325
" counter++;\n" +
@@ -136,12 +351,73 @@ private String appSource() {
136351
" }\n" +
137352
" return result;\n" +
138353
" }\n" +
354+
" private int loopArrays(int base) {\n" +
355+
" int[] values = { base, base + 1, base + 2, STATIC_INCREMENT };\n" +
356+
" int total = 0;\n" +
357+
" for (int i = 0; i < values.length; i++) {\n" +
358+
" total += values[i] * (i + 1);\n" +
359+
" }\n" +
360+
" return total + values.length;\n" +
361+
" }\n" +
362+
" private int multiArrayUsage(int factor) {\n" +
363+
" int[][] grid = new int[2][3];\n" +
364+
" int v = factor;\n" +
365+
" for (int i = 0; i < grid.length; i++) {\n" +
366+
" for (int j = 0; j < grid[i].length; j++) {\n" +
367+
" grid[i][j] = v++;\n" +
368+
" }\n" +
369+
" }\n" +
370+
" int total = 0;\n" +
371+
" for (int[] row : grid) {\n" +
372+
" for (int cell : row) {\n" +
373+
" total += cell;\n" +
374+
" }\n" +
375+
" total += row.length;\n" +
376+
" }\n" +
377+
" String[][] labels = new String[][] { { \"a\", \"b\" }, { \"c\", \"d\" } };\n" +
378+
" int labelLength = 0;\n" +
379+
" for (int i = 0; i < labels.length; i++) {\n" +
380+
" labelLength += labels[i].length;\n" +
381+
" }\n" +
382+
" return total + labelLength;\n" +
383+
" }\n" +
384+
" private int useFieldsAndMethods(int offset) {\n" +
385+
" instanceCounter += offset;\n" +
386+
" int[] mix = new int[] { offset, offset + baseField, instanceCounter };\n" +
387+
" int altSum = 0;\n" +
388+
" for (int i = 0; i < mix.length; i++) {\n" +
389+
" altSum += mix[i] + i;\n" +
390+
" }\n" +
391+
" int nestedLoop = loopArrays(offset);\n" +
392+
" int nestedMulti = multiArrayUsage(offset);\n" +
393+
" report(instanceCounter);\n" +
394+
" report(baseField);\n" +
395+
" report(altSum);\n" +
396+
" report(nestedLoop);\n" +
397+
" report(nestedMulti);\n" +
398+
" return nestedLoop + nestedMulti + instanceCounter + altSum + baseField;\n" +
399+
" }\n" +
139400
" public static void main(String[] args) {\n" +
401+
" BytecodeInstructionApp app = new BytecodeInstructionApp(4);\n" +
140402
" int first = optimizedComputation(1, 3);\n" +
141403
" int second = optimizedComputation(5, 2);\n" +
142404
" int switched = switchComputation(first) + switchComputation(second);\n" +
143405
" int synchronizedValue = synchronizedIncrement(second);\n" +
144-
" report(first + second + switched + synchronizedValue);\n" +
406+
" int arrays = app.loopArrays(2);\n" +
407+
" int multi = app.multiArrayUsage(3);\n" +
408+
" int arraysFive = app.loopArrays(5);\n" +
409+
" int multiFive = app.multiArrayUsage(5);\n" +
410+
" int fieldCalls = app.useFieldsAndMethods(5);\n" +
411+
" report(first);\n" +
412+
" report(second);\n" +
413+
" report(switched);\n" +
414+
" report(synchronizedValue);\n" +
415+
" report(arrays);\n" +
416+
" report(multi);\n" +
417+
" report(arraysFive);\n" +
418+
" report(multiFive);\n" +
419+
" report(fieldCalls);\n" +
420+
" report(first + second + switched + synchronizedValue + arrays + multi + fieldCalls);\n" +
145421
" }\n" +
146422
"}\n";
147423
}

vm/tests/src/test/java/com/codename1/tools/translator/CleanTargetIntegrationTest.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,58 @@ static void writeRuntimeStubs(Path srcRoot) throws IOException {
219219
" free(obj);\n" +
220220
"}\n" +
221221
"\n" +
222-
"void initConstantPool() {}\n" +
222+
"JAVA_OBJECT* constantPoolObjects = NULL;\n" +
223+
"\n" +
224+
"void initConstantPool() {\n" +
225+
" if (constantPoolObjects == NULL) {\n" +
226+
" constantPoolObjects = calloc(32, sizeof(JAVA_OBJECT));\n" +
227+
" }\n" +
228+
"}\n" +
229+
"\n" +
230+
"void arrayFinalizerFunction(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT array) {\n" +
231+
" (void)threadStateData;\n" +
232+
" free(array);\n" +
233+
"}\n" +
234+
"\n" +
235+
"void gcMarkArrayObject(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj, JAVA_BOOLEAN force) {\n" +
236+
" (void)threadStateData;\n" +
237+
" (void)obj;\n" +
238+
" (void)force;\n" +
239+
"}\n" +
240+
"\n" +
241+
"void** initVtableForInterface() {\n" +
242+
" static void* table[1];\n" +
243+
" return (void**)table;\n" +
244+
"}\n" +
245+
"\n" +
246+
"struct clazz class_array1__JAVA_INT = {0};\n" +
247+
"struct clazz class_array2__JAVA_INT = {0};\n" +
248+
"\n" +
249+
"static JAVA_OBJECT allocArrayInternal(CODENAME_ONE_THREAD_STATE, int length, struct clazz* type, int primitiveSize, int dim) {\n" +
250+
" struct JavaArrayPrototype* arr = (struct JavaArrayPrototype*)calloc(1, sizeof(struct JavaArrayPrototype));\n" +
251+
" arr->__codenameOneParentClsReference = type;\n" +
252+
" arr->length = length;\n" +
253+
" arr->dimensions = dim;\n" +
254+
" arr->primitiveSize = primitiveSize;\n" +
255+
" if (length > 0) {\n" +
256+
" int elementSize = primitiveSize > 0 ? primitiveSize : sizeof(JAVA_OBJECT);\n" +
257+
" arr->data = calloc((size_t)length, (size_t)elementSize);\n" +
258+
" }\n" +
259+
" return (JAVA_OBJECT)arr;\n" +
260+
"}\n" +
261+
"\n" +
262+
"JAVA_OBJECT allocArray(CODENAME_ONE_THREAD_STATE, int length, struct clazz* type, int primitiveSize, int dim) {\n" +
263+
" return allocArrayInternal(threadStateData, length, type, primitiveSize, dim);\n" +
264+
"}\n" +
265+
"\n" +
266+
"JAVA_OBJECT alloc2DArray(CODENAME_ONE_THREAD_STATE, int length1, int length2, struct clazz* parentType, struct clazz* childType, int primitiveSize) {\n" +
267+
" struct JavaArrayPrototype* outer = (struct JavaArrayPrototype*)allocArrayInternal(threadStateData, length1, parentType, sizeof(JAVA_OBJECT), 2);\n" +
268+
" JAVA_OBJECT* rows = (JAVA_OBJECT*)outer->data;\n" +
269+
" for (int i = 0; i < length1; i++) {\n" +
270+
" rows[i] = allocArrayInternal(threadStateData, length2, childType, primitiveSize, 1);\n" +
271+
" }\n" +
272+
" return (JAVA_OBJECT)outer;\n" +
273+
"}\n" +
223274
"\n" +
224275
"void initMethodStack(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, int stackSize, int localsStackSize, int classNameId, int methodNameId) {\n" +
225276
" (void)__cn1ThisObject;\n" +

0 commit comments

Comments
 (0)