Make Function Types implement others to allow Subtyping, fixes #337
This commit is contained in:
parent
12bb613eb0
commit
7e6aeaf728
4
pom.xml
4
pom.xml
@ -54,8 +54,8 @@ http://maven.apache.org/maven-v4_0_0.xsd">
|
|||||||
<version>3.11.0</version>
|
<version>3.11.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
<compilerArgs>--enable-preview</compilerArgs>
|
||||||
<source>21</source>
|
<source>22</source>
|
||||||
<target>21</target>
|
<target>22</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
10
resources/bytecode/javFiles/Bug337.jav
Normal file
10
resources/bytecode/javFiles/Bug337.jav
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Number;
|
||||||
|
import java.lang.Object;
|
||||||
|
|
||||||
|
public class Bug337 {
|
||||||
|
public void main() {
|
||||||
|
Fun1$$<Object, Integer> fun1 = x -> x.hashCode() + 1;
|
||||||
|
Fun1$$<Number, Number> fun2 = fun1;
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,23 @@ public class FunNGenerator {
|
|||||||
public static class GenericParameters {
|
public static class GenericParameters {
|
||||||
int start;
|
int start;
|
||||||
public List<TargetType> parameters = new ArrayList<>();
|
public List<TargetType> parameters = new ArrayList<>();
|
||||||
|
final String descriptor;
|
||||||
|
final List<TargetType> inParams;
|
||||||
|
|
||||||
|
public GenericParameters(List<TargetType> params) {
|
||||||
|
this.inParams = params;
|
||||||
|
var type = new TargetRefType(FunNGenerator.getSuperClassName(params.size() - 1), params);
|
||||||
|
descriptor = applyDescriptor(type, this);
|
||||||
|
System.out.println(this.parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TargetType getReturnType() {
|
||||||
|
return FunNGenerator.getReturnType(inParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TargetType> getArguments() {
|
||||||
|
return FunNGenerator.getArguments(inParams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String applyDescriptor(TargetType type, GenericParameters gep) {
|
private static String applyDescriptor(TargetType type, GenericParameters gep) {
|
||||||
@ -98,12 +115,11 @@ public class FunNGenerator {
|
|||||||
return String.format("Fun%d$$", numberArguments);
|
return String.format("Fun%d$$", numberArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] generateSpecializedBytecode(List<TargetType> argumentTypes, TargetType returnType, GenericParameters gep) {
|
public static byte[] generateSpecializedBytecode(GenericParameters gep, List<String> superInterfaces) {
|
||||||
List<TargetType> parameters = Stream
|
var argumentTypes = gep.getArguments();
|
||||||
.concat(argumentTypes.stream(), Stream.of(returnType))
|
var returnType = gep.getReturnType();
|
||||||
.toList();
|
|
||||||
|
|
||||||
StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters), gep));
|
StringBuilder funNClassSignature = new StringBuilder(objectSignature + gep.descriptor);
|
||||||
boolean containsGeneric = false;
|
boolean containsGeneric = false;
|
||||||
|
|
||||||
String genericSignature = "<";
|
String genericSignature = "<";
|
||||||
@ -114,10 +130,18 @@ public class FunNGenerator {
|
|||||||
|
|
||||||
genericSignature += ">";
|
genericSignature += ">";
|
||||||
if (containsGeneric) funNClassSignature.insert(0, genericSignature);
|
if (containsGeneric) funNClassSignature.insert(0, genericSignature);
|
||||||
System.out.println(funNClassSignature.toString());
|
|
||||||
|
for (var superInterface : superInterfaces) {
|
||||||
|
funNClassSignature.append('L');
|
||||||
|
funNClassSignature.append(superInterface);
|
||||||
|
funNClassSignature.append(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
var interfaces = new ArrayList<>(superInterfaces);
|
||||||
|
interfaces.add(getSuperClassName(argumentTypes.size()));
|
||||||
|
|
||||||
ClassWriter classWriter = new ClassWriter(0);
|
ClassWriter classWriter = new ClassWriter(0);
|
||||||
classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSpecializedClassName(argumentTypes, returnType), funNClassSignature.toString(), objectSuperType, new String[]{getSuperClassName(argumentTypes.size())});
|
classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSpecializedClassName(argumentTypes, returnType), funNClassSignature.toString(), objectSuperType, interfaces.toArray(String[]::new));
|
||||||
classWriter.visitEnd();
|
classWriter.visitEnd();
|
||||||
return classWriter.toByteArray();
|
return classWriter.toByteArray();
|
||||||
}
|
}
|
||||||
@ -133,6 +157,10 @@ public class FunNGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getSpecializedClassName(GenericParameters gep) {
|
||||||
|
return getSpecializedClassName(getArguments(gep.inParams), getReturnType(gep.inParams));
|
||||||
|
}
|
||||||
|
|
||||||
public static String getSpecializedClassName(List<TargetType> argumentTypes, TargetType returnType) {
|
public static String getSpecializedClassName(List<TargetType> argumentTypes, TargetType returnType) {
|
||||||
return String.format("Fun%d$$%s%s",
|
return String.format("Fun%d$$%s%s",
|
||||||
argumentTypes.size(),
|
argumentTypes.size(),
|
||||||
|
@ -783,6 +783,7 @@ public class JavaTXCompiler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
generatedGenerics.put(sf, converter.javaGenerics());
|
generatedGenerics.put(sf, converter.javaGenerics());
|
||||||
|
converter.generateFunNTypes();
|
||||||
return generatedClasses;
|
return generatedClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,6 +479,55 @@ public class ASTToTargetAST {
|
|||||||
return TargetFunNType.fromParams(params, filteredParams);
|
return TargetFunNType.fromParams(params, filteredParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSubtype(TargetType test, TargetType other) {
|
||||||
|
var testClass = compiler.getClass(new JavaClassName(test.name()));
|
||||||
|
var otherClass = compiler.getClass(new JavaClassName(other.name()));
|
||||||
|
if (testClass == null) return false;
|
||||||
|
while (testClass != null) {
|
||||||
|
if (testClass.equals(otherClass)) return true;
|
||||||
|
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break;
|
||||||
|
testClass = compiler.getClass(testClass.getSuperClass().getName());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSupertype(TargetType test, TargetType other) {
|
||||||
|
return isSubtype(other, test);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSubtype(FunNGenerator.GenericParameters test, FunNGenerator.GenericParameters other) {
|
||||||
|
if (test.getArguments().size() != other.getArguments().size()) return false;
|
||||||
|
if (!isSubtype(test.getReturnType(), other.getReturnType())) return false;
|
||||||
|
for (int i = 0; i < test.getArguments().size(); i++) {
|
||||||
|
var arg1 = test.getArguments().get(i);
|
||||||
|
var arg2 = other.getArguments().get(i);
|
||||||
|
if (!isSupertype(arg1, arg2)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateFunNTypes() {
|
||||||
|
for (var entry : usedFunN.entrySet()) {
|
||||||
|
var gep = entry.getValue();
|
||||||
|
var superInterfaces = usedFunN.values().stream()
|
||||||
|
.filter(g -> !g.equals(gep))
|
||||||
|
.filter(genericParameters -> isSubtype(gep, genericParameters))
|
||||||
|
.map(FunNGenerator::getSpecializedClassName)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var code = FunNGenerator.generateSpecializedBytecode(gep, superInterfaces);
|
||||||
|
|
||||||
|
try {
|
||||||
|
classLoader.findClass(entry.getKey());
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
try {
|
||||||
|
classLoader.loadClass(code);
|
||||||
|
} catch (LinkageError ignored) {}
|
||||||
|
}
|
||||||
|
auxiliaries.put(entry.getKey(), code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input, GenerateGenerics generics) {
|
protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input, GenerateGenerics generics) {
|
||||||
return input.acceptTV(new TypeVisitor<>() {
|
return input.acceptTV(new TypeVisitor<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -513,17 +562,8 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
FunNGenerator.GenericParameters gep = null;
|
FunNGenerator.GenericParameters gep = null;
|
||||||
if (!usedFunN.containsKey(className)) {
|
if (!usedFunN.containsKey(className)) {
|
||||||
gep = new FunNGenerator.GenericParameters();
|
gep = new FunNGenerator.GenericParameters(params);
|
||||||
var code = FunNGenerator.generateSpecializedBytecode(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params), gep);
|
|
||||||
try {
|
|
||||||
classLoader.findClass(className);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
try {
|
|
||||||
classLoader.loadClass(code);
|
|
||||||
} catch (LinkageError ignored) {}
|
|
||||||
}
|
|
||||||
usedFunN.put(className, gep);
|
usedFunN.put(className, gep);
|
||||||
auxiliaries.put(className, code);
|
|
||||||
} else {
|
} else {
|
||||||
gep = usedFunN.get(className);
|
gep = usedFunN.get(className);
|
||||||
}
|
}
|
||||||
|
@ -1131,4 +1131,12 @@ public class TestComplete {
|
|||||||
var clazz = classFiles.get("Bug333");
|
var clazz = classFiles.get("Bug333");
|
||||||
var instance = clazz.getDeclaredConstructor().newInstance();
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBug337() throws Exception {
|
||||||
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug337.jav");
|
||||||
|
var clazz = classFiles.get("Bug337");
|
||||||
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
clazz.getDeclaredMethod("main").invoke(instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,8 @@ public class TestCodegen {
|
|||||||
}
|
}
|
||||||
}).collect(Collectors.toMap(Class::getName, Function.identity())));
|
}).collect(Collectors.toMap(Class::getName, Function.identity())));
|
||||||
|
|
||||||
|
converter.generateFunNTypes();
|
||||||
|
|
||||||
for (var entry : converter.auxiliaries.entrySet()) {
|
for (var entry : converter.auxiliaries.entrySet()) {
|
||||||
writeClassFile(entry.getKey(), entry.getValue());
|
writeClassFile(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
@ -104,6 +106,8 @@ public class TestCodegen {
|
|||||||
}
|
}
|
||||||
}).collect(Collectors.toMap(Class::getName, Function.identity()));
|
}).collect(Collectors.toMap(Class::getName, Function.identity()));
|
||||||
|
|
||||||
|
converter.generateFunNTypes();
|
||||||
|
|
||||||
for (var entry : converter.auxiliaries.entrySet()) {
|
for (var entry : converter.auxiliaries.entrySet()) {
|
||||||
writeClassFile(entry.getKey(), entry.getValue());
|
writeClassFile(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user