Skip to content

Commit 5eaa77c

Browse files
committed
Expand translator integration coverage
1 parent 82361e3 commit 5eaa77c

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

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

Lines changed: 208 additions & 0 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.*;
@@ -93,6 +111,196 @@ void translatesOptimizedBytecodeToLLVMExecutable() throws Exception {
93111
assertTrue(output.contains("RESULT=54"), "Compiled program should print the expected arithmetic result");
94112
}
95113

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"));
302+
}
303+
96304
private Path findGeneratedSource(Path srcRoot) throws Exception {
97305
try (Stream<Path> paths = Files.walk(srcRoot)) {
98306
return paths

0 commit comments

Comments
 (0)