Skip to content

Commit 1201717

Browse files
authored
Fix interface default method overrides (#4402)
Fixed #4401
1 parent 0991d8a commit 1201717

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,20 @@ private boolean hasFinalizer() {
426426
}
427427
return false;
428428
}
429+
430+
private boolean isInterfaceInHierarchy(String className) {
431+
if (clsName.equals(className)) {
432+
return true;
433+
}
434+
if (baseInterfacesObject != null) {
435+
for (ByteCodeClass bc : baseInterfacesObject) {
436+
if (bc.isInterfaceInHierarchy(className)) {
437+
return true;
438+
}
439+
}
440+
}
441+
return false;
442+
}
429443

430444
public static void addArrayType(String type, int dimenstions) {
431445
String arr = dimenstions + "_" + type;
@@ -1614,7 +1628,7 @@ private void fillVirtualMethodTable(List<BytecodeMethod> virtualMethods, boolean
16141628
bm.setForceVirtual(true);
16151629
}
16161630
} else {
1617-
if(replace) {
1631+
if(replace || (isInterface && isInterfaceInHierarchy(virtualMethods.get(offset).getClsName()))) {
16181632
virtualMethods.set(offset, bm);
16191633
if(isInterface) {
16201634
bm.setForceVirtual(true);

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

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,40 @@ void translatesDefaultInterfaceMethodImplementations() throws Exception {
131131
"Implementing class should point vtable slot to the interface default method implementation");
132132
}
133133

134+
@Test
135+
void translatesDefaultInterfaceMethodOverridesWithSuperCalls() throws Exception {
136+
Parser.cleanup();
137+
138+
Path baseInterfaceFile = createBaseGreeterInterface();
139+
Path derivedInterfaceFile = createDerivedGreeterInterface();
140+
Path implFile = createDerivedGreeterImplementation();
141+
142+
Parser.parse(baseInterfaceFile.toFile());
143+
Parser.parse(derivedInterfaceFile.toFile());
144+
Parser.parse(implFile.toFile());
145+
146+
ByteCodeClass base = Parser.getClassObject("com_example_BaseGreeter");
147+
ByteCodeClass derived = Parser.getClassObject("com_example_DerivedGreeter");
148+
ByteCodeClass impl = Parser.getClassObject("com_example_DerivedGreeterImpl");
149+
150+
base.setBaseInterfacesObject(Collections.emptyList());
151+
derived.setBaseInterfacesObject(Collections.singletonList(base));
152+
impl.setBaseInterfacesObject(Collections.singletonList(derived));
153+
154+
base.updateAllDependencies();
155+
derived.updateAllDependencies();
156+
impl.updateAllDependencies();
157+
158+
List<ByteCodeClass> classes = Arrays.asList(base, derived, impl);
159+
String derivedCode = derived.generateCCode(classes);
160+
assertTrue(derivedCode.contains("com_example_BaseGreeter_greet___R_java_lang_String"),
161+
"Derived default method should invoke base interface default method");
162+
163+
String implCode = impl.generateCCode(classes);
164+
assertTrue(implCode.contains("&com_example_DerivedGreeter_greet___R_java_lang_String"),
165+
"Implementing class should point vtable slot to the derived interface default method implementation");
166+
}
167+
134168
private ByteCodeField findField(ByteCodeClass cls, String name) {
135169
return cls.getFields()
136170
.stream()
@@ -349,6 +383,74 @@ private Path createGreeterImplementation() throws Exception {
349383
});
350384
}
351385

386+
private Path createBaseGreeterInterface() throws Exception {
387+
return writeClass("com/example/BaseGreeter", cw -> {
388+
cw.visit(
389+
Opcodes.V1_8,
390+
Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
391+
"com/example/BaseGreeter",
392+
null,
393+
"java/lang/Object",
394+
null
395+
);
396+
397+
MethodVisitor greet = cw.visitMethod(Opcodes.ACC_PUBLIC, "greet", "()Ljava/lang/String;", null, null);
398+
greet.visitCode();
399+
greet.visitLdcInsn("base");
400+
greet.visitInsn(Opcodes.ARETURN);
401+
greet.visitMaxs(1, 1);
402+
greet.visitEnd();
403+
404+
cw.visitEnd();
405+
});
406+
}
407+
408+
private Path createDerivedGreeterInterface() throws Exception {
409+
return writeClass("com/example/DerivedGreeter", cw -> {
410+
cw.visit(
411+
Opcodes.V1_8,
412+
Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
413+
"com/example/DerivedGreeter",
414+
null,
415+
"java/lang/Object",
416+
new String[]{"com/example/BaseGreeter"}
417+
);
418+
419+
MethodVisitor greet = cw.visitMethod(Opcodes.ACC_PUBLIC, "greet", "()Ljava/lang/String;", null, null);
420+
greet.visitCode();
421+
greet.visitVarInsn(Opcodes.ALOAD, 0);
422+
greet.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/example/BaseGreeter", "greet", "()Ljava/lang/String;", true);
423+
greet.visitInsn(Opcodes.ARETURN);
424+
greet.visitMaxs(1, 1);
425+
greet.visitEnd();
426+
427+
cw.visitEnd();
428+
});
429+
}
430+
431+
private Path createDerivedGreeterImplementation() throws Exception {
432+
return writeClass("com/example/DerivedGreeterImpl", cw -> {
433+
cw.visit(
434+
Opcodes.V1_8,
435+
Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
436+
"com/example/DerivedGreeterImpl",
437+
null,
438+
"java/lang/Object",
439+
new String[]{"com/example/DerivedGreeter"}
440+
);
441+
442+
MethodVisitor init = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
443+
init.visitCode();
444+
init.visitVarInsn(Opcodes.ALOAD, 0);
445+
init.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
446+
init.visitInsn(Opcodes.RETURN);
447+
init.visitMaxs(1, 1);
448+
init.visitEnd();
449+
450+
cw.visitEnd();
451+
});
452+
}
453+
352454
private int enumFieldFlags() {
353455
return Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_ENUM;
354456
}

0 commit comments

Comments
 (0)